merge develop
This commit is contained in:
@@ -3,13 +3,13 @@ orbs:
|
||||
node: circleci/node@3.0.0
|
||||
|
||||
executors:
|
||||
node:
|
||||
parameters:
|
||||
image:
|
||||
type: string
|
||||
default: "12.13"
|
||||
docker:
|
||||
- image: circleci/node:<< parameters.image >>
|
||||
node:
|
||||
parameters:
|
||||
image:
|
||||
type: string
|
||||
default: "14.18"
|
||||
docker:
|
||||
- image: circleci/node:<< parameters.image >>
|
||||
|
||||
aliases:
|
||||
install_node_modules: &install_node_modules
|
||||
@@ -18,7 +18,7 @@ aliases:
|
||||
command: yarn --frozen-lockfile
|
||||
attach_to_bootstrap: &attach_to_bootstrap
|
||||
attach_workspace:
|
||||
at: ./
|
||||
at: ./
|
||||
|
||||
jobs:
|
||||
bootstrap:
|
||||
|
||||
96
.eslintignore
Normal file
96
.eslintignore
Normal file
@@ -0,0 +1,96 @@
|
||||
# FILES TODO
|
||||
|
||||
/packages/medusa/src/services/cart.js
|
||||
/packages/medusa/src/services/claim-item.js
|
||||
/packages/medusa/src/services/event-bus.js
|
||||
/packages/medusa/src/services/fulfillment-provider.js
|
||||
/packages/medusa/src/services/idempotency-key.js
|
||||
/packages/medusa/src/services/inventory.js
|
||||
/packages/medusa/src/services/middleware.js
|
||||
/packages/medusa/src/services/note.js
|
||||
/packages/medusa/src/services/oauth.js
|
||||
/packages/medusa/src/services/payment-provider.js
|
||||
/packages/medusa/src/services/product-collection.js
|
||||
/packages/medusa/src/services/product-variant.js
|
||||
/packages/medusa/src/services/product.js
|
||||
/packages/medusa/src/services/query-builder.js
|
||||
/packages/medusa/src/services/return.js
|
||||
/packages/medusa/src/services/shipping-profile.js
|
||||
/packages/medusa/src/services/store.js
|
||||
/packages/medusa/src/services/swap.js
|
||||
/packages/medusa/src/services/system-payment-provider.js
|
||||
/packages/medusa/src/services/totals.js
|
||||
|
||||
/packages/medusa/src/subscribers/notification.js
|
||||
/packages/medusa/src/subscribers/order.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
|
||||
|
||||
# END OF FILES TODO
|
||||
|
||||
/packages/medusa/src/api
|
||||
/packages/medusa/src/models
|
||||
/packages/medusa/src/repositories
|
||||
/packages/medusa/src/commands
|
||||
/packages/medusa/src/helpers
|
||||
/packages/medusa/src/migrations
|
||||
/packages/medusa/src/utils
|
||||
|
||||
/integration-tests
|
||||
/docs
|
||||
/docs-util
|
||||
/scripts
|
||||
/www
|
||||
/packages/babel-preset-medusa-package
|
||||
/packages/create-medusa-app
|
||||
/packages/medusa-cli
|
||||
/packages/medusa-core-utils
|
||||
/packages/medusa-dev-cli
|
||||
/packages/medusa-file-s3
|
||||
/packages/medusa-file-spaces
|
||||
/packages/medusa-fulfillment-manual
|
||||
/packages/medusa-fulfillment-webshipper
|
||||
/packages/medusa-interfaces
|
||||
/packages/medusa-payment-adyen
|
||||
/packages/medusa-payment-klarna
|
||||
/packages/medusa-payment-manual
|
||||
/packages/medusa-payment-paypal
|
||||
/packages/medusa-payment-stripe
|
||||
/packages/medusa-plugin-add-ons
|
||||
/packages/medusa-plugin-brightpearl
|
||||
/packages/medusa-plugin-contentful
|
||||
/packages/medusa-plugin-discount-generator
|
||||
/packages/medusa-plugin-economic
|
||||
/packages/medusa-plugin-ip-lookup
|
||||
/packages/medusa-plugin-mailchimp
|
||||
/packages/medusa-plugin-meilisearch
|
||||
/packages/medusa-plugin-permissions
|
||||
/packages/medusa-plugin-restock-notification
|
||||
/packages/medusa-plugin-segment
|
||||
/packages/medusa-plugin-sendgrid
|
||||
/packages/medusa-plugin-slack-notification
|
||||
/packages/medusa-plugin-twilio-sms
|
||||
/packages/medusa-plugin-wishlist
|
||||
/packages/medusa-telemetry
|
||||
/packages/medusa-test-utils
|
||||
|
||||
packages/**/scripts
|
||||
packages/*/*.js
|
||||
**/dist/*
|
||||
|
||||
**/__mocks__/*
|
||||
**/__tests__/*
|
||||
|
||||
jest.config.js
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"plugins": ["prettier"],
|
||||
"extends": ["prettier"],
|
||||
"rules": {
|
||||
"prettier/prettier": "error",
|
||||
"semi": "error",
|
||||
"no-unused-expressions": "true"
|
||||
}
|
||||
}
|
||||
33
.eslintrc.js
Normal file
33
.eslintrc.js
Normal file
@@ -0,0 +1,33 @@
|
||||
module.exports = {
|
||||
parser: `@babel/eslint-parser`,
|
||||
parserOptions: {
|
||||
requireConfigFile: false,
|
||||
ecmaFeatures: {
|
||||
experimentalDecorators: true,
|
||||
},
|
||||
},
|
||||
plugins: [`eslint-plugin-prettier`],
|
||||
extends: [`eslint:recommended`, `google`, `eslint-config-prettier`],
|
||||
rules: {
|
||||
"prettier/prettier": `error`,
|
||||
curly: [2, `all`],
|
||||
"new-cap": `off`,
|
||||
"require-jsdoc": `off`,
|
||||
semi: `off`,
|
||||
"no-unused-expressions": `off`,
|
||||
camelcase: `off`,
|
||||
},
|
||||
env: {
|
||||
es6: true,
|
||||
node: true,
|
||||
jest: true,
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
files: [`*.ts`],
|
||||
parser: `@typescript-eslint/parser`,
|
||||
plugins: [`@typescript-eslint/eslint-plugin`],
|
||||
extends: [`plugin:@typescript-eslint/recommended`],
|
||||
},
|
||||
],
|
||||
}
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -3,3 +3,5 @@ node_modules
|
||||
.DS_Store
|
||||
lerna-debug.log
|
||||
|
||||
.eslintcache
|
||||
|
||||
|
||||
4
.husky/pre-commit
Executable file
4
.husky/pre-commit
Executable file
@@ -0,0 +1,4 @@
|
||||
#!/bin/sh
|
||||
. "$(dirname "$0")/_/husky.sh"
|
||||
|
||||
node_modules/.bin/lint-staged || node scripts/on-lint-error.js
|
||||
62
README.md
62
README.md
@@ -6,6 +6,17 @@
|
||||
<h1 align="center">
|
||||
Medusa
|
||||
</h1>
|
||||
|
||||
<h4 align="center">
|
||||
<a href="https://www.medusa-commerce.com">Website</a> |
|
||||
<a href="https://roadmap.medusa-commerce.com">Roadmap</a> |
|
||||
<a href="https://www.medusa-commerce.com/blog">Blog</a> |
|
||||
<a href="https://www.linkedin.com/company/medusa-commerce">LinkedIn</a> |
|
||||
<a href="https://twitter.com/medusajs">Twitter</a> |
|
||||
<a href="https://docs.medusa-commerce.com">Documentation</a> |
|
||||
<a href="https://medusajs.notion.site/medusajs/Medusa-Home-3485f8605d834a07949b17d1a9f7eafd">Notion</a>
|
||||
</h4>
|
||||
|
||||
<p align="center">
|
||||
Medusa is an open-source headless commerce engine that enables developers to create amazing digital commerce experiences.
|
||||
</p>
|
||||
@@ -30,26 +41,26 @@ Medusa is an open-source headless commerce engine that enables developers to cre
|
||||
## 🚀 Quickstart
|
||||
|
||||
1. **Install Medusa CLI**
|
||||
```bash
|
||||
npm install -g @medusajs/medusa-cli
|
||||
```
|
||||
```bash
|
||||
npm install -g @medusajs/medusa-cli
|
||||
```
|
||||
2. **Create a new Medusa project**
|
||||
```
|
||||
medusa new my-medusa-store --seed
|
||||
```
|
||||
```
|
||||
medusa new my-medusa-store --seed
|
||||
```
|
||||
3. **Start your Medusa engine**
|
||||
```bash
|
||||
medusa develop
|
||||
```
|
||||
|
||||
```bash
|
||||
medusa develop
|
||||
```
|
||||
4. **Use the API**
|
||||
```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.medusa-commerce.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.
|
||||
|
||||
## 🛒 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.
|
||||
|
||||
To provide a quick way to get you started with a storefront install one of our traditional e-commerce starters:
|
||||
@@ -67,7 +78,9 @@ To provide a quick way to get you started with a storefront install one of our t
|
||||
With your starter and your Medusa store running you can open http://localhost:8000 (for Gatsby) or http://localhost:3000 (for Nextjs) in your browser and view the products in your store, build a cart, add shipping details and pay and complete an order.
|
||||
|
||||
## ⭐️ Features
|
||||
|
||||
Medusa comes with a set of building blocks that allow you to create amazing digital commerce experiences, below is a list of some of the features that Medusa come with out of the box:
|
||||
|
||||
- **Headless**: Medusa is a highly customizable commerce API which means that you may use any presentation layer such as a website, app, chatbots, etc.
|
||||
- **Regions** allow you to specify currencies, payment providers, shipping providers, tax rates and more for one or more countries for truly international sales.
|
||||
- **Orders** come with all the functionality necessary to perform powerful customer service operations with ease.
|
||||
@@ -78,13 +91,14 @@ Medusa comes with a set of building blocks that allow you to create amazing digi
|
||||
- **Returns** allow customers to send back products and can be configured to function in a 100% automated flow through accounting and payment plugins.
|
||||
- **Fulfillment API** makes it easy to integrate with any fulfillment provider by creating fulfillment plugins, check the `/packages` directory for a full list of plugins.
|
||||
- **Payments API** makes it easy to integrate with any payment provider by creating payment plugins, we already support Stripe, Paypal and Klarna.
|
||||
- **Notification API** allow integrations with email providers, chatbots, Slack channels, etc.
|
||||
- **Customer Login** to give customers a way of managing their data, viewing their orders and saving payment details.
|
||||
- **Notification API** allow integrations with email providers, chatbots, Slack channels, etc.
|
||||
- **Customer Login** to give customers a way of managing their data, viewing their orders and saving payment details.
|
||||
- **Shipping Options & Profiles** enable powerful rules for free shipping limits, multiple fulfillment methods and more.
|
||||
- **Medusa's Plugin Architecture** makes it intuitive and easy to manage your integrations, switch providers and grow with ease.
|
||||
- **Customization** is supported for those special use cases that all the other e-commerce platforms can't accommodate.
|
||||
|
||||
## Database support
|
||||
|
||||
In production Medusa requires Postgres and Redis, but SQLite is supported for development and testing purposes. If you plan on using Medusa for a project it is recommended that you install Postgres and Redis on your dev machine.
|
||||
|
||||
- [Install PostgreSQL](https://www.postgresql.org/download/)
|
||||
@@ -105,21 +119,3 @@ The Medusa repository is a mono-repository managed using Lerna. Lerna allows us
|
||||
Licensed under the [MIT License](https://github.com/medusajs/medusa/blob/master/LICENSE)
|
||||
|
||||
## Thank you!
|
||||
|
||||
<p>
|
||||
<a href="https://www.medusa-commerce.com">
|
||||
Website
|
||||
</a>
|
||||
|
|
||||
<a href="https://medusajs.notion.site/medusajs/Medusa-Home-3485f8605d834a07949b17d1a9f7eafd">
|
||||
Notion Home
|
||||
</a>
|
||||
|
|
||||
<a href="https://twitter.com/intent/follow?screen_name=medusajs">
|
||||
Twitter
|
||||
</a>
|
||||
|
|
||||
<a href="https://docs.medusa-commerce.com">
|
||||
Docs
|
||||
</a>
|
||||
</p>
|
||||
|
||||
69
docs/content/how-to/deploying-admin-on-netlify.md
Normal file
69
docs/content/how-to/deploying-admin-on-netlify.md
Normal file
@@ -0,0 +1,69 @@
|
||||
---
|
||||
title: "Deploying Admin on Netlify"
|
||||
---
|
||||
|
||||
# Deploying Admin on Netlify
|
||||
|
||||
This is a guide for deploying Medusa Admin on Netlify. Netlify is a platform that offers hosting and backend services for applications and static websites.
|
||||
|
||||
> At this point, you should have a running instance of Medusa Admin. If not, check out [these steps](https://github.com/medusajs/admin#-setting-up-admin) or use `npx create-medusa-app` to set up your application in a matter of minutes. For the latter, see [this guide](https://docs.medusa-commerce.com/how-to/create-medusa-app) for a small walkthrough.
|
||||
|
||||
### 1. Install the Netlify CLI
|
||||
|
||||
Install Netlify CLI on your machine using npm:
|
||||
|
||||
```shell=
|
||||
npm install netlify-cli -g
|
||||
```
|
||||
|
||||
### 2. Login to your Netlify account
|
||||
|
||||
Connect to your Netlify account from your terminal:
|
||||
|
||||
```shell=
|
||||
netlify login
|
||||
```
|
||||
|
||||
Follow the instructions in your terminal.
|
||||
|
||||
### 3. Netlify setup
|
||||
|
||||
In order to deploy on Netlify, you need to create a new site, link the admin repository to the site and configure environment variables.
|
||||
|
||||
The Netlify CLI is used to achieve this.
|
||||
|
||||
#### Create a new site
|
||||
|
||||
```shell=
|
||||
netlify init
|
||||
```
|
||||
|
||||
Follow the instructions in your terminal to authorize and connect your Git repository.
|
||||
|
||||
The default build and deploy settings fit the needs of a Gatsby application, so leave these as is.
|
||||
|
||||
#### Add an environment variable
|
||||
|
||||
```shell=
|
||||
netlify env:set GATSBY_MEDUSA_BACKEND_URL "https://your-medusa-server.com"
|
||||
```
|
||||
|
||||
The above environment variable should point to your Medusa server.
|
||||
|
||||
### 4. Push and deploy
|
||||
|
||||
Finally to deploy the admin, commit and push your changes to the repository connected in step 3.
|
||||
|
||||
```shell=
|
||||
git add .
|
||||
git commit -m "Deploy Medusa Admin on Netlify"
|
||||
git push origin main
|
||||
```
|
||||
|
||||
Within a couple of minutes, your Medusa Admin is live and running on Netlify.
|
||||
|
||||
> If you experience CORS issues in your new setup, you might need to add your admin url as part of the ADMIN_CORS environment variable in your server setup.
|
||||
|
||||
### What's next?
|
||||
|
||||
If you haven't deployed your Medusa server to use with your new admin, check out our guide [Deploying on Heroku](https://docs.medusa-commerce.com/how-to/deploying-on-heroku).
|
||||
200
docs/content/how-to/deploying-on-heroku.md
Normal file
200
docs/content/how-to/deploying-on-heroku.md
Normal file
@@ -0,0 +1,200 @@
|
||||
---
|
||||
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.
|
||||
|
||||
> We assume, that you are currently running a local instance of Medusa. If not, check out our [Quickstart](https://docs.medusa-commerce.com/quickstart/quick-start) or use `npx create-medusa-app` to set up your application in a matter of minutes. For the latter, see [this guide](https://docs.medusa-commerce.com/how-to/create-medusa-app) for a small walkthrough.
|
||||
|
||||
### 1. Install the Heroku CLI
|
||||
|
||||
Install Heroku on your machine:
|
||||
|
||||
**Ubuntu**
|
||||
|
||||
```shell=
|
||||
sudo snap install --classic heroku
|
||||
```
|
||||
|
||||
**MacOS**
|
||||
|
||||
```shell=
|
||||
brew tap heroku/brew && brew install heroku
|
||||
```
|
||||
|
||||
**Windows**
|
||||
|
||||
Download the appropriate installer for your Windows installation:
|
||||
|
||||
[64-bit installer](https://cli-assets.heroku.com/heroku-x64.exe)
|
||||
[32-bit installer](https://cli-assets.heroku.com/heroku-x86.exe)
|
||||
|
||||
### 2. Login to Heroku from your CLI
|
||||
|
||||
Connect to your Heroku account from your terminal:
|
||||
|
||||
```shell=
|
||||
heroku login
|
||||
```
|
||||
|
||||
> 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.
|
||||
|
||||
```shell=
|
||||
heroku create medusa-test-app
|
||||
heroku git:remote -a medusa-test-app
|
||||
```
|
||||
|
||||
### 4. Install Postgresql and Redis on Heroku
|
||||
|
||||
Medusa requires a Postgres database and a Redis instance to work. These are added through the Heroku CLI using the following commands.
|
||||
|
||||
> In this below example, 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
|
||||
|
||||
```shell=
|
||||
heroku addons:create heroku-postgresql:hobby-dev
|
||||
```
|
||||
|
||||
You can find more informations, plans and pricing about Heroku Postgres [here](https://elements.heroku.com/addons/heroku-postgresql).
|
||||
|
||||
#### Redis To Go
|
||||
|
||||
Add a Redis instance to your Heroku app
|
||||
|
||||
> The addon `redistogo:nano` is free, but Heroku requires you to add a payment method to proceed.
|
||||
|
||||
```shell=
|
||||
heroku addons:create redistogo:nano
|
||||
```
|
||||
|
||||
You can find more informations, plans and pricing about Redis To Go [here](https://elements.heroku.com/addons/redistogo).
|
||||
|
||||
### 5. Configure environment variables on Heroku
|
||||
|
||||
Medusa requires a set of environment variables. From you project repository run the following commands:.
|
||||
|
||||
```shell=
|
||||
heroku config:set NODE_ENV=production
|
||||
heroku config:set JWT_SECRET=your-super-secret
|
||||
heroku config:set COOKIE_SECRET=your-super-secret-pt2
|
||||
heroku config:set NPM_CONFIG_PRODUCTION=false
|
||||
```
|
||||
|
||||
> Make sure to use actual secrets in a production environment.
|
||||
> Additionally, we need to set the buildpack to Node.js
|
||||
|
||||
```shell=
|
||||
heroku buildpacks:set heroku/nodejs
|
||||
```
|
||||
|
||||
#### Configure the Redis URL
|
||||
|
||||
The library we use for connecting to Redis, does not allow usernames in the connection string. Therefore, we need to perform the following commands to remove it.
|
||||
Get the current Redis URL:
|
||||
|
||||
```shell=
|
||||
heroku config:get REDISTOGO_URL
|
||||
```
|
||||
|
||||
You should get something like:
|
||||
|
||||
```shell=
|
||||
redis://redistogo:some_password_123@some.redistogo.com:9660/
|
||||
```
|
||||
|
||||
Remove the username from the Redis URL:
|
||||
|
||||
```shell=
|
||||
redis://r̶e̶d̶i̶s̶t̶o̶g̶o̶:some_password_123@sole.redistogo.com:9660/
|
||||
```
|
||||
|
||||
Set the new environment variable `REDIS_URL`
|
||||
|
||||
```shell=
|
||||
heroku config:set REDIS_URL=redis://:some_password_123@sole.redistogo.com:9660/
|
||||
```
|
||||
|
||||
### 6. Configure Medusa
|
||||
|
||||
Before jumping into the deployment, we need to configure Medusa.
|
||||
|
||||
#### `medusa-config.js`
|
||||
|
||||
Update `module.exports` to include the following:
|
||||
|
||||
```javascript=
|
||||
module.exports = {
|
||||
projectConfig: {
|
||||
redis_url: REDIS_URL,
|
||||
database_url: DATABASE_URL,
|
||||
database_type: "postgres",
|
||||
store_cors: STORE_CORS,
|
||||
admin_cors: ADMIN_CORS,
|
||||
database_extra:
|
||||
process.env.NODE_ENV !== "development"
|
||||
? { ssl: { rejectUnauthorized: false } }
|
||||
: {},
|
||||
},
|
||||
plugins,
|
||||
};
|
||||
```
|
||||
|
||||
#### `package.json`
|
||||
|
||||
Update `scripts` to include the following:
|
||||
|
||||
```json=
|
||||
...
|
||||
"scripts": {
|
||||
"serve": "medusa start",
|
||||
"start": "medusa develop",
|
||||
"heroku-postbuild": "medusa migrations run",
|
||||
"prepare": "npm run build",
|
||||
"build": "babel src -d dist --extensions \".ts,.js\""
|
||||
},
|
||||
...
|
||||
```
|
||||
|
||||
### 6. Launch you Medusa app
|
||||
|
||||
Finally, we need to commit and push our changes to Heroku:
|
||||
|
||||
```shell=
|
||||
git add .
|
||||
git commit -m "Deploy Medusa App on Heroku"
|
||||
git push heroku HEAD:master
|
||||
```
|
||||
|
||||
### 7. Inspect your build logs
|
||||
|
||||
You can explore your Heroku app build logs using the following command in your project directory.
|
||||
|
||||
```shell=
|
||||
heroku logs -n 500000 --remote heroku --tail
|
||||
```
|
||||
|
||||
### 8. Create a user (optional)
|
||||
|
||||
As an optional extra step, we can create a user for you to use when your admin system is up and running.
|
||||
|
||||
```shell=
|
||||
heroku run -a medusa-test-app -- medusa user -e "some-user@test.com" -p "SuperSecret1234"
|
||||
```
|
||||
|
||||
### What's next?
|
||||
|
||||
You now have a production ready application running on Heroku. This can be scaled and configured to fit your business needs.
|
||||
|
||||
Furthermore, you can deploy a Medusa Admin for your application, such that you can start managing your store from an interface.
|
||||
|
||||
- [Deploy Admin on Netlify](https://docs.medusa-commerce.com/how-to/deploying-admin-on-netlify)
|
||||
- Deploy Admin on Gatsby Cloud (Coming soon)
|
||||
122
docs/content/how-to/deploying-on-qovery.md
Normal file
122
docs/content/how-to/deploying-on-qovery.md
Normal file
@@ -0,0 +1,122 @@
|
||||
# Deploying on Qovery
|
||||
|
||||
This is a guide for deploying a Medusa project to Qovery. Qovery is a Continuous Deployment Platform, that provides you with the developer experience of Heroku on top of your cloud provider (e.g. AWS, DigitalOcean).
|
||||
|
||||
> We assume, that you are currently running a local instance of Medusa. If not, check out our [Quickstart](https://docs.medusa-commerce.com/quickstart/quick-start) or use `npx create-medusa-app` to set up your application in a matter of minutes. For the latter, see [this guide](https://docs.medusa-commerce.com/how-to/create-medusa-app) for a small walkthrough.
|
||||
|
||||
### 1. Qovery Console
|
||||
|
||||
Create an account on [Qovery](https://www.qovery.com/) on their free community plan and jump into the console.
|
||||
|
||||
### 2. Setup
|
||||
|
||||
Create a project and an environment.
|
||||
|
||||
### 3. Add your Medusa app
|
||||
|
||||
Add a new app to your Qovery environment and connect the Git repository that holds your Medusa project. In your application settings, set the port to 9000 unless something else is specified in your setup.
|
||||
|
||||
> If you used our `npx` starter, your repository will most likely hold all components; storefront, admin and backend. Ensure that **Root application path** in Qovery is pointing to your Medusa project (`/backend`).
|
||||
|
||||
### 4. Add a database
|
||||
|
||||
Navigate to your environment overview and add the databases required by Medusa.
|
||||
|
||||
- Add Postgres database version 10, 11 or 12
|
||||
- Add Redis database version 5 or 6
|
||||
|
||||
### 5. Configure Medusa
|
||||
|
||||
Our Medusa project needs a bit of configuration to fit the needs of Qovery.
|
||||
|
||||
#### Update `medusa-config.js`
|
||||
|
||||
First, add the Postgres and Redis database url to your `medusa-config.js`. In Qovery, click on your Medusa app in the environment overview. Navigate to environment variables in the sidebar on the left. Among the secret variables you should find your database urls. They should look something like this:
|
||||
|
||||
```javascript=
|
||||
QOVERY_REDIS_123456789_DATABASE_URL
|
||||
QOVERY_POSTGRESQL_123456789_DATABASE_URL
|
||||
```
|
||||
|
||||
Add these to your `medusa-config.js`.
|
||||
|
||||
```javascript=
|
||||
const DATABASE_URL = process.env.QOVERY_POSTGRESQL_123456789_DATABASE_URL
|
||||
const REDIS_URL= process.env.QOVERY_REDIS_123456789_DATABASE_URL
|
||||
```
|
||||
|
||||
Furthermore, update `module.exports` to include the following:
|
||||
|
||||
```javascript=
|
||||
module.exports = {
|
||||
projectConfig: {
|
||||
redis_url: REDIS_URL,
|
||||
database_url: DATABASE_URL,
|
||||
database_type: "postgres",
|
||||
store_cors: STORE_CORS,
|
||||
admin_cors: ADMIN_CORS,
|
||||
database_extra: { }
|
||||
},
|
||||
plugins,
|
||||
};
|
||||
```
|
||||
|
||||
> **IMPORTANT**: We are using the Qovery community plan, that does not allow SSL connections for the database, so this is disabled.
|
||||
>
|
||||
> In a production environment, you would need the following in the config:
|
||||
> `database_extra: { ssl: { rejectUnauthorized: false } }`
|
||||
|
||||
#### Add some extra variables
|
||||
|
||||
We need to add a couple of more environment variables in Qovery. Add the following variables in your Console with an application scope:
|
||||
|
||||
```javascript=
|
||||
JTW_SECRET=something_secret_jwt
|
||||
COOKIE_SECRET=something_secret_cookie
|
||||
```
|
||||
|
||||
> Make sure to use actual secrets in a production environment.
|
||||
|
||||
#### Update `package.json`
|
||||
|
||||
Update `scripts` to the following:
|
||||
|
||||
```json=
|
||||
"scripts": {
|
||||
"serve": "medusa start",
|
||||
"start": "medusa migrations run && medusa start",
|
||||
"prepare": "npm run build",
|
||||
"build": "babel src -d dist --extensions \".ts,.js\""
|
||||
},
|
||||
```
|
||||
|
||||
### 6. Deploy Medusa
|
||||
|
||||
Finally, deploy your Redis and Postgres followed by your Medusa application.
|
||||
|
||||
#### Deploy databases
|
||||
|
||||
In your environment overview in Qovery, deploy your databases one after the other. Only when these are deployed, proceed to next step.
|
||||
|
||||
#### Push changes to your repository
|
||||
|
||||
To initialise your first build Qovery, simply commit and push your changes.
|
||||
|
||||
```shell=
|
||||
git add .
|
||||
git commit -m "chore: Qovery setup"
|
||||
git push origin main
|
||||
```
|
||||
|
||||
### 6. Try it out!
|
||||
|
||||
In Qovery, click on your Medusa app in the environment overview. In the top right you are able to open up your application. Navigate to `/health` to ensure, that the app is running.
|
||||
|
||||
### What's next?
|
||||
|
||||
You now have an application running on Qovery. This can be scaled and configured to fit your business needs. As mentioned, we used the community plan, so this should be upgraded when moving to production.
|
||||
|
||||
Furthermore, you can deploy Medusa Admin for your application, such that you can start managing your store from an interface.
|
||||
|
||||
- [Deploy Admin on Netlify](https://docs.medusa-commerce.com/how-to/deploying-admin-on-netlify)
|
||||
- Deploy Admin on Gatsby Cloud (Coming soon)
|
||||
@@ -0,0 +1,310 @@
|
||||
---
|
||||
title: Making your store more powerful with Contentful
|
||||
---
|
||||
|
||||
# Making your store more powerful with Contentful
|
||||
|
||||
In [part 1](https://docs.medusa-commerce.com/how-to/headless-ecommerce-store-with-gatsby-contentful-medusa/) of this series you have set up [Medusa](https://medusa-commerce.com) with Contentful as your CMS system and added a Gatsby storefront. In this part you will get a further introduction to Contentful and learn how [`medusa-plugin-contentful`](https://github.com/medusajs/medusa/tree/master/packages/medusa-plugin-contentful) can be leveraged to make your store more powerful. Apart from a front page, product pages and a checkout flow, most ecommerce stores also need miscalleneous pages like About and Contact pages. In this guide you will add a Rich Text content module to your Contentful space so that you can make this pages cool. You will also see how the content modules can be used to give your product pages more life.
|
||||
|
||||
What you will do in this guide:
|
||||
|
||||
- Add a rich text content module
|
||||
- Add rich text to your `/about` page
|
||||
- Add a "Related Products" section to your product page
|
||||
|
||||
Topics covered:
|
||||
|
||||
- Contentful Migrations
|
||||
- Product enrichment
|
||||
|
||||
## Creating a rich text content module
|
||||
|
||||
In this guide you will make use of [Contentful Migrations](https://github.com/contentful/contentful-migration) to keep a versioned controlled record of how your Content evolves over time. The Contentful app allows you to create content models straight from their dashboard, however, when using the migrations tool you will be able to 1) quickly replicate your Contentful space and 2) incorporate migrations as part of a CI/CD pipeline. [You can read more about how to use CMS as Code here](https://www.contentful.com/help/cms-as-code/).
|
||||
|
||||
To prepare your migration create a new file at `contentful-migrations/rich-text.js` and add the following code:
|
||||
|
||||
```javascript
|
||||
// contentful-migrations/rich-text.js
|
||||
|
||||
module.exports = function (migration, context) {
|
||||
const richText = migration
|
||||
.createContentType("richText")
|
||||
.name("Rich Text")
|
||||
.displayField("title")
|
||||
|
||||
richText.createField("title").name("Title (Internal)").type("Symbol")
|
||||
richText.createField("body").name("Body").type("RichText")
|
||||
}
|
||||
```
|
||||
|
||||
This small snippet will create a content model in your Contentful space with two fields: a title which will be used to name entries in a meaningful manner (i.e. it won't be displayed to customers) and a body which contains the rich text to display. To apply your migration run:
|
||||
|
||||
```shell
|
||||
yarn migrate:contentful --file contentful-migrations/rich-text.js
|
||||
```
|
||||
|
||||
If you go to your Contentful space and click Content Model you will see that the Rich Text model has been added to your space:
|
||||

|
||||
|
||||
The validation rules in the Page model only allow Hero and Tile Sections to be added to the Content Modules fields so you will need another migration to make it possible for pages to make use of the new Rich Text modules. Create a new migration at `contentful-migrations/update-page-module-validation.js` and add the following:
|
||||
|
||||
```javascript
|
||||
// contentful-migrations/update-page-module-validation.js
|
||||
|
||||
module.exports = function (migration, context) {
|
||||
const page = migration.editContentType("page")
|
||||
|
||||
page.editField("contentModules").items({
|
||||
type: "Link",
|
||||
linkType: "Entry",
|
||||
validations: [
|
||||
{
|
||||
linkContentType: ["hero", "tileSection", "richText"],
|
||||
},
|
||||
],
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
After migrating your space you are ready create your new contact page:
|
||||
|
||||
```shell
|
||||
yarn migrate:contentful --file contentful-migrations/update-page-module-validation.js
|
||||
```
|
||||
|
||||
## Adding Rich Text to About
|
||||
|
||||
To use your new Rich Text module **Content > Page > About**, and click **Add Content > Page**. You will now make use of the new Rich Text module to add some more details about your store. You can write your own text or use the text provided below if you just want to copy/paste.
|
||||
|
||||
> ### About Medusa
|
||||
>
|
||||
> Medusa is an open-source headless commerce engine for fast-growing businesses. Getting started with Medusa is very easy and you will be able to start selling online with a basic setup in no time, however, the real power of Medusa starts showing up when you add custom functionality and extend your core to fit your needs.
|
||||
>
|
||||
> The core Medusa package and all the official Medusa plugins ship as individual NPM packages that you install into a Node project. You store and plugins are configured in your medusa-config.js file making it very easy to manage your store as your business grows. Custom functionality doesn't have to come from plugins, you can also add project-level functionality by simply adding files in your `src/` folder. Medusa will automatically register your custom functionalities in the bootstrap phase.
|
||||
|
||||

|
||||
|
||||
When you have added your text you can click **Publish changes** (make sure the About page is published too).
|
||||
|
||||
## Updating the storefront to support the Rich Text module
|
||||
|
||||
> If you want to jump straight to the final frontend code visit [medusajs/medusa-contentful-storefront@part-2](https://github.com/medusajs/medusa-contentful-storefront/tree/part-2).
|
||||
|
||||
To display your newly created Rich Text module open up the storefront code and create a new file at `src/components/rich-text/rich-text.jsx`.
|
||||
|
||||
```jsx
|
||||
// src/components/rich-text/rich-text.jsx
|
||||
|
||||
import React from "react"
|
||||
import { renderRichText } from "gatsby-source-contentful/rich-text"
|
||||
|
||||
import * as styles from "../../styles/rich-text.module.css"
|
||||
|
||||
const RichText = ({ data }) => {
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<div className={styles.content}>
|
||||
{data.body ? renderRichText(data.body) : ""}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default RichText
|
||||
```
|
||||
|
||||
The `renderRichText` function is imported from the `gatsby-source-contentful` plugin to easily transform the text you entered in the Rich Text module to html. To make the Rich Text component render nicely add a style file as well at `src/styles/rich-text.module.css`.
|
||||
|
||||
```css
|
||||
/* src/styles/rich-text.module.css */
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
padding-top: 100px;
|
||||
padding-bottom: 100px;
|
||||
}
|
||||
|
||||
.content {
|
||||
margin: auto;
|
||||
max-width: 870px;
|
||||
}
|
||||
```
|
||||
|
||||
If you restart your storefront server now you will not be able to see your new Rich Text module just yet. The last step to making that happen will be to let the Page component know to render the new Rich Text component when it encounters Rich Text in the Page's Content Modules. In your editor open up the file `src/pages/{ContentfulPage.slug}.js` and add the following:
|
||||
|
||||
At the top of the file import your `RichText` component:
|
||||
|
||||
```javascript
|
||||
...
|
||||
import RichText from "../components/rich-text/rich-text"
|
||||
...
|
||||
```
|
||||
|
||||
Now in the `contentModules.map` function return the `RichText` component whenever a `ContentfulRichText` module is encountered. Add a case to the switch statement:
|
||||
|
||||
```javascript
|
||||
case "ContentfulRichText":
|
||||
return <RichText key={cm.id} data={cm} />
|
||||
```
|
||||
|
||||
Finally you will need to fetch the Rich Text data from Gatsby's data layer by modifying the GraphQL code at the bottom of the file after the line with `contentModules {` add:
|
||||
|
||||
```graphql
|
||||
... on ContentfulRichText {
|
||||
id
|
||||
body {
|
||||
raw
|
||||
}
|
||||
internal {
|
||||
type
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Restart your local Gatsby server and visit `http://localhost:8000/about`, you will now see the your newly added Rich Text module.
|
||||
|
||||

|
||||
|
||||
## Enriching your Product pages
|
||||
|
||||
You have now seen how the Page model in Contentful can be extended to include a new content module in a reusable and modular manner. The same idea can be extended to your Product pages allowing you to create completely bespoke universes around your products. You will use the same techniques as above to create a Related Products section below the "Medusa Shirt" product.
|
||||
|
||||
### Migrating Products
|
||||
|
||||
First, add a new field to the Product content model. Using migrations you can create a file `contentful-migrations/product-add-modules.js`:
|
||||
|
||||
```javascript
|
||||
// contentful-migrations/product-add-modules.js
|
||||
|
||||
module.exports = function (migration, context) {
|
||||
const product = migration.editContentType("product")
|
||||
|
||||
product
|
||||
.createField("contentModules")
|
||||
.name("Content Modules")
|
||||
.type("Array")
|
||||
.items({
|
||||
type: "Link",
|
||||
linkType: "Entry",
|
||||
validations: [
|
||||
{
|
||||
linkContentType: ["hero", "tileSection", "richText"],
|
||||
},
|
||||
],
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
Run the migration:
|
||||
|
||||
```
|
||||
yarn migrate:contentful --file contentful-migrations/product-add-modules.js
|
||||
```
|
||||
|
||||
### Adding "Related Products" Tile Section
|
||||
|
||||
After the migration you can now add Content Modules to Products, to enrich the Product pages with relevant content. In this guide you will add a Tile Section that holds "Related Products", but the functionality could be further extended to showcase look book images, inspirational content or more detailed product descriptions.
|
||||
|
||||
In Contentful go to **Content > Product > Medusa Shirt** scroll all the way to the bottom, where you should be able to find the new _Content Modules_ field:
|
||||
|
||||

|
||||
|
||||
Click **Add content > Tile Section** which will open a new Tile Section. For the Title write "Related Products", and for Tiles click **Add content > Add existing content > Medusa Waterbottle > Insert 1 entry**.
|
||||
|
||||

|
||||
|
||||
Click **Publish** and make sure that the Medusa Shirt product is published too.
|
||||
|
||||
Your data is now ready to be used in the storefront, but you still need to make a couple of changes to the storefront code to be able to view the new content.
|
||||
|
||||
## Adding Content Modules to Product pages
|
||||
|
||||
Just like you did for the Page component, you will have to fetch the Content Modules from Gatsby's GraphQL data layer.
|
||||
|
||||
In the file `src/pages/products/{ContentfulProduct.handle}.js` add the following in the GraphQL query at the bottom of the file (e.g. after the variants query):
|
||||
|
||||
```graphql
|
||||
# src/pages/products/{ContentfulProduct.handle}.js
|
||||
|
||||
contentModules {
|
||||
... on ContentfulTileSection {
|
||||
id
|
||||
title
|
||||
tiles {
|
||||
... on ContentfulProduct {
|
||||
id
|
||||
title
|
||||
handle
|
||||
thumbnail {
|
||||
gatsbyImageData
|
||||
}
|
||||
internal {
|
||||
type
|
||||
}
|
||||
}
|
||||
... on ContentfulTile {
|
||||
id
|
||||
title
|
||||
cta
|
||||
image {
|
||||
gatsbyImageData
|
||||
}
|
||||
link {
|
||||
linkTo
|
||||
reference {
|
||||
slug
|
||||
}
|
||||
}
|
||||
internal {
|
||||
type
|
||||
}
|
||||
}
|
||||
}
|
||||
internal {
|
||||
type
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This snippet will query the Content Modules defined for the product and will allow you to use the data in your components.
|
||||
|
||||
Next open up the `src/views/products.jsx` file and add the following snippets.
|
||||
|
||||
Import the `TileSection` component:
|
||||
|
||||
```javascript
|
||||
import TileSection from "../components/tile-section/tile-section"
|
||||
```
|
||||
|
||||
Add the Content Modules in the JSX just before the final closing `div`:
|
||||
|
||||
```jsx
|
||||
// src/views/products.jsx
|
||||
|
||||
<div className={styles.contentModules}>
|
||||
{product.contentModules?.map((cm) => {
|
||||
switch (cm.internal.type) {
|
||||
case "ContentfulTileSection":
|
||||
return <TileSection key={cm.id} data={cm} />
|
||||
default:
|
||||
return null
|
||||
}
|
||||
})}
|
||||
</div>
|
||||
```
|
||||
|
||||
Restart the Gatsby server and visit http://localhost:8000/product/medusa-shirt you should now see the new "Related Products" Tile Section below the Product page controls.
|
||||
|
||||

|
||||
|
||||
## Summary
|
||||
|
||||
In this guide you created a new content model for Rich Text input in Contentful using [contentful-migration](https://github.com/contentful/contentful-migration). You further extended the storefront to render the new Rich Text plugin. The concepts in this guide are meant to demonstrate how Contentful can be used to make your store more powerful in a modular and scalable way. The content modules covered in this guide could be further extended to add other custom modules, for example, you could add a Newsletter Signup, module that when encountered in the code renders a newsletter form.
|
||||
|
||||
## What's next
|
||||
|
||||
In the next part of this guide you will learn how to implement further commerce functionalities to your site such as adding support for discount codes, region based shopping and more. (Coming soon)
|
||||
|
||||
- [Deploying Medusa on Heroku](https://docs.medusa-commerce.com/how-to/deploying-on-heroku)
|
||||
- [Deploying Medusa Admin on Netlify](https://docs.medusa-commerce.com/how-to/deploying-admin-on-netlify)
|
||||
85
docs/content/how-to/uploading-images-to-s3.md
Normal file
85
docs/content/how-to/uploading-images-to-s3.md
Normal file
@@ -0,0 +1,85 @@
|
||||
# Uploading images to S3
|
||||
|
||||
In order to work with images in Medusa, you need a file service plugin responsible for hosting. Following this guide will allow you to upload images to AWS S3.
|
||||
|
||||
### Before you start
|
||||
|
||||
At this point, you should have an instance of our store engine running. If not, we have a [full guide](https://docs.medusa-commerce.com/tutorial/set-up-your-development-environment) for setting up your local environment.
|
||||
|
||||
### Set up up AWS
|
||||
|
||||
#### Create an S3 bucket
|
||||
|
||||
In the AWS console navigate to S3 and create a bucket for your images. Make sure to uncheck "Block _all_ public access".
|
||||
|
||||
Additionally, you need to add a policy to your bucket, that will allow public access to objects that are uploaded. Navigate to the permissions tab of your bucket and add the following policy:
|
||||
|
||||
```shell=
|
||||
{
|
||||
"Id": "Policy1397632521960",
|
||||
"Statement": [
|
||||
{
|
||||
"Sid": "Stmt1397633323327",
|
||||
"Action": [
|
||||
"s3:GetObject"
|
||||
],
|
||||
"Effect": "Allow",
|
||||
"Resource": "arn:aws:s3:::YOUR-BUCKET-NAME/*",
|
||||
"Principal": {
|
||||
"AWS": [
|
||||
"*"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Be aware, that this will allow for anyone to acces your bucket. Avoid storing sensitive data.
|
||||
|
||||
#### Generate access keys
|
||||
|
||||
Navigate to the IAM section of your AWS console and perform the following steps:
|
||||
|
||||
- Add a new user with programmatic access
|
||||
- Add the existing **AmazonS3FullAccess** policy to the user
|
||||
- Submit the details
|
||||
|
||||
Upon successfull creation of the user, you are presented with an **Access key ID** and a **Secret access key**. Note both of them down for later use.
|
||||
|
||||
### Installation
|
||||
|
||||
First, install the plugin using your preferred package manager:
|
||||
|
||||
```
|
||||
yarn add medusa-file-s3
|
||||
```
|
||||
|
||||
Then configure your `medusa-config.js` to include the plugin alongside the required options:
|
||||
|
||||
```=javascript
|
||||
{
|
||||
resolve: `medusa-file-s3`,
|
||||
options: {
|
||||
s3_url: "https://s3-guide-test.s3.eu-west-1.amazonaws.com",
|
||||
bucket: "test",
|
||||
region: "eu-west-1"
|
||||
access_key_id: "YOUR-ACCESS-KEY",
|
||||
secret_access_key: "YOUR-SECRET-KEY",
|
||||
},
|
||||
},
|
||||
```
|
||||
|
||||
In the above options, an `s3_url` is included. The url has the following format:
|
||||
|
||||
```shell=
|
||||
https://[bucket].s3.[region].amazonaws.com
|
||||
```
|
||||
|
||||
The two access keys in the options are the ones created in the previous section.
|
||||
|
||||
> Make sure to use an environment variable for the secret key in a live environment.
|
||||
|
||||
### Try it out
|
||||
|
||||
Finally, run your Medusa server alongside our admin system to try out your new file service. Upon editing or creating products, you can now upload thumbnails and images, that are stored in an AWS S3 bucket.
|
||||
48
docs/content/how-to/uploading-images-to-spaces.md
Normal file
48
docs/content/how-to/uploading-images-to-spaces.md
Normal file
@@ -0,0 +1,48 @@
|
||||
# Uploading images to Spaces
|
||||
|
||||
In order to work with images in Medusa, you need a file service plugin responsible for hosting. Following this guide will allow you to upload images to DigitalOcean Spaces.
|
||||
|
||||
### Before you start
|
||||
|
||||
At this point, you should have an instance of our store engine running. If not, we have a [full guide](https://docs.medusa-commerce.com/tutorial/set-up-your-development-environment) for setting up your local environment.
|
||||
|
||||
### Set up up DigitalOcean
|
||||
|
||||
#### Create a Space
|
||||
|
||||
Create an account on DigitalOcean and navigate to Spaces. Create a new Space with the default settings.
|
||||
|
||||
#### Generate access keys
|
||||
|
||||
Navigate to API in the left sidebar. Generate a new Spaces access key. This should provide you with an access key id and a secret key. Note them both down.
|
||||
|
||||
### Installation
|
||||
|
||||
First, install the plugin using your preferred package manager:
|
||||
|
||||
```
|
||||
yarn add medusa-file-spaces
|
||||
```
|
||||
|
||||
Then configure your `medusa-config.js` to include the plugin alongside the required options:
|
||||
|
||||
```=javascript
|
||||
{
|
||||
resolve: `medusa-file-spaces`,
|
||||
options: {
|
||||
spaces_url: "https://test.fra1.digitaloceanspaces.com",
|
||||
bucket: "test",
|
||||
endpoint: "fra1.digitaloceanspaces.com",
|
||||
access_key_id: "YOUR-ACCESS-KEY",
|
||||
secret_access_key: "YOUR-SECRET-KEY",
|
||||
},
|
||||
},
|
||||
```
|
||||
|
||||
In the above options, a `spaces_url` is included. This can be found in your Space overview. The `bucket` should point to the name you gave your Space. The `endpoint` identifies the region in which you created the Space. And finally the two keys are the ones created in the previous section.
|
||||
|
||||
> Make sure to use an environment variable for the secret key in a live environment.
|
||||
|
||||
### Try it out!
|
||||
|
||||
Finally, run your Medusa server alongside our admin system to try out your new file service. Upon editing or creating products, you can now upload thumbnails and images, that are stored in DigitalOcean Spaces.
|
||||
@@ -1,13 +1,13 @@
|
||||
let ignore = [`**/dist`];
|
||||
let ignore = [`**/dist`]
|
||||
|
||||
// Jest needs to compile this code, but generally we don't want this copied
|
||||
// to output folders
|
||||
if (process.env.NODE_ENV !== `test`) {
|
||||
ignore.push(`**/__tests__`);
|
||||
ignore.push(`**/__tests__`)
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
sourceMaps: true,
|
||||
presets: ["babel-preset-medusa-package"],
|
||||
ignore,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`/admin/auth creates admin session correctly 1`] = `
|
||||
Object {
|
||||
"api_token": "test_token",
|
||||
"created_at": Any<String>,
|
||||
"deleted_at": null,
|
||||
"email": "admin@medusa.js",
|
||||
"first_name": null,
|
||||
"id": "admin_user",
|
||||
"last_name": null,
|
||||
"metadata": null,
|
||||
"updated_at": Any<String>,
|
||||
}
|
||||
`;
|
||||
@@ -380,5 +380,198 @@ 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,
|
||||
"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",
|
||||
"created_at": Any<String>,
|
||||
"deleted_at": null,
|
||||
"description": "test-product-description",
|
||||
"discountable": true,
|
||||
"handle": "test-product_filtering_1",
|
||||
"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": "proposed",
|
||||
"subtitle": null,
|
||||
"tags": Any<Array>,
|
||||
"thumbnail": null,
|
||||
"title": "Test product filtering 1",
|
||||
"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-collection2",
|
||||
"created_at": Any<String>,
|
||||
"deleted_at": null,
|
||||
"description": "test-product-description",
|
||||
"discountable": true,
|
||||
"handle": "test-product_filtering_2",
|
||||
"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": "published",
|
||||
"subtitle": null,
|
||||
"tags": Any<Array>,
|
||||
"thumbnail": null,
|
||||
"title": "Test product filtering 2",
|
||||
"type": Any<Object>,
|
||||
"type_id": "test-type",
|
||||
"updated_at": Any<String>,
|
||||
"variants": Any<Array>,
|
||||
"weight": null,
|
||||
"width": null,
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`/admin/products GET /admin/products returns a list of products with giftcard in list 1`] = `
|
||||
Array [
|
||||
Object {
|
||||
"collection": null,
|
||||
"collection_id": null,
|
||||
"created_at": Any<String>,
|
||||
"deleted_at": null,
|
||||
"description": "test-giftcard-description",
|
||||
"discountable": false,
|
||||
"handle": "test-giftcard",
|
||||
"height": null,
|
||||
"hs_code": null,
|
||||
"id": StringMatching /\\^prod_\\*/,
|
||||
"images": Array [],
|
||||
"is_giftcard": true,
|
||||
"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": "Denominations",
|
||||
"updated_at": Any<String>,
|
||||
},
|
||||
],
|
||||
"origin_country": null,
|
||||
"profile_id": StringMatching /\\^sp_\\*/,
|
||||
"status": "draft",
|
||||
"subtitle": null,
|
||||
"tags": Array [],
|
||||
"thumbnail": null,
|
||||
"title": "Test Giftcard",
|
||||
"type": null,
|
||||
"type_id": null,
|
||||
"updated_at": Any<String>,
|
||||
"variants": Array [
|
||||
Object {
|
||||
"allow_backorder": false,
|
||||
"barcode": null,
|
||||
"created_at": Any<String>,
|
||||
"deleted_at": null,
|
||||
"ean": null,
|
||||
"height": null,
|
||||
"hs_code": null,
|
||||
"id": StringMatching /\\^variant_\\*/,
|
||||
"inventory_quantity": 0,
|
||||
"length": null,
|
||||
"manage_inventory": true,
|
||||
"material": null,
|
||||
"metadata": null,
|
||||
"mid_code": null,
|
||||
"options": Array [
|
||||
Object {
|
||||
"created_at": Any<String>,
|
||||
"deleted_at": null,
|
||||
"id": StringMatching /\\^opt_\\*/,
|
||||
"metadata": null,
|
||||
"option_id": StringMatching /\\^opt_\\*/,
|
||||
"updated_at": Any<String>,
|
||||
"value": "100",
|
||||
"variant_id": StringMatching /\\^variant_\\*/,
|
||||
},
|
||||
],
|
||||
"origin_country": null,
|
||||
"prices": Array [
|
||||
Object {
|
||||
"amount": 100,
|
||||
"created_at": Any<String>,
|
||||
"currency_code": "usd",
|
||||
"deleted_at": null,
|
||||
"id": Any<String>,
|
||||
"region_id": null,
|
||||
"sale_amount": 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,
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
54
integration-tests/api/__tests__/admin/auth.js
Normal file
54
integration-tests/api/__tests__/admin/auth.js
Normal file
@@ -0,0 +1,54 @@
|
||||
const path = require("path")
|
||||
const { Region, DiscountRule, Discount } = require("@medusajs/medusa")
|
||||
|
||||
const setupServer = require("../../../helpers/setup-server")
|
||||
const { useApi } = require("../../../helpers/use-api")
|
||||
const { initDb, useDb } = require("../../../helpers/use-db")
|
||||
const adminSeeder = require("../../helpers/admin-seeder")
|
||||
const { exportAllDeclaration } = require("@babel/types")
|
||||
|
||||
jest.setTimeout(30000)
|
||||
|
||||
describe("/admin/auth", () => {
|
||||
let medusaProcess
|
||||
let dbConnection
|
||||
|
||||
beforeAll(async () => {
|
||||
const cwd = path.resolve(path.join(__dirname, "..", ".."))
|
||||
dbConnection = await initDb({ cwd })
|
||||
medusaProcess = await setupServer({ cwd })
|
||||
|
||||
try {
|
||||
await adminSeeder(dbConnection)
|
||||
} catch (e) {
|
||||
throw e
|
||||
}
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
const db = useDb()
|
||||
await db.shutdown()
|
||||
medusaProcess.kill()
|
||||
})
|
||||
|
||||
it("creates admin session correctly", async () => {
|
||||
const api = useApi()
|
||||
|
||||
const response = await api
|
||||
.post("/admin/auth", {
|
||||
email: "admin@medusa.js",
|
||||
password: "secret_password",
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err)
|
||||
})
|
||||
|
||||
expect(response.status).toEqual(200)
|
||||
expect(response.data.user.password_hash).toEqual(undefined)
|
||||
expect(response.data.user).toMatchSnapshot({
|
||||
email: "admin@medusa.js",
|
||||
created_at: expect.any(String),
|
||||
updated_at: expect.any(String),
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -158,6 +158,193 @@ describe("/admin/discounts", () => {
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("creates a discount with start and end dates", async () => {
|
||||
const api = useApi()
|
||||
|
||||
const response = await api
|
||||
.post(
|
||||
"/admin/discounts",
|
||||
{
|
||||
code: "HELLOWORLD",
|
||||
rule: {
|
||||
description: "test",
|
||||
type: "percentage",
|
||||
value: 10,
|
||||
allocation: "total",
|
||||
},
|
||||
usage_limit: 10,
|
||||
starts_at: new Date("09/15/2021 11:50"),
|
||||
ends_at: new Date("09/15/2021 17:50"),
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
Authorization: "Bearer test_token",
|
||||
},
|
||||
}
|
||||
)
|
||||
.catch((err) => {
|
||||
console.log(err)
|
||||
})
|
||||
|
||||
expect(response.status).toEqual(200)
|
||||
expect(response.data.discount).toEqual(
|
||||
expect.objectContaining({
|
||||
code: "HELLOWORLD",
|
||||
usage_limit: 10,
|
||||
starts_at: expect.any(String),
|
||||
ends_at: expect.any(String),
|
||||
})
|
||||
)
|
||||
|
||||
expect(new Date(response.data.discount.starts_at)).toEqual(
|
||||
new Date("09/15/2021 11:50")
|
||||
)
|
||||
|
||||
expect(new Date(response.data.discount.ends_at)).toEqual(
|
||||
new Date("09/15/2021 17:50")
|
||||
)
|
||||
|
||||
const updated = await api
|
||||
.post(
|
||||
`/admin/discounts/${response.data.discount.id}`,
|
||||
{
|
||||
usage_limit: 20,
|
||||
starts_at: new Date("09/14/2021 11:50"),
|
||||
ends_at: new Date("09/17/2021 17:50"),
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
Authorization: "Bearer test_token",
|
||||
},
|
||||
}
|
||||
)
|
||||
.catch((err) => {
|
||||
console.log(err)
|
||||
})
|
||||
|
||||
expect(updated.status).toEqual(200)
|
||||
expect(updated.data.discount).toEqual(
|
||||
expect.objectContaining({
|
||||
code: "HELLOWORLD",
|
||||
usage_limit: 20,
|
||||
starts_at: expect.any(String),
|
||||
ends_at: expect.any(String),
|
||||
})
|
||||
)
|
||||
|
||||
expect(new Date(updated.data.discount.starts_at)).toEqual(
|
||||
new Date("09/14/2021 11:50")
|
||||
)
|
||||
|
||||
expect(new Date(updated.data.discount.ends_at)).toEqual(
|
||||
new Date("09/17/2021 17:50")
|
||||
)
|
||||
})
|
||||
|
||||
it("fails to update end date to a date before start date", async () => {
|
||||
expect.assertions(6)
|
||||
|
||||
const api = useApi()
|
||||
|
||||
const response = await api
|
||||
.post(
|
||||
"/admin/discounts",
|
||||
{
|
||||
code: "HELLOWORLD",
|
||||
rule: {
|
||||
description: "test",
|
||||
type: "percentage",
|
||||
value: 10,
|
||||
allocation: "total",
|
||||
},
|
||||
usage_limit: 10,
|
||||
starts_at: new Date("09/15/2021 11:50"),
|
||||
ends_at: new Date("09/15/2021 17:50"),
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
Authorization: "Bearer test_token",
|
||||
},
|
||||
}
|
||||
)
|
||||
.catch((err) => {
|
||||
console.log(err)
|
||||
})
|
||||
|
||||
expect(response.status).toEqual(200)
|
||||
expect(response.data.discount).toEqual(
|
||||
expect.objectContaining({
|
||||
code: "HELLOWORLD",
|
||||
usage_limit: 10,
|
||||
starts_at: expect.any(String),
|
||||
ends_at: expect.any(String),
|
||||
})
|
||||
)
|
||||
|
||||
expect(new Date(response.data.discount.starts_at)).toEqual(
|
||||
new Date("09/15/2021 11:50")
|
||||
)
|
||||
|
||||
expect(new Date(response.data.discount.ends_at)).toEqual(
|
||||
new Date("09/15/2021 17:50")
|
||||
)
|
||||
|
||||
await api
|
||||
.post(
|
||||
`/admin/discounts/${response.data.discount.id}`,
|
||||
{
|
||||
usage_limit: 20,
|
||||
ends_at: new Date("09/11/2021 17:50"),
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
Authorization: "Bearer test_token",
|
||||
},
|
||||
}
|
||||
)
|
||||
.catch((err) => {
|
||||
expect(err.response.status).toEqual(400)
|
||||
expect(err.response.data.message).toEqual(
|
||||
`"ends_at" must be greater than "starts_at"`
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
it("fails to create discount with end date before start date", async () => {
|
||||
expect.assertions(2)
|
||||
const api = useApi()
|
||||
|
||||
const response = await api
|
||||
.post(
|
||||
"/admin/discounts",
|
||||
{
|
||||
code: "HELLOWORLD",
|
||||
rule: {
|
||||
description: "test",
|
||||
type: "percentage",
|
||||
value: 10,
|
||||
allocation: "total",
|
||||
},
|
||||
usage_limit: 10,
|
||||
starts_at: new Date("09/15/2021 11:50"),
|
||||
ends_at: new Date("09/14/2021 17:50"),
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
Authorization: "Bearer test_token",
|
||||
},
|
||||
}
|
||||
)
|
||||
.catch((err) => {
|
||||
expect(err.response.status).toEqual(400)
|
||||
expect(err.response.data.message).toEqual([
|
||||
expect.objectContaining({
|
||||
message: `"ends_at" must be greater than "ref:starts_at"`,
|
||||
}),
|
||||
])
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("testing for soft-deletion + uniqueness on discount codes", () => {
|
||||
@@ -286,6 +473,21 @@ describe("/admin/discounts", () => {
|
||||
is_dynamic: true,
|
||||
is_disabled: false,
|
||||
rule_id: "test-discount-rule",
|
||||
valid_duration: "P2Y",
|
||||
})
|
||||
await manager.insert(DiscountRule, {
|
||||
id: "test-discount-rule1",
|
||||
description: "Dynamic rule",
|
||||
type: "percentage",
|
||||
value: 10,
|
||||
allocation: "total",
|
||||
})
|
||||
await manager.insert(Discount, {
|
||||
id: "test-discount1",
|
||||
code: "DYNAMICCode",
|
||||
is_dynamic: true,
|
||||
is_disabled: false,
|
||||
rule_id: "test-discount-rule1",
|
||||
})
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
@@ -298,7 +500,7 @@ describe("/admin/discounts", () => {
|
||||
await db.teardown()
|
||||
})
|
||||
|
||||
it("creates a dynamic discount", async () => {
|
||||
it("creates a dynamic discount with ends_at", async () => {
|
||||
const api = useApi()
|
||||
|
||||
const response = await api
|
||||
@@ -318,6 +520,40 @@ describe("/admin/discounts", () => {
|
||||
})
|
||||
|
||||
expect(response.status).toEqual(200)
|
||||
expect(response.data.discount).toEqual(
|
||||
expect.objectContaining({
|
||||
code: "HELLOWORLD",
|
||||
ends_at: expect.any(String),
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("creates a dynamic discount without ends_at", async () => {
|
||||
const api = useApi()
|
||||
|
||||
const response = await api
|
||||
.post(
|
||||
"/admin/discounts/test-discount1/dynamic-codes",
|
||||
{
|
||||
code: "HELLOWORLD",
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
Authorization: "Bearer test_token",
|
||||
},
|
||||
}
|
||||
)
|
||||
.catch((err) => {
|
||||
// console.log(err)
|
||||
})
|
||||
|
||||
expect(response.status).toEqual(200)
|
||||
expect(response.data.discount).toEqual(
|
||||
expect.objectContaining({
|
||||
code: "HELLOWORLD",
|
||||
ends_at: null,
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -4,6 +4,7 @@ const {
|
||||
Order,
|
||||
LineItem,
|
||||
ProductVariant,
|
||||
CustomShippingOption,
|
||||
} = require("@medusajs/medusa")
|
||||
|
||||
const setupServer = require("../../../helpers/setup-server")
|
||||
@@ -79,7 +80,6 @@ describe("/admin/orders", () => {
|
||||
await adminSeeder(dbConnection)
|
||||
await orderSeeder(dbConnection)
|
||||
await swapSeeder(dbConnection)
|
||||
await claimSeeder(dbConnection)
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
throw err
|
||||
@@ -222,6 +222,48 @@ describe("/admin/orders", () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe("POST /admin/orders/:id/swaps", () => {
|
||||
beforeEach(async () => {
|
||||
try {
|
||||
await adminSeeder(dbConnection)
|
||||
await orderSeeder(dbConnection)
|
||||
await claimSeeder(dbConnection)
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
throw err
|
||||
}
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
const db = useDb()
|
||||
await db.teardown()
|
||||
})
|
||||
|
||||
it("creates a swap on a claim", async () => {
|
||||
const api = useApi()
|
||||
|
||||
const swapOnSwap = await api.post(
|
||||
"/admin/orders/order-with-claim/swaps",
|
||||
{
|
||||
return_items: [
|
||||
{
|
||||
item_id: "test-item-co-2",
|
||||
quantity: 1,
|
||||
},
|
||||
],
|
||||
additional_items: [{ variant_id: "test-variant", quantity: 1 }],
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
authorization: "Bearer test_token",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
expect(swapOnSwap.status).toEqual(200)
|
||||
})
|
||||
})
|
||||
|
||||
describe("POST /admin/orders/:id/claims", () => {
|
||||
beforeEach(async () => {
|
||||
try {
|
||||
@@ -317,6 +359,43 @@ describe("/admin/orders", () => {
|
||||
)
|
||||
})
|
||||
|
||||
it("creates a claim on a claim", async () => {
|
||||
const api = useApi()
|
||||
|
||||
const claimOnClaim = await api
|
||||
.post(
|
||||
"/admin/orders/order-with-claim/claims",
|
||||
{
|
||||
type: "replace",
|
||||
claim_items: [
|
||||
{
|
||||
item_id: "test-item-co-2",
|
||||
quantity: 1,
|
||||
reason: "production_failure",
|
||||
tags: ["fluff"],
|
||||
images: ["https://test.image.com"],
|
||||
},
|
||||
],
|
||||
additional_items: [
|
||||
{
|
||||
variant_id: "test-variant",
|
||||
quantity: 1,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
authorization: "Bearer test_token",
|
||||
},
|
||||
}
|
||||
)
|
||||
.catch((err) => {
|
||||
console.log(err)
|
||||
})
|
||||
|
||||
expect(claimOnClaim.status).toEqual(200)
|
||||
})
|
||||
|
||||
it("creates a claim with a shipping address", async () => {
|
||||
const api = useApi()
|
||||
|
||||
@@ -840,6 +919,61 @@ describe("/admin/orders", () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe("POST /admin/orders/:id/claims", () => {
|
||||
beforeEach(async () => {
|
||||
try {
|
||||
await adminSeeder(dbConnection)
|
||||
await orderSeeder(dbConnection)
|
||||
await swapSeeder(dbConnection)
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
throw err
|
||||
}
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
const db = useDb()
|
||||
await db.teardown()
|
||||
})
|
||||
|
||||
it("creates a claim on a swap", async () => {
|
||||
const api = useApi()
|
||||
|
||||
const claimOnClaim = await api
|
||||
.post(
|
||||
"/admin/orders/order-with-swap/claims",
|
||||
{
|
||||
type: "replace",
|
||||
claim_items: [
|
||||
{
|
||||
item_id: "return-item-1",
|
||||
quantity: 1,
|
||||
reason: "production_failure",
|
||||
tags: ["fluff"],
|
||||
images: ["https://test.image.com"],
|
||||
},
|
||||
],
|
||||
additional_items: [
|
||||
{
|
||||
variant_id: "test-variant",
|
||||
quantity: 1,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
authorization: "Bearer test_token",
|
||||
},
|
||||
}
|
||||
)
|
||||
.catch((err) => {
|
||||
console.log(err)
|
||||
})
|
||||
|
||||
expect(claimOnClaim.status).toEqual(200)
|
||||
})
|
||||
})
|
||||
|
||||
describe("POST /admin/orders/:id/return", () => {
|
||||
let rrId
|
||||
beforeEach(async () => {
|
||||
@@ -1276,6 +1410,47 @@ describe("/admin/orders", () => {
|
||||
expect(response.status).toEqual(200)
|
||||
})
|
||||
|
||||
it("creates a swap with custom shipping options", 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 }],
|
||||
custom_shipping_options: [{ option_id: "test-option", price: 0 }],
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
authorization: "Bearer test_token",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
const swap = response.data.order.swaps[0]
|
||||
|
||||
const manager = dbConnection.manager
|
||||
const customOptions = await manager.find(CustomShippingOption, {
|
||||
shipping_option_id: "test-option",
|
||||
})
|
||||
|
||||
expect(response.status).toEqual(200)
|
||||
expect(customOptions).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
shipping_option_id: "test-option",
|
||||
price: 0,
|
||||
cart_id: swap.cart_id,
|
||||
}),
|
||||
])
|
||||
)
|
||||
})
|
||||
|
||||
it("creates a swap and a return", async () => {
|
||||
const api = useApi()
|
||||
|
||||
|
||||
@@ -46,7 +46,35 @@ describe("/admin/products", () => {
|
||||
const api = useApi()
|
||||
|
||||
const res = await api
|
||||
.get("/admin/products?status%5B%5D=null", {
|
||||
.get("/admin/products", {
|
||||
headers: {
|
||||
Authorization: "Bearer test_token",
|
||||
},
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err)
|
||||
})
|
||||
|
||||
expect(res.status).toEqual(200)
|
||||
expect(res.data.products).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: "test-product",
|
||||
status: "draft",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: "test-product1",
|
||||
status: "draft",
|
||||
}),
|
||||
])
|
||||
)
|
||||
})
|
||||
|
||||
it("returns a list of all products when no query is provided", async () => {
|
||||
const api = useApi()
|
||||
|
||||
const res = await api
|
||||
.get("/admin/products?q=", {
|
||||
headers: {
|
||||
Authorization: "Bearer test_token",
|
||||
},
|
||||
@@ -89,7 +117,7 @@ describe("/admin/products", () => {
|
||||
})
|
||||
|
||||
const response = await api
|
||||
.get("/admin/products?status%5B%5D=proposed", {
|
||||
.get("/admin/products?status[]=proposed", {
|
||||
headers: {
|
||||
Authorization: "Bearer test_token",
|
||||
},
|
||||
@@ -109,6 +137,258 @@ describe("/admin/products", () => {
|
||||
)
|
||||
})
|
||||
|
||||
it("returns a list of products where status is proposed or published", async () => {
|
||||
const api = useApi()
|
||||
|
||||
const notExpected = [
|
||||
expect.objectContaining({ status: "draft" }),
|
||||
expect.objectContaining({ status: "rejected" }),
|
||||
]
|
||||
|
||||
const response = await api
|
||||
.get("/admin/products?status[]=published,proposed", {
|
||||
headers: {
|
||||
Authorization: "Bearer test_token",
|
||||
},
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err)
|
||||
})
|
||||
|
||||
expect(response.status).toEqual(200)
|
||||
expect(response.data.products).toEqual([
|
||||
expect.objectContaining({
|
||||
id: "test-product_filtering_1",
|
||||
status: "proposed",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: "test-product_filtering_2",
|
||||
status: "published",
|
||||
}),
|
||||
])
|
||||
|
||||
for (const notExpect of notExpected) {
|
||||
expect(response.data.products).toEqual(
|
||||
expect.not.arrayContaining([notExpect])
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it("returns a list of products in collection", async () => {
|
||||
const api = useApi()
|
||||
|
||||
const notExpected = [
|
||||
expect.objectContaining({ collection_id: "test-collection" }),
|
||||
expect.objectContaining({ collection_id: "test-collection2" }),
|
||||
]
|
||||
|
||||
const response = await api
|
||||
.get("/admin/products?collection_id[]=test-collection1", {
|
||||
headers: {
|
||||
Authorization: "Bearer test_token",
|
||||
},
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err)
|
||||
})
|
||||
|
||||
expect(response.status).toEqual(200)
|
||||
expect(response.data.products).toEqual([
|
||||
expect.objectContaining({
|
||||
id: "test-product_filtering_1",
|
||||
collection_id: "test-collection1",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: "test-product_filtering_3",
|
||||
collection_id: "test-collection1",
|
||||
}),
|
||||
])
|
||||
|
||||
for (const notExpect of notExpected) {
|
||||
expect(response.data.products).toEqual(
|
||||
expect.not.arrayContaining([notExpect])
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it("returns a list of products with tags", async () => {
|
||||
const api = useApi()
|
||||
|
||||
const notExpected = [
|
||||
expect.objectContaining({ id: "tag1" }),
|
||||
expect.objectContaining({ id: "tag2" }),
|
||||
expect.objectContaining({ id: "tag4" }),
|
||||
]
|
||||
|
||||
const response = await api
|
||||
.get("/admin/products?tags[]=tag3", {
|
||||
headers: {
|
||||
Authorization: "Bearer test_token",
|
||||
},
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err)
|
||||
})
|
||||
|
||||
expect(response.status).toEqual(200)
|
||||
expect(response.data.products).toEqual([
|
||||
expect.objectContaining({
|
||||
id: "test-product_filtering_1",
|
||||
tags: [expect.objectContaining({ id: "tag3" })],
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: "test-product_filtering_2",
|
||||
tags: [expect.objectContaining({ id: "tag3" })],
|
||||
}),
|
||||
])
|
||||
for (const product of response.data.products) {
|
||||
for (const notExpect of notExpected) {
|
||||
expect(product.tags).toEqual(expect.not.arrayContaining([notExpect]))
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
it("returns a list of products with tags in a collection", async () => {
|
||||
const api = useApi()
|
||||
|
||||
const notExpectedTags = [
|
||||
expect.objectContaining({ id: "tag1" }),
|
||||
expect.objectContaining({ id: "tag2" }),
|
||||
expect.objectContaining({ id: "tag3" }),
|
||||
]
|
||||
|
||||
const notExpectedCollections = [
|
||||
expect.objectContaining({ collection_id: "test-collection" }),
|
||||
expect.objectContaining({ collection_id: "test-collection2" }),
|
||||
]
|
||||
|
||||
const response = await api
|
||||
.get("/admin/products?collection_id[]=test-collection1&tags[]=tag4", {
|
||||
headers: {
|
||||
Authorization: "Bearer test_token",
|
||||
},
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err)
|
||||
})
|
||||
|
||||
expect(response.status).toEqual(200)
|
||||
expect(response.data.products).toEqual([
|
||||
expect.objectContaining({
|
||||
id: "test-product_filtering_3",
|
||||
collection_id: "test-collection1",
|
||||
tags: [expect.objectContaining({ id: "tag4" })],
|
||||
}),
|
||||
])
|
||||
|
||||
for (const notExpect of notExpectedCollections) {
|
||||
expect(response.data.products).toEqual(
|
||||
expect.not.arrayContaining([notExpect])
|
||||
)
|
||||
}
|
||||
|
||||
for (const product of response.data.products) {
|
||||
for (const notExpect of notExpectedTags) {
|
||||
expect(product.tags).toEqual(expect.not.arrayContaining([notExpect]))
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
it("returns a list of products with 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=true", {
|
||||
headers: {
|
||||
Authorization: "Bearer test_token",
|
||||
},
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err)
|
||||
})
|
||||
|
||||
expect(response.data.products).toEqual(
|
||||
expect.not.arrayContaining([
|
||||
expect.objectContaining({ is_giftcard: false }),
|
||||
])
|
||||
)
|
||||
|
||||
expect(response.status).toEqual(200)
|
||||
expect(response.data.products).toMatchSnapshot([
|
||||
{
|
||||
title: "Test Giftcard",
|
||||
id: expect.stringMatching(/^prod_*/),
|
||||
is_giftcard: true,
|
||||
description: "test-giftcard-description",
|
||||
profile_id: expect.stringMatching(/^sp_*/),
|
||||
options: [
|
||||
{
|
||||
title: "Denominations",
|
||||
id: expect.stringMatching(/^opt_*/),
|
||||
product_id: expect.stringMatching(/^prod_*/),
|
||||
created_at: expect.any(String),
|
||||
updated_at: expect.any(String),
|
||||
},
|
||||
],
|
||||
|
||||
variants: [
|
||||
{
|
||||
title: "Test variant",
|
||||
id: expect.stringMatching(/^variant_*/),
|
||||
product_id: expect.stringMatching(/^prod_*/),
|
||||
created_at: expect.any(String),
|
||||
updated_at: expect.any(String),
|
||||
prices: [
|
||||
{
|
||||
id: expect.any(String),
|
||||
currency_code: "usd",
|
||||
amount: 100,
|
||||
variant_id: expect.stringMatching(/^variant_*/),
|
||||
created_at: expect.any(String),
|
||||
updated_at: expect.any(String),
|
||||
},
|
||||
],
|
||||
options: [
|
||||
{
|
||||
id: expect.stringMatching(/^opt_*/),
|
||||
option_id: expect.stringMatching(/^opt_*/),
|
||||
created_at: expect.any(String),
|
||||
variant_id: expect.stringMatching(/^variant_*/),
|
||||
updated_at: expect.any(String),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
created_at: expect.any(String),
|
||||
updated_at: expect.any(String),
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
it("returns a list of products with child entities", async () => {
|
||||
const api = useApi()
|
||||
|
||||
@@ -306,6 +586,42 @@ describe("/admin/products", () => {
|
||||
created_at: expect.any(String),
|
||||
updated_at: expect.any(String),
|
||||
},
|
||||
{
|
||||
id: expect.stringMatching(/^test-*/),
|
||||
profile_id: expect.stringMatching(/^sp_*/),
|
||||
created_at: expect.any(String),
|
||||
type: expect.any(Object),
|
||||
collection: expect.any(Object),
|
||||
options: expect.any(Array),
|
||||
tags: expect.any(Array),
|
||||
variants: expect.any(Array),
|
||||
created_at: expect.any(String),
|
||||
updated_at: expect.any(String),
|
||||
},
|
||||
{
|
||||
id: expect.stringMatching(/^test-*/),
|
||||
profile_id: expect.stringMatching(/^sp_*/),
|
||||
created_at: expect.any(String),
|
||||
type: expect.any(Object),
|
||||
collection: expect.any(Object),
|
||||
options: expect.any(Array),
|
||||
tags: expect.any(Array),
|
||||
variants: expect.any(Array),
|
||||
created_at: expect.any(String),
|
||||
updated_at: expect.any(String),
|
||||
},
|
||||
{
|
||||
id: expect.stringMatching(/^test-*/),
|
||||
profile_id: expect.stringMatching(/^sp_*/),
|
||||
created_at: expect.any(String),
|
||||
type: expect.any(Object),
|
||||
collection: expect.any(Object),
|
||||
options: expect.any(Array),
|
||||
tags: expect.any(Array),
|
||||
variants: expect.any(Array),
|
||||
created_at: expect.any(String),
|
||||
updated_at: expect.any(String),
|
||||
},
|
||||
])
|
||||
})
|
||||
})
|
||||
@@ -592,6 +908,28 @@ describe("/admin/products", () => {
|
||||
)
|
||||
})
|
||||
|
||||
it("updates product (removes images when empty array included)", async () => {
|
||||
const api = useApi()
|
||||
|
||||
const payload = {
|
||||
images: [],
|
||||
}
|
||||
|
||||
const response = await api
|
||||
.post("/admin/products/test-product", payload, {
|
||||
headers: {
|
||||
Authorization: "Bearer test_token",
|
||||
},
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err)
|
||||
})
|
||||
|
||||
expect(response.status).toEqual(200)
|
||||
|
||||
expect(response.data.product.images.length).toEqual(0)
|
||||
})
|
||||
|
||||
it("fails to update product with invalid status", async () => {
|
||||
const api = useApi()
|
||||
|
||||
@@ -696,6 +1034,7 @@ describe("/admin/products", () => {
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("testing for soft-deletion + uniqueness on handles, collection and variant properties", () => {
|
||||
beforeEach(async () => {
|
||||
try {
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`/admin/auth creates store session correctly 1`] = `
|
||||
Object {
|
||||
"billing_address_id": null,
|
||||
"created_at": Any<String>,
|
||||
"deleted_at": null,
|
||||
"email": "test@testesen.dk",
|
||||
"first_name": "test",
|
||||
"has_account": true,
|
||||
"id": Any<String>,
|
||||
"last_name": "testesen",
|
||||
"metadata": null,
|
||||
"orders": Array [],
|
||||
"phone": "12345678",
|
||||
"updated_at": Any<String>,
|
||||
}
|
||||
`;
|
||||
@@ -17,6 +17,18 @@ Object {
|
||||
"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,
|
||||
"prices": Array [
|
||||
Object {
|
||||
@@ -61,6 +73,18 @@ Object {
|
||||
"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,
|
||||
"prices": Array [
|
||||
Object {
|
||||
|
||||
64
integration-tests/api/__tests__/store/auth.js
Normal file
64
integration-tests/api/__tests__/store/auth.js
Normal file
@@ -0,0 +1,64 @@
|
||||
const path = require("path")
|
||||
const { Region, DiscountRule, Discount } = require("@medusajs/medusa")
|
||||
|
||||
const setupServer = require("../../../helpers/setup-server")
|
||||
const { useApi } = require("../../../helpers/use-api")
|
||||
const { initDb, useDb } = require("../../../helpers/use-db")
|
||||
const adminSeeder = require("../../helpers/admin-seeder")
|
||||
const { exportAllDeclaration } = require("@babel/types")
|
||||
|
||||
jest.setTimeout(30000)
|
||||
|
||||
describe("/admin/auth", () => {
|
||||
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()
|
||||
})
|
||||
|
||||
it("creates store session correctly", async () => {
|
||||
const api = useApi()
|
||||
|
||||
await api
|
||||
.post("/store/customers", {
|
||||
email: "test@testesen.dk",
|
||||
password: "secret_password",
|
||||
first_name: "test",
|
||||
last_name: "testesen",
|
||||
phone: "12345678",
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err)
|
||||
})
|
||||
|
||||
const response = await api
|
||||
.post("/store/auth", {
|
||||
email: "test@testesen.dk",
|
||||
password: "secret_password",
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err)
|
||||
})
|
||||
|
||||
expect(response.status).toEqual(200)
|
||||
expect(response.data.customer.password_hash).toEqual(undefined)
|
||||
expect(response.data.customer).toMatchSnapshot({
|
||||
id: expect.any(String),
|
||||
created_at: expect.any(String),
|
||||
updated_at: expect.any(String),
|
||||
first_name: "test",
|
||||
last_name: "testesen",
|
||||
phone: "12345678",
|
||||
email: "test@testesen.dk",
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,5 +1,12 @@
|
||||
const path = require("path")
|
||||
const { Region, LineItem, GiftCard } = require("@medusajs/medusa")
|
||||
const {
|
||||
Region,
|
||||
LineItem,
|
||||
GiftCard,
|
||||
Cart,
|
||||
CustomShippingOption,
|
||||
ShippingOption,
|
||||
} = require("@medusajs/medusa")
|
||||
|
||||
const setupServer = require("../../../helpers/setup-server")
|
||||
const { useApi } = require("../../../helpers/use-api")
|
||||
@@ -63,6 +70,24 @@ describe("/store/carts", () => {
|
||||
expect(getRes.status).toEqual(200)
|
||||
})
|
||||
|
||||
it("fails to create a cart when no region exist", async () => {
|
||||
const api = useApi()
|
||||
|
||||
await dbConnection.manager.query(
|
||||
`UPDATE "country" SET region_id=null WHERE iso_2 = 'us'`
|
||||
)
|
||||
await dbConnection.manager.query(`DELETE from region`)
|
||||
|
||||
try {
|
||||
await api.post("/store/carts")
|
||||
} catch (error) {
|
||||
expect(error.response.status).toEqual(400)
|
||||
expect(error.response.data.message).toEqual(
|
||||
"A region is required to create a cart"
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it("creates a cart with country", async () => {
|
||||
const api = useApi()
|
||||
|
||||
@@ -131,20 +156,77 @@ describe("/store/carts", () => {
|
||||
})
|
||||
|
||||
it("fails on apply discount if limit has been reached", async () => {
|
||||
expect.assertions(2)
|
||||
const api = useApi()
|
||||
|
||||
await api
|
||||
.post("/store/carts/test-cart", {
|
||||
discounts: [{ code: "SPENT" }],
|
||||
})
|
||||
.catch((error) => {
|
||||
expect(error.response.status).toEqual(400)
|
||||
expect(error.response.data.message).toEqual(
|
||||
"Discount has been used maximum allowed times"
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
it("fails to apply expired discount", async () => {
|
||||
expect.assertions(2)
|
||||
const api = useApi()
|
||||
|
||||
try {
|
||||
await api.post("/store/carts/test-cart", {
|
||||
discounts: [{ code: "CREATED" }],
|
||||
discounts: [{ code: "EXP_DISC" }],
|
||||
})
|
||||
} catch (error) {
|
||||
expect(error.response.status).toEqual(400)
|
||||
expect(error.response.data.message).toEqual(
|
||||
"Discount has been used maximum allowed times"
|
||||
)
|
||||
expect(error.response.data.message).toEqual("Discount is expired")
|
||||
}
|
||||
})
|
||||
|
||||
it("fails on discount before start day", async () => {
|
||||
expect.assertions(2)
|
||||
const api = useApi()
|
||||
|
||||
try {
|
||||
await api.post("/store/carts/test-cart", {
|
||||
discounts: [{ code: "PREM_DISC" }],
|
||||
})
|
||||
} catch (error) {
|
||||
expect(error.response.status).toEqual(400)
|
||||
expect(error.response.data.message).toEqual("Discount is not valid yet")
|
||||
}
|
||||
})
|
||||
|
||||
it("fails on apply invalid dynamic discount", async () => {
|
||||
const api = useApi()
|
||||
|
||||
try {
|
||||
await api.post("/store/carts/test-cart", {
|
||||
discounts: [{ code: "INV_DYN_DISC" }],
|
||||
})
|
||||
} catch (error) {
|
||||
expect(error.response.status).toEqual(400)
|
||||
expect(error.response.data.message).toEqual("Discount is expired")
|
||||
}
|
||||
})
|
||||
|
||||
it("Applies dynamic discount to cart correctly", async () => {
|
||||
const api = useApi()
|
||||
|
||||
const cart = await api.post(
|
||||
"/store/carts/test-cart",
|
||||
{
|
||||
discounts: [{ code: "DYN_DISC" }],
|
||||
},
|
||||
{ withCredentials: true }
|
||||
)
|
||||
|
||||
expect(cart.data.cart.shipping_total).toBe(1000)
|
||||
expect(cart.status).toEqual(200)
|
||||
})
|
||||
|
||||
it("updates cart customer id", async () => {
|
||||
const api = useApi()
|
||||
|
||||
@@ -377,14 +459,42 @@ describe("/store/carts", () => {
|
||||
|
||||
describe("POST /store/carts/:id/shipping-methods", () => {
|
||||
beforeEach(async () => {
|
||||
await cartSeeder(dbConnection)
|
||||
try {
|
||||
await cartSeeder(dbConnection)
|
||||
const manager = dbConnection.manager
|
||||
|
||||
const _cart = await manager.create(Cart, {
|
||||
id: "test-cart-with-cso",
|
||||
customer_id: "some-customer",
|
||||
email: "some-customer@email.com",
|
||||
shipping_address: {
|
||||
id: "test-shipping-address",
|
||||
first_name: "lebron",
|
||||
country_code: "us",
|
||||
},
|
||||
region_id: "test-region",
|
||||
currency_code: "usd",
|
||||
type: "swap",
|
||||
})
|
||||
|
||||
let cartWithCustomSo = await manager.save(_cart)
|
||||
|
||||
await manager.insert(CustomShippingOption, {
|
||||
id: "another-cso-test",
|
||||
cart_id: "test-cart-with-cso",
|
||||
shipping_option_id: "test-option",
|
||||
price: 5,
|
||||
})
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
}
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
await doAfterEach()
|
||||
})
|
||||
|
||||
it("adds a shipping method to cart", async () => {
|
||||
it("adds a normal shipping method to cart", async () => {
|
||||
const api = useApi()
|
||||
|
||||
const cartWithShippingMethod = await api.post(
|
||||
@@ -401,6 +511,49 @@ describe("/store/carts", () => {
|
||||
expect(cartWithShippingMethod.status).toEqual(200)
|
||||
})
|
||||
|
||||
it("given a cart with custom options and a shipping option already belonging to said cart, then it should add a shipping method based on the given custom shipping option", async () => {
|
||||
const shippingOptionId = "test-option"
|
||||
|
||||
const api = useApi()
|
||||
|
||||
const cartWithCustomShippingMethod = await api
|
||||
.post(
|
||||
"/store/carts/test-cart-with-cso/shipping-methods",
|
||||
{
|
||||
option_id: shippingOptionId,
|
||||
},
|
||||
{ withCredentials: true }
|
||||
)
|
||||
.catch((err) => err.response)
|
||||
|
||||
expect(
|
||||
cartWithCustomShippingMethod.data.cart.shipping_methods
|
||||
).toContainEqual(
|
||||
expect.objectContaining({
|
||||
shipping_option_id: shippingOptionId,
|
||||
price: 5,
|
||||
})
|
||||
)
|
||||
expect(cartWithCustomShippingMethod.status).toEqual(200)
|
||||
})
|
||||
|
||||
it("given a cart with custom options and an option id not corresponding to any custom shipping option, then it should throw an invalid error", async () => {
|
||||
const api = useApi()
|
||||
|
||||
try {
|
||||
await api.post(
|
||||
"/store/carts/test-cart-with-cso/shipping-methods",
|
||||
{
|
||||
option_id: "orphan-so",
|
||||
},
|
||||
{ withCredentials: true }
|
||||
)
|
||||
} catch (err) {
|
||||
expect(err.response.status).toEqual(400)
|
||||
expect(err.response.data.message).toEqual("Wrong shipping option")
|
||||
}
|
||||
})
|
||||
|
||||
it("adds a giftcard to cart, but ensures discount only applied to discountable items", async () => {
|
||||
const api = useApi()
|
||||
|
||||
@@ -425,13 +578,15 @@ describe("/store/carts", () => {
|
||||
)
|
||||
|
||||
// Add a 10% discount to the cart
|
||||
const cartWithGiftcard = await api.post(
|
||||
"/store/carts/test-cart",
|
||||
{
|
||||
discounts: [{ code: "10PERCENT" }],
|
||||
},
|
||||
{ withCredentials: true }
|
||||
)
|
||||
const cartWithGiftcard = await api
|
||||
.post(
|
||||
"/store/carts/test-cart",
|
||||
{
|
||||
discounts: [{ code: "10PERCENT" }],
|
||||
},
|
||||
{ withCredentials: true }
|
||||
)
|
||||
.catch((err) => console.log(err))
|
||||
|
||||
// Ensure that the discount is only applied to the standard item
|
||||
expect(cartWithGiftcard.data.cart.total).toBe(1900) // 1000 (giftcard) + 900 (standard item with 10% discount)
|
||||
|
||||
@@ -221,5 +221,47 @@ describe("/store/customers", () => {
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("unsets customer billing address", async () => {
|
||||
const api = useApi()
|
||||
|
||||
const authResponse = await api.post("/store/auth", {
|
||||
email: "john@deere.com",
|
||||
password: "test",
|
||||
})
|
||||
|
||||
const customerId = authResponse.data.customer.id
|
||||
const [authCookie] = authResponse.headers["set-cookie"][0].split(";")
|
||||
|
||||
const check = await api.post(
|
||||
`/store/customers/me`,
|
||||
{
|
||||
billing_address: "addr_test",
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
Cookie: authCookie,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
expect(check.status).toEqual(200)
|
||||
expect(check.data.customer.billing_address_id).toEqual("addr_test")
|
||||
|
||||
const response = await api.post(
|
||||
`/store/customers/me`,
|
||||
{
|
||||
billing_address: null,
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
Cookie: authCookie,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
expect(response.status).toEqual(200)
|
||||
expect(response.data.customer.billing_address_id).toEqual(null)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,63 +1,63 @@
|
||||
const path = require("path");
|
||||
const path = require("path")
|
||||
|
||||
const setupServer = require("../../../helpers/setup-server");
|
||||
const { useApi } = require("../../../helpers/use-api");
|
||||
const { initDb, useDb } = require("../../../helpers/use-db");
|
||||
const setupServer = require("../../../helpers/setup-server")
|
||||
const { useApi } = require("../../../helpers/use-api")
|
||||
const { initDb, useDb } = require("../../../helpers/use-db")
|
||||
|
||||
const draftOrderSeeder = require("../../helpers/draft-order-seeder");
|
||||
const draftOrderSeeder = require("../../helpers/draft-order-seeder")
|
||||
|
||||
jest.setTimeout(30000);
|
||||
jest.setTimeout(30000)
|
||||
|
||||
describe("/store/carts (draft-orders)", () => {
|
||||
let medusaProcess;
|
||||
let dbConnection;
|
||||
let medusaProcess
|
||||
let dbConnection
|
||||
|
||||
beforeAll(async () => {
|
||||
const cwd = path.resolve(path.join(__dirname, "..", ".."));
|
||||
dbConnection = await initDb({ cwd });
|
||||
medusaProcess = await setupServer({ cwd });
|
||||
});
|
||||
const cwd = path.resolve(path.join(__dirname, "..", ".."))
|
||||
dbConnection = await initDb({ cwd })
|
||||
medusaProcess = await setupServer({ cwd })
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
const db = useDb();
|
||||
await db.shutdown();
|
||||
const db = useDb()
|
||||
await db.shutdown()
|
||||
|
||||
medusaProcess.kill();
|
||||
});
|
||||
medusaProcess.kill()
|
||||
})
|
||||
|
||||
describe("POST /admin/draft-order", () => {
|
||||
beforeEach(async () => {
|
||||
try {
|
||||
await draftOrderSeeder(dbConnection);
|
||||
await draftOrderSeeder(dbConnection)
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
throw err;
|
||||
console.log(err)
|
||||
throw err
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
const db = useDb();
|
||||
await db.teardown();
|
||||
});
|
||||
const db = useDb()
|
||||
await db.teardown()
|
||||
})
|
||||
|
||||
it("completes a cart for a draft order thereby creating an order for the draft order", async () => {
|
||||
const api = useApi();
|
||||
const api = useApi()
|
||||
|
||||
const response = await api
|
||||
.post("/store/carts/test-cart/complete-cart", {})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
console.log(err)
|
||||
})
|
||||
|
||||
expect(response.status).toEqual(200);
|
||||
expect(response.status).toEqual(200)
|
||||
|
||||
const createdOrder = await api
|
||||
.get(`/store/orders/${response.data.data.id}`, {})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
console.log(err)
|
||||
})
|
||||
|
||||
expect(createdOrder.data.order.cart_id).toEqual("test-cart");
|
||||
});
|
||||
});
|
||||
});
|
||||
expect(createdOrder.data.order.cart_id).toEqual("test-cart")
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,63 +1,63 @@
|
||||
const path = require("path");
|
||||
const { Region, GiftCard } = require("@medusajs/medusa");
|
||||
const path = require("path")
|
||||
const { Region, GiftCard } = require("@medusajs/medusa")
|
||||
|
||||
const setupServer = require("../../../helpers/setup-server");
|
||||
const { useApi } = require("../../../helpers/use-api");
|
||||
const { initDb, useDb } = require("../../../helpers/use-db");
|
||||
const setupServer = require("../../../helpers/setup-server")
|
||||
const { useApi } = require("../../../helpers/use-api")
|
||||
const { initDb, useDb } = require("../../../helpers/use-db")
|
||||
|
||||
jest.setTimeout(30000);
|
||||
jest.setTimeout(30000)
|
||||
|
||||
describe("/store/gift-cards", () => {
|
||||
let medusaProcess;
|
||||
let dbConnection;
|
||||
let medusaProcess
|
||||
let dbConnection
|
||||
|
||||
beforeAll(async () => {
|
||||
const cwd = path.resolve(path.join(__dirname, "..", ".."));
|
||||
dbConnection = await initDb({ cwd });
|
||||
medusaProcess = await setupServer({ cwd });
|
||||
});
|
||||
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();
|
||||
});
|
||||
const db = useDb()
|
||||
await db.shutdown()
|
||||
medusaProcess.kill()
|
||||
})
|
||||
|
||||
describe("GET /store/gift-cards/:code", () => {
|
||||
beforeEach(async () => {
|
||||
const manager = dbConnection.manager;
|
||||
const manager = dbConnection.manager
|
||||
await manager.insert(Region, {
|
||||
id: "region",
|
||||
name: "Test Region",
|
||||
currency_code: "usd",
|
||||
tax_rate: 0,
|
||||
});
|
||||
})
|
||||
await manager.insert(GiftCard, {
|
||||
id: "gift_test",
|
||||
code: "GC_TEST",
|
||||
value: 200,
|
||||
balance: 120,
|
||||
region_id: "region",
|
||||
});
|
||||
});
|
||||
})
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
const db = useDb();
|
||||
await db.teardown();
|
||||
});
|
||||
const db = useDb()
|
||||
await db.teardown()
|
||||
})
|
||||
|
||||
it("retrieves a gift card", async () => {
|
||||
const api = useApi();
|
||||
const api = useApi()
|
||||
|
||||
const response = await api.get("/store/gift-cards/GC_TEST");
|
||||
expect(response.status).toEqual(200);
|
||||
const response = await api.get("/store/gift-cards/GC_TEST")
|
||||
expect(response.status).toEqual(200)
|
||||
expect(response.data.gift_card).toEqual({
|
||||
id: "gift_test",
|
||||
code: "GC_TEST",
|
||||
value: 200,
|
||||
balance: 120,
|
||||
region: expect.any(Object),
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -65,6 +65,12 @@ describe("/store/variants", () => {
|
||||
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),
|
||||
@@ -79,6 +85,9 @@ describe("/store/variants", () => {
|
||||
},
|
||||
],
|
||||
product: expect.any(Object),
|
||||
options: [
|
||||
{ created_at: expect.any(String), updated_at: expect.any(String) },
|
||||
],
|
||||
},
|
||||
],
|
||||
})
|
||||
@@ -113,6 +122,12 @@ describe("/store/variants", () => {
|
||||
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),
|
||||
@@ -127,6 +142,9 @@ describe("/store/variants", () => {
|
||||
},
|
||||
],
|
||||
product: expect.any(Object),
|
||||
options: [
|
||||
{ created_at: expect.any(String), updated_at: expect.any(String) },
|
||||
],
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,94 +1,96 @@
|
||||
const path = require("path");
|
||||
const path = require("path")
|
||||
|
||||
const { ReturnReason } = require("@medusajs/medusa");
|
||||
const { ReturnReason } = require("@medusajs/medusa")
|
||||
|
||||
const setupServer = require("../../../helpers/setup-server");
|
||||
const { useApi } = require("../../../helpers/use-api");
|
||||
const { initDb, useDb } = require("../../../helpers/use-db");
|
||||
const setupServer = require("../../../helpers/setup-server")
|
||||
const { useApi } = require("../../../helpers/use-api")
|
||||
const { initDb, useDb } = require("../../../helpers/use-db")
|
||||
|
||||
jest.setTimeout(30000);
|
||||
jest.setTimeout(30000)
|
||||
|
||||
describe("/store/return-reasons", () => {
|
||||
let medusaProcess;
|
||||
let dbConnection;
|
||||
let medusaProcess
|
||||
let dbConnection
|
||||
|
||||
beforeAll(async () => {
|
||||
const cwd = path.resolve(path.join(__dirname, "..", ".."));
|
||||
dbConnection = await initDb({ cwd });
|
||||
medusaProcess = await setupServer({ cwd });
|
||||
});
|
||||
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();
|
||||
});
|
||||
const db = useDb()
|
||||
await db.shutdown()
|
||||
medusaProcess.kill()
|
||||
})
|
||||
|
||||
describe("GET /store/return-reasons", () => {
|
||||
let rrId;
|
||||
let rrId_1;
|
||||
let rrId_2;
|
||||
let rrId
|
||||
let rrId_1
|
||||
let rrId_2
|
||||
|
||||
beforeEach(async () => {
|
||||
try {
|
||||
const created = dbConnection.manager.create(ReturnReason, {
|
||||
value: "wrong_size",
|
||||
label: "Wrong size",
|
||||
});
|
||||
})
|
||||
|
||||
const result = await dbConnection.manager.save(created);
|
||||
rrId = result.id;
|
||||
const result = await dbConnection.manager.save(created)
|
||||
rrId = result.id
|
||||
|
||||
const created_child = dbConnection.manager.create(ReturnReason, {
|
||||
value: "too_big",
|
||||
label: "Too Big",
|
||||
parent_return_reason_id: rrId
|
||||
});
|
||||
parent_return_reason_id: rrId,
|
||||
})
|
||||
|
||||
const result_child = await dbConnection.manager.save(created_child);
|
||||
rrId_1 = result_child.id;
|
||||
const result_child = await dbConnection.manager.save(created_child)
|
||||
rrId_1 = result_child.id
|
||||
|
||||
const created_2 = dbConnection.manager.create(ReturnReason, {
|
||||
value: "too_big_1",
|
||||
label: "Too Big 1",
|
||||
});
|
||||
})
|
||||
|
||||
const result_2 = await dbConnection.manager.save(created_2);
|
||||
rrId_2 = result_2.id;
|
||||
const result_2 = await dbConnection.manager.save(created_2)
|
||||
rrId_2 = result_2.id
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
throw err;
|
||||
console.log(err)
|
||||
throw err
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
const db = useDb();
|
||||
await db.teardown();
|
||||
});
|
||||
const db = useDb()
|
||||
await db.teardown()
|
||||
})
|
||||
|
||||
it("list return reasons", async () => {
|
||||
const api = useApi();
|
||||
const api = useApi()
|
||||
|
||||
const response = await api.get("/store/return-reasons").catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
console.log(err)
|
||||
})
|
||||
|
||||
expect(response.status).toEqual(200);
|
||||
expect(response.status).toEqual(200)
|
||||
|
||||
expect(response.data.return_reasons).toEqual([
|
||||
expect.objectContaining({
|
||||
id: rrId,
|
||||
value: "wrong_size",
|
||||
return_reason_children:[expect.objectContaining({
|
||||
id: rrId_1,
|
||||
value: "too_big",
|
||||
}),]
|
||||
return_reason_children: [
|
||||
expect.objectContaining({
|
||||
id: rrId_1,
|
||||
value: "too_big",
|
||||
}),
|
||||
],
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: rrId_2,
|
||||
value: "too_big_1",
|
||||
}),
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
])
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const path = require("path");
|
||||
const path = require("path")
|
||||
const {
|
||||
Region,
|
||||
ReturnReason,
|
||||
@@ -12,56 +12,56 @@ const {
|
||||
LineItem,
|
||||
Discount,
|
||||
DiscountRule,
|
||||
} = require("@medusajs/medusa");
|
||||
} = require("@medusajs/medusa")
|
||||
|
||||
const setupServer = require("../../../helpers/setup-server");
|
||||
const { useApi } = require("../../../helpers/use-api");
|
||||
const { initDb, useDb } = require("../../../helpers/use-db");
|
||||
const setupServer = require("../../../helpers/setup-server")
|
||||
const { useApi } = require("../../../helpers/use-api")
|
||||
const { initDb, useDb } = require("../../../helpers/use-db")
|
||||
|
||||
jest.setTimeout(30000);
|
||||
jest.setTimeout(30000)
|
||||
|
||||
describe("/store/carts", () => {
|
||||
let medusaProcess;
|
||||
let dbConnection;
|
||||
let medusaProcess
|
||||
let dbConnection
|
||||
|
||||
beforeAll(async () => {
|
||||
const cwd = path.resolve(path.join(__dirname, "..", ".."));
|
||||
dbConnection = await initDb({ cwd });
|
||||
medusaProcess = await setupServer({ cwd });
|
||||
});
|
||||
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();
|
||||
});
|
||||
const db = useDb()
|
||||
await db.shutdown()
|
||||
medusaProcess.kill()
|
||||
})
|
||||
|
||||
describe("POST /store/returns", () => {
|
||||
let rrId;
|
||||
let rrId_child;
|
||||
let rrResult;
|
||||
let rrId
|
||||
let rrId_child
|
||||
let rrResult
|
||||
|
||||
beforeEach(async () => {
|
||||
const manager = dbConnection.manager;
|
||||
const manager = dbConnection.manager
|
||||
await manager.query(
|
||||
`ALTER SEQUENCE order_display_id_seq RESTART WITH 111`
|
||||
);
|
||||
)
|
||||
|
||||
const defaultProfile = await manager.findOne(ShippingProfile, {
|
||||
type: "default",
|
||||
});
|
||||
|
||||
})
|
||||
|
||||
await manager.insert(Region, {
|
||||
id: "region",
|
||||
name: "Test Region",
|
||||
currency_code: "usd",
|
||||
tax_rate: 0,
|
||||
});
|
||||
|
||||
})
|
||||
|
||||
await manager.insert(Customer, {
|
||||
id: "cus_1234",
|
||||
email: "test@email.com",
|
||||
});
|
||||
})
|
||||
|
||||
await manager.insert(Order, {
|
||||
id: "order_test",
|
||||
@@ -71,7 +71,7 @@ describe("/store/carts", () => {
|
||||
region_id: "region",
|
||||
tax_rate: 0,
|
||||
currency_code: "usd",
|
||||
});
|
||||
})
|
||||
|
||||
await manager.insert(DiscountRule, {
|
||||
id: "discount_rule_id",
|
||||
@@ -79,7 +79,7 @@ describe("/store/carts", () => {
|
||||
value: 10,
|
||||
allocation: "total",
|
||||
type: "percentage",
|
||||
});
|
||||
})
|
||||
|
||||
const d = manager.create(Discount, {
|
||||
id: "test-discount",
|
||||
@@ -87,9 +87,9 @@ describe("/store/carts", () => {
|
||||
is_dynamic: false,
|
||||
is_disabled: false,
|
||||
rule_id: "discount_rule_id",
|
||||
});
|
||||
})
|
||||
|
||||
await manager.save(d);
|
||||
await manager.save(d)
|
||||
|
||||
const ord = manager.create(Order, {
|
||||
id: "order_with_discount",
|
||||
@@ -99,18 +99,18 @@ describe("/store/carts", () => {
|
||||
region_id: "region",
|
||||
tax_rate: 0,
|
||||
currency_code: "usd",
|
||||
});
|
||||
})
|
||||
|
||||
ord.discounts = [d];
|
||||
ord.discounts = [d]
|
||||
|
||||
await manager.save(ord);
|
||||
await manager.save(ord)
|
||||
|
||||
await manager.insert(Product, {
|
||||
id: "test-product",
|
||||
title: "test product",
|
||||
profile_id: defaultProfile.id,
|
||||
options: [{ id: "test-option", title: "Size" }],
|
||||
});
|
||||
})
|
||||
|
||||
await manager.insert(ProductVariant, {
|
||||
id: "test-variant",
|
||||
@@ -123,7 +123,7 @@ describe("/store/carts", () => {
|
||||
value: "Size",
|
||||
},
|
||||
],
|
||||
});
|
||||
})
|
||||
|
||||
await manager.insert(LineItem, {
|
||||
id: "test-item",
|
||||
@@ -135,7 +135,7 @@ describe("/store/carts", () => {
|
||||
unit_price: 8000,
|
||||
quantity: 1,
|
||||
variant_id: "test-variant",
|
||||
});
|
||||
})
|
||||
|
||||
await manager.insert(ShippingOption, {
|
||||
id: "test-option",
|
||||
@@ -147,35 +147,35 @@ describe("/store/carts", () => {
|
||||
price_type: "flat_rate",
|
||||
amount: 1000,
|
||||
is_return: true,
|
||||
});
|
||||
})
|
||||
|
||||
const created = dbConnection.manager.create(ReturnReason, {
|
||||
value: "wrong_size",
|
||||
label: "Wrong Size",
|
||||
});
|
||||
const result = await dbConnection.manager.save(created);
|
||||
})
|
||||
const result = await dbConnection.manager.save(created)
|
||||
|
||||
rrResult = result
|
||||
rrId = result.id;
|
||||
rrId = result.id
|
||||
|
||||
const created_1 = dbConnection.manager.create(ReturnReason, {
|
||||
value: "too_big",
|
||||
label: "Too Big",
|
||||
parent_return_reason_id: rrId,
|
||||
});
|
||||
})
|
||||
|
||||
const result_1 = await dbConnection.manager.save(created_1);
|
||||
const result_1 = await dbConnection.manager.save(created_1)
|
||||
|
||||
rrId_child = result_1.id;
|
||||
});
|
||||
rrId_child = result_1.id
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
const db = useDb();
|
||||
await db.teardown();
|
||||
});
|
||||
const db = useDb()
|
||||
await db.teardown()
|
||||
})
|
||||
|
||||
it("creates a return", async () => {
|
||||
const api = useApi();
|
||||
const api = useApi()
|
||||
|
||||
const response = await api
|
||||
.post("/store/returns", {
|
||||
@@ -188,16 +188,16 @@ describe("/store/carts", () => {
|
||||
],
|
||||
})
|
||||
.catch((err) => {
|
||||
return err.response;
|
||||
});
|
||||
expect(response.status).toEqual(200);
|
||||
return err.response
|
||||
})
|
||||
expect(response.status).toEqual(200)
|
||||
|
||||
expect(response.data.return.refund_amount).toEqual(8000);
|
||||
});
|
||||
expect(response.data.return.refund_amount).toEqual(8000)
|
||||
})
|
||||
|
||||
it("failes to create a return with a reason category", async () => {
|
||||
const api = useApi();
|
||||
|
||||
const api = useApi()
|
||||
|
||||
const response = await api
|
||||
.post("/store/returns", {
|
||||
order_id: "order_test",
|
||||
@@ -211,17 +211,18 @@ describe("/store/carts", () => {
|
||||
],
|
||||
})
|
||||
.catch((err) => {
|
||||
return err.response;
|
||||
});
|
||||
return err.response
|
||||
})
|
||||
|
||||
expect(response.status).toEqual(400);
|
||||
expect(response.data.message).toEqual('Cannot apply return reason category')
|
||||
|
||||
});
|
||||
expect(response.status).toEqual(400)
|
||||
expect(response.data.message).toEqual(
|
||||
"Cannot apply return reason category"
|
||||
)
|
||||
})
|
||||
|
||||
it("creates a return with reasons", async () => {
|
||||
const api = useApi();
|
||||
|
||||
const api = useApi()
|
||||
|
||||
const response = await api
|
||||
.post("/store/returns", {
|
||||
order_id: "order_test",
|
||||
@@ -236,28 +237,28 @@ describe("/store/carts", () => {
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err.response)
|
||||
return err.response;
|
||||
});
|
||||
expect(response.status).toEqual(200);
|
||||
return err.response
|
||||
})
|
||||
expect(response.status).toEqual(200)
|
||||
|
||||
expect(response.data.return.items).toEqual([
|
||||
expect.objectContaining({
|
||||
reason_id: rrId_child,
|
||||
note: "TOO small",
|
||||
}),
|
||||
]);
|
||||
});
|
||||
])
|
||||
})
|
||||
|
||||
it("creates a return with discount and non-discountable item", async () => {
|
||||
const api = useApi();
|
||||
const api = useApi()
|
||||
|
||||
await dbConnection.manager.query(
|
||||
`UPDATE line_item set allow_discounts=false where id='test-item'`
|
||||
);
|
||||
)
|
||||
|
||||
await dbConnection.manager.query(
|
||||
`UPDATE line_item set order_id='order_with_discount' where id='test-item'`
|
||||
);
|
||||
)
|
||||
|
||||
const response = await api
|
||||
.post("/store/returns", {
|
||||
@@ -270,15 +271,15 @@ describe("/store/carts", () => {
|
||||
],
|
||||
})
|
||||
.catch((err) => {
|
||||
return err.response;
|
||||
});
|
||||
return err.response
|
||||
})
|
||||
|
||||
expect(response.status).toEqual(200);
|
||||
expect(response.data.return.refund_amount).toEqual(8000);
|
||||
});
|
||||
expect(response.status).toEqual(200)
|
||||
expect(response.data.return.refund_amount).toEqual(8000)
|
||||
})
|
||||
|
||||
it("creates a return with shipping method", async () => {
|
||||
const api = useApi();
|
||||
const api = useApi()
|
||||
|
||||
const response = await api
|
||||
.post("/store/returns", {
|
||||
@@ -294,12 +295,11 @@ describe("/store/carts", () => {
|
||||
],
|
||||
})
|
||||
.catch((err) => {
|
||||
return err.response;
|
||||
});
|
||||
expect(response.status).toEqual(200);
|
||||
return err.response
|
||||
})
|
||||
expect(response.status).toEqual(200)
|
||||
|
||||
expect(response.data.return.refund_amount).toEqual(7000);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
expect(response.data.return.refund_amount).toEqual(7000)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,51 +1,53 @@
|
||||
const path = require("path");
|
||||
const { Region, ShippingProfile, ShippingOption } = require("@medusajs/medusa");
|
||||
const path = require("path")
|
||||
const { Region, ShippingProfile, ShippingOption } = require("@medusajs/medusa")
|
||||
|
||||
const setupServer = require("../../../helpers/setup-server");
|
||||
const { useApi } = require("../../../helpers/use-api");
|
||||
const { initDb, useDb } = require("../../../helpers/use-db");
|
||||
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 swapSeeder = require("../../helpers/swap-seeder")
|
||||
|
||||
jest.setTimeout(30000);
|
||||
jest.setTimeout(30000)
|
||||
|
||||
describe("/store/shipping-options", () => {
|
||||
let medusaProcess;
|
||||
let dbConnection;
|
||||
let medusaProcess
|
||||
let dbConnection
|
||||
|
||||
beforeAll(async () => {
|
||||
const cwd = path.resolve(path.join(__dirname, "..", ".."));
|
||||
dbConnection = await initDb({ cwd });
|
||||
medusaProcess = await setupServer({ cwd });
|
||||
});
|
||||
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();
|
||||
});
|
||||
const db = useDb()
|
||||
await db.shutdown()
|
||||
medusaProcess.kill()
|
||||
})
|
||||
|
||||
describe("POST /store/shipping-options", () => {
|
||||
beforeEach(async () => {
|
||||
const manager = dbConnection.manager;
|
||||
const manager = dbConnection.manager
|
||||
await manager.query(
|
||||
`ALTER SEQUENCE order_display_id_seq RESTART WITH 111`
|
||||
);
|
||||
)
|
||||
|
||||
await manager.insert(Region, {
|
||||
id: "region",
|
||||
name: "Test Region",
|
||||
currency_code: "usd",
|
||||
tax_rate: 0,
|
||||
});
|
||||
})
|
||||
await manager.insert(Region, {
|
||||
id: "region2",
|
||||
name: "Test Region 2",
|
||||
currency_code: "usd",
|
||||
tax_rate: 0,
|
||||
});
|
||||
})
|
||||
|
||||
const defaultProfile = await manager.findOne(ShippingProfile, {
|
||||
type: "default",
|
||||
});
|
||||
})
|
||||
|
||||
await manager.insert(ShippingOption, {
|
||||
id: "test-out",
|
||||
@@ -57,7 +59,7 @@ describe("/store/shipping-options", () => {
|
||||
price_type: "flat_rate",
|
||||
amount: 2000,
|
||||
is_return: false,
|
||||
});
|
||||
})
|
||||
|
||||
await manager.insert(ShippingOption, {
|
||||
id: "test-return",
|
||||
@@ -69,7 +71,7 @@ describe("/store/shipping-options", () => {
|
||||
price_type: "flat_rate",
|
||||
amount: 1000,
|
||||
is_return: true,
|
||||
});
|
||||
})
|
||||
|
||||
await manager.insert(ShippingOption, {
|
||||
id: "test-region2",
|
||||
@@ -81,51 +83,102 @@ describe("/store/shipping-options", () => {
|
||||
price_type: "flat_rate",
|
||||
amount: 1000,
|
||||
is_return: false,
|
||||
});
|
||||
});
|
||||
})
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
const db = useDb();
|
||||
await db.teardown();
|
||||
});
|
||||
const db = useDb()
|
||||
await db.teardown()
|
||||
})
|
||||
|
||||
it("retrieves all shipping options", async () => {
|
||||
const api = useApi();
|
||||
const api = useApi()
|
||||
|
||||
const response = await api.get("/store/shipping-options").catch((err) => {
|
||||
return err.response;
|
||||
});
|
||||
return err.response
|
||||
})
|
||||
|
||||
expect(response.status).toEqual(200);
|
||||
expect(response.data.shipping_options.length).toEqual(3);
|
||||
});
|
||||
expect(response.status).toEqual(200)
|
||||
expect(response.data.shipping_options.length).toEqual(3)
|
||||
})
|
||||
|
||||
it("creates a return with shipping method", async () => {
|
||||
const api = useApi();
|
||||
const api = useApi()
|
||||
|
||||
const response = await api
|
||||
.get("/store/shipping-options?is_return=true")
|
||||
.catch((err) => {
|
||||
return err.response;
|
||||
});
|
||||
return err.response
|
||||
})
|
||||
|
||||
expect(response.status).toEqual(200);
|
||||
expect(response.data.shipping_options.length).toEqual(1);
|
||||
expect(response.data.shipping_options[0].id).toEqual("test-return");
|
||||
});
|
||||
expect(response.status).toEqual(200)
|
||||
expect(response.data.shipping_options.length).toEqual(1)
|
||||
expect(response.data.shipping_options[0].id).toEqual("test-return")
|
||||
})
|
||||
|
||||
it("creates a return with shipping method", async () => {
|
||||
const api = useApi();
|
||||
const api = useApi()
|
||||
|
||||
const response = await api
|
||||
.get("/store/shipping-options?region_id=region2")
|
||||
.catch((err) => {
|
||||
return err.response;
|
||||
});
|
||||
return err.response
|
||||
})
|
||||
|
||||
expect(response.status).toEqual(200);
|
||||
expect(response.data.shipping_options.length).toEqual(1);
|
||||
expect(response.data.shipping_options[0].id).toEqual("test-region2");
|
||||
});
|
||||
});
|
||||
});
|
||||
expect(response.status).toEqual(200)
|
||||
expect(response.data.shipping_options.length).toEqual(1)
|
||||
expect(response.data.shipping_options[0].id).toEqual("test-region2")
|
||||
})
|
||||
})
|
||||
|
||||
describe("GET /store/shipping-options/:cart_id", () => {
|
||||
beforeEach(async () => {
|
||||
await cartSeeder(dbConnection)
|
||||
await swapSeeder(dbConnection)
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
const db = useDb()
|
||||
await db.teardown()
|
||||
})
|
||||
|
||||
it("given a default cart, when user retrieves its shipping options, then should return a list of shipping options", async () => {
|
||||
const api = useApi()
|
||||
|
||||
const response = await api
|
||||
.get("/store/shipping-options/test-cart-2")
|
||||
.catch((err) => {
|
||||
return err.response
|
||||
})
|
||||
|
||||
expect(response.status).toEqual(200)
|
||||
expect(response.data.shipping_options).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({ id: "test-option", amount: 1000 }),
|
||||
expect.objectContaining({ id: "test-option-2", amount: 500 }),
|
||||
])
|
||||
)
|
||||
})
|
||||
|
||||
it("given a cart with custom shipping options, when user retrieves its shipping options, then should return the list of custom shipping options", async () => {
|
||||
const api = useApi()
|
||||
|
||||
const response = await api
|
||||
.get("/store/shipping-options/test-cart-rma")
|
||||
.catch((err) => {
|
||||
return err.response
|
||||
})
|
||||
|
||||
expect(response.status).toEqual(200)
|
||||
expect(response.data.shipping_options).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: "test-option",
|
||||
amount: 0,
|
||||
name: "test-option",
|
||||
}),
|
||||
])
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,33 +1,33 @@
|
||||
const { useApi } = require("../../helpers/use-api");
|
||||
const { useApi } = require("../../helpers/use-api")
|
||||
|
||||
const header = {
|
||||
headers: {
|
||||
authorization: "Bearer test_token",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const resolveCall = async (path, payload, header) => {
|
||||
const api = useApi();
|
||||
let res;
|
||||
const api = useApi()
|
||||
let res
|
||||
try {
|
||||
const resp = await api.post(path, payload, header);
|
||||
res = resp.status;
|
||||
const resp = await api.post(path, payload, header)
|
||||
res = resp.status
|
||||
} catch (expectedException) {
|
||||
try {
|
||||
res = expectedException.response.status;
|
||||
res = expectedException.response.status
|
||||
} catch (_) {
|
||||
console.error(expectedException);
|
||||
console.error(expectedException)
|
||||
}
|
||||
}
|
||||
return res;
|
||||
};
|
||||
return res
|
||||
}
|
||||
|
||||
const determineFail = (actual, expected, path) => {
|
||||
if (expected !== actual) {
|
||||
console.log(`failed at path : ${path}`);
|
||||
console.log(`failed at path : ${path}`)
|
||||
}
|
||||
expect(actual).toEqual(expected);
|
||||
};
|
||||
expect(actual).toEqual(expected)
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows you to wrap a Call function so that you may reuse some input values.
|
||||
@@ -36,8 +36,8 @@ const determineFail = (actual, expected, path) => {
|
||||
* @returns
|
||||
*/
|
||||
module.exports.partial = function (fun, input = {}) {
|
||||
return async (remaining) => await fun({ ...remaining, ...input });
|
||||
};
|
||||
return async (remaining) => await fun({ ...remaining, ...input })
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows you to assert a specific code result from a POST call.
|
||||
@@ -51,9 +51,9 @@ module.exports.expectPostCallToReturn = async function (
|
||||
payload: {},
|
||||
}
|
||||
) {
|
||||
const res = await resolveCall(input.path, input.payload, header);
|
||||
determineFail(res, input.code, input.path);
|
||||
};
|
||||
const res = await resolveCall(input.path, input.payload, header)
|
||||
determineFail(res, input.code, input.path)
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows you to assert a specific code result from multiple POST
|
||||
@@ -76,10 +76,10 @@ module.exports.expectAllPostCallsToReturn = async function (
|
||||
input.pathf(i),
|
||||
input.payloadf ? input.payloadf(i) : {},
|
||||
header
|
||||
);
|
||||
determineFail(res, input.code, input.pathf(i));
|
||||
)
|
||||
determineFail(res, input.code, input.pathf(i))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows you to retrieve a specific object the response
|
||||
@@ -92,9 +92,9 @@ module.exports.expectAllPostCallsToReturn = async function (
|
||||
* to the get parameter provided.
|
||||
*/
|
||||
module.exports.callGet = async function ({ path, get }) {
|
||||
const api = useApi();
|
||||
const res = await api.get(path, header);
|
||||
const api = useApi()
|
||||
const res = await api.get(path, header)
|
||||
|
||||
determineFail(res.status, 200, path);
|
||||
return res?.data[get];
|
||||
};
|
||||
determineFail(res.status, 200, path)
|
||||
return res?.data[get]
|
||||
}
|
||||
|
||||
@@ -17,6 +17,18 @@ const {
|
||||
} = 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, {
|
||||
@@ -88,12 +100,36 @@ module.exports = async (connection, data = {}) => {
|
||||
code: "10PERCENT",
|
||||
is_dynamic: false,
|
||||
is_disabled: false,
|
||||
starts_at: tenDaysAgo,
|
||||
ends_at: tenDaysFromToday,
|
||||
})
|
||||
|
||||
tenPercent.regions = [r]
|
||||
tenPercent.rule = tenPercentRule
|
||||
await manager.save(tenPercent)
|
||||
|
||||
const dUsageLimit = await manager.create(Discount, {
|
||||
id: "test-discount-usage-limit",
|
||||
code: "SPENT",
|
||||
is_dynamic: false,
|
||||
is_disabled: false,
|
||||
usage_limit: 10,
|
||||
usage_count: 10,
|
||||
})
|
||||
|
||||
const drUsage = await manager.create(DiscountRule, {
|
||||
id: "test-discount-rule-usage-limit",
|
||||
description: "Created",
|
||||
type: "fixed",
|
||||
value: 10000,
|
||||
allocation: "total",
|
||||
})
|
||||
|
||||
dUsageLimit.rule = drUsage
|
||||
dUsageLimit.regions = [r]
|
||||
|
||||
await manager.save(dUsageLimit)
|
||||
|
||||
const d = await manager.create(Discount, {
|
||||
id: "test-discount",
|
||||
code: "CREATED",
|
||||
@@ -114,6 +150,103 @@ module.exports = async (connection, data = {}) => {
|
||||
|
||||
await manager.save(d)
|
||||
|
||||
const usedDiscount = manager.create(Discount, {
|
||||
id: "used-discount",
|
||||
code: "USED",
|
||||
is_dynamic: false,
|
||||
is_disabled: false,
|
||||
usage_limit: 1,
|
||||
usage_count: 1,
|
||||
})
|
||||
|
||||
await manager.save(usedDiscount)
|
||||
|
||||
const expiredRule = manager.create(DiscountRule, {
|
||||
id: "expiredRule",
|
||||
description: "expired rule",
|
||||
type: "fixed",
|
||||
value: 100,
|
||||
allocation: "total",
|
||||
})
|
||||
|
||||
const expiredDisc = manager.create(Discount, {
|
||||
id: "expiredDisc",
|
||||
code: "EXP_DISC",
|
||||
is_dynamic: false,
|
||||
is_disabled: false,
|
||||
starts_at: tenDaysAgo,
|
||||
ends_at: yesterday,
|
||||
})
|
||||
|
||||
expiredDisc.regions = [r]
|
||||
expiredDisc.rule = expiredRule
|
||||
await manager.save(expiredDisc)
|
||||
|
||||
const prematureRule = manager.create(DiscountRule, {
|
||||
id: "prematureRule",
|
||||
description: "premature rule",
|
||||
type: "fixed",
|
||||
value: 100,
|
||||
allocation: "total",
|
||||
})
|
||||
|
||||
const prematureDiscount = manager.create(Discount, {
|
||||
id: "prematureDiscount",
|
||||
code: "PREM_DISC",
|
||||
is_dynamic: false,
|
||||
is_disabled: false,
|
||||
starts_at: tomorrow,
|
||||
ends_at: tenDaysFromToday,
|
||||
})
|
||||
|
||||
prematureDiscount.regions = [r]
|
||||
prematureDiscount.rule = prematureRule
|
||||
await manager.save(prematureDiscount)
|
||||
|
||||
const invalidDynamicRule = manager.create(DiscountRule, {
|
||||
id: "invalidDynamicRule",
|
||||
description: "invalidDynamic rule",
|
||||
type: "fixed",
|
||||
value: 100,
|
||||
allocation: "total",
|
||||
})
|
||||
|
||||
const invalidDynamicDiscount = manager.create(Discount, {
|
||||
id: "invalidDynamicDiscount",
|
||||
code: "INV_DYN_DISC",
|
||||
is_dynamic: true,
|
||||
is_disabled: false,
|
||||
starts_at: tenDaysAgo,
|
||||
ends_at: tenDaysFromToday,
|
||||
valid_duration: "P1D", // one day
|
||||
})
|
||||
|
||||
invalidDynamicDiscount.regions = [r]
|
||||
invalidDynamicDiscount.rule = invalidDynamicRule
|
||||
await manager.save(invalidDynamicDiscount)
|
||||
|
||||
const DynamicRule = manager.create(DiscountRule, {
|
||||
id: "DynamicRule",
|
||||
description: "Dynamic rule",
|
||||
type: "fixed",
|
||||
value: 10000,
|
||||
allocation: "total",
|
||||
})
|
||||
|
||||
const DynamicDiscount = manager.create(Discount, {
|
||||
id: "DynamicDiscount",
|
||||
code: "DYN_DISC",
|
||||
is_dynamic: true,
|
||||
is_disabled: false,
|
||||
starts_at: tenDaysAgo,
|
||||
ends_at: tenDaysFromToday,
|
||||
valid_duration: "P1M", //one month
|
||||
})
|
||||
|
||||
DynamicDiscount.regions = [r]
|
||||
DynamicDiscount.rule = DynamicRule
|
||||
await manager.save(DynamicDiscount)
|
||||
|
||||
await manager.query(
|
||||
`UPDATE "country" SET region_id='test-region' WHERE iso_2 = 'us'`
|
||||
)
|
||||
@@ -304,6 +437,11 @@ module.exports = async (connection, data = {}) => {
|
||||
data: {},
|
||||
})
|
||||
|
||||
await manager.save(pay)
|
||||
|
||||
cart2.payment = pay
|
||||
|
||||
await manager.save(cart2)
|
||||
const swapPay = manager.create(Payment, {
|
||||
id: "test-swap-payment",
|
||||
amount: 10000,
|
||||
|
||||
@@ -4,10 +4,10 @@ const {
|
||||
LineItem,
|
||||
Fulfillment,
|
||||
Return,
|
||||
} = require("@medusajs/medusa");
|
||||
} = require("@medusajs/medusa")
|
||||
|
||||
module.exports = async (connection, data = {}) => {
|
||||
const manager = connection.manager;
|
||||
const manager = connection.manager
|
||||
|
||||
let orderWithClaim = manager.create(Order, {
|
||||
id: "order-with-claim",
|
||||
@@ -40,9 +40,9 @@ module.exports = async (connection, data = {}) => {
|
||||
],
|
||||
items: [],
|
||||
...data,
|
||||
});
|
||||
})
|
||||
|
||||
await manager.save(orderWithClaim);
|
||||
await manager.save(orderWithClaim)
|
||||
|
||||
const li = manager.create(LineItem, {
|
||||
id: "test-item-co-2",
|
||||
@@ -54,9 +54,9 @@ module.exports = async (connection, data = {}) => {
|
||||
quantity: 1,
|
||||
variant_id: "test-variant",
|
||||
order_id: orderWithClaim.id,
|
||||
});
|
||||
})
|
||||
|
||||
await manager.save(li);
|
||||
await manager.save(li)
|
||||
|
||||
const li2 = manager.create(LineItem, {
|
||||
id: "test-item-co-3",
|
||||
@@ -68,9 +68,9 @@ module.exports = async (connection, data = {}) => {
|
||||
quantity: 4,
|
||||
variant_id: "test-variant",
|
||||
order_id: orderWithClaim.id,
|
||||
});
|
||||
})
|
||||
|
||||
await manager.save(li2);
|
||||
await manager.save(li2)
|
||||
|
||||
const claimWithFulfillment = manager.create(ClaimOrder, {
|
||||
id: "claim-w-f",
|
||||
@@ -79,23 +79,23 @@ module.exports = async (connection, data = {}) => {
|
||||
fulfillment_status: "not_fulfilled",
|
||||
order_id: "order-with-claim",
|
||||
...data,
|
||||
});
|
||||
})
|
||||
|
||||
const ful1 = manager.create(Fulfillment, {
|
||||
id: "fulfillment-co-1",
|
||||
data: {},
|
||||
provider_id: "test-ful",
|
||||
});
|
||||
})
|
||||
|
||||
const ful2 = manager.create(Fulfillment, {
|
||||
id: "fulfillment-co-2",
|
||||
data: {},
|
||||
provider_id: "test-ful",
|
||||
});
|
||||
})
|
||||
|
||||
claimWithFulfillment.fulfillments = [ful1, ful2];
|
||||
claimWithFulfillment.fulfillments = [ful1, ful2]
|
||||
|
||||
await manager.save(claimWithFulfillment);
|
||||
await manager.save(claimWithFulfillment)
|
||||
|
||||
const claimWithReturn = manager.create(ClaimOrder, {
|
||||
id: "claim-w-r",
|
||||
@@ -104,9 +104,9 @@ module.exports = async (connection, data = {}) => {
|
||||
fulfillment_status: "not_fulfilled",
|
||||
order_id: "order-with-claim",
|
||||
...data,
|
||||
});
|
||||
})
|
||||
|
||||
await manager.save(claimWithReturn);
|
||||
await manager.save(claimWithReturn)
|
||||
|
||||
await manager.insert(Return, {
|
||||
id: "return-id-2",
|
||||
@@ -114,5 +114,5 @@ module.exports = async (connection, data = {}) => {
|
||||
status: "requested",
|
||||
refund_amount: 0,
|
||||
data: {},
|
||||
});
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
@@ -14,35 +14,35 @@ const {
|
||||
Discount,
|
||||
DiscountRule,
|
||||
Payment,
|
||||
} = require("@medusajs/medusa");
|
||||
} = require("@medusajs/medusa")
|
||||
|
||||
module.exports = async (connection, data = {}) => {
|
||||
const manager = connection.manager;
|
||||
const manager = connection.manager
|
||||
|
||||
const defaultProfile = await manager.findOne(ShippingProfile, {
|
||||
type: "default",
|
||||
});
|
||||
})
|
||||
|
||||
await manager.insert(Product, {
|
||||
id: "test-product",
|
||||
title: "test product",
|
||||
profile_id: defaultProfile.id,
|
||||
options: [{ id: "test-option", title: "Size" }],
|
||||
});
|
||||
})
|
||||
|
||||
await manager.insert(Address, {
|
||||
id: "oli-shipping",
|
||||
first_name: "oli",
|
||||
last_name: "test",
|
||||
country_code: "us",
|
||||
});
|
||||
})
|
||||
|
||||
await manager.insert(Product, {
|
||||
id: "test-product-2",
|
||||
title: "test product 2",
|
||||
profile_id: defaultProfile.id,
|
||||
options: [{ id: "test-option-color", title: "Color" }],
|
||||
});
|
||||
})
|
||||
|
||||
await manager.insert(ProductVariant, {
|
||||
id: "test-variant",
|
||||
@@ -55,7 +55,7 @@ module.exports = async (connection, data = {}) => {
|
||||
value: "Size",
|
||||
},
|
||||
],
|
||||
});
|
||||
})
|
||||
|
||||
await manager.insert(ProductVariant, {
|
||||
id: "test-variant-2",
|
||||
@@ -68,22 +68,22 @@ module.exports = async (connection, data = {}) => {
|
||||
value: "Color",
|
||||
},
|
||||
],
|
||||
});
|
||||
})
|
||||
|
||||
const ma = manager.create(MoneyAmount, {
|
||||
variant_id: "test-variant",
|
||||
currency_code: "usd",
|
||||
amount: 8000,
|
||||
});
|
||||
await manager.save(ma);
|
||||
})
|
||||
await manager.save(ma)
|
||||
|
||||
const ma2 = manager.create(MoneyAmount, {
|
||||
variant_id: "test-variant-2",
|
||||
currency_code: "usd",
|
||||
amount: 10000,
|
||||
});
|
||||
})
|
||||
|
||||
await manager.save(ma2);
|
||||
await manager.save(ma2)
|
||||
|
||||
await manager.insert(Region, {
|
||||
id: "test-region",
|
||||
@@ -96,7 +96,7 @@ module.exports = async (connection, data = {}) => {
|
||||
is_installed: true,
|
||||
},
|
||||
],
|
||||
});
|
||||
})
|
||||
|
||||
await manager.insert(Region, {
|
||||
id: "test-region-2",
|
||||
@@ -109,7 +109,7 @@ module.exports = async (connection, data = {}) => {
|
||||
is_installed: true,
|
||||
},
|
||||
],
|
||||
});
|
||||
})
|
||||
|
||||
await manager.insert(DiscountRule, {
|
||||
id: "discount_rule_id",
|
||||
@@ -117,7 +117,7 @@ module.exports = async (connection, data = {}) => {
|
||||
value: 10,
|
||||
allocation: "total",
|
||||
type: "percentage",
|
||||
});
|
||||
})
|
||||
|
||||
const d = manager.create(Discount, {
|
||||
id: "test-discount",
|
||||
@@ -125,7 +125,7 @@ module.exports = async (connection, data = {}) => {
|
||||
is_dynamic: false,
|
||||
is_disabled: false,
|
||||
rule_id: "discount_rule_id",
|
||||
});
|
||||
})
|
||||
|
||||
d.regions = [
|
||||
{
|
||||
@@ -134,27 +134,27 @@ module.exports = async (connection, data = {}) => {
|
||||
currency_code: "usd",
|
||||
tax_rate: 0,
|
||||
},
|
||||
];
|
||||
]
|
||||
|
||||
await manager.save(d);
|
||||
await manager.save(d)
|
||||
|
||||
await manager.query(
|
||||
`UPDATE "country" SET region_id='test-region' WHERE iso_2 = 'us'`
|
||||
);
|
||||
)
|
||||
|
||||
await manager.query(
|
||||
`UPDATE "country" SET region_id='test-region-2' WHERE iso_2 = 'de'`
|
||||
);
|
||||
)
|
||||
|
||||
await manager.insert(Customer, {
|
||||
id: "oli-test",
|
||||
email: "oli@test.dk",
|
||||
});
|
||||
})
|
||||
|
||||
await manager.insert(Customer, {
|
||||
id: "lebron-james",
|
||||
email: "lebron@james.com",
|
||||
});
|
||||
})
|
||||
|
||||
await manager.insert(ShippingOption, {
|
||||
id: "test-option",
|
||||
@@ -165,7 +165,7 @@ module.exports = async (connection, data = {}) => {
|
||||
price_type: "flat_rate",
|
||||
amount: 1000,
|
||||
data: {},
|
||||
});
|
||||
})
|
||||
|
||||
await manager.insert(ShippingOption, {
|
||||
id: "test-option-req",
|
||||
@@ -176,14 +176,14 @@ module.exports = async (connection, data = {}) => {
|
||||
price_type: "flat_rate",
|
||||
amount: 1000,
|
||||
data: {},
|
||||
});
|
||||
})
|
||||
|
||||
await manager.insert(ShippingOptionRequirement, {
|
||||
id: "option-req",
|
||||
shipping_option_id: "test-option-req",
|
||||
type: "min_subtotal",
|
||||
amount: 10,
|
||||
});
|
||||
})
|
||||
|
||||
const c = manager.create(Cart, {
|
||||
id: "test-cart",
|
||||
@@ -207,7 +207,7 @@ module.exports = async (connection, data = {}) => {
|
||||
],
|
||||
type: "draft_order",
|
||||
metadata: { draft_order_id: "test-draft-order" },
|
||||
});
|
||||
})
|
||||
|
||||
const pay = manager.create(Payment, {
|
||||
id: "test-payment",
|
||||
@@ -216,13 +216,13 @@ module.exports = async (connection, data = {}) => {
|
||||
amount_refunded: 0,
|
||||
provider_id: "test-pay",
|
||||
data: {},
|
||||
});
|
||||
})
|
||||
|
||||
await manager.save(pay);
|
||||
await manager.save(pay)
|
||||
|
||||
c.payment = pay;
|
||||
c.payment = pay
|
||||
|
||||
await manager.save(c);
|
||||
await manager.save(c)
|
||||
|
||||
await manager.insert(PaymentSession, {
|
||||
id: "test-session",
|
||||
@@ -231,7 +231,7 @@ module.exports = async (connection, data = {}) => {
|
||||
is_selected: true,
|
||||
data: {},
|
||||
status: "authorized",
|
||||
});
|
||||
})
|
||||
|
||||
const draftOrder = manager.create(DraftOrder, {
|
||||
id: "test-draft-order",
|
||||
@@ -255,7 +255,7 @@ module.exports = async (connection, data = {}) => {
|
||||
region_id: "test-region",
|
||||
discounts: [],
|
||||
...data,
|
||||
});
|
||||
})
|
||||
|
||||
await manager.save(draftOrder);
|
||||
};
|
||||
await manager.save(draftOrder)
|
||||
}
|
||||
|
||||
@@ -12,21 +12,21 @@ const {
|
||||
Payment,
|
||||
Order,
|
||||
Swap,
|
||||
} = require("@medusajs/medusa");
|
||||
} = require("@medusajs/medusa")
|
||||
|
||||
module.exports = async (connection, data = {}) => {
|
||||
const manager = connection.manager;
|
||||
const manager = connection.manager
|
||||
|
||||
const defaultProfile = await manager.findOne(ShippingProfile, {
|
||||
type: "default",
|
||||
});
|
||||
})
|
||||
|
||||
await manager.insert(Product, {
|
||||
id: "test-product",
|
||||
title: "test product",
|
||||
profile_id: defaultProfile.id,
|
||||
options: [{ id: "test-option", title: "Size" }],
|
||||
});
|
||||
})
|
||||
|
||||
await manager.insert(ProductVariant, {
|
||||
id: "test-variant",
|
||||
@@ -39,7 +39,7 @@ module.exports = async (connection, data = {}) => {
|
||||
value: "Size",
|
||||
},
|
||||
],
|
||||
});
|
||||
})
|
||||
|
||||
await manager.insert(ProductVariant, {
|
||||
id: "test-variant-2",
|
||||
@@ -52,37 +52,37 @@ module.exports = async (connection, data = {}) => {
|
||||
value: "Large",
|
||||
},
|
||||
],
|
||||
});
|
||||
})
|
||||
|
||||
const ma2 = manager.create(MoneyAmount, {
|
||||
variant_id: "test-variant-2",
|
||||
currency_code: "usd",
|
||||
amount: 8000,
|
||||
});
|
||||
await manager.save(ma2);
|
||||
})
|
||||
await manager.save(ma2)
|
||||
|
||||
const ma = manager.create(MoneyAmount, {
|
||||
variant_id: "test-variant",
|
||||
currency_code: "usd",
|
||||
amount: 8000,
|
||||
});
|
||||
await manager.save(ma);
|
||||
})
|
||||
await manager.save(ma)
|
||||
|
||||
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'`
|
||||
);
|
||||
)
|
||||
|
||||
await manager.insert(Customer, {
|
||||
id: "test-customer",
|
||||
email: "test@email.com",
|
||||
});
|
||||
})
|
||||
|
||||
await manager.insert(ShippingOption, {
|
||||
id: "test-option",
|
||||
@@ -93,7 +93,7 @@ module.exports = async (connection, data = {}) => {
|
||||
price_type: "flat_rate",
|
||||
amount: 1000,
|
||||
data: {},
|
||||
});
|
||||
})
|
||||
|
||||
await manager.insert(ShippingOption, {
|
||||
id: "test-return-option",
|
||||
@@ -105,7 +105,7 @@ module.exports = async (connection, data = {}) => {
|
||||
price_type: "flat_rate",
|
||||
amount: 1000,
|
||||
is_return: true,
|
||||
});
|
||||
})
|
||||
|
||||
const order = manager.create(Order, {
|
||||
id: "test-order",
|
||||
@@ -157,9 +157,9 @@ module.exports = async (connection, data = {}) => {
|
||||
],
|
||||
items: [],
|
||||
...data,
|
||||
});
|
||||
})
|
||||
|
||||
await manager.save(order);
|
||||
await manager.save(order)
|
||||
|
||||
const li = manager.create(LineItem, {
|
||||
id: "test-item",
|
||||
@@ -172,9 +172,9 @@ module.exports = async (connection, data = {}) => {
|
||||
quantity: 1,
|
||||
variant_id: "test-variant",
|
||||
order_id: "test-order",
|
||||
});
|
||||
})
|
||||
|
||||
await manager.save(li);
|
||||
await manager.save(li)
|
||||
|
||||
await manager.insert(ShippingMethod, {
|
||||
id: "test-method",
|
||||
@@ -183,7 +183,7 @@ module.exports = async (connection, data = {}) => {
|
||||
order_id: "test-order",
|
||||
price: 1000,
|
||||
data: {},
|
||||
});
|
||||
})
|
||||
|
||||
const orderTemplate = () => {
|
||||
return {
|
||||
@@ -195,8 +195,8 @@ module.exports = async (connection, data = {}) => {
|
||||
currency_code: "usd",
|
||||
tax_rate: 0,
|
||||
...data,
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const paymentTemplate = () => {
|
||||
return {
|
||||
@@ -204,8 +204,8 @@ module.exports = async (connection, data = {}) => {
|
||||
currency_code: "usd",
|
||||
provider_id: "test-pay",
|
||||
data: {},
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const orderWithClaim = manager.create(Order, {
|
||||
id: "test-order-w-c",
|
||||
@@ -219,59 +219,59 @@ module.exports = async (connection, data = {}) => {
|
||||
},
|
||||
],
|
||||
...orderTemplate(),
|
||||
});
|
||||
})
|
||||
|
||||
await manager.save(orderWithClaim);
|
||||
await manager.save(orderWithClaim)
|
||||
|
||||
await manager.insert(Payment, {
|
||||
order_id: "test-order-w-c",
|
||||
id: "o-pay1",
|
||||
...paymentTemplate(),
|
||||
});
|
||||
})
|
||||
|
||||
const orderWithSwap = manager.create(Order, {
|
||||
id: "test-order-w-s",
|
||||
...orderTemplate(),
|
||||
});
|
||||
})
|
||||
|
||||
await manager.save(orderWithSwap);
|
||||
await manager.save(orderWithSwap)
|
||||
|
||||
await manager.insert(Payment, {
|
||||
order_id: "test-order-w-s",
|
||||
id: "o-pay2",
|
||||
...paymentTemplate(),
|
||||
});
|
||||
})
|
||||
|
||||
const swap1 = manager.create(Swap, {
|
||||
id: "swap-1",
|
||||
order_id: "test-order-w-s",
|
||||
fulfillment_status: "not_fulfilled",
|
||||
payment_status: "not_paid",
|
||||
});
|
||||
})
|
||||
|
||||
const pay1 = manager.create(Payment, {
|
||||
id: "pay1",
|
||||
...paymentTemplate(),
|
||||
});
|
||||
})
|
||||
|
||||
swap1.payment = pay1;
|
||||
swap1.payment = pay1
|
||||
|
||||
const swap2 = manager.create(Swap, {
|
||||
id: "swap-2",
|
||||
order_id: "test-order-w-s",
|
||||
fulfillment_status: "not_fulfilled",
|
||||
payment_status: "not_paid",
|
||||
});
|
||||
})
|
||||
|
||||
const pay2 = manager.create(Payment, {
|
||||
id: "pay2",
|
||||
...paymentTemplate(),
|
||||
});
|
||||
})
|
||||
|
||||
swap2.payment = pay2;
|
||||
swap2.payment = pay2
|
||||
|
||||
await manager.save(swap1);
|
||||
await manager.save(swap2);
|
||||
await manager.save(swap1)
|
||||
await manager.save(swap2)
|
||||
|
||||
const orderWithFulfillment = manager.create(Order, {
|
||||
id: "test-order-w-f",
|
||||
@@ -288,15 +288,15 @@ module.exports = async (connection, data = {}) => {
|
||||
},
|
||||
],
|
||||
...orderTemplate(),
|
||||
});
|
||||
})
|
||||
|
||||
await manager.save(orderWithFulfillment);
|
||||
await manager.save(orderWithFulfillment)
|
||||
|
||||
await manager.insert(Payment, {
|
||||
order_id: "test-order-w-f",
|
||||
id: "o-pay3",
|
||||
...paymentTemplate(),
|
||||
});
|
||||
})
|
||||
|
||||
const orderWithReturn = manager.create(Order, {
|
||||
id: "test-order-w-r",
|
||||
@@ -311,13 +311,13 @@ module.exports = async (connection, data = {}) => {
|
||||
},
|
||||
],
|
||||
...orderTemplate(),
|
||||
});
|
||||
})
|
||||
|
||||
await manager.save(orderWithReturn);
|
||||
await manager.save(orderWithReturn)
|
||||
|
||||
await manager.insert(Payment, {
|
||||
order_id: "test-order-w-r",
|
||||
id: "o-pay4",
|
||||
...paymentTemplate(),
|
||||
});
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
@@ -25,6 +25,22 @@ module.exports = async (connection, data = {}) => {
|
||||
|
||||
await manager.save(coll)
|
||||
|
||||
const coll1 = manager.create(ProductCollection, {
|
||||
id: "test-collection1",
|
||||
handle: "test-collection1",
|
||||
title: "Test collection 1",
|
||||
})
|
||||
|
||||
await manager.save(coll1)
|
||||
|
||||
const coll2 = manager.create(ProductCollection, {
|
||||
id: "test-collection2",
|
||||
handle: "test-collection2",
|
||||
title: "Test collection 2",
|
||||
})
|
||||
|
||||
await manager.save(coll2)
|
||||
|
||||
const tag = manager.create(ProductTag, {
|
||||
id: "tag1",
|
||||
value: "123",
|
||||
@@ -32,6 +48,20 @@ module.exports = async (connection, data = {}) => {
|
||||
|
||||
await manager.save(tag)
|
||||
|
||||
const tag3 = manager.create(ProductTag, {
|
||||
id: "tag3",
|
||||
value: "123",
|
||||
})
|
||||
|
||||
await manager.save(tag3)
|
||||
|
||||
const tag4 = manager.create(ProductTag, {
|
||||
id: "tag4",
|
||||
value: "123",
|
||||
})
|
||||
|
||||
await manager.save(tag4)
|
||||
|
||||
const type = manager.create(ProductType, {
|
||||
id: "test-type",
|
||||
value: "test-type",
|
||||
@@ -199,4 +229,46 @@ module.exports = async (connection, data = {}) => {
|
||||
})
|
||||
|
||||
await manager.save(variant5)
|
||||
|
||||
const product1 = manager.create(Product, {
|
||||
id: "test-product_filtering_1",
|
||||
handle: "test-product_filtering_1",
|
||||
title: "Test product filtering 1",
|
||||
profile_id: defaultProfile.id,
|
||||
description: "test-product-description",
|
||||
type: { id: "test-type", value: "test-type" },
|
||||
collection_id: "test-collection1",
|
||||
status: "proposed",
|
||||
tags: [{ id: "tag3", value: "123" }],
|
||||
})
|
||||
|
||||
await manager.save(product1)
|
||||
|
||||
const product2 = manager.create(Product, {
|
||||
id: "test-product_filtering_2",
|
||||
handle: "test-product_filtering_2",
|
||||
title: "Test product filtering 2",
|
||||
profile_id: defaultProfile.id,
|
||||
description: "test-product-description",
|
||||
type: { id: "test-type", value: "test-type" },
|
||||
collection_id: "test-collection2",
|
||||
status: "published",
|
||||
tags: [{ id: "tag3", value: "123" }],
|
||||
})
|
||||
|
||||
await manager.save(product2)
|
||||
|
||||
const product3 = manager.create(Product, {
|
||||
id: "test-product_filtering_3",
|
||||
handle: "test-product_filtering_3",
|
||||
title: "Test product filtering 3",
|
||||
profile_id: defaultProfile.id,
|
||||
description: "test-product-description",
|
||||
type: { id: "test-type", value: "test-type" },
|
||||
collection_id: "test-collection1",
|
||||
status: "draft",
|
||||
tags: [{ id: "tag4", value: "1234" }],
|
||||
})
|
||||
|
||||
await manager.save(product3)
|
||||
}
|
||||
|
||||
@@ -15,6 +15,9 @@ const {
|
||||
Cart,
|
||||
Return,
|
||||
} = require("@medusajs/medusa")
|
||||
const {
|
||||
CustomShippingOption,
|
||||
} = require("@medusajs/medusa/dist/models/custom-shipping-option")
|
||||
|
||||
module.exports = async (connection, data = {}) => {
|
||||
const manager = connection.manager
|
||||
@@ -101,6 +104,72 @@ module.exports = async (connection, data = {}) => {
|
||||
|
||||
await manager.save(swap)
|
||||
|
||||
const cartWithCustomSo = manager.create(Cart, {
|
||||
id: "test-cart-rma",
|
||||
customer_id: "test-customer",
|
||||
email: "test-customer@email.com",
|
||||
shipping_address_id: "test-shipping-address",
|
||||
billing_address_id: "test-billing-address",
|
||||
region_id: "test-region",
|
||||
type: "swap",
|
||||
metadata: {
|
||||
swap_id: "test-swap",
|
||||
parent_order_id: orderWithSwap.id,
|
||||
},
|
||||
})
|
||||
|
||||
await manager.save(cartWithCustomSo)
|
||||
|
||||
const liRma = manager.create(LineItem, {
|
||||
id: "test-item-rma",
|
||||
title: "Line Item RMA",
|
||||
description: "Line Item Desc",
|
||||
thumbnail: "https://test.js/1234",
|
||||
unit_price: 8000,
|
||||
quantity: 1,
|
||||
variant_id: "test-variant",
|
||||
cart_id: "test-cart-rma",
|
||||
})
|
||||
await manager.save(liRma)
|
||||
|
||||
manager.insert(CustomShippingOption, {
|
||||
id: "cso-test",
|
||||
cart_id: cartWithCustomSo.id,
|
||||
price: 0,
|
||||
shipping_option_id: "test-option",
|
||||
})
|
||||
|
||||
const swapWithRMAMethod = manager.create(Swap, {
|
||||
id: "test-swap-rma",
|
||||
order_id: "order-with-swap",
|
||||
payment_status: "captured",
|
||||
fulfillment_status: "fulfilled",
|
||||
cart_id: cartWithCustomSo.id,
|
||||
payment: {
|
||||
id: "test-payment-swap",
|
||||
amount: 10000,
|
||||
currency_code: "usd",
|
||||
amount_refunded: 0,
|
||||
provider_id: "test-pay",
|
||||
data: {},
|
||||
},
|
||||
additional_items: [
|
||||
{
|
||||
id: "test-item-swapped",
|
||||
fulfilled_quantity: 1,
|
||||
title: "Line Item",
|
||||
description: "Line Item Desc",
|
||||
thumbnail: "https://test.js/1234",
|
||||
unit_price: 9000,
|
||||
quantity: 1,
|
||||
variant_id: "test-variant-2",
|
||||
cart_id: "test-cart",
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
await manager.save(swapWithRMAMethod)
|
||||
|
||||
const cartTemplate = async (cartId) => {
|
||||
const cart = manager.create(Cart, {
|
||||
id: cartId,
|
||||
@@ -112,13 +181,13 @@ module.exports = async (connection, data = {}) => {
|
||||
type: "swap",
|
||||
metadata: {},
|
||||
...data,
|
||||
});
|
||||
})
|
||||
|
||||
await manager.save(cart);
|
||||
};
|
||||
await manager.save(cart)
|
||||
}
|
||||
|
||||
const swapTemplate = async (cartId) => {
|
||||
await cartTemplate(cartId);
|
||||
await cartTemplate(cartId)
|
||||
return {
|
||||
order_id: orderWithSwap.id,
|
||||
fulfillment_status: "fulfilled",
|
||||
@@ -132,8 +201,8 @@ module.exports = async (connection, data = {}) => {
|
||||
data: {},
|
||||
},
|
||||
...data,
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const swapWithFulfillments = manager.create(Swap, {
|
||||
id: "swap-w-f",
|
||||
@@ -150,9 +219,9 @@ module.exports = async (connection, data = {}) => {
|
||||
},
|
||||
],
|
||||
...(await swapTemplate("sc-w-f")),
|
||||
});
|
||||
})
|
||||
|
||||
await manager.save(swapWithFulfillments);
|
||||
await manager.save(swapWithFulfillments)
|
||||
|
||||
const swapWithReturn = manager.create(Swap, {
|
||||
id: "swap-w-r",
|
||||
@@ -162,9 +231,9 @@ module.exports = async (connection, data = {}) => {
|
||||
refund_amount: 0,
|
||||
},
|
||||
...(await swapTemplate("sc-w-r")),
|
||||
});
|
||||
})
|
||||
|
||||
await manager.save(swapWithReturn);
|
||||
await manager.save(swapWithReturn)
|
||||
const li = manager.create(LineItem, {
|
||||
id: "return-item-1",
|
||||
fulfilled_quantity: 1,
|
||||
|
||||
@@ -13,4 +13,4 @@ module.exports = {
|
||||
],
|
||||
transform: { "^.+\\.[jt]s$": `../../jest-transformer.js` },
|
||||
setupFilesAfterEnv: ["../setup.js"],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -8,15 +8,15 @@
|
||||
"build": "babel src -d dist --extensions \".ts,.js\""
|
||||
},
|
||||
"dependencies": {
|
||||
"@medusajs/medusa": "1.1.40-dev-1631630701835",
|
||||
"medusa-interfaces": "1.1.21-dev-1631630701835",
|
||||
"@medusajs/medusa": "1.1.41-dev-1634316075104",
|
||||
"medusa-interfaces": "1.1.23-dev-1634316075104",
|
||||
"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.13-dev-1631630701835",
|
||||
"babel-preset-medusa-package": "1.1.15-dev-1634316075104",
|
||||
"jest": "^26.6.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { FulfillmentService } from "medusa-interfaces";
|
||||
import { FulfillmentService } from "medusa-interfaces"
|
||||
|
||||
class TestFulService extends FulfillmentService {
|
||||
static identifier = "test-ful";
|
||||
static identifier = "test-ful"
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
super()
|
||||
}
|
||||
|
||||
getFulfillmentOptions() {
|
||||
@@ -12,42 +12,42 @@ class TestFulService extends FulfillmentService {
|
||||
{
|
||||
id: "manual-fulfillment",
|
||||
},
|
||||
];
|
||||
]
|
||||
}
|
||||
|
||||
validateFulfillmentData(data, cart) {
|
||||
return data;
|
||||
return data
|
||||
}
|
||||
|
||||
validateOption(data) {
|
||||
return true;
|
||||
return true
|
||||
}
|
||||
|
||||
canCalculate() {
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
|
||||
calculatePrice() {
|
||||
throw Error("Manual Fulfillment service cannot calculatePrice");
|
||||
throw Error("Manual Fulfillment service cannot calculatePrice")
|
||||
}
|
||||
|
||||
createOrder() {
|
||||
// No data is being sent anywhere
|
||||
return Promise.resolve({});
|
||||
return Promise.resolve({})
|
||||
}
|
||||
|
||||
createReturn() {
|
||||
return Promise.resolve({});
|
||||
return Promise.resolve({})
|
||||
}
|
||||
|
||||
createFulfillment() {
|
||||
// No data is being sent anywhere
|
||||
return Promise.resolve({});
|
||||
return Promise.resolve({})
|
||||
}
|
||||
|
||||
cancelFulfillment() {
|
||||
return Promise.resolve({});
|
||||
return Promise.resolve({})
|
||||
}
|
||||
}
|
||||
|
||||
export default TestFulService;
|
||||
export default TestFulService
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
import { NotificationService } from "medusa-interfaces";
|
||||
import { NotificationService } from "medusa-interfaces"
|
||||
|
||||
class TestNotiService extends NotificationService {
|
||||
static identifier = "test-not";
|
||||
static identifier = "test-not"
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
super()
|
||||
}
|
||||
|
||||
async sendNotification() {
|
||||
return Promise.resolve();
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
async resendNotification() {
|
||||
return Promise.resolve();
|
||||
return Promise.resolve()
|
||||
}
|
||||
}
|
||||
|
||||
export default TestNotiService;
|
||||
export default TestNotiService
|
||||
|
||||
@@ -1,59 +1,59 @@
|
||||
import { PaymentService } from "medusa-interfaces";
|
||||
import { PaymentService } from "medusa-interfaces"
|
||||
|
||||
class TestPayService extends PaymentService {
|
||||
static identifier = "test-pay";
|
||||
static identifier = "test-pay"
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
super()
|
||||
}
|
||||
|
||||
async getStatus(paymentData) {
|
||||
return "authorized";
|
||||
return "authorized"
|
||||
}
|
||||
|
||||
async retrieveSavedMethods(customer) {
|
||||
return Promise.resolve([]);
|
||||
return Promise.resolve([])
|
||||
}
|
||||
|
||||
async createPayment() {
|
||||
return {};
|
||||
return {}
|
||||
}
|
||||
|
||||
async retrievePayment(data) {
|
||||
return {};
|
||||
return {}
|
||||
}
|
||||
|
||||
async getPaymentData(sessionData) {
|
||||
return {};
|
||||
return {}
|
||||
}
|
||||
|
||||
async authorizePayment(sessionData, context = {}) {
|
||||
return { data: {}, status: "authorized" };
|
||||
return { data: {}, status: "authorized" }
|
||||
}
|
||||
|
||||
async updatePaymentData(sessionData, update) {
|
||||
return {};
|
||||
return {}
|
||||
}
|
||||
|
||||
async updatePayment(sessionData, cart) {
|
||||
return {};
|
||||
return {}
|
||||
}
|
||||
|
||||
async deletePayment(payment) {
|
||||
return {};
|
||||
return {}
|
||||
}
|
||||
|
||||
async capturePayment(payment) {
|
||||
return {};
|
||||
return {}
|
||||
}
|
||||
|
||||
async refundPayment(payment, amountToRefund) {
|
||||
return {};
|
||||
return {}
|
||||
}
|
||||
|
||||
async cancelPayment(payment) {
|
||||
return {};
|
||||
return {}
|
||||
}
|
||||
}
|
||||
|
||||
export default TestPayService;
|
||||
export default TestPayService
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,8 +6,10 @@ const importFrom = require("import-from")
|
||||
const initialize = async () => {
|
||||
const app = express()
|
||||
|
||||
const loaders = importFrom(process.cwd(), "@medusajs/medusa/dist/loaders")
|
||||
.default
|
||||
const loaders = importFrom(
|
||||
process.cwd(),
|
||||
"@medusajs/medusa/dist/loaders"
|
||||
).default
|
||||
|
||||
const { dbConnection } = await loaders({
|
||||
directory: path.resolve(process.cwd()),
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
const glob = require(`glob`);
|
||||
const glob = require(`glob`)
|
||||
|
||||
const pkgs = glob
|
||||
.sync(`${__dirname}/*/`)
|
||||
.map((p) => p.replace(__dirname, `<rootDir>/integration-tests`));
|
||||
.map((p) => p.replace(__dirname, `<rootDir>/integration-tests`))
|
||||
|
||||
module.exports = {
|
||||
testEnvironment: `node`,
|
||||
@@ -19,4 +19,4 @@ module.exports = {
|
||||
],
|
||||
transform: { "^.+\\.[jt]s$": `<rootDir>/jest-transformer.js` },
|
||||
setupFilesAfterEnv: ["<rootDir>/integration-tests/setup.js"],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
const path = require('path');
|
||||
const {dropDatabase} = require('pg-god');
|
||||
const path = require("path")
|
||||
const { dropDatabase } = require("pg-god")
|
||||
|
||||
require('dotenv').config({path: path.join(__dirname, '.env')});
|
||||
require("dotenv").config({ path: path.join(__dirname, ".env") })
|
||||
|
||||
const DB_USERNAME = process.env.DB_USERNAME || 'postgres';
|
||||
const DB_PASSWORD = process.env.DB_PASSWORD || '';
|
||||
const DB_USERNAME = process.env.DB_USERNAME || "postgres"
|
||||
const DB_PASSWORD = process.env.DB_PASSWORD || ""
|
||||
|
||||
const pgGodCredentials = {
|
||||
user: DB_USERNAME,
|
||||
password: DB_PASSWORD,
|
||||
};
|
||||
}
|
||||
|
||||
afterAll(() => {
|
||||
dropDatabase({databaseName: 'medusa-integration'}, pgGodCredentials);
|
||||
});
|
||||
dropDatabase({ databaseName: "medusa-integration" }, pgGodCredentials)
|
||||
})
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const babelPreset = require(`babel-preset-medusa-package`)();
|
||||
const babelPreset = require(`babel-preset-medusa-package`)()
|
||||
module.exports = require(`babel-jest`).createTransformer({
|
||||
...babelPreset,
|
||||
});
|
||||
})
|
||||
|
||||
16
package.json
16
package.json
@@ -3,6 +3,7 @@
|
||||
"private": true,
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.12.7",
|
||||
"@babel/eslint-parser": "^7.15.8",
|
||||
"@babel/node": "^7.12.6",
|
||||
"@babel/plugin-proposal-class-properties": "^7.10.4",
|
||||
"@babel/plugin-transform-classes": "^7.10.4",
|
||||
@@ -11,16 +12,24 @@
|
||||
"@babel/preset-env": "^7.11.5",
|
||||
"@babel/register": "^7.11.5",
|
||||
"@babel/runtime": "^7.11.2",
|
||||
"@typescript-eslint/eslint-plugin": "^5.0.0",
|
||||
"@typescript-eslint/parser": "^5.0.0",
|
||||
"axios": "^0.21.1",
|
||||
"axios-mock-adapter": "^1.19.0",
|
||||
"babel-jest": "^26.6.3",
|
||||
"babel-preset-medusa-package": "^1.0.0",
|
||||
"cross-env": "^7.0.2",
|
||||
"eslint": "^8.0.0",
|
||||
"eslint-config-google": "^0.14.0",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"express": "^4.17.1",
|
||||
"get-port": "^5.1.1",
|
||||
"husky": "^7.0.2",
|
||||
"import-from": "^3.0.0",
|
||||
"jest": "^26.6.3",
|
||||
"lerna": "^3.22.1",
|
||||
"lint-staged": "^11.2.3",
|
||||
"microbundle": "^0.13.3",
|
||||
"mongoose": "^5.10.15",
|
||||
"pg-god": "^1.0.11",
|
||||
@@ -28,9 +37,16 @@
|
||||
"resolve-cwd": "^3.0.0",
|
||||
"typeorm": "^0.2.31"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{js,jsx,ts,tsx}": "eslint --ext .js,.jsx,.ts,.tsx --fix",
|
||||
"*.{md,yaml,yml}": "prettier --write"
|
||||
},
|
||||
"scripts": {
|
||||
"hooks:install": "husky install",
|
||||
"hooks:uninstall": "husky uninstall",
|
||||
"publish:next": "lerna publish --canary --preid next --dist-tag next",
|
||||
"bootstrap": "lerna bootstrap",
|
||||
"lint": "eslint --ext .js,.jsx,.ts,.tsx .",
|
||||
"jest": "jest",
|
||||
"test": "jest",
|
||||
"prettier": "prettier",
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"plugins": ["prettier"],
|
||||
"extends": ["prettier"],
|
||||
"rules": {
|
||||
"prettier/prettier": "error",
|
||||
"semi": "error",
|
||||
"no-unused-expressions": "true"
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"endOfLine": "lf",
|
||||
"semi": false,
|
||||
"singleQuote": false,
|
||||
"tabWidth": 2,
|
||||
"trailingComma": "es5"
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"plugins": ["prettier"],
|
||||
"extends": ["prettier"],
|
||||
"rules": {
|
||||
"prettier/prettier": "error",
|
||||
"semi": "error",
|
||||
"no-unused-expressions": "true"
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"endOfLine": "lf",
|
||||
"semi": false,
|
||||
"singleQuote": false,
|
||||
"tabWidth": 2,
|
||||
"trailingComma": "es5"
|
||||
}
|
||||
@@ -96,4 +96,33 @@ Joi.orderFilter = () => {
|
||||
})
|
||||
}
|
||||
|
||||
Joi.productFilter = () => {
|
||||
return Joi.object().keys({
|
||||
id: Joi.string(),
|
||||
q: Joi.string().allow(null, ""),
|
||||
status: Joi.array()
|
||||
.items(Joi.string().valid("proposed", "draft", "published", "rejected"))
|
||||
.single(),
|
||||
collection_id: Joi.array()
|
||||
.items(Joi.string())
|
||||
.single(),
|
||||
tags: Joi.array()
|
||||
.items(Joi.string())
|
||||
.single(),
|
||||
title: Joi.string(),
|
||||
description: Joi.string(),
|
||||
handle: Joi.string(),
|
||||
is_giftcard: Joi.string(),
|
||||
type: Joi.string(),
|
||||
offset: Joi.string(),
|
||||
limit: Joi.string(),
|
||||
expand: Joi.string(),
|
||||
fields: Joi.string(),
|
||||
order: Joi.string().optional(),
|
||||
created_at: Joi.dateFilter(),
|
||||
updated_at: Joi.dateFilter(),
|
||||
deleted_at: Joi.dateFilter(),
|
||||
})
|
||||
}
|
||||
|
||||
export default Joi
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"plugins": ["prettier"],
|
||||
"extends": ["prettier"],
|
||||
"rules": {
|
||||
"prettier/prettier": "error",
|
||||
"semi": "error",
|
||||
"no-unused-expressions": "true"
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"endOfLine": "lf",
|
||||
"semi": false,
|
||||
"singleQuote": false,
|
||||
"tabWidth": 2,
|
||||
"trailingComma": "es5"
|
||||
}
|
||||
15
packages/medusa-file-s3/README.md
Normal file
15
packages/medusa-file-s3/README.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# medusa-file-s3
|
||||
|
||||
Upload files to an AWS S3 bucket.
|
||||
|
||||
## Options
|
||||
|
||||
```
|
||||
s3_url: [url of your s3 bucket],
|
||||
access_key_id: [access-key],
|
||||
secret_access_key: [secret-access-key],
|
||||
bucket: [name of your bucket],
|
||||
region: [region of your bucket],
|
||||
```
|
||||
|
||||
Follow [this guide](https://docs.medusa-commerce.com/how-to/uploading-images-to-s3) to configure the plugin.
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"plugins": ["prettier"],
|
||||
"extends": ["prettier"],
|
||||
"rules": {
|
||||
"prettier/prettier": "error",
|
||||
"semi": "error",
|
||||
"no-unused-expressions": "true"
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"endOfLine": "lf",
|
||||
"semi": false,
|
||||
"singleQuote": false,
|
||||
"tabWidth": 2,
|
||||
"trailingComma": "es5"
|
||||
}
|
||||
15
packages/medusa-file-spaces/README.md
Normal file
15
packages/medusa-file-spaces/README.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# medusa-file-spaces
|
||||
|
||||
Upload files to a DigitalOcean Space.
|
||||
|
||||
## Options
|
||||
|
||||
```
|
||||
spaces_url: [url of your DigitalOcean space],
|
||||
access_key_id: [access-key],
|
||||
secret_access_key: [secret-access-key],
|
||||
bucket: [name of your bucket],
|
||||
endpoint: [endpoint of you DigitalOcean space],
|
||||
```
|
||||
|
||||
Follow [this guide](https://docs.medusa-commerce.com/how-to/uploading-images-to-spaces) to configure the plugin.
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"plugins": ["prettier"],
|
||||
"extends": ["prettier"],
|
||||
"rules": {
|
||||
"prettier/prettier": "error",
|
||||
"semi": "error",
|
||||
"no-unused-expressions": "true"
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"endOfLine": "lf",
|
||||
"semi": false,
|
||||
"singleQuote": false,
|
||||
"tabWidth": 2,
|
||||
"trailingComma": "es5"
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports["default"] = void 0;
|
||||
|
||||
var _medusaInterfaces = require("medusa-interfaces");
|
||||
|
||||
function _instanceof(left, right) { if (right != null && typeof Symbol !== "undefined" && right[Symbol.hasInstance]) { return !!right[Symbol.hasInstance](left); } else { return left instanceof right; } }
|
||||
|
||||
function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
|
||||
|
||||
function _classCallCheck(instance, Constructor) { if (!_instanceof(instance, Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
||||
|
||||
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
|
||||
|
||||
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
|
||||
|
||||
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }
|
||||
|
||||
function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
|
||||
|
||||
function _createSuper(Derived) { return function () { var Super = _getPrototypeOf(Derived), result; if (_isNativeReflectConstruct()) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }
|
||||
|
||||
function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); }
|
||||
|
||||
function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
|
||||
|
||||
function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } }
|
||||
|
||||
function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
|
||||
|
||||
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
||||
|
||||
var ManualFulfillmentService = /*#__PURE__*/function (_FulfillmentService) {
|
||||
_inherits(ManualFulfillmentService, _FulfillmentService);
|
||||
|
||||
var _super = _createSuper(ManualFulfillmentService);
|
||||
|
||||
function ManualFulfillmentService() {
|
||||
_classCallCheck(this, ManualFulfillmentService);
|
||||
|
||||
return _super.call(this);
|
||||
}
|
||||
|
||||
_createClass(ManualFulfillmentService, [{
|
||||
key: "getFulfillmentOptions",
|
||||
value: function getFulfillmentOptions() {
|
||||
return [{
|
||||
id: "manual-fulfillment"
|
||||
}];
|
||||
}
|
||||
}, {
|
||||
key: "validateFulfillmentData",
|
||||
value: function validateFulfillmentData(data, cart) {
|
||||
return data;
|
||||
}
|
||||
}, {
|
||||
key: "validateOption",
|
||||
value: function validateOption(data) {
|
||||
return true;
|
||||
}
|
||||
}, {
|
||||
key: "canCalculate",
|
||||
value: function canCalculate() {
|
||||
return false;
|
||||
}
|
||||
}, {
|
||||
key: "calculatePrice",
|
||||
value: function calculatePrice() {
|
||||
throw Error("Manual Fulfillment service cannot calculatePrice");
|
||||
}
|
||||
}, {
|
||||
key: "createOrder",
|
||||
value: function createOrder() {
|
||||
// No data is being sent anywhere
|
||||
return;
|
||||
}
|
||||
}]);
|
||||
|
||||
return ManualFulfillmentService;
|
||||
}(_medusaInterfaces.FulfillmentService);
|
||||
|
||||
_defineProperty(ManualFulfillmentService, "identifier", "manual");
|
||||
|
||||
var _default = ManualFulfillmentService;
|
||||
exports["default"] = _default;
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"plugins": ["prettier"],
|
||||
"extends": ["prettier"],
|
||||
"rules": {
|
||||
"prettier/prettier": "error",
|
||||
"semi": "error",
|
||||
"no-unused-expressions": "true"
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"endOfLine": "lf",
|
||||
"semi": false,
|
||||
"singleQuote": false,
|
||||
"tabWidth": 2,
|
||||
"trailingComma": "es5"
|
||||
}
|
||||
@@ -106,7 +106,13 @@ class WebshipperFulfillmentService extends FulfillmentService {
|
||||
|
||||
const fromOrder = await this.orderService_.retrieve(orderId, {
|
||||
select: ["total"],
|
||||
relations: ["discounts", "shipping_address", "returns"],
|
||||
relations: [
|
||||
"discounts",
|
||||
"discounts.rule",
|
||||
"discounts.rule.valid_for",
|
||||
"shipping_address",
|
||||
"returns",
|
||||
],
|
||||
})
|
||||
|
||||
const methodData = returnOrder.shipping_method.data
|
||||
@@ -286,10 +292,11 @@ class WebshipperFulfillmentService extends FulfillmentService {
|
||||
)) &&
|
||||
this.invoiceGenerator_.createCertificateOfOrigin
|
||||
) {
|
||||
const base64Coo = await this.invoiceGenerator_.createCertificateOfOrigin(
|
||||
fromOrder,
|
||||
fulfillmentItems
|
||||
)
|
||||
const base64Coo =
|
||||
await this.invoiceGenerator_.createCertificateOfOrigin(
|
||||
fromOrder,
|
||||
fulfillmentItems
|
||||
)
|
||||
|
||||
certificateOfOrigin = await this.client_.documents
|
||||
.create({
|
||||
@@ -419,9 +426,8 @@ class WebshipperFulfillmentService extends FulfillmentService {
|
||||
url: l.url,
|
||||
tracking_number: l.number,
|
||||
}))
|
||||
const [orderId, fulfillmentIndex] = wsOrder.data.attributes.ext_ref.split(
|
||||
"."
|
||||
)
|
||||
const [orderId, fulfillmentIndex] =
|
||||
wsOrder.data.attributes.ext_ref.split(".")
|
||||
|
||||
if (orderId.charAt(0).toLowerCase() === "s") {
|
||||
if (fulfillmentIndex.startsWith("ful")) {
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"plugins": ["prettier"],
|
||||
"extends": ["prettier"],
|
||||
"rules": {
|
||||
"prettier/prettier": "error",
|
||||
"semi": "error",
|
||||
"no-unused-expressions": "true"
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"endOfLine": "lf",
|
||||
"semi": false,
|
||||
"singleQuote": false,
|
||||
"tabWidth": 2,
|
||||
"trailingComma": "es5"
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"plugins": ["prettier"],
|
||||
"extends": ["prettier"],
|
||||
"rules": {
|
||||
"prettier/prettier": "error",
|
||||
"semi": "error",
|
||||
"no-unused-expressions": "true"
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"endOfLine": "lf",
|
||||
"semi": false,
|
||||
"singleQuote": false,
|
||||
"tabWidth": 2,
|
||||
"trailingComma": "es5"
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"plugins": ["prettier"],
|
||||
"extends": ["prettier"],
|
||||
"rules": {
|
||||
"prettier/prettier": "error",
|
||||
"semi": "error",
|
||||
"no-unused-expressions": "true"
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"endOfLine": "lf",
|
||||
"semi": false,
|
||||
"singleQuote": false,
|
||||
"tabWidth": 2,
|
||||
"trailingComma": "es5"
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"plugins": ["prettier"],
|
||||
"extends": ["prettier"],
|
||||
"rules": {
|
||||
"prettier/prettier": "error",
|
||||
"semi": "error",
|
||||
"no-unused-expressions": "true"
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"endOfLine": "lf",
|
||||
"semi": false,
|
||||
"singleQuote": false,
|
||||
"tabWidth": 2,
|
||||
"trailingComma": "es5"
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"plugins": ["prettier"],
|
||||
"extends": ["prettier"],
|
||||
"rules": {
|
||||
"prettier/prettier": "error",
|
||||
"semi": "error",
|
||||
"no-unused-expressions": "true"
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"endOfLine": "lf",
|
||||
"semi": false,
|
||||
"singleQuote": false,
|
||||
"tabWidth": 2,
|
||||
"trailingComma": "es5"
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"plugins": ["prettier"],
|
||||
"extends": ["prettier"],
|
||||
"rules": {
|
||||
"prettier/prettier": "error",
|
||||
"semi": "error",
|
||||
"no-unused-expressions": "true"
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"endOfLine": "lf",
|
||||
"semi": false,
|
||||
"singleQuote": false,
|
||||
"tabWidth": 2,
|
||||
"trailingComma": "es5"
|
||||
}
|
||||
241
packages/medusa-payment-stripe/src/services/stripe-bancontact.js
Normal file
241
packages/medusa-payment-stripe/src/services/stripe-bancontact.js
Normal file
@@ -0,0 +1,241 @@
|
||||
import _ from "lodash"
|
||||
import Stripe from "stripe"
|
||||
import { PaymentService } from "medusa-interfaces"
|
||||
|
||||
class BancontactProviderService extends PaymentService {
|
||||
static identifier = "stripe-bancontact"
|
||||
|
||||
constructor(
|
||||
{ stripeProviderService, customerService, totalsService, regionService },
|
||||
options
|
||||
) {
|
||||
super()
|
||||
|
||||
/**
|
||||
* Required Stripe options:
|
||||
* {
|
||||
* api_key: "stripe_secret_key", REQUIRED
|
||||
* webhook_secret: "stripe_webhook_secret", REQUIRED
|
||||
* // Use this flag to capture payment immediately (default is false)
|
||||
* capture: true
|
||||
* }
|
||||
*/
|
||||
this.options_ = options
|
||||
|
||||
/** @private @const {Stripe} */
|
||||
this.stripe_ = Stripe(options.api_key)
|
||||
|
||||
/** @private @const {CustomerService} */
|
||||
this.stripeProviderService_ = stripeProviderService
|
||||
|
||||
/** @private @const {CustomerService} */
|
||||
this.customerService_ = customerService
|
||||
|
||||
/** @private @const {RegionService} */
|
||||
this.regionService_ = regionService
|
||||
|
||||
/** @private @const {TotalsService} */
|
||||
this.totalsService_ = totalsService
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches Stripe payment intent. Check its status and returns the
|
||||
* corresponding Medusa status.
|
||||
* @param {object} paymentData - payment method data from cart
|
||||
* @returns {string} the status of the payment intent
|
||||
*/
|
||||
async getStatus(paymentData) {
|
||||
return await this.stripeProviderService_.getStatus(paymentData)
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches a customers saved payment methods if registered in Stripe.
|
||||
* @param {object} customer - customer to fetch saved cards for
|
||||
* @returns {Promise<Array<object>>} saved payments methods
|
||||
*/
|
||||
async retrieveSavedMethods(customer) {
|
||||
return Promise.resolve([])
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches a Stripe customer
|
||||
* @param {string} customerId - Stripe customer id
|
||||
* @returns {Promise<object>} Stripe customer
|
||||
*/
|
||||
async retrieveCustomer(customerId) {
|
||||
return await this.stripeProviderService_.retrieveCustomer(customerId)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Stripe customer using a Medusa customer.
|
||||
* @param {object} customer - Customer data from Medusa
|
||||
* @returns {Promise<object>} Stripe customer
|
||||
*/
|
||||
async createCustomer(customer) {
|
||||
return await this.stripeProviderService_.createCustomer(customer)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Stripe payment intent.
|
||||
* If customer is not registered in Stripe, we do so.
|
||||
* @param {object} cart - cart to create a payment for
|
||||
* @returns {object} Stripe payment intent
|
||||
*/
|
||||
async createPayment(cart) {
|
||||
const { customer_id, region_id, email } = cart
|
||||
const region = await this.regionService_.retrieve(region_id)
|
||||
const { currency_code } = region
|
||||
|
||||
const amount = await this.totalsService_.getTotal(cart)
|
||||
|
||||
const intentRequest = {
|
||||
amount: Math.round(amount),
|
||||
currency: currency_code,
|
||||
payment_method_types: ["bancontact"],
|
||||
capture_method: "automatic",
|
||||
metadata: { cart_id: `${cart.id}` },
|
||||
}
|
||||
|
||||
if (customer_id) {
|
||||
const customer = await this.customerService_.retrieve(customer_id)
|
||||
|
||||
if (customer.metadata?.stripe_id) {
|
||||
intentRequest.customer = customer.metadata.stripe_id
|
||||
} else {
|
||||
const stripeCustomer = await this.createCustomer({
|
||||
email,
|
||||
id: customer_id,
|
||||
})
|
||||
|
||||
intentRequest.customer = stripeCustomer.id
|
||||
}
|
||||
} else {
|
||||
const stripeCustomer = await this.createCustomer({
|
||||
email,
|
||||
})
|
||||
|
||||
intentRequest.customer = stripeCustomer.id
|
||||
}
|
||||
|
||||
const paymentIntent = await this.stripe_.paymentIntents.create(
|
||||
intentRequest
|
||||
)
|
||||
|
||||
return paymentIntent
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves Stripe payment intent.
|
||||
* @param {object} data - the data of the payment to retrieve
|
||||
* @returns {Promise<object>} Stripe payment intent
|
||||
*/
|
||||
async retrievePayment(data) {
|
||||
return await this.stripeProviderService_.retrievePayment(data)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a Stripe payment intent and returns it.
|
||||
* @param {object} sessionData - the data of the payment to retrieve
|
||||
* @returns {Promise<object>} Stripe payment intent
|
||||
*/
|
||||
async getPaymentData(sessionData) {
|
||||
return await this.stripeProviderService_.getPaymentData(sessionData)
|
||||
}
|
||||
|
||||
/**
|
||||
* Authorizes Stripe payment intent by simply returning
|
||||
* the status for the payment intent in use.
|
||||
* @param {object} sessionData - payment session data
|
||||
* @param {object} context - properties relevant to current context
|
||||
* @returns {Promise<{ status: string, data: object }>} result with data and status
|
||||
*/
|
||||
async authorizePayment(sessionData, context = {}) {
|
||||
return await this.stripeProviderService_.authorizePayment(
|
||||
sessionData,
|
||||
context
|
||||
)
|
||||
}
|
||||
|
||||
async updatePaymentData(sessionData, update) {
|
||||
return await this.stripeProviderService_.updatePaymentData(
|
||||
sessionData,
|
||||
update
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates Stripe payment intent.
|
||||
* @param {object} sessionData - payment session data.
|
||||
* @param {object} update - objec to update intent with
|
||||
* @returns {object} Stripe payment intent
|
||||
*/
|
||||
async updatePayment(sessionData, cart) {
|
||||
try {
|
||||
const stripeId = cart.customer?.metadata?.stripe_id || undefined
|
||||
|
||||
if (stripeId !== sessionData.customer) {
|
||||
return this.createPayment(cart)
|
||||
} else {
|
||||
if (cart.total && sessionData.amount === Math.round(cart.total)) {
|
||||
return sessionData
|
||||
}
|
||||
|
||||
return this.stripe_.paymentIntents.update(sessionData.id, {
|
||||
amount: Math.round(cart.total),
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
async deletePayment(payment) {
|
||||
return await this.stripeProviderService_.deletePayment(payment)
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates customer of Stripe payment intent.
|
||||
* @param {string} paymentIntentId - id of payment intent to update
|
||||
* @param {string} customerId - id of new Stripe customer
|
||||
* @returns {object} Stripe payment intent
|
||||
*/
|
||||
async updatePaymentIntentCustomer(paymentIntentId, customerId) {
|
||||
return await this.stripeProviderService_.updatePaymentIntentCustomer(
|
||||
paymentIntentId,
|
||||
customerId
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Captures payment for Stripe payment intent.
|
||||
* @param {object} paymentData - payment method data from cart
|
||||
* @returns {object} Stripe payment intent
|
||||
*/
|
||||
async capturePayment(payment) {
|
||||
return await this.stripeProviderService_.capturePayment(payment)
|
||||
}
|
||||
|
||||
/**
|
||||
* Refunds payment for Stripe payment intent.
|
||||
* @param {object} paymentData - payment method data from cart
|
||||
* @param {number} amountToRefund - amount to refund
|
||||
* @returns {string} refunded payment intent
|
||||
*/
|
||||
async refundPayment(payment, amountToRefund) {
|
||||
return await this.stripeProviderService_.refundPayment(
|
||||
payment,
|
||||
amountToRefund
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels payment for Stripe payment intent.
|
||||
* @param {object} paymentData - payment method data from cart
|
||||
* @returns {object} canceled payment intent
|
||||
*/
|
||||
async cancelPayment(payment) {
|
||||
return await this.stripeProviderService_.cancelPayment(payment)
|
||||
}
|
||||
}
|
||||
|
||||
export default BancontactProviderService
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"plugins": ["prettier"],
|
||||
"extends": ["prettier"],
|
||||
"rules": {
|
||||
"prettier/prettier": "error",
|
||||
"semi": "error",
|
||||
"no-unused-expressions": "true"
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"endOfLine": "lf",
|
||||
"semi": false,
|
||||
"singleQuote": false,
|
||||
"tabWidth": 2,
|
||||
"trailingComma": "es5"
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"plugins": ["prettier"],
|
||||
"extends": ["prettier"],
|
||||
"rules": {
|
||||
"prettier/prettier": "error",
|
||||
"semi": "error",
|
||||
"no-unused-expressions": "true"
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"endOfLine": "lf",
|
||||
"semi": false,
|
||||
"singleQuote": false,
|
||||
"tabWidth": 2,
|
||||
"trailingComma": "es5"
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"plugins": ["prettier"],
|
||||
"extends": ["prettier"],
|
||||
"rules": {
|
||||
"prettier/prettier": "error",
|
||||
"semi": "error",
|
||||
"no-unused-expressions": "true"
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"endOfLine": "lf",
|
||||
"semi": false,
|
||||
"singleQuote": false,
|
||||
"tabWidth": 2,
|
||||
"trailingComma": "es5"
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.ContentfulMock = void 0;
|
||||
var ContentfulMock = {
|
||||
createClient: jest.fn()
|
||||
};
|
||||
exports.ContentfulMock = ContentfulMock;
|
||||
@@ -0,0 +1,6 @@
|
||||
export const createClient = jest.fn()
|
||||
const mock = jest.fn().mockImplementation(() => {
|
||||
return { createClient }
|
||||
})
|
||||
|
||||
export default mock
|
||||
@@ -0,0 +1,179 @@
|
||||
import ContentfulService from "../contentful"
|
||||
|
||||
describe("ContentfulService", () => {
|
||||
describe("delete in medusa", () => {
|
||||
const regionService = {
|
||||
retrieve: jest.fn((id) => {
|
||||
if (id === "exists") {
|
||||
return Promise.resolve({ id: "exists" })
|
||||
}
|
||||
return Promise.resolve(undefined)
|
||||
}),
|
||||
}
|
||||
const productService = {
|
||||
retrieve: jest.fn((id) => {
|
||||
if (id === "exists") {
|
||||
return Promise.resolve({ id: "exists" })
|
||||
}
|
||||
return Promise.resolve(undefined)
|
||||
}),
|
||||
}
|
||||
const redisClient = {
|
||||
get: async (id) => {
|
||||
// const key = `${id}_ignore_${side}`
|
||||
if (id === `ignored_ignore_contentful`) {
|
||||
return { id }
|
||||
}
|
||||
return undefined
|
||||
},
|
||||
set: async (id) => {
|
||||
return undefined
|
||||
},
|
||||
}
|
||||
const productVariantService = {
|
||||
retrieve: jest.fn((id) => {
|
||||
if (id === "exists") {
|
||||
return Promise.resolve({ id: "exists" })
|
||||
}
|
||||
return Promise.resolve(undefined)
|
||||
}),
|
||||
}
|
||||
const eventBusService = {}
|
||||
|
||||
const service = new ContentfulService(
|
||||
{
|
||||
regionService,
|
||||
productService,
|
||||
redisClient,
|
||||
productVariantService,
|
||||
eventBusService,
|
||||
},
|
||||
{
|
||||
space_id: "test_id",
|
||||
environment: "master",
|
||||
access_token: "test_token",
|
||||
}
|
||||
)
|
||||
|
||||
const entry = {
|
||||
unpublish: jest.fn(async () => {
|
||||
return {
|
||||
id: "id",
|
||||
}
|
||||
}),
|
||||
archive: jest.fn(async () => {
|
||||
return {
|
||||
id: "id",
|
||||
}
|
||||
}),
|
||||
}
|
||||
|
||||
service.contentful_ = {
|
||||
getSpace: async (space_id) => {
|
||||
return {
|
||||
getEnvironment: async (env) => {
|
||||
return {
|
||||
getEntry: async (id) => {
|
||||
if (id === "onlyMedusa") {
|
||||
throw new Error("doesn't exist")
|
||||
}
|
||||
return entry
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
describe("archiveProductInContentful", () => {
|
||||
it("Calls entry.unpublish and entry.archive", async () => {
|
||||
await service.archiveProductInContentful({ id: "test" })
|
||||
|
||||
expect(entry.unpublish).toHaveBeenCalledTimes(1)
|
||||
expect(entry.archive).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it("Doesn't call entry.unpublish and entry.archive if the product still exists in medusa", async () => {
|
||||
await service.archiveProductInContentful({ id: "exists" })
|
||||
|
||||
expect(entry.unpublish).toHaveBeenCalledTimes(0)
|
||||
expect(entry.archive).toHaveBeenCalledTimes(0)
|
||||
})
|
||||
|
||||
it("Doesn't call productService if request should be ignored", async () => {
|
||||
await service.archiveProductInContentful({ id: "ignored" })
|
||||
|
||||
expect(productService.retrieve).toHaveBeenCalledTimes(0)
|
||||
expect(entry.unpublish).toHaveBeenCalledTimes(0)
|
||||
expect(entry.archive).toHaveBeenCalledTimes(0)
|
||||
})
|
||||
})
|
||||
|
||||
describe("archiveProductVariantInContentful", () => {
|
||||
it("Calls entry.unpublish and entry.archive", async () => {
|
||||
await service.archiveProductVariantInContentful({ id: "test" })
|
||||
|
||||
expect(entry.unpublish).toHaveBeenCalledTimes(1)
|
||||
expect(entry.archive).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it("Doesn't call entry.unpublish and entry.archive if the variant still exists in medusa", async () => {
|
||||
await service.archiveProductVariantInContentful({ id: "exists" })
|
||||
|
||||
expect(entry.unpublish).toHaveBeenCalledTimes(0)
|
||||
expect(entry.archive).toHaveBeenCalledTimes(0)
|
||||
})
|
||||
|
||||
it("Doesn't call productVariantService if request should be ignored", async () => {
|
||||
await service.archiveProductVariantInContentful({ id: "ignored" })
|
||||
|
||||
expect(productVariantService.retrieve).toHaveBeenCalledTimes(0)
|
||||
expect(entry.unpublish).toHaveBeenCalledTimes(0)
|
||||
expect(entry.archive).toHaveBeenCalledTimes(0)
|
||||
})
|
||||
})
|
||||
|
||||
describe("archiveRegionInContentful", () => {
|
||||
it("Calls entry.unpublish and entry.archive", async () => {
|
||||
await service.archiveRegionInContentful({ id: "test" })
|
||||
|
||||
expect(entry.unpublish).toHaveBeenCalledTimes(1)
|
||||
expect(entry.archive).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it("Doesn't call entry.unpublish and entry.archive if the region still exists in medusa", async () => {
|
||||
await service.archiveRegionInContentful({ id: "exists" })
|
||||
|
||||
expect(entry.unpublish).toHaveBeenCalledTimes(0)
|
||||
expect(entry.archive).toHaveBeenCalledTimes(0)
|
||||
})
|
||||
|
||||
it("Doesn't call RegionService if request should be ignored", async () => {
|
||||
await service.archiveRegionInContentful({ id: "ignored" })
|
||||
|
||||
expect(regionService.retrieve).toHaveBeenCalledTimes(0)
|
||||
expect(entry.unpublish).toHaveBeenCalledTimes(0)
|
||||
expect(entry.archive).toHaveBeenCalledTimes(0)
|
||||
})
|
||||
})
|
||||
|
||||
describe("archiveEntryWidthId", () => {
|
||||
it("Calls archive if entry exists", async () => {
|
||||
await service.archiveEntryWidthId("exists")
|
||||
|
||||
expect(entry.unpublish).toHaveBeenCalledTimes(1)
|
||||
expect(entry.archive).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
it("Doesnt call archive if entry doesn't exists", async () => {
|
||||
await service.archiveEntryWidthId("onlyMedusa")
|
||||
|
||||
expect(entry.unpublish).toHaveBeenCalledTimes(0)
|
||||
expect(entry.archive).toHaveBeenCalledTimes(0)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -91,7 +91,7 @@ class ContentfulService extends BaseService {
|
||||
async createImageAssets(product) {
|
||||
const environment = await this.getContentfulEnvironment_()
|
||||
|
||||
let assets = []
|
||||
const assets = []
|
||||
await Promise.all(
|
||||
product.images
|
||||
.filter((image) => image.url !== product.thumbnail)
|
||||
@@ -646,6 +646,92 @@ class ContentfulService extends BaseService {
|
||||
}
|
||||
}
|
||||
|
||||
async archiveProductVariantInContentful(variant) {
|
||||
let variantEntity
|
||||
try {
|
||||
const ignore = await this.shouldIgnore_(variant.id, "contentful")
|
||||
if (ignore) {
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
try {
|
||||
variantEntity = await this.productVariantService_.retrieve(variant.id)
|
||||
} catch (err) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
if (variantEntity) {
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
return await this.archiveEntryWidthId(variant.id)
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
async archiveProductInContentful(product) {
|
||||
let productEntity
|
||||
try {
|
||||
const ignore = await this.shouldIgnore_(product.id, "contentful")
|
||||
if (ignore) {
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
try {
|
||||
productEntity = await this.productService_.retrieve(product.id)
|
||||
} catch (err) {}
|
||||
|
||||
if (productEntity) {
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
return await this.archiveEntryWidthId(product.id)
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
async archiveRegionInContentful(region) {
|
||||
let regionEntity
|
||||
try {
|
||||
const ignore = await this.shouldIgnore_(region.id, "contentful")
|
||||
if (ignore) {
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
try {
|
||||
regionEntity = await this.regionService_.retrieve(region.id)
|
||||
} catch (err) {}
|
||||
|
||||
if (regionEntity) {
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
return await this.archiveEntryWidthId(region.id)
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
async archiveEntryWidthId(id) {
|
||||
const environment = await this.getContentfulEnvironment_()
|
||||
// check if product exists
|
||||
let entry = undefined
|
||||
try {
|
||||
entry = await environment.getEntry(id)
|
||||
} catch (error) {
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
const unpublishEntry = await entry.unpublish()
|
||||
const archivedEntry = await entry.archive()
|
||||
|
||||
await this.addIgnore_(id, "medusa")
|
||||
|
||||
return archivedEntry
|
||||
}
|
||||
|
||||
async sendContentfulProductToAdmin(productId) {
|
||||
const ignore = await this.shouldIgnore_(productId, "medusa")
|
||||
if (ignore) {
|
||||
@@ -658,7 +744,7 @@ class ContentfulService extends BaseService {
|
||||
|
||||
const product = await this.productService_.retrieve(productId)
|
||||
|
||||
let update = {}
|
||||
const update = {}
|
||||
|
||||
const title =
|
||||
productEntry.fields[this.getCustomField("title", "product")]["en-US"]
|
||||
@@ -741,9 +827,9 @@ class ContentfulService extends BaseService {
|
||||
isArray = false
|
||||
}
|
||||
|
||||
let output = []
|
||||
const output = []
|
||||
for (const obj of input) {
|
||||
let transformed = Object.assign({}, obj)
|
||||
const transformed = Object.assign({}, obj)
|
||||
transformed.medusaId = obj.id
|
||||
output.push(transformed)
|
||||
}
|
||||
|
||||
@@ -18,10 +18,18 @@ class ContentfulSubscriber {
|
||||
await this.contentfulService_.updateRegionInContentful(data)
|
||||
})
|
||||
|
||||
this.eventBus_.subscribe("region.deleted", async (data) => {
|
||||
await this.contentfulService_.updateRegionInContentful(data)
|
||||
})
|
||||
|
||||
this.eventBus_.subscribe("product-variant.updated", async (data) => {
|
||||
await this.contentfulService_.updateProductVariantInContentful(data)
|
||||
})
|
||||
|
||||
this.eventBus_.subscribe("product-variant.deleted", async (data) => {
|
||||
await this.contentfulService_.archiveProductVariantInContentful(data)
|
||||
})
|
||||
|
||||
this.eventBus_.subscribe("product.updated", async (data) => {
|
||||
await this.contentfulService_.updateProductInContentful(data)
|
||||
})
|
||||
@@ -29,6 +37,10 @@ class ContentfulSubscriber {
|
||||
this.eventBus_.subscribe("product.created", async (data) => {
|
||||
await this.contentfulService_.createProductInContentful(data)
|
||||
})
|
||||
|
||||
this.eventBus_.subscribe("product.deleted", async (data) => {
|
||||
await this.contentfulService_.archiveProductInContentful(data)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"plugins": ["prettier"],
|
||||
"extends": ["prettier"],
|
||||
"rules": {
|
||||
"prettier/prettier": "error",
|
||||
"semi": "error",
|
||||
"no-unused-expressions": "true"
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"endOfLine": "lf",
|
||||
"semi": false,
|
||||
"singleQuote": false,
|
||||
"tabWidth": 2,
|
||||
"trailingComma": "es5"
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"plugins": ["prettier"],
|
||||
"extends": ["prettier"],
|
||||
"rules": {
|
||||
"prettier/prettier": "error",
|
||||
"semi": "error",
|
||||
"no-unused-expressions": "true"
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"endOfLine": "lf",
|
||||
"semi": false,
|
||||
"singleQuote": false,
|
||||
"tabWidth": 2,
|
||||
"trailingComma": "es5"
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"endOfLine": "lf",
|
||||
"semi": false,
|
||||
"singleQuote": false,
|
||||
"tabWidth": 2,
|
||||
"trailingComma": "es5"
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"plugins": ["prettier"],
|
||||
"extends": ["prettier"],
|
||||
"rules": {
|
||||
"prettier/prettier": "error",
|
||||
"semi": "error",
|
||||
"no-unused-expressions": "true"
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"endOfLine": "lf",
|
||||
"semi": false,
|
||||
"singleQuote": false,
|
||||
"tabWidth": 2,
|
||||
"trailingComma": "es5"
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"plugins": ["prettier"],
|
||||
"extends": ["prettier"],
|
||||
"rules": {
|
||||
"prettier/prettier": "error",
|
||||
"semi": "error",
|
||||
"no-unused-expressions": "true"
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"endOfLine": "lf",
|
||||
"semi": false,
|
||||
"singleQuote": false,
|
||||
"tabWidth": 2,
|
||||
"trailingComma": "es5"
|
||||
}
|
||||
@@ -59,7 +59,9 @@ class MeiliSearchService extends SearchService {
|
||||
}
|
||||
|
||||
transformProducts(products) {
|
||||
if (!products) return []
|
||||
if (!products) {
|
||||
return []
|
||||
}
|
||||
return products.map(transformProduct)
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user