From bdd1763bb216b1cbbc060942526cc4af7555b03c Mon Sep 17 00:00:00 2001 From: Vadim Smirnov <62517920+FuzzyReason@users.noreply.github.com> Date: Wed, 15 Sep 2021 20:12:19 +0300 Subject: [PATCH 01/43] docs: create-medusa-app article (#401) --- docs/content/how-to/create-medusa-app.md | 117 +++++++++++++++++++++++ www/docs/sidebars.js | 4 + 2 files changed, 121 insertions(+) create mode 100644 docs/content/how-to/create-medusa-app.md diff --git a/docs/content/how-to/create-medusa-app.md b/docs/content/how-to/create-medusa-app.md new file mode 100644 index 0000000000..a3394df3c0 --- /dev/null +++ b/docs/content/how-to/create-medusa-app.md @@ -0,0 +1,117 @@ +--- +title: Using create-medusa-app +--- +# Using create-medusa-app +With the new `create-medusa-app` tool you will get your [Medusa](https://github.com/medusajs/medusa) development environment ready within a couple of minutes. After completion, you will have a Medusa backend, a Gatsby or Next.js storefront, and an admin dashboard up and running on your local machine. + +Starting a new e-commerce project just got easier, now with one command. + +## Getting started with `create-medusa-app` + +Use `create-medusa-app` with your preferred package manager: + +```bash +yarn create medusa-app + +npx create-medusa-app +``` + +Behind the scenes, `create-medusa-app` is populating your database with some initial set of mock data, which helps to interact with Medusa setup intuitively straight away. + +Right after hitting one of those commands, the multistep installation process will be initiated, so the starter can be shaped right for the specific needs. + +### Destination folder + +Enter the path to the directory that will become the root of your Medusa project: + +```bash +? Where should your project be installed? › my-medusa-store +``` + +### Pick the starter you prefer + +```bash +? Which Medusa starter would you like to install? … +❯ medusa-starter-default + medusa-starter-contentful + Other +``` + +You will be presented with three options: + +- `medusa-starter-default` is the most lightweight version of a Medusa project +- `medusa-starter-contentful` almost like the default starter, but with `medusa-plugin-contentful` preinstalled +- `Other` if you have a different starter that you would wish to install from `Other` will give you the option of providing a URL to that starter. An additional question will be asked if you choose this option: + + ```bash + Where is the starter located? (URL or path) › https://github.com/somecoolusername/my-custom-medusa-starter + ``` + +For the walkthrough purposes, we assume that the selected starter is `medusa-starter-default` and proceed to the next step. + +### Selecting a Storefront + +After selecting your Medusa starter you will be given the option to install one of our storefront starters. At the moment we have starters for Gatsby and Next.js: + +```bash +Which storefront starter would you like to install? … +❯ Gatsby Starter + Next.js Starter + None +``` + +You may also select `None` if the choice is to craft a custom storefront for your product. + +`create-medusa-app` now has all of the info necessary for the installation to begin. + +```bash +Creating new project from git: https://github.com/medusajs/medusa-starter-default.git +✔ Created starter directory layout +Installing packages... +``` + +Once the installation has been completed you will have a Medusa backend, a demo storefront, and an admin dashboard. + +## What's inside + +Inside the root folder which was specified at the beginning of the installation process the following structure could be found: + +```bash +/my-medusa-store + /storefront // Medusa storefront starter + /backend // Medusa starter as a backend option + /admin // Medusa admin panel +``` + +`create-medusa-app` prints out the commands that are available to you after installation. When each project is started you can visit your storefront, complete the order, and view the order in Medusa admin. + +```bash +⠴ Installing packages... +✔ Packages installed +Initialising git in my-medusa-store/admin +Create initial git commit in my-medusa-store/admin + + Your project is ready 🚀. The available commands are: + + Medusa API + cd my-medusa-store/backend + yarn start + + Admin + cd my-medusa-store/admin + yarn start + + Storefront + cd my-medusa-store/storefront + yarn start +``` + +## **What's next?** + +To learn more about Medusa to go through our docs to get some inspiration and guidance for the next steps and further development: + +- [Find out how to set up a Medusa project with Gatsby and Contentful](https://docs.medusa-commerce.com/how-to/headless-ecommerce-store-with-gatsby-contentful-medusa) +- [Move your Medusa setup to the next level with some custom functionality](https://docs.medusa-commerce.com/tutorial/adding-custom-functionality) +- [Create your own Medusa plugin](https://docs.medusa-commerce.com/how-to/plugins) + +If you have any follow-up questions or want to chat directly with our engineering team we are always happy to meet you at our [Discord](https://discord.gg/DSHySyMu). diff --git a/www/docs/sidebars.js b/www/docs/sidebars.js index 20ed1bbe4c..085a89b5d7 100644 --- a/www/docs/sidebars.js +++ b/www/docs/sidebars.js @@ -75,6 +75,10 @@ module.exports = { type: "doc", id: "how-to/setting-up-a-nextjs-storefront-for-your-medusa-project", }, + { + type: "doc", + id: "how-to/create-medusa-app" + } ], }, { From d2a14a5d515431fee19163eae5bbce430706c707 Mon Sep 17 00:00:00 2001 From: Vadim Smirnov <62517920+FuzzyReason@users.noreply.github.com> Date: Thu, 16 Sep 2021 08:52:03 +0300 Subject: [PATCH 02/43] fix: temporarily comment out cloud related docs (#387) --- docs/content/quickstart/quick-start.md | 4 ++-- .../tutorial/0-set-up-your-development-environment.md | 6 +++--- docs/content/tutorial/2-adding-custom-functionality.md | 2 +- www/docs/sidebars.js | 8 ++++---- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/content/quickstart/quick-start.md b/docs/content/quickstart/quick-start.md index e39c21faf8..fcc16e9475 100644 --- a/docs/content/quickstart/quick-start.md +++ b/docs/content/quickstart/quick-start.md @@ -32,6 +32,6 @@ We have created two starters for you that can help you lay a foundation for your - [Nextjs Starter](https://github.com/medusajs/nextjs-starter-medusa) - [Gatsby Starter](https://github.com/medusajs/gatsby-starter-medusa) -### Link you local development to Medusa Cloud (Coming soon!) + diff --git a/docs/content/tutorial/0-set-up-your-development-environment.md b/docs/content/tutorial/0-set-up-your-development-environment.md index a93bccea3c..48583f1b00 100644 --- a/docs/content/tutorial/0-set-up-your-development-environment.md +++ b/docs/content/tutorial/0-set-up-your-development-environment.md @@ -10,7 +10,7 @@ Welcome to Medusa - we are so excited to get you on board! This tutorial will walk you through the steps to take to set up your local development environment. You will familiarize yourself with some of the core parts that make Medusa work and learn how to configure your development environment. Furthermore you will be introduced to how the plugin architecture works and how to customize your commerce functionalities with custom logic and endpoints. -As a final part of the tutorial you will be linking your local project to Medusa Cloud where you can leverage advanced tools that make it easy to develop, test and deploy your Medusa project. + ## Background Knowledge and Prerequisites @@ -107,11 +107,11 @@ If you don't already have a text editor of choice you should find one you like - It is not important which editor you use as long as you feel comfortable working with it. -## Medusa Cloud account + ## Summary diff --git a/docs/content/tutorial/2-adding-custom-functionality.md b/docs/content/tutorial/2-adding-custom-functionality.md index 0aa78ae9d7..cc0d02d652 100644 --- a/docs/content/tutorial/2-adding-custom-functionality.md +++ b/docs/content/tutorial/2-adding-custom-functionality.md @@ -252,4 +252,4 @@ You have now learned how to add custom functionality to your Medusa server, whic You have now been introduced to many of the key parts of Medusa and with your knowledge of customization you can now begin creating some really powerful commerce experiences. If you have an idea for a cool customization go ahead and make it right now! If you are not completely ready yet you can browse the reference docs further. -In the next part of this tutorial we will look into linking your local project with Medusa Cloud to make develpment smoother while leveraging the powerful management tools that merchants use to manage their Medusa store. + diff --git a/www/docs/sidebars.js b/www/docs/sidebars.js index 085a89b5d7..6d2b962185 100644 --- a/www/docs/sidebars.js +++ b/www/docs/sidebars.js @@ -43,10 +43,10 @@ module.exports = { type: "doc", id: "tutorial/adding-custom-functionality", }, - { - type: "doc", - id: "tutorial/linking-your-local-project-with-medusa-cloud", - }, + // { + // type: "doc", + // id: "tutorial/linking-your-local-project-with-medusa-cloud", + // }, ], }, { From d2739507ab182ca97087a468b02789b2a28134ea Mon Sep 17 00:00:00 2001 From: ps-89 <91064940+ps-89@users.noreply.github.com> Date: Tue, 21 Sep 2021 11:14:14 +0530 Subject: [PATCH 03/43] Fix typo in registerOptin section (#407) --- docs/content/tutorial/2-adding-custom-functionality.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content/tutorial/2-adding-custom-functionality.md b/docs/content/tutorial/2-adding-custom-functionality.md index cc0d02d652..e04d84303a 100644 --- a/docs/content/tutorial/2-adding-custom-functionality.md +++ b/docs/content/tutorial/2-adding-custom-functionality.md @@ -56,7 +56,7 @@ In the constructor we specify that our `WelcomeService` will depend upon the `ca ### `registerOptin` -The `registerOption` function will take to arguments: `cartId` and `optin`, where `cartId` holds the id of the cart that we wish to register optin for and `optin` is a boolean to indicate if the customer has accepted or optin or not. We will save the `optin` preferences in the cart's `metadata` field, so that it can be persisted for the future when we need to evaluate if we should send the welcome or not. +The `registerOption` function will take two arguments: `cartId` and `optin`, where `cartId` holds the id of the cart that we wish to register optin for and `optin` is a boolean to indicate if the customer has accepted or optin or not. We will save the `optin` preferences in the cart's `metadata` field, so that it can be persisted for the future when we need to evaluate if we should send the welcome or not. ```javascript async registerOptin(cartId, optin) { From 4ee6c0afb2984c27e006fad88984893cedcfb535 Mon Sep 17 00:00:00 2001 From: Sebastian Rindom Date: Tue, 21 Sep 2021 08:03:18 +0200 Subject: [PATCH 04/43] docs: Carts in Medusa (#406) --- docs/content/guides/carts-in-medusa.md | 163 +++++++++++++++++++++++++ www/docs/sidebars.js | 12 +- 2 files changed, 173 insertions(+), 2 deletions(-) create mode 100644 docs/content/guides/carts-in-medusa.md diff --git a/docs/content/guides/carts-in-medusa.md b/docs/content/guides/carts-in-medusa.md new file mode 100644 index 0000000000..7d7e673954 --- /dev/null +++ b/docs/content/guides/carts-in-medusa.md @@ -0,0 +1,163 @@ +--- +title: Carts in Medusa +--- + +# Carts in Medusa + +In Medusa a Cart serves the purpose of collecting the information needed to create an Order, including what products to purchase, what address to send the products to and which payment method the purchase will be processed by. + +To create a cart using the `@medusajs/medusa-js` SDK you can use: + +```javascript +const client = new Medusa({ baseUrl: "http://localhost:9000" }) +const { cart } = await client.carts.create() +``` + +A Cart will always belong to a Region and you may provide a `region_id` upon Cart creation. If no `region_id` is specified Medusa will assign the Cart to a random Region. Regions specify information about how the Cart should be taxed, what currency the Cart should be paid with and what payment and fulfillment options will be available at checkout. Below are some of the properties that can be found on the Cart response. For a full example of a Cart response [check our fixtures](https://github.com/medusajs/medusa/blob/docs/api/docs/api/fixtures/store/GetCartsCart.json). + +```json + "cart": { + "id": "cart_01FEWZSRFWT8QWMHJ7ZCPRP3BZ", + "email": null, + "billing_address": null, + "shipping_address": null, + "items": [ ... ], + "region": { + "id": "reg_01FEWZSRD7HVHBSQRC4KYMG5XM", + "name": "United States", + "currency_code": "usd", + "tax_rate": "0", + ... + }, + "discounts": [], + "gift_cards": [], + "customer_id": null, + "payment_sessions": [], + "payment": null, + "shipping_methods": [], + "type": "default", + "metadata": null, + "shipping_total": 0, + "discount_total": 0, + "tax_total": 0, + "gift_card_total": 0, + "subtotal": 1000, + "total": 1000, + ... + } +``` + +## Adding products to the Cart + +Customers can add products to the Cart in order to start gathering the items that will eventually be purchased. In Medusa adding a product to a Cart will result in a _Line Item_ being generated. To add a product using the SDK use: + +```javascript +const { cart } = await client.carts.lineItems.create(cartId, { + variant_id: "[id-of-variant-to-add]", + quantity: 1, +}) +``` + +The resulting response will look something like this: + +```json +{ + "cart": { + "id": "cart_01FEWZSRFWT8QWMHJ7ZCPRP3BZ", + "items": [ + { + "id": "item_01FEWZSRMBAN85SKPCRMM30N6W", + "cart_id": "cart_01FEWZSRFWT8QWMHJ7ZCPRP3BZ", + "title": "Basic Tee", + "description": "Small", + "thumbnail": null, + "is_giftcard": false, + "should_merge": true, + "allow_discounts": true, + "has_shipping": false, + "unit_price": 1000, + "variant": { + "id": "variant_01FEWZSRDNWABVFZTZ21JWKHRG", + "title": "Small", + "product_id": "prod_01FEWZSRDHDDSHQV6ATG6MS2MF", + "sku": null, + "barcode": null, + "ean": null, + "upc": null, + "allow_backorder": false, + "hs_code": null, + "origin_country": null, + "mid_code": null, + "material": null, + "weight": null, + "length": null, + "height": null, + "width": null, + "metadata": null, + ... + }, + "quantity": 1, + "metadata": {}, + ... + } + ], + ... + } +} +``` + +The properties stored on a Line Item are useful for explaining and displaying the contents of the Cart. For example, Line Items can have a thumbnail assigned which can be used to display a pack shot of the product that is being purchased, a title to show name the products in the cart and a description to give further details about the product. By default the Line Item will be generated with properties inherited from the Product that is being added to the Cart, but the behaviour can be customized for other purposes as well. + +## Adding Customer information to a Cart + +After adding products to the Cart, you should gather information about where to send the products, this is done using the `update` method in the SDK. + +```javascript +const { cart } = await client.carts.update(cartId, { + email: "jane.doe@mail.com", + shipping_address: { + first_name: "Jane", + last_name: "Doe", + address_1: "4242 Hollywood Dr", + postal_code: "12345", + country_code: "us", + city: "Los Angeles", + region: "CA", + }, +}) +``` + +Note that the country code in the shipping address must be the country code for one of the countries in a Region - otherwise this method will fail. + +## Initializing Payment Sessions + +In Medusa payments are handled through the long lived entities called _Payment Sessions_. Payment Sessions cary provider specific data that can later be used to authorize the payments, which is the step required before an order can be created. The SDK provides a `createPaymentSessions` method that can be used to initialize the payment sessions with the Payment Providers available in the Region. + +```javascript +const { cart } = await client.carts.createPaymentSessions(cartId) +``` + +You can read more about Payment Sessions in our [guide to checkouts](https://docs.medusa-commerce.com/guides/checkouts). + +## Changing the Cart region + +To update the Region that the cart belongs to you should also use the `update` method from the SDK. + +```javascript +const { cart } = await client.carts.update(cartId, { + region_id: "[id-of-region-to-switch-to]", +}) +``` + +When changing the Cart region you should be aware of a couple of things: + +- If switching to a Region with a different currency the line item prices and cart totals will change +- If switching to a Region with a different tax rate prices and totals will change +- If switching to a Region serving only one country the `shipping_address.country_code` will automatically be set to that country +- If the Cart already has initialized payment sessions all of these will be canceled and a new call to `createPaymentSessions` will have to be made + +## What's next? + +Carts are at the core of the shopping process in Medusa and provide all the necessary functionality to gather products for purchase. If you want to read a more detailed guide about how to complete checkouts please go to our [Checkout Guide](https://docs.medusa-commerce.com/guides/checkout). + +If you have questions or issues feel free to reach out via our [Discord server](https://discord.gg/xpCwq3Kfn8) for direct access to the Medusa engineering team. diff --git a/www/docs/sidebars.js b/www/docs/sidebars.js index 6d2b962185..b8fadb9968 100644 --- a/www/docs/sidebars.js +++ b/www/docs/sidebars.js @@ -77,8 +77,8 @@ module.exports = { }, { type: "doc", - id: "how-to/create-medusa-app" - } + id: "how-to/create-medusa-app", + }, ], }, { @@ -89,6 +89,14 @@ module.exports = { type: "doc", id: "guides/fulfillment-api", }, + { + type: "doc", + id: "guides/checkouts", + }, + { + type: "doc", + id: "guides/carts-in-medusa", + }, ], }, ], From c715077be5c66b2809c21829d85b0f01c3499ab0 Mon Sep 17 00:00:00 2001 From: Oliver Windall Juhl <59018053+olivermrbl@users.noreply.github.com> Date: Wed, 22 Sep 2021 13:42:31 +0200 Subject: [PATCH 05/43] hotfix: Stripe cancelPayment catch block (#416) --- packages/medusa-payment-stripe/src/services/stripe-provider.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/medusa-payment-stripe/src/services/stripe-provider.js b/packages/medusa-payment-stripe/src/services/stripe-provider.js index da97bc9c01..781dabbcf4 100644 --- a/packages/medusa-payment-stripe/src/services/stripe-provider.js +++ b/packages/medusa-payment-stripe/src/services/stripe-provider.js @@ -333,7 +333,7 @@ class StripeProviderService extends PaymentService { async cancelPayment(payment) { const { id } = payment.data try { - return this.stripe_.paymentIntents.cancel(id) + return await this.stripe_.paymentIntents.cancel(id) } catch (error) { if (error.payment_intent.status === "canceled") { return error.payment_intent From 84343c88e3ad084561bb8625e47b9a4af8567d3d Mon Sep 17 00:00:00 2001 From: olivermrbl Date: Wed, 22 Sep 2021 13:52:35 +0200 Subject: [PATCH 06/43] chore(release): Publish - medusa-payment-stripe@1.1.26 --- packages/medusa-payment-stripe/CHANGELOG.md | 8 ++++++++ packages/medusa-payment-stripe/package.json | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/medusa-payment-stripe/CHANGELOG.md b/packages/medusa-payment-stripe/CHANGELOG.md index 688e73c8d9..a38c7a8a2e 100644 --- a/packages/medusa-payment-stripe/CHANGELOG.md +++ b/packages/medusa-payment-stripe/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.1.26](https://github.com/medusajs/medusa/compare/medusa-payment-stripe@1.1.25...medusa-payment-stripe@1.1.26) (2021-09-22) + +**Note:** Version bump only for package medusa-payment-stripe + + + + + ## [1.1.25](https://github.com/medusajs/medusa/compare/medusa-payment-stripe@1.1.24...medusa-payment-stripe@1.1.25) (2021-09-15) **Note:** Version bump only for package medusa-payment-stripe diff --git a/packages/medusa-payment-stripe/package.json b/packages/medusa-payment-stripe/package.json index afd846d38d..74ea0729eb 100644 --- a/packages/medusa-payment-stripe/package.json +++ b/packages/medusa-payment-stripe/package.json @@ -1,6 +1,6 @@ { "name": "medusa-payment-stripe", - "version": "1.1.25", + "version": "1.1.26", "description": "Stripe Payment provider for Meduas Commerce", "main": "index.js", "repository": { From a8f06a49d78c88c77c00651f2f24fb44197f7640 Mon Sep 17 00:00:00 2001 From: ps-89 <91064940+ps-89@users.noreply.github.com> Date: Wed, 22 Sep 2021 22:07:50 +0530 Subject: [PATCH 07/43] Fix typo in Plugins in Medusa doc (#414) --- docs/content/how-to/plugins.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content/how-to/plugins.md b/docs/content/how-to/plugins.md index 83dd41d30c..8d8c837123 100644 --- a/docs/content/how-to/plugins.md +++ b/docs/content/how-to/plugins.md @@ -10,7 +10,7 @@ The purpose of this guide is to give an introduction to the structure of a plugi Plugins offer a way to extend and integrate the core functionality of Medusa. -In most commerce solutions, you can extend the basic features but it often comes with the expense of having to build standalone web applications. Our architecture is built such that plugins run within the same process as the core eliminating the need for extra server capacaity, infrastructure and maintenance. As a result, the plugins can use all other services as dependencies and access the database. +In most commerce solutions, you can extend the basic features but it often comes with the expense of having to build standalone web applications. Our architecture is built such that plugins run within the same process as the core eliminating the need for extra server capacity, infrastructure and maintenance. As a result, the plugins can use all other services as dependencies and access the database. > You will notice that plugins vary in naming. The name should signal what functionality they provide. From ff9cc0fa417b3ecf161a81608a653c173f56993a Mon Sep 17 00:00:00 2001 From: Oliver Windall Juhl <59018053+olivermrbl@users.noreply.github.com> Date: Tue, 28 Sep 2021 19:31:50 +0200 Subject: [PATCH 08/43] docs: Deploying Medusa on Heroku (#425) --- docs/content/how-to/deploying-on-heroku.md | 198 +++++++++++++++++++++ www/docs/sidebars.js | 10 ++ 2 files changed, 208 insertions(+) create mode 100644 docs/content/how-to/deploying-on-heroku.md diff --git a/docs/content/how-to/deploying-on-heroku.md b/docs/content/how-to/deploying-on-heroku.md new file mode 100644 index 0000000000..67bb7dedee --- /dev/null +++ b/docs/content/how-to/deploying-on-heroku.md @@ -0,0 +1,198 @@ +--- +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 (Coming soon) +- Deploy Admin on Gatsby Cloud (Coming soon) diff --git a/www/docs/sidebars.js b/www/docs/sidebars.js index b8fadb9968..4d5ca97c0d 100644 --- a/www/docs/sidebars.js +++ b/www/docs/sidebars.js @@ -99,5 +99,15 @@ module.exports = { }, ], }, + { + type: "category", + label: "Deploy", + items: [ + { + type: "doc", + id: "how-to/deploying-on-heroku", + }, + ], + }, ], } From b9301004797128f1a61bcb13b252b4e1d6eed09a Mon Sep 17 00:00:00 2001 From: Oliver Windall Juhl <59018053+olivermrbl@users.noreply.github.com> Date: Thu, 30 Sep 2021 12:49:12 +0200 Subject: [PATCH 09/43] docs: Deploying Medusa Admin on Netlify (#431) --- .../how-to/deploying-admin-on-netlify.md | 69 +++++++++++++++++++ www/docs/sidebars.js | 6 +- 2 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 docs/content/how-to/deploying-admin-on-netlify.md diff --git a/docs/content/how-to/deploying-admin-on-netlify.md b/docs/content/how-to/deploying-admin-on-netlify.md new file mode 100644 index 0000000000..22cd735a5d --- /dev/null +++ b/docs/content/how-to/deploying-admin-on-netlify.md @@ -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). diff --git a/www/docs/sidebars.js b/www/docs/sidebars.js index 4d5ca97c0d..4c77faaa41 100644 --- a/www/docs/sidebars.js +++ b/www/docs/sidebars.js @@ -99,7 +99,7 @@ module.exports = { }, ], }, - { + { type: "category", label: "Deploy", items: [ @@ -107,6 +107,10 @@ module.exports = { type: "doc", id: "how-to/deploying-on-heroku", }, + { + type: "doc", + id: "how-to/deploying-admin-on-netlify", + }, ], }, ], From 22f3f2af93fcebef5ebf89ce66cd926393ae5d25 Mon Sep 17 00:00:00 2001 From: Kasper Fabricius Kristensen <45367945+kasperkristensen@users.noreply.github.com> Date: Fri, 1 Oct 2021 08:18:56 +0200 Subject: [PATCH 10/43] fix: shipping option updates (#426) * fix to remove req * tested fix --- ...te_date_on_shipping_option_requirements.ts | 18 ++++++++ .../src/models/shipping-option-requirement.ts | 5 +- .../src/services/__tests__/shipping-option.js | 38 ++++++--------- .../medusa/src/services/shipping-option.js | 46 ++++++++++++------- 4 files changed, 66 insertions(+), 41 deletions(-) create mode 100644 packages/medusa/src/migrations/1632828114899-delete_date_on_shipping_option_requirements.ts diff --git a/packages/medusa/src/migrations/1632828114899-delete_date_on_shipping_option_requirements.ts b/packages/medusa/src/migrations/1632828114899-delete_date_on_shipping_option_requirements.ts new file mode 100644 index 0000000000..12011f31a9 --- /dev/null +++ b/packages/medusa/src/migrations/1632828114899-delete_date_on_shipping_option_requirements.ts @@ -0,0 +1,18 @@ +import { MigrationInterface, QueryRunner } from "typeorm" + +export class deleteDateOnShippingOptionRequirements1632828114899 + implements MigrationInterface { + name = "deleteDateOnShippingOptionRequirements1632828114899" + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "shipping_option_requirement" ADD "deleted_at" TIMESTAMP WITH TIME ZONE` + ) + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "shipping_option_requirement" DROP COLUMN "deleted_at"` + ) + } +} diff --git a/packages/medusa/src/models/shipping-option-requirement.ts b/packages/medusa/src/models/shipping-option-requirement.ts index b847687d02..547ddf58cd 100644 --- a/packages/medusa/src/models/shipping-option-requirement.ts +++ b/packages/medusa/src/models/shipping-option-requirement.ts @@ -16,7 +16,7 @@ import { JoinTable, } from "typeorm" import { ulid } from "ulid" -import { DbAwareColumn } from "../utils/db-aware-column" +import { DbAwareColumn, resolveDbType } from "../utils/db-aware-column" import { ShippingOption } from "./shipping-option" @@ -44,6 +44,9 @@ export class ShippingOptionRequirement { @Column({ type: "int" }) amount: number + @DeleteDateColumn({ type: resolveDbType("timestamptz") }) + deleted_at: Date + @BeforeInsert() private beforeInsert() { if (this.id) return diff --git a/packages/medusa/src/services/__tests__/shipping-option.js b/packages/medusa/src/services/__tests__/shipping-option.js index e72c4d74b3..8001065535 100644 --- a/packages/medusa/src/services/__tests__/shipping-option.js +++ b/packages/medusa/src/services/__tests__/shipping-option.js @@ -296,24 +296,19 @@ describe("ShippingOptionService", () => { }) describe("removeRequirement", () => { - const shippingOptionRepository = MockRepository({ - findOne: q => { - switch (q.where.id) { - default: - return Promise.resolve({ - requirements: [ - { - id: IdMap.getId("requirement_id"), - }, - ], - }) - } + const shippingOptionRequirementRepository = MockRepository({ + softRemove: q => { + return Promise.resolve() }, + findOne: i => + i.where.id === IdMap.getId("requirement_id") + ? { id: IdMap.getId("requirement_id") } + : null, }) const optionService = new ShippingOptionService({ manager: MockManager, - shippingOptionRepository, + shippingOptionRequirementRepository, }) beforeEach(() => { @@ -321,22 +316,19 @@ describe("ShippingOptionService", () => { }) it("remove requirement successfully", async () => { - await optionService.removeRequirement( - IdMap.getId("validId"), - IdMap.getId("requirement_id") - ) + await optionService.removeRequirement(IdMap.getId("requirement_id")) - expect(shippingOptionRepository.save).toBeCalledTimes(1) - expect(shippingOptionRepository.save).toBeCalledWith({ requirements: [] }) + expect(shippingOptionRequirementRepository.findOne).toBeCalledTimes(1) + expect(shippingOptionRequirementRepository.findOne).toBeCalledWith({ + where: { id: IdMap.getId("requirement_id") }, + }) + expect(shippingOptionRequirementRepository.softRemove).toBeCalledTimes(1) }) it("is idempotent", async () => { await optionService.removeRequirement(IdMap.getId("validId"), "something") - expect(shippingOptionRepository.save).toBeCalledTimes(1) - expect(shippingOptionRepository.save).toBeCalledWith({ - requirements: [{ id: IdMap.getId("requirement_id") }], - }) + expect(shippingOptionRequirementRepository.softRemove).toBeCalledTimes(1) }) }) diff --git a/packages/medusa/src/services/shipping-option.js b/packages/medusa/src/services/shipping-option.js index c745bfbfb1..ad2cbf8724 100644 --- a/packages/medusa/src/services/shipping-option.js +++ b/packages/medusa/src/services/shipping-option.js @@ -450,7 +450,9 @@ class ShippingOptionService extends BaseService { */ async update(optionId, update) { return this.atomicPhase_(async manager => { - const option = await this.retrieve(optionId) + const option = await this.retrieve(optionId, { + relations: ["requirements"], + }) if ("metadata" in update) { option.metadata = await this.setMetadata_(option, update.metadata) @@ -498,6 +500,20 @@ class ShippingOptionService extends BaseService { acc.push(validated) } + + if (option.requirements) { + const accReqs = acc.map(a => a.id) + const toRemove = option.requirements.filter( + r => !accReqs.includes(r.id) + ) + await Promise.all( + toRemove.map(async req => { + await this.removeRequirement(req.id) + }) + ) + } + + option.requirements = acc } if ("price_type" in update) { @@ -585,28 +601,24 @@ class ShippingOptionService extends BaseService { /** * Removes a requirement from a shipping option - * @param {string} optionId - the shipping option to remove from * @param {string} requirementId - the id of the requirement to remove * @return {Promise} the result of update */ - async removeRequirement(optionId, requirementId) { + async removeRequirement(requirementId) { return this.atomicPhase_(async manager => { - const option = await this.retrieve(optionId, { - relations: "requirements", - }) - const newReqs = option.requirements.map(r => { - if (r.id === requirementId) { - return null - } else { - return r - } - }) + try { + const reqRepo = manager.getCustomRepository(this.requirementRepository_) + const requirement = await reqRepo.findOne({ + where: { id: requirementId }, + }) - option.requirements = newReqs.filter(Boolean) + const result = await reqRepo.softRemove(requirement) - const optionRepo = manager.getCustomRepository(this.optionRepository_) - const result = await optionRepo.save(option) - return result + return result + } catch (error) { + // Delete is idempotent, but we return a promise to allow then-chaining + return Promise.resolve() + } }) } From 4c343e718de74e0108b29738baf4f136560506b6 Mon Sep 17 00:00:00 2001 From: Oliver Windall Juhl <59018053+olivermrbl@users.noreply.github.com> Date: Fri, 1 Oct 2021 09:29:46 +0200 Subject: [PATCH 11/43] fix: Add deploy admin link to guide (#437) --- docs/content/how-to/deploying-on-heroku.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content/how-to/deploying-on-heroku.md b/docs/content/how-to/deploying-on-heroku.md index 67bb7dedee..888c092f1b 100644 --- a/docs/content/how-to/deploying-on-heroku.md +++ b/docs/content/how-to/deploying-on-heroku.md @@ -194,5 +194,5 @@ You now have a production ready application running on Heroku. This can be scale 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 (Coming soon) +- [Deploy Admin on Netlify](https://docs.medusa-commerce.com/how-to/deploying-admin-on-netlify) - Deploy Admin on Gatsby Cloud (Coming soon) From 337fc16c38fe90c2b985f5434fe62da758ba3f59 Mon Sep 17 00:00:00 2001 From: Oliver Windall Juhl <59018053+olivermrbl@users.noreply.github.com> Date: Fri, 1 Oct 2021 12:58:44 +0200 Subject: [PATCH 12/43] docs: Add fileservice guides for S3 and Spaces (#430) * docs: Add fileservice guides for S3 and Spaces * fix: remove we in s3 guide * fix: typos in spaces guide --- docs/content/how-to/uploading-images-to-s3.md | 85 +++++++++++++++++++ .../how-to/uploading-images-to-spaces.md | 36 ++++++++ packages/medusa-file-s3/README.md | 15 ++++ packages/medusa-file-spaces/README.md | 15 ++++ www/docs/sidebars.js | 18 ++++ 5 files changed, 169 insertions(+) create mode 100644 docs/content/how-to/uploading-images-to-s3.md create mode 100644 docs/content/how-to/uploading-images-to-spaces.md create mode 100644 packages/medusa-file-s3/README.md create mode 100644 packages/medusa-file-spaces/README.md diff --git a/docs/content/how-to/uploading-images-to-s3.md b/docs/content/how-to/uploading-images-to-s3.md new file mode 100644 index 0000000000..bc71a22736 --- /dev/null +++ b/docs/content/how-to/uploading-images-to-s3.md @@ -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. diff --git a/docs/content/how-to/uploading-images-to-spaces.md b/docs/content/how-to/uploading-images-to-spaces.md new file mode 100644 index 0000000000..976b077dd6 --- /dev/null +++ b/docs/content/how-to/uploading-images-to-spaces.md @@ -0,0 +1,36 @@ +# 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. diff --git a/packages/medusa-file-s3/README.md b/packages/medusa-file-s3/README.md new file mode 100644 index 0000000000..e5ea97a571 --- /dev/null +++ b/packages/medusa-file-s3/README.md @@ -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. diff --git a/packages/medusa-file-spaces/README.md b/packages/medusa-file-spaces/README.md new file mode 100644 index 0000000000..f756dc1cde --- /dev/null +++ b/packages/medusa-file-spaces/README.md @@ -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. diff --git a/www/docs/sidebars.js b/www/docs/sidebars.js index b8fadb9968..20fd3988f7 100644 --- a/www/docs/sidebars.js +++ b/www/docs/sidebars.js @@ -79,6 +79,14 @@ module.exports = { type: "doc", id: "how-to/create-medusa-app", }, + { + type: "doc", + id: "how-to/uploading-images-to-spaces", + }, + { + type: "doc", + id: "how-to/uploading-images-to-s3", + }, ], }, { @@ -99,5 +107,15 @@ module.exports = { }, ], }, + { + type: "category", + label: "Deploy", + items: [ + { + type: "doc", + id: "how-to/deploying-on-heroku", + }, + ], + }, ], } From 55d175bda4eb488822af02894742d8fe7c693154 Mon Sep 17 00:00:00 2001 From: Sebastian Rindom Date: Wed, 6 Oct 2021 09:11:42 +0200 Subject: [PATCH 13/43] hotfix(medusa): shipping address updates (#454) * hotfix(medusa): shipping address updates * fix: prettier --- packages/medusa/src/services/cart.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/medusa/src/services/cart.js b/packages/medusa/src/services/cart.js index fede0289d8..e0755d0b2c 100644 --- a/packages/medusa/src/services/cart.js +++ b/packages/medusa/src/services/cart.js @@ -611,7 +611,9 @@ class CartService extends BaseService { }) if ("region_id" in update) { - await this.setRegion_(cart, update.region_id, update.country_code) + const countryCode = + update.country_code || update.shipping_address?.country_code + await this.setRegion_(cart, update.region_id, countryCode) } if ("customer_id" in update) { From 66b17055ead7dd49765f50182621e24841c36660 Mon Sep 17 00:00:00 2001 From: Oliver Windall Juhl <59018053+olivermrbl@users.noreply.github.com> Date: Wed, 6 Oct 2021 11:07:44 +0200 Subject: [PATCH 14/43] docs: Deploying on Qovery (#456) --- docs/content/how-to/deploying-on-qovery.md | 121 +++++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 docs/content/how-to/deploying-on-qovery.md diff --git a/docs/content/how-to/deploying-on-qovery.md b/docs/content/how-to/deploying-on-qovery.md new file mode 100644 index 0000000000..a8e397ac55 --- /dev/null +++ b/docs/content/how-to/deploying-on-qovery.md @@ -0,0 +1,121 @@ +# 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) From 17b192fe37c14d978ae78a9537f552cf055bf5e2 Mon Sep 17 00:00:00 2001 From: Oliver Windall Juhl <59018053+olivermrbl@users.noreply.github.com> Date: Wed, 6 Oct 2021 11:19:38 +0200 Subject: [PATCH 15/43] fix: Throw on cart creation when no region exist (#455) If you try to create a cart without a region in your setup, we should throw a proper error message. --- integration-tests/api/__tests__/store/cart.js | 18 ++++++++++++++++++ .../src/api/routes/store/carts/create-cart.js | 8 ++++++++ 2 files changed, 26 insertions(+) diff --git a/integration-tests/api/__tests__/store/cart.js b/integration-tests/api/__tests__/store/cart.js index 034601e839..00177015de 100644 --- a/integration-tests/api/__tests__/store/cart.js +++ b/integration-tests/api/__tests__/store/cart.js @@ -63,6 +63,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() diff --git a/packages/medusa/src/api/routes/store/carts/create-cart.js b/packages/medusa/src/api/routes/store/carts/create-cart.js index 88fe6dc6ca..eb3ac34cec 100644 --- a/packages/medusa/src/api/routes/store/carts/create-cart.js +++ b/packages/medusa/src/api/routes/store/carts/create-cart.js @@ -82,6 +82,14 @@ export default async (req, res) => { if (!value.region_id) { const regionService = req.scope.resolve("regionService") const regions = await regionService.withTransaction(manager).list({}) + + if (!regions?.length) { + throw new MedusaError( + MedusaError.Types.INVALID_DATA, + `A region is required to create a cart` + ) + } + regionId = regions[0].id } From ee2f7c6333a0e8a4fa1454c514662bb83ce16346 Mon Sep 17 00:00:00 2001 From: Sebastian Rindom Date: Wed, 6 Oct 2021 17:32:01 +0200 Subject: [PATCH 16/43] fix: add delay before triggering email (#458) --- .../src/services/restock-notification.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/medusa-plugin-restock-notification/src/services/restock-notification.js b/packages/medusa-plugin-restock-notification/src/services/restock-notification.js index d91c576785..4244742379 100644 --- a/packages/medusa-plugin-restock-notification/src/services/restock-notification.js +++ b/packages/medusa-plugin-restock-notification/src/services/restock-notification.js @@ -111,6 +111,12 @@ class RestockNotificationService extends BaseService { * @return {Promise} The resulting restock notification */ async triggerRestock(variantId) { + if (this.options_?.trigger_delay) { + await new Promise((resolve) => + setTimeout(resolve, this.options_.trigger_delay) + ) + } + return this.atomicPhase_(async (manager) => { const restockRepo = manager.getRepository(this.restockNotificationModel_) From fe0696569cf4ba31f0067abd916e2c4a7d3865b2 Mon Sep 17 00:00:00 2001 From: Vilfred Sikker Dreijer Date: Thu, 7 Oct 2021 11:01:37 +0200 Subject: [PATCH 17/43] Feat/bancontact payment (#452) * add bancontact stripe payment * rename to BancontactProviderService --- .../src/services/stripe-bancontact.js | 241 ++++++++++++++++++ 1 file changed, 241 insertions(+) create mode 100644 packages/medusa-payment-stripe/src/services/stripe-bancontact.js diff --git a/packages/medusa-payment-stripe/src/services/stripe-bancontact.js b/packages/medusa-payment-stripe/src/services/stripe-bancontact.js new file mode 100644 index 0000000000..3b731e74d4 --- /dev/null +++ b/packages/medusa-payment-stripe/src/services/stripe-bancontact.js @@ -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>} saved payments methods + */ + async retrieveSavedMethods(customer) { + return Promise.resolve([]) + } + + /** + * Fetches a Stripe customer + * @param {string} customerId - Stripe customer id + * @returns {Promise} 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} 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} 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} 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 From fb4793866115a5e33f9ca6921cc46778ada07aab Mon Sep 17 00:00:00 2001 From: Sebastian Rindom Date: Thu, 7 Oct 2021 19:32:39 +0200 Subject: [PATCH 18/43] docs: contentful part 2 (#484) * Create making-your-store-more-powerful-with-contentful.md * Update sidebars.js --- ...our-store-more-powerful-with-contentful.md | 304 ++++++++++++++++++ www/docs/sidebars.js | 4 + 2 files changed, 308 insertions(+) create mode 100644 docs/content/how-to/making-your-store-more-powerful-with-contentful.md diff --git a/docs/content/how-to/making-your-store-more-powerful-with-contentful.md b/docs/content/how-to/making-your-store-more-powerful-with-contentful.md new file mode 100644 index 0000000000..fd8c37eee3 --- /dev/null +++ b/docs/content/how-to/making-your-store-more-powerful-with-contentful.md @@ -0,0 +1,304 @@ +--- +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: +![](https://i.imgur.com/sCMjr4B.png) + +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. + +![](https://i.imgur.com/hqiaoFq.png) + +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 ( +
+
+ {data.body ? renderRichText(data.body) : ""} +
+
+ ) +} + +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 +``` + +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. + +![](https://i.imgur.com/8Teuxin.png) + +## 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: + +![](https://i.imgur.com/jUUpW9I.png) + +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**. + +![](https://i.imgur.com/N7alMGz.png) + + +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 + +
+ {product.contentModules?.map((cm) => { + switch (cm.internal.type) { + case "ContentfulTileSection": + return + default: + return null + } + })} +
+``` + +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. + +![](https://i.imgur.com/AQHKA6j.png) + + +## 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) diff --git a/www/docs/sidebars.js b/www/docs/sidebars.js index efe1ee8235..c8b65336cc 100644 --- a/www/docs/sidebars.js +++ b/www/docs/sidebars.js @@ -69,6 +69,10 @@ module.exports = { type: "doc", id: "how-to/headless-ecommerce-store-with-gatsby-contentful-medusa", }, + { + type: "doc", + id: "how-to/making-your-store-more-powerful-with-contentful", + }, ], }, { From a175560efaf990a3d307d39e3f8e303c86afb633 Mon Sep 17 00:00:00 2001 From: Oliver Windall Juhl <59018053+olivermrbl@users.noreply.github.com> Date: Thu, 7 Oct 2021 20:34:08 +0200 Subject: [PATCH 19/43] chore: Update README --- README.md | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index ac5f3b2616..436fd37199 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,17 @@

Medusa

+ +

+ Website | + Roadmap | + Blog | + LinkedIn | + Twitter | + Documentation | + Notion +

+

Medusa is an open-source headless commerce engine that enables developers to create amazing digital commerce experiences.

@@ -105,21 +116,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! - -

- - Website - - | - - Notion Home - - | - - Twitter - - | - - Docs - -

From 3d891c9b4f1e6ff25aba66efb6206164481425f5 Mon Sep 17 00:00:00 2001 From: Mark <79023646+a-developer-named-mark@users.noreply.github.com> Date: Fri, 8 Oct 2021 10:11:58 +0000 Subject: [PATCH 20/43] Issue #442 Prettier rules not applied to some integration tests files (#445) --- integration-tests/api/.babelrc.js | 6 +- .../api/__tests__/store/draft-order.js | 66 +++---- .../api/__tests__/store/gift-cards.js | 60 +++---- .../api/__tests__/store/return-reason.js | 96 +++++----- .../api/__tests__/store/returns.js | 170 +++++++++--------- .../api/__tests__/store/shipping-options.js | 100 +++++------ integration-tests/api/helpers/call-helpers.js | 52 +++--- integration-tests/api/helpers/claim-seeder.js | 34 ++-- .../api/helpers/draft-order-seeder.js | 68 +++---- integration-tests/api/helpers/order-seeder.js | 90 +++++----- integration-tests/api/helpers/swap-seeder.js | 20 +-- integration-tests/api/jest.config.js | 2 +- .../api/src/services/test-ful.js | 26 +-- .../api/src/services/test-not.js | 12 +- .../api/src/services/test-pay.js | 32 ++-- integration-tests/helpers/test-server.js | 6 +- integration-tests/jest.config.js | 6 +- 17 files changed, 425 insertions(+), 421 deletions(-) diff --git a/integration-tests/api/.babelrc.js b/integration-tests/api/.babelrc.js index 39c0fa3e45..bde709c495 100644 --- a/integration-tests/api/.babelrc.js +++ b/integration-tests/api/.babelrc.js @@ -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, -}; +} diff --git a/integration-tests/api/__tests__/store/draft-order.js b/integration-tests/api/__tests__/store/draft-order.js index 6821163f8d..f36bac585f 100644 --- a/integration-tests/api/__tests__/store/draft-order.js +++ b/integration-tests/api/__tests__/store/draft-order.js @@ -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") + }) + }) +}) diff --git a/integration-tests/api/__tests__/store/gift-cards.js b/integration-tests/api/__tests__/store/gift-cards.js index 7b69120fa2..83b72f86ff 100644 --- a/integration-tests/api/__tests__/store/gift-cards.js +++ b/integration-tests/api/__tests__/store/gift-cards.js @@ -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), - }); - }); - }); -}); + }) + }) + }) +}) diff --git a/integration-tests/api/__tests__/store/return-reason.js b/integration-tests/api/__tests__/store/return-reason.js index 8aa0c4c9d4..9cc8138daf 100644 --- a/integration-tests/api/__tests__/store/return-reason.js +++ b/integration-tests/api/__tests__/store/return-reason.js @@ -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", }), - ]); - }); - }); -}); + ]) + }) + }) +}) diff --git a/integration-tests/api/__tests__/store/returns.js b/integration-tests/api/__tests__/store/returns.js index 3d5151dedb..d01a90047e 100644 --- a/integration-tests/api/__tests__/store/returns.js +++ b/integration-tests/api/__tests__/store/returns.js @@ -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) + }) + }) +}) diff --git a/integration-tests/api/__tests__/store/shipping-options.js b/integration-tests/api/__tests__/store/shipping-options.js index 2a9858facd..5f929bf008 100644 --- a/integration-tests/api/__tests__/store/shipping-options.js +++ b/integration-tests/api/__tests__/store/shipping-options.js @@ -1,51 +1,51 @@ -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") -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 +57,7 @@ describe("/store/shipping-options", () => { price_type: "flat_rate", amount: 2000, is_return: false, - }); + }) await manager.insert(ShippingOption, { id: "test-return", @@ -69,7 +69,7 @@ describe("/store/shipping-options", () => { price_type: "flat_rate", amount: 1000, is_return: true, - }); + }) await manager.insert(ShippingOption, { id: "test-region2", @@ -81,51 +81,51 @@ 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") + }) + }) +}) diff --git a/integration-tests/api/helpers/call-helpers.js b/integration-tests/api/helpers/call-helpers.js index 9ecc592811..9a9233d42d 100644 --- a/integration-tests/api/helpers/call-helpers.js +++ b/integration-tests/api/helpers/call-helpers.js @@ -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] +} diff --git a/integration-tests/api/helpers/claim-seeder.js b/integration-tests/api/helpers/claim-seeder.js index 7c34d987e8..ae19e09e68 100644 --- a/integration-tests/api/helpers/claim-seeder.js +++ b/integration-tests/api/helpers/claim-seeder.js @@ -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: {}, - }); -}; + }) +} diff --git a/integration-tests/api/helpers/draft-order-seeder.js b/integration-tests/api/helpers/draft-order-seeder.js index 5adfc9bf30..19b63b0d7c 100644 --- a/integration-tests/api/helpers/draft-order-seeder.js +++ b/integration-tests/api/helpers/draft-order-seeder.js @@ -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) +} diff --git a/integration-tests/api/helpers/order-seeder.js b/integration-tests/api/helpers/order-seeder.js index ac3bde962c..dcd270a139 100644 --- a/integration-tests/api/helpers/order-seeder.js +++ b/integration-tests/api/helpers/order-seeder.js @@ -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(), - }); -}; + }) +} diff --git a/integration-tests/api/helpers/swap-seeder.js b/integration-tests/api/helpers/swap-seeder.js index e19203493f..eff5553017 100644 --- a/integration-tests/api/helpers/swap-seeder.js +++ b/integration-tests/api/helpers/swap-seeder.js @@ -112,13 +112,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 +132,8 @@ module.exports = async (connection, data = {}) => { data: {}, }, ...data, - }; - }; + } + } const swapWithFulfillments = manager.create(Swap, { id: "swap-w-f", @@ -150,9 +150,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 +162,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, diff --git a/integration-tests/api/jest.config.js b/integration-tests/api/jest.config.js index af00a0c2f4..caebf0e68a 100644 --- a/integration-tests/api/jest.config.js +++ b/integration-tests/api/jest.config.js @@ -13,4 +13,4 @@ module.exports = { ], transform: { "^.+\\.[jt]s$": `../../jest-transformer.js` }, setupFilesAfterEnv: ["../setup.js"], -}; +} diff --git a/integration-tests/api/src/services/test-ful.js b/integration-tests/api/src/services/test-ful.js index b73427707c..f7726108ac 100644 --- a/integration-tests/api/src/services/test-ful.js +++ b/integration-tests/api/src/services/test-ful.js @@ -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 diff --git a/integration-tests/api/src/services/test-not.js b/integration-tests/api/src/services/test-not.js index 852bed1b72..39c5ba665a 100644 --- a/integration-tests/api/src/services/test-not.js +++ b/integration-tests/api/src/services/test-not.js @@ -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 diff --git a/integration-tests/api/src/services/test-pay.js b/integration-tests/api/src/services/test-pay.js index 22725965ab..7536c45a45 100644 --- a/integration-tests/api/src/services/test-pay.js +++ b/integration-tests/api/src/services/test-pay.js @@ -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 diff --git a/integration-tests/helpers/test-server.js b/integration-tests/helpers/test-server.js index a06bc12ab8..7feb62d855 100644 --- a/integration-tests/helpers/test-server.js +++ b/integration-tests/helpers/test-server.js @@ -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()), diff --git a/integration-tests/jest.config.js b/integration-tests/jest.config.js index bf175fbbeb..83f803a68e 100644 --- a/integration-tests/jest.config.js +++ b/integration-tests/jest.config.js @@ -1,8 +1,8 @@ -const glob = require(`glob`); +const glob = require(`glob`) const pkgs = glob .sync(`${__dirname}/*/`) - .map((p) => p.replace(__dirname, `/integration-tests`)); + .map((p) => p.replace(__dirname, `/integration-tests`)) module.exports = { testEnvironment: `node`, @@ -19,4 +19,4 @@ module.exports = { ], transform: { "^.+\\.[jt]s$": `/jest-transformer.js` }, setupFilesAfterEnv: ["/integration-tests/setup.js"], -}; +} From 84d4d791eaf9508367a20d9f930ca959a7b707dd Mon Sep 17 00:00:00 2001 From: Sebastian Mateos Nicolajsen <80953876+sebastiannicolajsen@users.noreply.github.com> Date: Mon, 11 Oct 2021 13:46:32 +0200 Subject: [PATCH 21/43] feat: allow product selection on discounts allocated to a specific item (#395) --- .../src/services/webshipper-fulfillment.js | 22 ++++++++----- .../src/subscribers/order.js | 6 ++++ .../src/services/sendgrid.js | 17 +++++++++- .../src/services/slack.js | 2 ++ .../routes/admin/discounts/create-discount.js | 10 +++--- .../api/routes/admin/draft-orders/index.js | 1 + .../admin/draft-orders/register-payment.js | 9 ++++- .../admin/orders/__tests__/get-order.js | 2 ++ .../api/routes/admin/orders/create-claim.js | 7 +++- .../src/api/routes/admin/orders/index.js | 4 +++ .../src/api/routes/store/carts/index.js | 2 ++ .../src/api/routes/store/orders/index.js | 4 +++ packages/medusa/src/models/discount.ts | 2 +- .../medusa/src/services/__tests__/cart.js | 3 +- .../medusa/src/services/__tests__/discount.js | 3 ++ .../medusa/src/services/__tests__/order.js | 2 ++ .../medusa/src/services/__tests__/swap.js | 1 + packages/medusa/src/services/cart.js | 33 ++++++++++++++++--- packages/medusa/src/services/discount.js | 9 ++++- packages/medusa/src/services/draft-order.js | 2 +- packages/medusa/src/services/order.js | 6 ++++ packages/medusa/src/services/return.js | 2 ++ packages/medusa/src/services/swap.js | 1 + packages/medusa/src/subscribers/order.js | 8 ++++- packages/medusa/yarn.lock | 5 +++ 25 files changed, 138 insertions(+), 25 deletions(-) diff --git a/packages/medusa-fulfillment-webshipper/src/services/webshipper-fulfillment.js b/packages/medusa-fulfillment-webshipper/src/services/webshipper-fulfillment.js index 2cf02de38e..150a6dfe8b 100644 --- a/packages/medusa-fulfillment-webshipper/src/services/webshipper-fulfillment.js +++ b/packages/medusa-fulfillment-webshipper/src/services/webshipper-fulfillment.js @@ -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")) { diff --git a/packages/medusa-plugin-segment/src/subscribers/order.js b/packages/medusa-plugin-segment/src/subscribers/order.js index 33f1d2f291..b048245d5f 100644 --- a/packages/medusa-plugin-segment/src/subscribers/order.js +++ b/packages/medusa-plugin-segment/src/subscribers/order.js @@ -44,6 +44,8 @@ class OrderSubscriber { "billing_address", "shipping_address", "discounts", + "discounts.rule", + "discounts.rule.valid_for", "shipping_methods", "payments", "fulfillments", @@ -146,6 +148,8 @@ class OrderSubscriber { "billing_address", "shipping_address", "discounts", + "discounts.rule", + "discounts.rule.valid_for", "shipping_methods", "payments", "fulfillments", @@ -253,6 +257,8 @@ class OrderSubscriber { "billing_address", "shipping_address", "discounts", + "discounts.rule", + "discounts.rule.valid_for", "shipping_methods", "payments", "fulfillments", diff --git a/packages/medusa-plugin-sendgrid/src/services/sendgrid.js b/packages/medusa-plugin-sendgrid/src/services/sendgrid.js index a577a67696..d951685bd9 100644 --- a/packages/medusa-plugin-sendgrid/src/services/sendgrid.js +++ b/packages/medusa-plugin-sendgrid/src/services/sendgrid.js @@ -320,6 +320,8 @@ class SendGridService extends NotificationService { "billing_address", "shipping_address", "discounts", + "discounts.rule", + "discounts.rule.valid_for", "shipping_methods", "shipping_methods.shipping_option", "payments", @@ -363,6 +365,8 @@ class SendGridService extends NotificationService { "billing_address", "shipping_address", "discounts", + "discounts.rule", + "discounts.rule.valid_for", "shipping_methods", "shipping_methods.shipping_option", "payments", @@ -496,6 +500,8 @@ class SendGridService extends NotificationService { relations: [ "items", "discounts", + "discounts.rule", + "discounts.rule.valid_for", "shipping_address", "returns", "swaps", @@ -601,6 +607,8 @@ class SendGridService extends NotificationService { relations: [ "items", "discounts", + "discounts.rule", + "discounts.rule.valid_for", "shipping_address", "swaps", "swaps.additional_items", @@ -686,7 +694,14 @@ class SendGridService extends NotificationService { }) const order = await this.orderService_.retrieve(swap.order_id, { - relations: ["items", "discounts", "swaps", "swaps.additional_items"], + relations: [ + "items", + "discounts", + "discounts.rule", + "discounts.rule.valid_for", + "swaps", + "swaps.additional_items", + ], }) let merged = [...order.items] diff --git a/packages/medusa-plugin-slack-notification/src/services/slack.js b/packages/medusa-plugin-slack-notification/src/services/slack.js index 5821191e95..0770f1c9db 100644 --- a/packages/medusa-plugin-slack-notification/src/services/slack.js +++ b/packages/medusa-plugin-slack-notification/src/services/slack.js @@ -40,6 +40,8 @@ class SlackService extends BaseService { "billing_address", "shipping_address", "discounts", + "discounts.rule", + "discounts.rule.valid_for", "shipping_methods", "payments", "fulfillments", diff --git a/packages/medusa/src/api/routes/admin/discounts/create-discount.js b/packages/medusa/src/api/routes/admin/discounts/create-discount.js index e7b9ea364e..66b51c64e3 100644 --- a/packages/medusa/src/api/routes/admin/discounts/create-discount.js +++ b/packages/medusa/src/api/routes/admin/discounts/create-discount.js @@ -1,4 +1,5 @@ import { MedusaError, Validator } from "medusa-core-utils" +import { defaultRelations } from "." /** * @oas [post] /discounts @@ -96,11 +97,10 @@ export default async (req, res) => { const discountService = req.scope.resolve("discountService") const created = await discountService.create(value) - const discount = await discountService.retrieve(created.id, [ - "rule", - "rule.valid_for", - "regions", - ]) + const discount = await discountService.retrieve( + created.id, + defaultRelations + ) res.status(200).json({ discount }) } catch (err) { diff --git a/packages/medusa/src/api/routes/admin/draft-orders/index.js b/packages/medusa/src/api/routes/admin/draft-orders/index.js index d5a08f41ce..e68f244c21 100644 --- a/packages/medusa/src/api/routes/admin/draft-orders/index.js +++ b/packages/medusa/src/api/routes/admin/draft-orders/index.js @@ -57,6 +57,7 @@ export const defaultCartRelations = [ "payment_sessions", "shipping_methods.shipping_option", "discounts", + "discounts.rule", ] export const defaultCartFields = [ diff --git a/packages/medusa/src/api/routes/admin/draft-orders/register-payment.js b/packages/medusa/src/api/routes/admin/draft-orders/register-payment.js index e38bc93d54..b9d854453f 100644 --- a/packages/medusa/src/api/routes/admin/draft-orders/register-payment.js +++ b/packages/medusa/src/api/routes/admin/draft-orders/register-payment.js @@ -43,7 +43,14 @@ export default async (req, res) => { .withTransaction(manager) .retrieve(draftOrder.cart_id, { select: ["total"], - relations: ["discounts", "shipping_methods", "region", "items"], + relations: [ + "discounts", + "discounts.rule", + "discounts.rule.valid_for", + "shipping_methods", + "region", + "items", + ], }) await paymentProviderService diff --git a/packages/medusa/src/api/routes/admin/orders/__tests__/get-order.js b/packages/medusa/src/api/routes/admin/orders/__tests__/get-order.js index 4a00dbbd89..bc54bdc90b 100644 --- a/packages/medusa/src/api/routes/admin/orders/__tests__/get-order.js +++ b/packages/medusa/src/api/routes/admin/orders/__tests__/get-order.js @@ -7,6 +7,8 @@ const defaultRelations = [ "billing_address", "shipping_address", "discounts", + "discounts.rule", + "discounts.rule.valid_for", "shipping_methods", "payments", "fulfillments", diff --git a/packages/medusa/src/api/routes/admin/orders/create-claim.js b/packages/medusa/src/api/routes/admin/orders/create-claim.js index ade142b418..77c8a37004 100644 --- a/packages/medusa/src/api/routes/admin/orders/create-claim.js +++ b/packages/medusa/src/api/routes/admin/orders/create-claim.js @@ -202,7 +202,12 @@ export default async (req, res) => { const order = await orderService .withTransaction(manager) .retrieve(id, { - relations: ["items", "discounts"], + relations: [ + "items", + "cart", + "cart.discounts", + "cart.discounts.rule", + ], }) await claimService.withTransaction(manager).create({ diff --git a/packages/medusa/src/api/routes/admin/orders/index.js b/packages/medusa/src/api/routes/admin/orders/index.js index 0a6f64ddfe..a9e4af97b6 100644 --- a/packages/medusa/src/api/routes/admin/orders/index.js +++ b/packages/medusa/src/api/routes/admin/orders/index.js @@ -221,6 +221,8 @@ export const defaultRelations = [ "billing_address", "shipping_address", "discounts", + "discounts.rule", + "discounts.rule.valid_for", "shipping_methods", "payments", "fulfillments", @@ -316,6 +318,8 @@ export const allowedRelations = [ "billing_address", "shipping_address", "discounts", + "discounts.rule", + "discounts.rule.valid_for", "shipping_methods", "payments", "fulfillments", diff --git a/packages/medusa/src/api/routes/store/carts/index.js b/packages/medusa/src/api/routes/store/carts/index.js index 42363bd12c..6c702af07b 100644 --- a/packages/medusa/src/api/routes/store/carts/index.js +++ b/packages/medusa/src/api/routes/store/carts/index.js @@ -116,4 +116,6 @@ export const defaultRelations = [ "payment_sessions", "shipping_methods.shipping_option", "discounts", + "discounts.rule", + "discounts.rule.valid_for", ] diff --git a/packages/medusa/src/api/routes/store/orders/index.js b/packages/medusa/src/api/routes/store/orders/index.js index 8aaa346bca..e7aefc2571 100644 --- a/packages/medusa/src/api/routes/store/orders/index.js +++ b/packages/medusa/src/api/routes/store/orders/index.js @@ -36,6 +36,8 @@ export const defaultRelations = [ "items.variant.product", "shipping_methods", "discounts", + "discounts.rule", + "discounts.rule.valid_for", "customer", "payments", "region", @@ -74,6 +76,8 @@ export const allowedRelations = [ "items.variant.product", "shipping_methods", "discounts", + "discounts.rule", + "discounts.rule.valid_for", "customer", "payments", "region", diff --git a/packages/medusa/src/models/discount.ts b/packages/medusa/src/models/discount.ts index a4ef833b3c..64ac6c29c5 100644 --- a/packages/medusa/src/models/discount.ts +++ b/packages/medusa/src/models/discount.ts @@ -35,7 +35,7 @@ export class Discount { @Column({ nullable: true }) rule_id: string - @ManyToOne(() => DiscountRule, { cascade: true, eager: true }) + @ManyToOne(() => DiscountRule, { cascade: true }) @JoinColumn({ name: "rule_id" }) rule: DiscountRule diff --git a/packages/medusa/src/services/__tests__/cart.js b/packages/medusa/src/services/__tests__/cart.js index 55817e2592..84c794f8cd 100644 --- a/packages/medusa/src/services/__tests__/cart.js +++ b/packages/medusa/src/services/__tests__/cart.js @@ -647,12 +647,13 @@ describe("CartService", () => { "shipping_address", "billing_address", "gift_cards", - "discounts", "customer", "region", "payment_sessions", "region.countries", + "discounts", "discounts.rule", + "discounts.rule.valid_for", "discounts.regions", ], { diff --git a/packages/medusa/src/services/__tests__/discount.js b/packages/medusa/src/services/__tests__/discount.js index 5802063386..acc61e58cf 100644 --- a/packages/medusa/src/services/__tests__/discount.js +++ b/packages/medusa/src/services/__tests__/discount.js @@ -13,6 +13,9 @@ describe("DiscountService", () => { id: IdMap.getId("france"), } }, + withTransaction: function() { + return this + }, } const discountService = new DiscountService({ diff --git a/packages/medusa/src/services/__tests__/order.js b/packages/medusa/src/services/__tests__/order.js index 76db532917..72555220f9 100644 --- a/packages/medusa/src/services/__tests__/order.js +++ b/packages/medusa/src/services/__tests__/order.js @@ -231,6 +231,8 @@ describe("OrderService", () => { "payment", "items", "discounts", + "discounts.rule", + "discounts.rule.valid_for", "gift_cards", "shipping_methods", ], diff --git a/packages/medusa/src/services/__tests__/swap.js b/packages/medusa/src/services/__tests__/swap.js index 9cc4088174..3577dada96 100644 --- a/packages/medusa/src/services/__tests__/swap.js +++ b/packages/medusa/src/services/__tests__/swap.js @@ -198,6 +198,7 @@ describe("SwapService", () => { "order.swaps", "order.swaps.additional_items", "order.discounts", + "order.discounts.rule", "additional_items", "return_order", "return_order.items", diff --git a/packages/medusa/src/services/cart.js b/packages/medusa/src/services/cart.js index fede0289d8..9719fa1700 100644 --- a/packages/medusa/src/services/cart.js +++ b/packages/medusa/src/services/cart.js @@ -166,6 +166,8 @@ class CartService extends BaseService { relationSet.add("items") relationSet.add("gift_cards") relationSet.add("discounts") + relationSet.add("discounts.rule") + relationSet.add("discounts.rule.valid_for") //relationSet.add("discounts.parent_discount") //relationSet.add("discounts.parent_discount.rule") //relationSet.add("discounts.parent_discount.regions") @@ -600,12 +602,13 @@ class CartService extends BaseService { "shipping_address", "billing_address", "gift_cards", - "discounts", "customer", "region", "payment_sessions", "region.countries", + "discounts", "discounts.rule", + "discounts.rule.valid_for", "discounts.regions", ], }) @@ -871,6 +874,7 @@ class CartService extends BaseService { async applyDiscount(cart, discountCode) { const discount = await this.discountService_.retrieveByCode(discountCode, [ "rule", + "rule.valid_for", "regions", ]) @@ -968,7 +972,13 @@ class CartService extends BaseService { async removeDiscount(cartId, discountCode) { return this.atomicPhase_(async manager => { const cart = await this.retrieve(cartId, { - relations: ["discounts", "payment_sessions", "shipping_methods"], + relations: [ + "discounts", + "discounts.rule", + "discounts.rule.valid_for", + "payment_sessions", + "shipping_methods", + ], }) if (cart.discounts.some(({ rule }) => rule.type === "free_shipping")) { @@ -1165,6 +1175,8 @@ class CartService extends BaseService { relations: [ "items", "discounts", + "discounts.rule", + "discounts.rule.valid_for", "gift_cards", "billing_address", "shipping_address", @@ -1317,6 +1329,8 @@ class CartService extends BaseService { relations: [ "shipping_methods", "discounts", + "discounts.rule", + "discounts.rule.valid_for", "shipping_methods.shipping_option", "items", "items.variant", @@ -1353,7 +1367,12 @@ class CartService extends BaseService { } const result = await this.retrieve(cartId, { - relations: ["discounts", "shipping_methods"], + relations: [ + "discounts", + "discounts.rule", + "discounts.rule.valid_for", + "shipping_methods", + ], }) // if cart has freeshipping, adjust price @@ -1507,7 +1526,13 @@ class CartService extends BaseService { async delete(cartId) { return this.atomicPhase_(async manager => { const cart = await this.retrieve(cartId, { - relations: ["items", "discounts", "payment_sessions"], + relations: [ + "items", + "discounts", + "discounts.rule", + "discounts.rule.valid_for", + "payment_sessions", + ], }) if (cart.completed_at) { diff --git a/packages/medusa/src/services/discount.js b/packages/medusa/src/services/discount.js index ef987e617d..70f404f378 100644 --- a/packages/medusa/src/services/discount.js +++ b/packages/medusa/src/services/discount.js @@ -180,12 +180,16 @@ class DiscountService extends BaseService { const discountRepo = manager.getCustomRepository(this.discountRepository_) const ruleRepo = manager.getCustomRepository(this.discountRuleRepository_) + if (discount.rule?.valid_for) { + discount.rule.valid_for = discount.rule.valid_for.map(id => ({ id })) + } + const validatedRule = this.validateDiscountRule_(discount.rule) if (discount.regions) { discount.regions = await Promise.all( discount.regions.map(regionId => - this.regionService_.retrieve(regionId) + this.regionService_.withTransaction(manager).retrieve(regionId) ) ) } @@ -293,6 +297,9 @@ class DiscountService extends BaseService { if (rule) { discount.rule = this.validateDiscountRule_(rule) + if (rule.valid_for) { + discount.rule.valid_for = discount.rule.valid_for.map(id => ({ id })) + } } for (const [key, value] of Object.entries(rest)) { diff --git a/packages/medusa/src/services/draft-order.js b/packages/medusa/src/services/draft-order.js index fe2ceb51aa..96d089b7bf 100644 --- a/packages/medusa/src/services/draft-order.js +++ b/packages/medusa/src/services/draft-order.js @@ -250,10 +250,10 @@ class DraftOrderService extends BaseService { } const { - items, shipping_methods, discounts, no_notification_order, + items, ...rest } = data diff --git a/packages/medusa/src/services/order.js b/packages/medusa/src/services/order.js index 64684ac6d6..c63e3d2cdb 100644 --- a/packages/medusa/src/services/order.js +++ b/packages/medusa/src/services/order.js @@ -296,6 +296,8 @@ class OrderService extends BaseService { relationSet.add("swaps") relationSet.add("swaps.additional_items") relationSet.add("discounts") + relationSet.add("discounts.rule") + relationSet.add("discounts.rule.valid_for") relationSet.add("gift_cards") relationSet.add("gift_card_transactions") relationSet.add("refunds") @@ -453,6 +455,8 @@ class OrderService extends BaseService { "payment", "items", "discounts", + "discounts.rule", + "discounts.rule.valid_for", "gift_cards", "shipping_methods", ], @@ -1119,6 +1123,8 @@ class OrderService extends BaseService { ], relations: [ "discounts", + "discounts.rule", + "discounts.rule.valid_for", "region", "fulfillments", "shipping_address", diff --git a/packages/medusa/src/services/return.js b/packages/medusa/src/services/return.js index 0092e0e5c7..06f92aff45 100644 --- a/packages/medusa/src/services/return.js +++ b/packages/medusa/src/services/return.js @@ -518,6 +518,8 @@ class ReturnService extends BaseService { "returns", "payments", "discounts", + "discounts.rule", + "discounts.rule.valid_for", "refunds", "shipping_methods", "region", diff --git a/packages/medusa/src/services/swap.js b/packages/medusa/src/services/swap.js index 886a6f70c0..eca04c59b3 100644 --- a/packages/medusa/src/services/swap.js +++ b/packages/medusa/src/services/swap.js @@ -533,6 +533,7 @@ class SwapService extends BaseService { "order.swaps", "order.swaps.additional_items", "order.discounts", + "order.discounts.rule", "additional_items", "return_order", "return_order.items", diff --git a/packages/medusa/src/subscribers/order.js b/packages/medusa/src/subscribers/order.js index 01ac3c9cca..ec49737a47 100644 --- a/packages/medusa/src/subscribers/order.js +++ b/packages/medusa/src/subscribers/order.js @@ -32,7 +32,13 @@ class OrderSubscriber { handleOrderPlaced = async data => { const order = await this.orderService_.retrieve(data.id, { select: ["subtotal"], - relations: ["discounts", "items", "gift_cards"], + relations: [ + "discounts", + "discounts.rule", + "discounts.rule.valid_for", + "items", + "gift_cards", + ], }) await Promise.all( diff --git a/packages/medusa/yarn.lock b/packages/medusa/yarn.lock index 82e2b85eb7..001dc1387c 100644 --- a/packages/medusa/yarn.lock +++ b/packages/medusa/yarn.lock @@ -4608,6 +4608,11 @@ isexe@^2.0.0: resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= +iso8601-duration@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/iso8601-duration/-/iso8601-duration-1.3.0.tgz#29d7b69e0574e4acdee50c5e5e09adab4137ba5a" + integrity sha512-K4CiUBzo3YeWk76FuET/dQPH03WE04R94feo5TSKQCXpoXQt9E4yx2CnY737QZnSAI3PI4WlKo/zfqizGx52QQ== + isobject@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" From 0cf9e416f3a9aa15ce3f545127cc18b13b5e6773 Mon Sep 17 00:00:00 2001 From: Sebastian Mateos Nicolajsen <80953876+sebastiannicolajsen@users.noreply.github.com> Date: Tue, 12 Oct 2021 11:09:38 +0200 Subject: [PATCH 22/43] Allow the user to log out (#459) --- .../src/api/routes/admin/auth/delete-session.js | 17 +++++++++++++++++ .../medusa/src/api/routes/admin/auth/index.js | 6 ++++++ 2 files changed, 23 insertions(+) create mode 100644 packages/medusa/src/api/routes/admin/auth/delete-session.js diff --git a/packages/medusa/src/api/routes/admin/auth/delete-session.js b/packages/medusa/src/api/routes/admin/auth/delete-session.js new file mode 100644 index 0000000000..7316311fea --- /dev/null +++ b/packages/medusa/src/api/routes/admin/auth/delete-session.js @@ -0,0 +1,17 @@ +import _ from "lodash" + +/** + * @oas [get] /auth + * operationId: "DeleteAuth" + * summary: "Delete Session" + * description: "Deletes the current session for the logged in user." + * tags: + * - Auth + * responses: + * "200": + * description: OK + */ +export default async (req, res) => { + req.session.destroy() + res.status(200).end() +} diff --git a/packages/medusa/src/api/routes/admin/auth/index.js b/packages/medusa/src/api/routes/admin/auth/index.js index 3a8a9f896a..b74d357a8f 100644 --- a/packages/medusa/src/api/routes/admin/auth/index.js +++ b/packages/medusa/src/api/routes/admin/auth/index.js @@ -13,5 +13,11 @@ export default app => { ) route.post("/", middlewares.wrap(require("./create-session").default)) + route.delete( + "/", + middlewares.authenticate(), + middlewares.wrap(require("./delete-session").default) + ) + return app } From f02cb638db861cebeb9987ea9dc202e2b6848ce4 Mon Sep 17 00:00:00 2001 From: Sebastian Rindom Date: Tue, 12 Oct 2021 21:24:01 +0200 Subject: [PATCH 23/43] chore: remove packages/*/.eslintrc|.prettierrc --- .eslintrc | 9 ---- .gitignore | 2 + package.json | 4 ++ packages/medusa-cli/.eslintrc | 9 ---- packages/medusa-cli/.prettierrc | 7 --- packages/medusa-core-utils/.eslintrc | 9 ---- packages/medusa-core-utils/.prettierrc | 7 --- packages/medusa-file-s3/.eslintrc | 9 ---- packages/medusa-file-s3/.prettierrc | 7 --- packages/medusa-file-spaces/.eslintrc | 9 ---- packages/medusa-file-spaces/.prettierrc | 7 --- packages/medusa-fulfillment-manual/.eslintrc | 9 ---- .../medusa-fulfillment-manual/.prettierrc | 7 --- .../medusa-fulfillment-webshipper/.eslintrc | 9 ---- .../medusa-fulfillment-webshipper/.prettierrc | 7 --- packages/medusa-interfaces/.eslintrc | 9 ---- packages/medusa-interfaces/.prettierrc | 7 --- packages/medusa-payment-adyen/.eslintrc | 9 ---- packages/medusa-payment-adyen/.prettierrc | 7 --- packages/medusa-payment-klarna/.eslintrc | 9 ---- packages/medusa-payment-klarna/.prettierrc | 7 --- packages/medusa-payment-manual/.eslintrc | 9 ---- packages/medusa-payment-manual/.prettierrc | 7 --- packages/medusa-payment-paypal/.eslintrc | 9 ---- packages/medusa-payment-paypal/.prettierrc | 7 --- packages/medusa-payment-stripe/.eslintrc | 9 ---- packages/medusa-payment-stripe/.prettierrc | 7 --- packages/medusa-plugin-add-ons/.eslintrc | 9 ---- packages/medusa-plugin-add-ons/.prettierrc | 7 --- packages/medusa-plugin-brightpearl/.eslintrc | 9 ---- .../medusa-plugin-brightpearl/.prettierrc | 7 --- packages/medusa-plugin-contentful/.eslintrc | 9 ---- packages/medusa-plugin-contentful/.prettierrc | 7 --- .../.eslintrc | 9 ---- .../.prettierrc | 7 --- packages/medusa-plugin-economic/.eslintrc | 9 ---- packages/medusa-plugin-economic/.prettierrc | 7 --- packages/medusa-plugin-ip-lookup/.prettierrc | 7 --- packages/medusa-plugin-mailchimp/.eslintrc | 9 ---- packages/medusa-plugin-mailchimp/.prettierrc | 7 --- .../medusa-plugin-permissions/.prettierrc | 7 --- .../.eslintrc | 9 ---- .../.prettierrc | 7 --- packages/medusa-plugin-segment/.eslintrc | 9 ---- packages/medusa-plugin-segment/.prettierrc | 7 --- packages/medusa-plugin-sendgrid/.eslintrc | 9 ---- packages/medusa-plugin-sendgrid/.prettierrc | 7 --- .../.eslintrc | 9 ---- .../.prettierrc | 7 --- packages/medusa-plugin-twilio-sms/.eslintrc | 9 ---- packages/medusa-plugin-twilio-sms/.prettierrc | 7 --- packages/medusa-plugin-wishlist/.eslintrc | 9 ---- packages/medusa-plugin-wishlist/.prettierrc | 7 --- packages/medusa-telemetry/.eslintrc | 9 ---- packages/medusa-telemetry/.prettierrc | 7 --- packages/medusa-test-utils/.eslintrc | 9 ---- packages/medusa/.eslintrc | 9 ---- packages/medusa/.prettierrc | 7 --- www/docs/.eslintrc | 9 ---- www/docs/.prettierrc | 7 --- www/reference/.prettierrc | 4 -- yarn.lock | 45 ++++++++++++++++++- 62 files changed, 50 insertions(+), 469 deletions(-) delete mode 100644 .eslintrc delete mode 100644 packages/medusa-cli/.eslintrc delete mode 100644 packages/medusa-cli/.prettierrc delete mode 100644 packages/medusa-core-utils/.eslintrc delete mode 100644 packages/medusa-core-utils/.prettierrc delete mode 100644 packages/medusa-file-s3/.eslintrc delete mode 100644 packages/medusa-file-s3/.prettierrc delete mode 100644 packages/medusa-file-spaces/.eslintrc delete mode 100644 packages/medusa-file-spaces/.prettierrc delete mode 100644 packages/medusa-fulfillment-manual/.eslintrc delete mode 100644 packages/medusa-fulfillment-manual/.prettierrc delete mode 100644 packages/medusa-fulfillment-webshipper/.eslintrc delete mode 100644 packages/medusa-fulfillment-webshipper/.prettierrc delete mode 100644 packages/medusa-interfaces/.eslintrc delete mode 100644 packages/medusa-interfaces/.prettierrc delete mode 100644 packages/medusa-payment-adyen/.eslintrc delete mode 100644 packages/medusa-payment-adyen/.prettierrc delete mode 100644 packages/medusa-payment-klarna/.eslintrc delete mode 100644 packages/medusa-payment-klarna/.prettierrc delete mode 100644 packages/medusa-payment-manual/.eslintrc delete mode 100644 packages/medusa-payment-manual/.prettierrc delete mode 100644 packages/medusa-payment-paypal/.eslintrc delete mode 100644 packages/medusa-payment-paypal/.prettierrc delete mode 100644 packages/medusa-payment-stripe/.eslintrc delete mode 100644 packages/medusa-payment-stripe/.prettierrc delete mode 100644 packages/medusa-plugin-add-ons/.eslintrc delete mode 100644 packages/medusa-plugin-add-ons/.prettierrc delete mode 100644 packages/medusa-plugin-brightpearl/.eslintrc delete mode 100644 packages/medusa-plugin-brightpearl/.prettierrc delete mode 100644 packages/medusa-plugin-contentful/.eslintrc delete mode 100644 packages/medusa-plugin-contentful/.prettierrc delete mode 100644 packages/medusa-plugin-discount-generator/.eslintrc delete mode 100644 packages/medusa-plugin-discount-generator/.prettierrc delete mode 100644 packages/medusa-plugin-economic/.eslintrc delete mode 100644 packages/medusa-plugin-economic/.prettierrc delete mode 100644 packages/medusa-plugin-ip-lookup/.prettierrc delete mode 100644 packages/medusa-plugin-mailchimp/.eslintrc delete mode 100644 packages/medusa-plugin-mailchimp/.prettierrc delete mode 100644 packages/medusa-plugin-permissions/.prettierrc delete mode 100644 packages/medusa-plugin-restock-notification/.eslintrc delete mode 100644 packages/medusa-plugin-restock-notification/.prettierrc delete mode 100644 packages/medusa-plugin-segment/.eslintrc delete mode 100644 packages/medusa-plugin-segment/.prettierrc delete mode 100644 packages/medusa-plugin-sendgrid/.eslintrc delete mode 100644 packages/medusa-plugin-sendgrid/.prettierrc delete mode 100644 packages/medusa-plugin-slack-notification/.eslintrc delete mode 100644 packages/medusa-plugin-slack-notification/.prettierrc delete mode 100644 packages/medusa-plugin-twilio-sms/.eslintrc delete mode 100644 packages/medusa-plugin-twilio-sms/.prettierrc delete mode 100644 packages/medusa-plugin-wishlist/.eslintrc delete mode 100644 packages/medusa-plugin-wishlist/.prettierrc delete mode 100644 packages/medusa-telemetry/.eslintrc delete mode 100644 packages/medusa-telemetry/.prettierrc delete mode 100644 packages/medusa-test-utils/.eslintrc delete mode 100644 packages/medusa/.eslintrc delete mode 100644 packages/medusa/.prettierrc delete mode 100644 www/docs/.eslintrc delete mode 100644 www/docs/.prettierrc delete mode 100644 www/reference/.prettierrc diff --git a/.eslintrc b/.eslintrc deleted file mode 100644 index 2a889697f0..0000000000 --- a/.eslintrc +++ /dev/null @@ -1,9 +0,0 @@ -{ - "plugins": ["prettier"], - "extends": ["prettier"], - "rules": { - "prettier/prettier": "error", - "semi": "error", - "no-unused-expressions": "true" - } -} diff --git a/.gitignore b/.gitignore index 99c023dd24..b4a5fb20f6 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,5 @@ node_modules .DS_Store lerna-debug.log +.eslintcache + diff --git a/package.json b/package.json index b30f2a4d29..ad7bb013f7 100644 --- a/package.json +++ b/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", @@ -16,6 +17,9 @@ "babel-jest": "^26.6.3", "babel-preset-medusa-package": "^1.0.0", "cross-env": "^7.0.2", + "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", "import-from": "^3.0.0", diff --git a/packages/medusa-cli/.eslintrc b/packages/medusa-cli/.eslintrc deleted file mode 100644 index 2a889697f0..0000000000 --- a/packages/medusa-cli/.eslintrc +++ /dev/null @@ -1,9 +0,0 @@ -{ - "plugins": ["prettier"], - "extends": ["prettier"], - "rules": { - "prettier/prettier": "error", - "semi": "error", - "no-unused-expressions": "true" - } -} diff --git a/packages/medusa-cli/.prettierrc b/packages/medusa-cli/.prettierrc deleted file mode 100644 index 70175ce150..0000000000 --- a/packages/medusa-cli/.prettierrc +++ /dev/null @@ -1,7 +0,0 @@ -{ - "endOfLine": "lf", - "semi": false, - "singleQuote": false, - "tabWidth": 2, - "trailingComma": "es5" -} \ No newline at end of file diff --git a/packages/medusa-core-utils/.eslintrc b/packages/medusa-core-utils/.eslintrc deleted file mode 100644 index 2a889697f0..0000000000 --- a/packages/medusa-core-utils/.eslintrc +++ /dev/null @@ -1,9 +0,0 @@ -{ - "plugins": ["prettier"], - "extends": ["prettier"], - "rules": { - "prettier/prettier": "error", - "semi": "error", - "no-unused-expressions": "true" - } -} diff --git a/packages/medusa-core-utils/.prettierrc b/packages/medusa-core-utils/.prettierrc deleted file mode 100644 index 70175ce150..0000000000 --- a/packages/medusa-core-utils/.prettierrc +++ /dev/null @@ -1,7 +0,0 @@ -{ - "endOfLine": "lf", - "semi": false, - "singleQuote": false, - "tabWidth": 2, - "trailingComma": "es5" -} \ No newline at end of file diff --git a/packages/medusa-file-s3/.eslintrc b/packages/medusa-file-s3/.eslintrc deleted file mode 100644 index 2a889697f0..0000000000 --- a/packages/medusa-file-s3/.eslintrc +++ /dev/null @@ -1,9 +0,0 @@ -{ - "plugins": ["prettier"], - "extends": ["prettier"], - "rules": { - "prettier/prettier": "error", - "semi": "error", - "no-unused-expressions": "true" - } -} diff --git a/packages/medusa-file-s3/.prettierrc b/packages/medusa-file-s3/.prettierrc deleted file mode 100644 index 48e90e8d40..0000000000 --- a/packages/medusa-file-s3/.prettierrc +++ /dev/null @@ -1,7 +0,0 @@ -{ - "endOfLine": "lf", - "semi": false, - "singleQuote": false, - "tabWidth": 2, - "trailingComma": "es5" -} diff --git a/packages/medusa-file-spaces/.eslintrc b/packages/medusa-file-spaces/.eslintrc deleted file mode 100644 index 2a889697f0..0000000000 --- a/packages/medusa-file-spaces/.eslintrc +++ /dev/null @@ -1,9 +0,0 @@ -{ - "plugins": ["prettier"], - "extends": ["prettier"], - "rules": { - "prettier/prettier": "error", - "semi": "error", - "no-unused-expressions": "true" - } -} diff --git a/packages/medusa-file-spaces/.prettierrc b/packages/medusa-file-spaces/.prettierrc deleted file mode 100644 index 70175ce150..0000000000 --- a/packages/medusa-file-spaces/.prettierrc +++ /dev/null @@ -1,7 +0,0 @@ -{ - "endOfLine": "lf", - "semi": false, - "singleQuote": false, - "tabWidth": 2, - "trailingComma": "es5" -} \ No newline at end of file diff --git a/packages/medusa-fulfillment-manual/.eslintrc b/packages/medusa-fulfillment-manual/.eslintrc deleted file mode 100644 index 797b6ef5bf..0000000000 --- a/packages/medusa-fulfillment-manual/.eslintrc +++ /dev/null @@ -1,9 +0,0 @@ -{ - "plugins": ["prettier"], - "extends": ["prettier"], - "rules": { - "prettier/prettier": "error", - "semi": "error", - "no-unused-expressions": "true" - } -} \ No newline at end of file diff --git a/packages/medusa-fulfillment-manual/.prettierrc b/packages/medusa-fulfillment-manual/.prettierrc deleted file mode 100644 index 70175ce150..0000000000 --- a/packages/medusa-fulfillment-manual/.prettierrc +++ /dev/null @@ -1,7 +0,0 @@ -{ - "endOfLine": "lf", - "semi": false, - "singleQuote": false, - "tabWidth": 2, - "trailingComma": "es5" -} \ No newline at end of file diff --git a/packages/medusa-fulfillment-webshipper/.eslintrc b/packages/medusa-fulfillment-webshipper/.eslintrc deleted file mode 100644 index 797b6ef5bf..0000000000 --- a/packages/medusa-fulfillment-webshipper/.eslintrc +++ /dev/null @@ -1,9 +0,0 @@ -{ - "plugins": ["prettier"], - "extends": ["prettier"], - "rules": { - "prettier/prettier": "error", - "semi": "error", - "no-unused-expressions": "true" - } -} \ No newline at end of file diff --git a/packages/medusa-fulfillment-webshipper/.prettierrc b/packages/medusa-fulfillment-webshipper/.prettierrc deleted file mode 100644 index 70175ce150..0000000000 --- a/packages/medusa-fulfillment-webshipper/.prettierrc +++ /dev/null @@ -1,7 +0,0 @@ -{ - "endOfLine": "lf", - "semi": false, - "singleQuote": false, - "tabWidth": 2, - "trailingComma": "es5" -} \ No newline at end of file diff --git a/packages/medusa-interfaces/.eslintrc b/packages/medusa-interfaces/.eslintrc deleted file mode 100644 index 2a889697f0..0000000000 --- a/packages/medusa-interfaces/.eslintrc +++ /dev/null @@ -1,9 +0,0 @@ -{ - "plugins": ["prettier"], - "extends": ["prettier"], - "rules": { - "prettier/prettier": "error", - "semi": "error", - "no-unused-expressions": "true" - } -} diff --git a/packages/medusa-interfaces/.prettierrc b/packages/medusa-interfaces/.prettierrc deleted file mode 100644 index 70175ce150..0000000000 --- a/packages/medusa-interfaces/.prettierrc +++ /dev/null @@ -1,7 +0,0 @@ -{ - "endOfLine": "lf", - "semi": false, - "singleQuote": false, - "tabWidth": 2, - "trailingComma": "es5" -} \ No newline at end of file diff --git a/packages/medusa-payment-adyen/.eslintrc b/packages/medusa-payment-adyen/.eslintrc deleted file mode 100644 index 2a889697f0..0000000000 --- a/packages/medusa-payment-adyen/.eslintrc +++ /dev/null @@ -1,9 +0,0 @@ -{ - "plugins": ["prettier"], - "extends": ["prettier"], - "rules": { - "prettier/prettier": "error", - "semi": "error", - "no-unused-expressions": "true" - } -} diff --git a/packages/medusa-payment-adyen/.prettierrc b/packages/medusa-payment-adyen/.prettierrc deleted file mode 100644 index 70175ce150..0000000000 --- a/packages/medusa-payment-adyen/.prettierrc +++ /dev/null @@ -1,7 +0,0 @@ -{ - "endOfLine": "lf", - "semi": false, - "singleQuote": false, - "tabWidth": 2, - "trailingComma": "es5" -} \ No newline at end of file diff --git a/packages/medusa-payment-klarna/.eslintrc b/packages/medusa-payment-klarna/.eslintrc deleted file mode 100644 index 2a889697f0..0000000000 --- a/packages/medusa-payment-klarna/.eslintrc +++ /dev/null @@ -1,9 +0,0 @@ -{ - "plugins": ["prettier"], - "extends": ["prettier"], - "rules": { - "prettier/prettier": "error", - "semi": "error", - "no-unused-expressions": "true" - } -} diff --git a/packages/medusa-payment-klarna/.prettierrc b/packages/medusa-payment-klarna/.prettierrc deleted file mode 100644 index 48e90e8d40..0000000000 --- a/packages/medusa-payment-klarna/.prettierrc +++ /dev/null @@ -1,7 +0,0 @@ -{ - "endOfLine": "lf", - "semi": false, - "singleQuote": false, - "tabWidth": 2, - "trailingComma": "es5" -} diff --git a/packages/medusa-payment-manual/.eslintrc b/packages/medusa-payment-manual/.eslintrc deleted file mode 100644 index 2a889697f0..0000000000 --- a/packages/medusa-payment-manual/.eslintrc +++ /dev/null @@ -1,9 +0,0 @@ -{ - "plugins": ["prettier"], - "extends": ["prettier"], - "rules": { - "prettier/prettier": "error", - "semi": "error", - "no-unused-expressions": "true" - } -} diff --git a/packages/medusa-payment-manual/.prettierrc b/packages/medusa-payment-manual/.prettierrc deleted file mode 100644 index 70175ce150..0000000000 --- a/packages/medusa-payment-manual/.prettierrc +++ /dev/null @@ -1,7 +0,0 @@ -{ - "endOfLine": "lf", - "semi": false, - "singleQuote": false, - "tabWidth": 2, - "trailingComma": "es5" -} \ No newline at end of file diff --git a/packages/medusa-payment-paypal/.eslintrc b/packages/medusa-payment-paypal/.eslintrc deleted file mode 100644 index 2a889697f0..0000000000 --- a/packages/medusa-payment-paypal/.eslintrc +++ /dev/null @@ -1,9 +0,0 @@ -{ - "plugins": ["prettier"], - "extends": ["prettier"], - "rules": { - "prettier/prettier": "error", - "semi": "error", - "no-unused-expressions": "true" - } -} diff --git a/packages/medusa-payment-paypal/.prettierrc b/packages/medusa-payment-paypal/.prettierrc deleted file mode 100644 index 70175ce150..0000000000 --- a/packages/medusa-payment-paypal/.prettierrc +++ /dev/null @@ -1,7 +0,0 @@ -{ - "endOfLine": "lf", - "semi": false, - "singleQuote": false, - "tabWidth": 2, - "trailingComma": "es5" -} \ No newline at end of file diff --git a/packages/medusa-payment-stripe/.eslintrc b/packages/medusa-payment-stripe/.eslintrc deleted file mode 100644 index 2a889697f0..0000000000 --- a/packages/medusa-payment-stripe/.eslintrc +++ /dev/null @@ -1,9 +0,0 @@ -{ - "plugins": ["prettier"], - "extends": ["prettier"], - "rules": { - "prettier/prettier": "error", - "semi": "error", - "no-unused-expressions": "true" - } -} diff --git a/packages/medusa-payment-stripe/.prettierrc b/packages/medusa-payment-stripe/.prettierrc deleted file mode 100644 index 70175ce150..0000000000 --- a/packages/medusa-payment-stripe/.prettierrc +++ /dev/null @@ -1,7 +0,0 @@ -{ - "endOfLine": "lf", - "semi": false, - "singleQuote": false, - "tabWidth": 2, - "trailingComma": "es5" -} \ No newline at end of file diff --git a/packages/medusa-plugin-add-ons/.eslintrc b/packages/medusa-plugin-add-ons/.eslintrc deleted file mode 100644 index 2a889697f0..0000000000 --- a/packages/medusa-plugin-add-ons/.eslintrc +++ /dev/null @@ -1,9 +0,0 @@ -{ - "plugins": ["prettier"], - "extends": ["prettier"], - "rules": { - "prettier/prettier": "error", - "semi": "error", - "no-unused-expressions": "true" - } -} diff --git a/packages/medusa-plugin-add-ons/.prettierrc b/packages/medusa-plugin-add-ons/.prettierrc deleted file mode 100644 index 70175ce150..0000000000 --- a/packages/medusa-plugin-add-ons/.prettierrc +++ /dev/null @@ -1,7 +0,0 @@ -{ - "endOfLine": "lf", - "semi": false, - "singleQuote": false, - "tabWidth": 2, - "trailingComma": "es5" -} \ No newline at end of file diff --git a/packages/medusa-plugin-brightpearl/.eslintrc b/packages/medusa-plugin-brightpearl/.eslintrc deleted file mode 100644 index 2a889697f0..0000000000 --- a/packages/medusa-plugin-brightpearl/.eslintrc +++ /dev/null @@ -1,9 +0,0 @@ -{ - "plugins": ["prettier"], - "extends": ["prettier"], - "rules": { - "prettier/prettier": "error", - "semi": "error", - "no-unused-expressions": "true" - } -} diff --git a/packages/medusa-plugin-brightpearl/.prettierrc b/packages/medusa-plugin-brightpearl/.prettierrc deleted file mode 100644 index 70175ce150..0000000000 --- a/packages/medusa-plugin-brightpearl/.prettierrc +++ /dev/null @@ -1,7 +0,0 @@ -{ - "endOfLine": "lf", - "semi": false, - "singleQuote": false, - "tabWidth": 2, - "trailingComma": "es5" -} \ No newline at end of file diff --git a/packages/medusa-plugin-contentful/.eslintrc b/packages/medusa-plugin-contentful/.eslintrc deleted file mode 100644 index 2a889697f0..0000000000 --- a/packages/medusa-plugin-contentful/.eslintrc +++ /dev/null @@ -1,9 +0,0 @@ -{ - "plugins": ["prettier"], - "extends": ["prettier"], - "rules": { - "prettier/prettier": "error", - "semi": "error", - "no-unused-expressions": "true" - } -} diff --git a/packages/medusa-plugin-contentful/.prettierrc b/packages/medusa-plugin-contentful/.prettierrc deleted file mode 100644 index 70175ce150..0000000000 --- a/packages/medusa-plugin-contentful/.prettierrc +++ /dev/null @@ -1,7 +0,0 @@ -{ - "endOfLine": "lf", - "semi": false, - "singleQuote": false, - "tabWidth": 2, - "trailingComma": "es5" -} \ No newline at end of file diff --git a/packages/medusa-plugin-discount-generator/.eslintrc b/packages/medusa-plugin-discount-generator/.eslintrc deleted file mode 100644 index 2a889697f0..0000000000 --- a/packages/medusa-plugin-discount-generator/.eslintrc +++ /dev/null @@ -1,9 +0,0 @@ -{ - "plugins": ["prettier"], - "extends": ["prettier"], - "rules": { - "prettier/prettier": "error", - "semi": "error", - "no-unused-expressions": "true" - } -} diff --git a/packages/medusa-plugin-discount-generator/.prettierrc b/packages/medusa-plugin-discount-generator/.prettierrc deleted file mode 100644 index 70175ce150..0000000000 --- a/packages/medusa-plugin-discount-generator/.prettierrc +++ /dev/null @@ -1,7 +0,0 @@ -{ - "endOfLine": "lf", - "semi": false, - "singleQuote": false, - "tabWidth": 2, - "trailingComma": "es5" -} \ No newline at end of file diff --git a/packages/medusa-plugin-economic/.eslintrc b/packages/medusa-plugin-economic/.eslintrc deleted file mode 100644 index 2a889697f0..0000000000 --- a/packages/medusa-plugin-economic/.eslintrc +++ /dev/null @@ -1,9 +0,0 @@ -{ - "plugins": ["prettier"], - "extends": ["prettier"], - "rules": { - "prettier/prettier": "error", - "semi": "error", - "no-unused-expressions": "true" - } -} diff --git a/packages/medusa-plugin-economic/.prettierrc b/packages/medusa-plugin-economic/.prettierrc deleted file mode 100644 index 70175ce150..0000000000 --- a/packages/medusa-plugin-economic/.prettierrc +++ /dev/null @@ -1,7 +0,0 @@ -{ - "endOfLine": "lf", - "semi": false, - "singleQuote": false, - "tabWidth": 2, - "trailingComma": "es5" -} \ No newline at end of file diff --git a/packages/medusa-plugin-ip-lookup/.prettierrc b/packages/medusa-plugin-ip-lookup/.prettierrc deleted file mode 100644 index 70175ce150..0000000000 --- a/packages/medusa-plugin-ip-lookup/.prettierrc +++ /dev/null @@ -1,7 +0,0 @@ -{ - "endOfLine": "lf", - "semi": false, - "singleQuote": false, - "tabWidth": 2, - "trailingComma": "es5" -} \ No newline at end of file diff --git a/packages/medusa-plugin-mailchimp/.eslintrc b/packages/medusa-plugin-mailchimp/.eslintrc deleted file mode 100644 index 2a889697f0..0000000000 --- a/packages/medusa-plugin-mailchimp/.eslintrc +++ /dev/null @@ -1,9 +0,0 @@ -{ - "plugins": ["prettier"], - "extends": ["prettier"], - "rules": { - "prettier/prettier": "error", - "semi": "error", - "no-unused-expressions": "true" - } -} diff --git a/packages/medusa-plugin-mailchimp/.prettierrc b/packages/medusa-plugin-mailchimp/.prettierrc deleted file mode 100644 index 70175ce150..0000000000 --- a/packages/medusa-plugin-mailchimp/.prettierrc +++ /dev/null @@ -1,7 +0,0 @@ -{ - "endOfLine": "lf", - "semi": false, - "singleQuote": false, - "tabWidth": 2, - "trailingComma": "es5" -} \ No newline at end of file diff --git a/packages/medusa-plugin-permissions/.prettierrc b/packages/medusa-plugin-permissions/.prettierrc deleted file mode 100644 index 70175ce150..0000000000 --- a/packages/medusa-plugin-permissions/.prettierrc +++ /dev/null @@ -1,7 +0,0 @@ -{ - "endOfLine": "lf", - "semi": false, - "singleQuote": false, - "tabWidth": 2, - "trailingComma": "es5" -} \ No newline at end of file diff --git a/packages/medusa-plugin-restock-notification/.eslintrc b/packages/medusa-plugin-restock-notification/.eslintrc deleted file mode 100644 index 2a889697f0..0000000000 --- a/packages/medusa-plugin-restock-notification/.eslintrc +++ /dev/null @@ -1,9 +0,0 @@ -{ - "plugins": ["prettier"], - "extends": ["prettier"], - "rules": { - "prettier/prettier": "error", - "semi": "error", - "no-unused-expressions": "true" - } -} diff --git a/packages/medusa-plugin-restock-notification/.prettierrc b/packages/medusa-plugin-restock-notification/.prettierrc deleted file mode 100644 index 70175ce150..0000000000 --- a/packages/medusa-plugin-restock-notification/.prettierrc +++ /dev/null @@ -1,7 +0,0 @@ -{ - "endOfLine": "lf", - "semi": false, - "singleQuote": false, - "tabWidth": 2, - "trailingComma": "es5" -} \ No newline at end of file diff --git a/packages/medusa-plugin-segment/.eslintrc b/packages/medusa-plugin-segment/.eslintrc deleted file mode 100644 index 2a889697f0..0000000000 --- a/packages/medusa-plugin-segment/.eslintrc +++ /dev/null @@ -1,9 +0,0 @@ -{ - "plugins": ["prettier"], - "extends": ["prettier"], - "rules": { - "prettier/prettier": "error", - "semi": "error", - "no-unused-expressions": "true" - } -} diff --git a/packages/medusa-plugin-segment/.prettierrc b/packages/medusa-plugin-segment/.prettierrc deleted file mode 100644 index 70175ce150..0000000000 --- a/packages/medusa-plugin-segment/.prettierrc +++ /dev/null @@ -1,7 +0,0 @@ -{ - "endOfLine": "lf", - "semi": false, - "singleQuote": false, - "tabWidth": 2, - "trailingComma": "es5" -} \ No newline at end of file diff --git a/packages/medusa-plugin-sendgrid/.eslintrc b/packages/medusa-plugin-sendgrid/.eslintrc deleted file mode 100644 index 2a889697f0..0000000000 --- a/packages/medusa-plugin-sendgrid/.eslintrc +++ /dev/null @@ -1,9 +0,0 @@ -{ - "plugins": ["prettier"], - "extends": ["prettier"], - "rules": { - "prettier/prettier": "error", - "semi": "error", - "no-unused-expressions": "true" - } -} diff --git a/packages/medusa-plugin-sendgrid/.prettierrc b/packages/medusa-plugin-sendgrid/.prettierrc deleted file mode 100644 index 70175ce150..0000000000 --- a/packages/medusa-plugin-sendgrid/.prettierrc +++ /dev/null @@ -1,7 +0,0 @@ -{ - "endOfLine": "lf", - "semi": false, - "singleQuote": false, - "tabWidth": 2, - "trailingComma": "es5" -} \ No newline at end of file diff --git a/packages/medusa-plugin-slack-notification/.eslintrc b/packages/medusa-plugin-slack-notification/.eslintrc deleted file mode 100644 index 2a889697f0..0000000000 --- a/packages/medusa-plugin-slack-notification/.eslintrc +++ /dev/null @@ -1,9 +0,0 @@ -{ - "plugins": ["prettier"], - "extends": ["prettier"], - "rules": { - "prettier/prettier": "error", - "semi": "error", - "no-unused-expressions": "true" - } -} diff --git a/packages/medusa-plugin-slack-notification/.prettierrc b/packages/medusa-plugin-slack-notification/.prettierrc deleted file mode 100644 index 70175ce150..0000000000 --- a/packages/medusa-plugin-slack-notification/.prettierrc +++ /dev/null @@ -1,7 +0,0 @@ -{ - "endOfLine": "lf", - "semi": false, - "singleQuote": false, - "tabWidth": 2, - "trailingComma": "es5" -} \ No newline at end of file diff --git a/packages/medusa-plugin-twilio-sms/.eslintrc b/packages/medusa-plugin-twilio-sms/.eslintrc deleted file mode 100644 index 2a889697f0..0000000000 --- a/packages/medusa-plugin-twilio-sms/.eslintrc +++ /dev/null @@ -1,9 +0,0 @@ -{ - "plugins": ["prettier"], - "extends": ["prettier"], - "rules": { - "prettier/prettier": "error", - "semi": "error", - "no-unused-expressions": "true" - } -} diff --git a/packages/medusa-plugin-twilio-sms/.prettierrc b/packages/medusa-plugin-twilio-sms/.prettierrc deleted file mode 100644 index 70175ce150..0000000000 --- a/packages/medusa-plugin-twilio-sms/.prettierrc +++ /dev/null @@ -1,7 +0,0 @@ -{ - "endOfLine": "lf", - "semi": false, - "singleQuote": false, - "tabWidth": 2, - "trailingComma": "es5" -} \ No newline at end of file diff --git a/packages/medusa-plugin-wishlist/.eslintrc b/packages/medusa-plugin-wishlist/.eslintrc deleted file mode 100644 index 2a889697f0..0000000000 --- a/packages/medusa-plugin-wishlist/.eslintrc +++ /dev/null @@ -1,9 +0,0 @@ -{ - "plugins": ["prettier"], - "extends": ["prettier"], - "rules": { - "prettier/prettier": "error", - "semi": "error", - "no-unused-expressions": "true" - } -} diff --git a/packages/medusa-plugin-wishlist/.prettierrc b/packages/medusa-plugin-wishlist/.prettierrc deleted file mode 100644 index 70175ce150..0000000000 --- a/packages/medusa-plugin-wishlist/.prettierrc +++ /dev/null @@ -1,7 +0,0 @@ -{ - "endOfLine": "lf", - "semi": false, - "singleQuote": false, - "tabWidth": 2, - "trailingComma": "es5" -} \ No newline at end of file diff --git a/packages/medusa-telemetry/.eslintrc b/packages/medusa-telemetry/.eslintrc deleted file mode 100644 index 2a889697f0..0000000000 --- a/packages/medusa-telemetry/.eslintrc +++ /dev/null @@ -1,9 +0,0 @@ -{ - "plugins": ["prettier"], - "extends": ["prettier"], - "rules": { - "prettier/prettier": "error", - "semi": "error", - "no-unused-expressions": "true" - } -} diff --git a/packages/medusa-telemetry/.prettierrc b/packages/medusa-telemetry/.prettierrc deleted file mode 100644 index 70175ce150..0000000000 --- a/packages/medusa-telemetry/.prettierrc +++ /dev/null @@ -1,7 +0,0 @@ -{ - "endOfLine": "lf", - "semi": false, - "singleQuote": false, - "tabWidth": 2, - "trailingComma": "es5" -} \ No newline at end of file diff --git a/packages/medusa-test-utils/.eslintrc b/packages/medusa-test-utils/.eslintrc deleted file mode 100644 index 2a889697f0..0000000000 --- a/packages/medusa-test-utils/.eslintrc +++ /dev/null @@ -1,9 +0,0 @@ -{ - "plugins": ["prettier"], - "extends": ["prettier"], - "rules": { - "prettier/prettier": "error", - "semi": "error", - "no-unused-expressions": "true" - } -} diff --git a/packages/medusa/.eslintrc b/packages/medusa/.eslintrc deleted file mode 100644 index 2a889697f0..0000000000 --- a/packages/medusa/.eslintrc +++ /dev/null @@ -1,9 +0,0 @@ -{ - "plugins": ["prettier"], - "extends": ["prettier"], - "rules": { - "prettier/prettier": "error", - "semi": "error", - "no-unused-expressions": "true" - } -} diff --git a/packages/medusa/.prettierrc b/packages/medusa/.prettierrc deleted file mode 100644 index 70175ce150..0000000000 --- a/packages/medusa/.prettierrc +++ /dev/null @@ -1,7 +0,0 @@ -{ - "endOfLine": "lf", - "semi": false, - "singleQuote": false, - "tabWidth": 2, - "trailingComma": "es5" -} \ No newline at end of file diff --git a/www/docs/.eslintrc b/www/docs/.eslintrc deleted file mode 100644 index 2a889697f0..0000000000 --- a/www/docs/.eslintrc +++ /dev/null @@ -1,9 +0,0 @@ -{ - "plugins": ["prettier"], - "extends": ["prettier"], - "rules": { - "prettier/prettier": "error", - "semi": "error", - "no-unused-expressions": "true" - } -} diff --git a/www/docs/.prettierrc b/www/docs/.prettierrc deleted file mode 100644 index 48e90e8d40..0000000000 --- a/www/docs/.prettierrc +++ /dev/null @@ -1,7 +0,0 @@ -{ - "endOfLine": "lf", - "semi": false, - "singleQuote": false, - "tabWidth": 2, - "trailingComma": "es5" -} diff --git a/www/reference/.prettierrc b/www/reference/.prettierrc deleted file mode 100644 index 33d2cfa3f6..0000000000 --- a/www/reference/.prettierrc +++ /dev/null @@ -1,4 +0,0 @@ -{ - "arrowParens": "avoid", - "semi": false -} diff --git a/yarn.lock b/yarn.lock index 2c3b66f84c..7b58e8e424 100644 --- a/yarn.lock +++ b/yarn.lock @@ -275,6 +275,15 @@ semver "^6.3.0" source-map "^0.5.0" +"@babel/eslint-parser@^7.15.8": + version "7.15.8" + resolved "https://registry.yarnpkg.com/@babel/eslint-parser/-/eslint-parser-7.15.8.tgz#8988660b59d739500b67d0585fd4daca218d9f11" + integrity sha512-fYP7QFngCvgxjUuw8O057SVH5jCXsbFFOoE77CFDcvzwBVgTOkMD/L4mIC5Ud1xf8chK/no2fRbSSn1wvNmKuQ== + dependencies: + eslint-scope "^5.1.1" + eslint-visitor-keys "^2.1.0" + semver "^6.3.0" + "@babel/generator@^7.11.5": version "7.11.6" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.11.6.tgz#b868900f81b163b4d464ea24545c61cbac4dc620" @@ -7667,7 +7676,24 @@ escodegen@^2.0.0: optionalDependencies: source-map "~0.6.1" -eslint-scope@5.1.1: +eslint-config-google@^0.14.0: + version "0.14.0" + resolved "https://registry.yarnpkg.com/eslint-config-google/-/eslint-config-google-0.14.0.tgz#4f5f8759ba6e11b424294a219dbfa18c508bcc1a" + integrity sha512-WsbX4WbjuMvTdeVL6+J3rK1RGhCTqjsFjX7UMSMgZiyxxaNLkoJENbrGExzERFeoTpGw3F3FypTiWAP9ZXzkEw== + +eslint-config-prettier@^8.3.0: + version "8.3.0" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.3.0.tgz#f7471b20b6fe8a9a9254cc684454202886a2dd7a" + integrity sha512-BgZuLUSeKzvlL/VUjx/Yb787VQ26RU3gGjA3iiFvdsp/2bMfVIWUVP7tjxtjS0e+HP409cPlPvNkQloz8C91ew== + +eslint-plugin-prettier@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-4.0.0.tgz#8b99d1e4b8b24a762472b4567992023619cb98e0" + integrity sha512-98MqmCJ7vJodoQK359bqQWaxOE0CS8paAz/GgjaZLyex4TTk3g9HugoO89EqWCrFiOqn9EVvcoo7gZzONCWVwQ== + dependencies: + prettier-linter-helpers "^1.0.0" + +eslint-scope@5.1.1, eslint-scope@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== @@ -7675,6 +7701,11 @@ eslint-scope@5.1.1: esrecurse "^4.3.0" estraverse "^4.1.1" +eslint-visitor-keys@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" + integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== + esprima@^4.0.0, esprima@^4.0.1, esprima@~4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" @@ -7943,6 +7974,11 @@ fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== +fast-diff@^1.1.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03" + integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w== + fast-glob@^2.2.6: version "2.2.7" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-2.2.7.tgz#6953857c3afa475fff92ee6015d52da70a4cd39d" @@ -12998,6 +13034,13 @@ prepend-http@^2.0.0: resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= +prettier-linter-helpers@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b" + integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w== + dependencies: + fast-diff "^1.1.2" + prettier@^2.1.1: version "2.3.2" resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.3.2.tgz#ef280a05ec253712e486233db5c6f23441e7342d" From 30b6a793f1fd549d8a9d2bb7947fc2597eadfac3 Mon Sep 17 00:00:00 2001 From: Sebastian Rindom Date: Tue, 12 Oct 2021 22:09:46 +0200 Subject: [PATCH 24/43] chore: add lint-staged + husky --- .eslintignore | 106 +++++++++ .eslintrc.js | 32 +++ .husky/pre-commit | 4 + package.json | 12 + scripts/on-lint-error.js | 11 + yarn.lock | 501 +++++++++++++++++++++++++++++++++++++-- 6 files changed, 643 insertions(+), 23 deletions(-) create mode 100644 .eslintignore create mode 100644 .eslintrc.js create mode 100755 .husky/pre-commit create mode 100644 scripts/on-lint-error.js diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000000..563f1ca420 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,106 @@ +/packages/medusa/src/services/auth.js +/packages/medusa/src/services/cart.js +/packages/medusa/src/services/claim-item.js +/packages/medusa/src/services/claim.js +/packages/medusa/src/services/customer.js +/packages/medusa/src/services/discount.js +/packages/medusa/src/services/draft-order.js +/packages/medusa/src/services/event-bus.js +/packages/medusa/src/services/fulfillment-provider.js +/packages/medusa/src/services/fulfillment.js +/packages/medusa/src/services/gift-card.js +/packages/medusa/src/services/idempotency-key.js +/packages/medusa/src/services/inventory.js +/packages/medusa/src/services/line-item.js +/packages/medusa/src/services/middleware.js +/packages/medusa/src/services/note.js +/packages/medusa/src/services/notification.js +/packages/medusa/src/services/oauth.js +/packages/medusa/src/services/order.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/region.js +/packages/medusa/src/services/return-reason.js +/packages/medusa/src/services/return.js +/packages/medusa/src/services/shipping-option.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/services/transaction.js +/packages/medusa/src/services/user.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 + +/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-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 diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000000..47bc35d527 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,32 @@ +module.exports = { + parser: `@babel/eslint-parser`, + parserOptions: { + requireConfigFile: false, + ecmaFeatures: { + experimentalDecorators: true, + }, + }, + plugins: [`eslint-plugin-prettier`], + extends: [`eslint:recommended`, `google`, `eslint-config-prettier`], + rules: { + 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`], + }, + ], +} diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100755 index 0000000000..4b69f33f48 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,4 @@ +#!/bin/sh +. "$(dirname "$0")/_/husky.sh" + +lint-staged || node scripts/on-lint-error.js diff --git a/package.json b/package.json index ad7bb013f7..358c4a0cb9 100644 --- a/package.json +++ b/package.json @@ -12,19 +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", @@ -32,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", diff --git a/scripts/on-lint-error.js b/scripts/on-lint-error.js new file mode 100644 index 0000000000..8d77e79021 --- /dev/null +++ b/scripts/on-lint-error.js @@ -0,0 +1,11 @@ +console.log(` + +Oops! Medusa noticed some lint or style warnings in the code for this +commit. Your changes have been committed, but you should fix the warnings before +creating a pull request. +Use 'npm run lint' to manually re-run these checks. You can also disable these +checks: +- for a single commit: git commit --no-verify +- for all future commits: npm run hooks:uninstall + +`) diff --git a/yarn.lock b/yarn.lock index 7b58e8e424..7f5b3d76d5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2994,6 +2994,21 @@ resolve-pathname "^3.0.0" tslib "^2.2.0" +"@eslint/eslintrc@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.0.2.tgz#6044884f7f93c4ecc2d1694c7486cce91ef8f746" + integrity sha512-x1ZXdEFsvTcnbTZgqcWUL9w2ybgZCw/qbKTPQnab+XnYA2bMQpJCh+/bBzCRfDJaJdlrrQlOk49jNtru9gL/6Q== + dependencies: + ajv "^6.12.4" + debug "^4.3.2" + espree "^9.0.0" + globals "^13.9.0" + ignore "^4.0.6" + import-fresh "^3.2.1" + js-yaml "^3.13.1" + minimatch "^3.0.4" + strip-json-comments "^3.1.1" + "@evocateur/libnpmaccess@^3.1.2": version "3.1.2" resolved "https://registry.yarnpkg.com/@evocateur/libnpmaccess/-/libnpmaccess-3.1.2.tgz#ecf7f6ce6b004e9f942b098d92200be4a4b1c845" @@ -3080,6 +3095,20 @@ dependencies: "@hapi/hoek" "^9.0.0" +"@humanwhocodes/config-array@^0.6.0": + version "0.6.0" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.6.0.tgz#b5621fdb3b32309d2d16575456cbc277fa8f021a" + integrity sha512-JQlEKbcgEUjBFhLIF4iqM7u/9lwgHRBcpHrmUNCALK0Q3amXN6lxdoXLnF0sm11E9VqTmBALR87IlUg1bZ8A9A== + dependencies: + "@humanwhocodes/object-schema" "^1.2.0" + debug "^4.1.1" + minimatch "^3.0.4" + +"@humanwhocodes/object-schema@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.0.tgz#87de7af9c231826fdd68ac7258f77c429e0e5fcf" + integrity sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w== + "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" @@ -4658,6 +4687,76 @@ resolved "https://registry.yarnpkg.com/@types/zen-observable/-/zen-observable-0.8.3.tgz#781d360c282436494b32fe7d9f7f8e64b3118aa3" integrity sha512-fbF6oTd4sGGy0xjHPKAt+eS2CrxJ3+6gQ3FGcBoIJR2TLAyCkCyI8JqZNy+FeON0AhVgNJoUumVoZQjBFUqHkw== +"@typescript-eslint/eslint-plugin@^5.0.0": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.0.0.tgz#ecc7cc69d1e6f342beb6ea9cf9fbc02c97a212ac" + integrity sha512-T6V6fCD2U0YesOedvydTnrNtsC8E+c2QzpawIpDdlaObX0OX5dLo7tLU5c64FhTZvA1Xrdim+cXDI7NPsVx8Cg== + dependencies: + "@typescript-eslint/experimental-utils" "5.0.0" + "@typescript-eslint/scope-manager" "5.0.0" + debug "^4.3.1" + functional-red-black-tree "^1.0.1" + ignore "^5.1.8" + regexpp "^3.1.0" + semver "^7.3.5" + tsutils "^3.21.0" + +"@typescript-eslint/experimental-utils@5.0.0": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-5.0.0.tgz#c7d7e67443dfb9fd93a5d060fb72c9e9b5638bbc" + integrity sha512-Dnp4dFIsZcPawD6CT1p5NibNUQyGSEz80sULJZkyhyna8AEqArmfwMwJPbmKzWVo4PabqNVzHYlzmcdLQWk+pg== + dependencies: + "@types/json-schema" "^7.0.7" + "@typescript-eslint/scope-manager" "5.0.0" + "@typescript-eslint/types" "5.0.0" + "@typescript-eslint/typescript-estree" "5.0.0" + eslint-scope "^5.1.1" + eslint-utils "^3.0.0" + +"@typescript-eslint/parser@^5.0.0": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.0.0.tgz#50d1be2e0def82d73e863cceba74aeeac9973592" + integrity sha512-B6D5rmmQ14I1fdzs71eL3DAuvnPHTY/t7rQABrL9BLnx/H51Un8ox1xqYAchs0/V2trcoyxB1lMJLlrwrJCDgw== + dependencies: + "@typescript-eslint/scope-manager" "5.0.0" + "@typescript-eslint/types" "5.0.0" + "@typescript-eslint/typescript-estree" "5.0.0" + debug "^4.3.1" + +"@typescript-eslint/scope-manager@5.0.0": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.0.0.tgz#aea0fb0e2480c1169a02e89d9005ac3f2835713f" + integrity sha512-5RFjdA/ain/MDUHYXdF173btOKncIrLuBmA9s6FJhzDrRAyVSA+70BHg0/MW6TE+UiKVyRtX91XpVS0gVNwVDQ== + dependencies: + "@typescript-eslint/types" "5.0.0" + "@typescript-eslint/visitor-keys" "5.0.0" + +"@typescript-eslint/types@5.0.0": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.0.0.tgz#25d93f6d269b2d25fdc51a0407eb81ccba60eb0f" + integrity sha512-dU/pKBUpehdEqYuvkojmlv0FtHuZnLXFBn16zsDmlFF3LXkOpkAQ2vrKc3BidIIve9EMH2zfTlxqw9XM0fFN5w== + +"@typescript-eslint/typescript-estree@5.0.0": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.0.0.tgz#bc20f413c6e572c7309dbe5fa3be027984952af3" + integrity sha512-V/6w+PPQMhinWKSn+fCiX5jwvd1vRBm7AX7SJQXEGQtwtBvjMPjaU3YTQ1ik2UF1u96X7tsB96HMnulG3eLi9Q== + dependencies: + "@typescript-eslint/types" "5.0.0" + "@typescript-eslint/visitor-keys" "5.0.0" + debug "^4.3.1" + globby "^11.0.3" + is-glob "^4.0.1" + semver "^7.3.5" + tsutils "^3.21.0" + +"@typescript-eslint/visitor-keys@5.0.0": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.0.0.tgz#b789f7cd105e59bee5c0983a353942a5a48f56df" + integrity sha512-yRyd2++o/IrJdyHuYMxyFyBhU762MRHQ/bAGQeTnN3pGikfh+nEmM61XTqaDH1XDp53afZ+waXrk0ZvenoZ6xw== + dependencies: + "@typescript-eslint/types" "5.0.0" + eslint-visitor-keys "^3.0.0" + "@webassemblyjs/ast@1.11.1": version "1.11.1" resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.1.tgz#2bfd767eae1a6996f432ff7e8d7fc75679c0b6a7" @@ -4832,6 +4931,11 @@ acorn-globals@^6.0.0: acorn "^7.1.1" acorn-walk "^7.1.1" +acorn-jsx@^5.3.1: + version "5.3.2" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" + integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== + acorn-walk@^7.1.1: version "7.2.0" resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" @@ -4852,6 +4956,11 @@ acorn@^8.0.4, acorn@^8.2.4, acorn@^8.4.1: resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.4.1.tgz#56c36251fc7cabc7096adc18f05afe814321a28c" integrity sha512-asabaBSkEKosYKMITunzX177CXxQ4Q8BSSzMTKD+FefUhipQC70gfW5SiUDhYQ3vk8G+81HqQk7Fv9OXwwn9KA== +acorn@^8.5.0: + version "8.5.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.5.0.tgz#4512ccb99b3698c752591e9bb4472e38ad43cee2" + integrity sha512-yXbYeFy+jUuYd3/CDcg2NkIYE991XYX/bje7LmjJigUciaeO1JR4XxXgCIV1/Zc/dRuFEyw1L0pbA+qynJkW5Q== + address@1.1.2, address@^1.0.1: version "1.1.2" resolved "https://registry.yarnpkg.com/address/-/address-1.1.2.tgz#bf1116c9c758c51b7a933d296b72c221ed9428b6" @@ -4913,7 +5022,7 @@ ajv@^5.0.1: fast-json-stable-stringify "^2.0.0" json-schema-traverse "^0.3.0" -ajv@^6.1.0, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.12.5: +ajv@^6.1.0, ajv@^6.10.0, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.12.5: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== @@ -4967,6 +5076,11 @@ ansi-colors@^3.0.0: resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.4.tgz#e3a3da4bfbae6c86a9c285625de124a234026fbf" integrity sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA== +ansi-colors@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" + integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== + ansi-escapes@^3.1.0, ansi-escapes@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" @@ -5173,6 +5287,11 @@ assign-symbols@^1.0.0: resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= +astral-regex@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" + integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== + async-each@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf" @@ -6115,6 +6234,13 @@ cli-cursor@^2.1.0: dependencies: restore-cursor "^2.0.0" +cli-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" + integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== + dependencies: + restore-cursor "^3.1.0" + cli-highlight@^2.1.11: version "2.1.11" resolved "https://registry.yarnpkg.com/cli-highlight/-/cli-highlight-2.1.11.tgz#49736fa452f0aaf4fae580e30acb26828d2dc1bf" @@ -6135,6 +6261,14 @@ cli-progress@^3.4.0: colors "^1.1.2" string-width "^4.2.0" +cli-truncate@2.1.0, cli-truncate@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-2.1.0.tgz#c39e28bf05edcde5be3b98992a22deed5a2b93c7" + integrity sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg== + dependencies: + slice-ansi "^3.0.0" + string-width "^4.2.0" + cli-ux@^5.4.9: version "5.6.3" resolved "https://registry.yarnpkg.com/cli-ux/-/cli-ux-5.6.3.tgz#eecdb2e0261171f2b28f2be6b18c490291c3a287" @@ -6315,6 +6449,11 @@ colorette@^1.3.0: resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.3.0.tgz#ff45d2f0edb244069d3b772adeb04fed38d0a0af" integrity sha512-ecORCqbSFP7Wm8Y6lyqMJjexBQqXSF7SSeaTyGGphogUjBlFP9m9o08wy86HL2uB7fMTxtOUzLMk7ogKcxMg1w== +colorette@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.4.0.tgz#5190fbb87276259a86ad700bff2c6d6faa3fca40" + integrity sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g== + colors@^1.1.2: version "1.4.0" resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" @@ -6370,6 +6509,11 @@ commander@^7.1.0: resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== +commander@^8.2.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-8.2.0.tgz#37fe2bde301d87d47a53adeff8b5915db1381ca8" + integrity sha512-LLKxDvHeL91/8MIyTAD5BFMNtoIwztGPMiM/7Bl8rIPmHCZXRxmSWr91h57dpOpnQ6jIUqEWdXE/uBYMfiVZDA== + comment-patterns@^0.11.0: version "0.11.0" resolved "https://registry.yarnpkg.com/comment-patterns/-/comment-patterns-0.11.0.tgz#de7533d5da9b4241457e53c164cee87b907f0bb0" @@ -6699,6 +6843,17 @@ cosmiconfig@^7.0.0: path-type "^4.0.0" yaml "^1.10.0" +cosmiconfig@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.0.1.tgz#714d756522cace867867ccb4474c5d01bbae5d6d" + integrity sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ== + dependencies: + "@types/parse-json" "^4.0.0" + import-fresh "^3.2.1" + parse-json "^5.0.0" + path-type "^4.0.0" + yaml "^1.10.0" + cross-env@^7.0.2: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-7.0.3.tgz#865264b29677dc015ba8418918965dd232fc54cf" @@ -6706,7 +6861,7 @@ cross-env@^7.0.2: dependencies: cross-spawn "^7.0.1" -cross-spawn@7.0.3, cross-spawn@^7.0.0, cross-spawn@^7.0.1, cross-spawn@^7.0.3: +cross-spawn@7.0.3, cross-spawn@^7.0.0, cross-spawn@^7.0.1, cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== @@ -7033,7 +7188,7 @@ debug@3.1.0: dependencies: ms "2.0.0" -debug@4, debug@^4.1.1, debug@^4.3.1: +debug@4, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2: version "4.3.2" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b" integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw== @@ -7111,6 +7266,11 @@ deep-extend@^0.6.0: resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== +deep-is@^0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" + integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== + deep-is@~0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" @@ -7312,6 +7472,13 @@ dns-txt@^2.0.2: dependencies: buffer-indexof "^1.0.0" +doctrine@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== + dependencies: + esutils "^2.0.2" + dom-converter@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/dom-converter/-/dom-converter-0.2.0.tgz#6721a9daee2e293682955b6afe416771627bb768" @@ -7542,6 +7709,13 @@ enhanced-resolve@^5.8.0: graceful-fs "^4.2.4" tapable "^2.2.0" +enquirer@^2.3.5, enquirer@^2.3.6: + version "2.3.6" + resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d" + integrity sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg== + dependencies: + ansi-colors "^4.1.1" + entities@^1.1.1, entities@~1.1.1: version "1.1.2" resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56" @@ -7701,16 +7875,96 @@ eslint-scope@5.1.1, eslint-scope@^5.1.1: esrecurse "^4.3.0" estraverse "^4.1.1" -eslint-visitor-keys@^2.1.0: +eslint-scope@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-6.0.0.tgz#9cf45b13c5ac8f3d4c50f46a5121f61b3e318978" + integrity sha512-uRDL9MWmQCkaFus8RF5K9/L/2fn+80yoW3jkD53l4shjCh26fCtvJGasxjUqP5OT87SYTxCVA3BwTUzuELx9kA== + dependencies: + esrecurse "^4.3.0" + estraverse "^5.2.0" + +eslint-utils@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-3.0.0.tgz#8aebaface7345bb33559db0a1f13a1d2d48c3672" + integrity sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA== + dependencies: + eslint-visitor-keys "^2.0.0" + +eslint-visitor-keys@^2.0.0, eslint-visitor-keys@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== +eslint-visitor-keys@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.0.0.tgz#e32e99c6cdc2eb063f204eda5db67bfe58bb4186" + integrity sha512-mJOZa35trBTb3IyRmo8xmKBZlxf+N7OnUl4+ZhJHs/r+0770Wh/LEACE2pqMGMe27G/4y8P2bYGk4J70IC5k1Q== + +eslint@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.0.0.tgz#2c2d0ac6353755667ac90c9ff4a9c1315e43fcff" + integrity sha512-03spzPzMAO4pElm44m60Nj08nYonPGQXmw6Ceai/S4QK82IgwWO1EXx1s9namKzVlbVu3Jf81hb+N+8+v21/HQ== + dependencies: + "@eslint/eslintrc" "^1.0.2" + "@humanwhocodes/config-array" "^0.6.0" + ajv "^6.10.0" + chalk "^4.0.0" + cross-spawn "^7.0.2" + debug "^4.3.2" + doctrine "^3.0.0" + enquirer "^2.3.5" + escape-string-regexp "^4.0.0" + eslint-scope "^6.0.0" + eslint-utils "^3.0.0" + eslint-visitor-keys "^3.0.0" + espree "^9.0.0" + esquery "^1.4.0" + esutils "^2.0.2" + fast-deep-equal "^3.1.3" + file-entry-cache "^6.0.1" + functional-red-black-tree "^1.0.1" + glob-parent "^6.0.1" + globals "^13.6.0" + ignore "^4.0.6" + import-fresh "^3.0.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + js-yaml "^4.1.0" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.4.1" + lodash.merge "^4.6.2" + minimatch "^3.0.4" + natural-compare "^1.4.0" + optionator "^0.9.1" + progress "^2.0.0" + regexpp "^3.2.0" + semver "^7.2.1" + strip-ansi "^6.0.0" + strip-json-comments "^3.1.0" + text-table "^0.2.0" + v8-compile-cache "^2.0.3" + +espree@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.0.0.tgz#e90a2965698228502e771c7a58489b1a9d107090" + integrity sha512-r5EQJcYZ2oaGbeR0jR0fFVijGOcwai07/690YRXLINuhmVeRY4UKSAsQPe/0BNuDgwP7Ophoc1PRsr2E3tkbdQ== + dependencies: + acorn "^8.5.0" + acorn-jsx "^5.3.1" + eslint-visitor-keys "^3.0.0" + esprima@^4.0.0, esprima@^4.0.1, esprima@~4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== +esquery@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.0.tgz#2148ffc38b82e8c7057dfed48425b3e61f0f24a5" + integrity sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w== + dependencies: + estraverse "^5.1.0" + esrecurse@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" @@ -7723,7 +7977,7 @@ estraverse@^4.1.1: resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== -estraverse@^5.2.0: +estraverse@^5.1.0, estraverse@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880" integrity sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ== @@ -7825,7 +8079,7 @@ execa@^4.0.0: signal-exit "^3.0.2" strip-final-newline "^2.0.0" -execa@^5.0.0: +execa@^5.0.0, execa@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== @@ -8007,7 +8261,7 @@ fast-json-stable-stringify@^2.0.0: resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== -fast-levenshtein@~2.0.6: +fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= @@ -8079,6 +8333,13 @@ figures@^3.2.0: dependencies: escape-string-regexp "^1.0.5" +file-entry-cache@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" + integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== + dependencies: + flat-cache "^3.0.4" + file-loader@^6.2.0: version "6.2.0" resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-6.2.0.tgz#baef7cf8e1840df325e4390b4484879480eebe4d" @@ -8185,6 +8446,19 @@ find-up@^3.0.0: dependencies: locate-path "^3.0.0" +flat-cache@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" + integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg== + dependencies: + flatted "^3.1.0" + rimraf "^3.0.2" + +flatted@^3.1.0: + version "3.2.2" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.2.tgz#64bfed5cb68fe3ca78b3eb214ad97b63bedce561" + integrity sha512-JaTY/wtrcSyvXJl4IMFHPKyFur1sE9AUqc0QnhOaJ0CxHtAoIV8pYDzeEfAaNEtGkOfq4gr3LBFmdXW5mOQFnA== + flush-write-stream@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.1.1.tgz#8dd7d873a1babc207d94ead0c2e0e44276ebf2e8" @@ -8327,6 +8601,11 @@ function-bind@^1.1.1: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== +functional-red-black-tree@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" + integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= + gauge@~2.7.3: version "2.7.4" resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" @@ -8522,6 +8801,13 @@ glob-parent@^6.0.0: dependencies: is-glob "^4.0.1" +glob-parent@^6.0.1: + version "6.0.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" + integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== + dependencies: + is-glob "^4.0.3" + glob-to-regexp@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab" @@ -8580,6 +8866,13 @@ globals@^11.1.0: resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== +globals@^13.6.0, globals@^13.9.0: + version "13.11.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.11.0.tgz#40ef678da117fe7bd2e28f1fab24951bd0255be7" + integrity sha512-08/xrJ7wQjK9kkkRoI3OFUBbLx4f+6x3SGwcPvQ0QH6goFDrOU2oyAWrmh3dJezu65buo+HBMzAMQy6rovVC3g== + dependencies: + type-fest "^0.20.2" + globalyzer@0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/globalyzer/-/globalyzer-0.1.0.tgz#cb76da79555669a1519d5a8edf093afaa0bf1465" @@ -9185,6 +9478,11 @@ humanize-ms@^1.2.1: dependencies: ms "^2.0.0" +husky@^7.0.2: + version "7.0.2" + resolved "https://registry.yarnpkg.com/husky/-/husky-7.0.2.tgz#21900da0f30199acca43a46c043c4ad84ae88dff" + integrity sha512-8yKEWNX4z2YsofXAMT7KvA1g8p+GxtB1ffV8XtpAEGuXNAbCV5wdNKH+qTpw8SM9fh4aMPDR+yQuKfgnreyZlg== + hyperlinker@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/hyperlinker/-/hyperlinker-1.0.0.tgz#23dc9e38a206b208ee49bc2d6c8ef47027df0c0e" @@ -9231,12 +9529,12 @@ ignore-walk@^3.0.1: dependencies: minimatch "^3.0.4" -ignore@^4.0.3: +ignore@^4.0.3, ignore@^4.0.6: version "4.0.6" resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== -ignore@^5.1.4: +ignore@^5.1.4, ignore@^5.1.8: version "5.1.8" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57" integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw== @@ -9261,7 +9559,7 @@ import-fresh@^2.0.0: caller-path "^2.0.0" resolve-from "^3.0.0" -import-fresh@^3.2.1, import-fresh@^3.2.2, import-fresh@^3.3.0: +import-fresh@^3.0.0, import-fresh@^3.2.1, import-fresh@^3.2.2, import-fresh@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== @@ -9663,6 +9961,13 @@ is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1: dependencies: is-extglob "^2.1.1" +is-glob@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + is-hexadecimal@^1.0.0: version "1.0.4" resolved "https://registry.yarnpkg.com/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz#cc35c97588da4bd49a8eedd6bc4082d44dcb23a7" @@ -10441,6 +10746,11 @@ json-schema@0.2.3: resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= + json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" @@ -10602,6 +10912,14 @@ levenary@^1.1.1: dependencies: leven "^3.1.0" +levn@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" + integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== + dependencies: + prelude-ls "^1.2.1" + type-check "~0.4.0" + levn@~0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" @@ -10625,6 +10943,39 @@ lines-and-columns@^1.1.6: resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00" integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA= +lint-staged@^11.2.3: + version "11.2.3" + resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-11.2.3.tgz#fc3f4569cc4f46553309dfc1447b8aef69f744fb" + integrity sha512-Tfmhk8O2XFMD25EswHPv+OYhUjsijy5D7liTdxeXvhG2rsadmOLFtyj8lmlfoFFXY8oXWAIOKpoI+lJe1DB1mw== + dependencies: + cli-truncate "2.1.0" + colorette "^1.4.0" + commander "^8.2.0" + cosmiconfig "^7.0.1" + debug "^4.3.2" + enquirer "^2.3.6" + execa "^5.1.1" + listr2 "^3.12.2" + micromatch "^4.0.4" + normalize-path "^3.0.0" + please-upgrade-node "^3.2.0" + string-argv "0.3.1" + stringify-object "3.3.0" + supports-color "8.1.1" + +listr2@^3.12.2: + version "3.12.2" + resolved "https://registry.yarnpkg.com/listr2/-/listr2-3.12.2.tgz#2d55cc627111603ad4768a9e87c9c7bb9b49997e" + integrity sha512-64xC2CJ/As/xgVI3wbhlPWVPx0wfTqbUAkpb7bjDi0thSWMqrf07UFhrfsGoo8YSXmF049Rp9C0cjLC8rZxK9A== + dependencies: + cli-truncate "^2.1.0" + colorette "^1.4.0" + log-update "^4.0.0" + p-map "^4.0.0" + rxjs "^6.6.7" + through "^2.3.8" + wrap-ansi "^7.0.0" + load-json-file@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" @@ -10853,6 +11204,16 @@ lodash@^4.17.14, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.7.0: resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== +log-update@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/log-update/-/log-update-4.0.0.tgz#589ecd352471f2a1c0c570287543a64dfd20e0a1" + integrity sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg== + dependencies: + ansi-escapes "^4.3.0" + cli-cursor "^3.1.0" + slice-ansi "^4.0.0" + wrap-ansi "^6.2.0" + loglevel@^1.6.8: version "1.7.1" resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.7.1.tgz#005fde2f5e6e47068f935ff28573e125ef72f197" @@ -12085,6 +12446,18 @@ optionator@^0.8.1: type-check "~0.3.2" word-wrap "~1.2.3" +optionator@^0.9.1: + version "0.9.1" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499" + integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw== + dependencies: + deep-is "^0.1.3" + fast-levenshtein "^2.0.6" + levn "^0.4.1" + prelude-ls "^1.2.1" + type-check "^0.4.0" + word-wrap "^1.2.3" + original@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/original/-/original-1.0.2.tgz#e442a61cffe1c5fd20a65f3261c26663b303f25f" @@ -12646,6 +13019,13 @@ pkg-up@3.1.0: dependencies: find-up "^3.0.0" +please-upgrade-node@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz#aeddd3f994c933e4ad98b99d9a556efa0e2fe942" + integrity sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg== + dependencies: + semver-compare "^1.0.0" + portfinder@^1.0.26: version "1.0.28" resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.28.tgz#67c4622852bd5374dd1dd900f779f53462fac778" @@ -13024,6 +13404,11 @@ postgres-interval@^1.1.0: dependencies: xtend "^4.0.0" +prelude-ls@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" + integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== + prelude-ls@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" @@ -13091,6 +13476,11 @@ process@^0.11.10: resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI= +progress@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" + integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== + promise-inflight@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" @@ -13701,6 +14091,11 @@ regexp.prototype.flags@^1.2.0: call-bind "^1.0.2" define-properties "^1.1.3" +regexpp@^3.1.0, regexpp@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" + integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== + regexpu-core@^4.7.0, regexpu-core@^4.7.1: version "4.7.1" resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.7.1.tgz#2dea5a9a07233298fbf0db91fa9abc4c6e0f8ad6" @@ -13984,6 +14379,14 @@ restore-cursor@^2.0.0: onetime "^2.0.0" signal-exit "^3.0.2" +restore-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" + integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== + dependencies: + onetime "^5.1.0" + signal-exit "^3.0.2" + ret@~0.1.10: version "0.1.15" resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" @@ -14119,7 +14522,7 @@ run-queue@^1.0.0, run-queue@^1.0.3: dependencies: aproba "^1.1.1" -rxjs@^6.4.0, rxjs@^6.6.3: +rxjs@^6.4.0, rxjs@^6.6.3, rxjs@^6.6.7: version "6.6.7" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ== @@ -14241,6 +14644,11 @@ selfsigned@^1.10.8: dependencies: node-forge "^0.10.0" +semver-compare@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc" + integrity sha1-De4hahyUGrN+nvsXiPavxf9VN/w= + semver-diff@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-3.1.1.tgz#05f77ce59f325e00e2706afd67bb506ddb1ca32b" @@ -14263,7 +14671,7 @@ semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -semver@^7.3.2, semver@^7.3.4, semver@^7.3.5: +semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5: version "7.3.5" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== @@ -14510,6 +14918,24 @@ slash@^3.0.0: resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== +slice-ansi@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-3.0.0.tgz#31ddc10930a1b7e0b67b08c96c2f49b77a789787" + integrity sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ== + dependencies: + ansi-styles "^4.0.0" + astral-regex "^2.0.0" + is-fullwidth-code-point "^3.0.0" + +slice-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" + integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ== + dependencies: + ansi-styles "^4.0.0" + astral-regex "^2.0.0" + is-fullwidth-code-point "^3.0.0" + sliced@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/sliced/-/sliced-1.0.1.tgz#0b3a662b5d04c3177b1926bea82b03f837a2ef41" @@ -14834,6 +15260,11 @@ strict-uri-encode@^2.0.0: resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546" integrity sha1-ucczDHBChi9rFC3CdLvMWGbONUY= +string-argv@0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.1.tgz#95e2fbec0427ae19184935f816d74aaa4c5c19da" + integrity sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg== + string-hash@^1.1.1: version "1.1.3" resolved "https://registry.yarnpkg.com/string-hash/-/string-hash-1.1.3.tgz#e8aafc0ac1855b4666929ed7dd1275df5d6c811b" @@ -14912,7 +15343,7 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -stringify-object@^3.3.0: +stringify-object@3.3.0, stringify-object@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/stringify-object/-/stringify-object-3.3.0.tgz#703065aefca19300d3ce88af4f5b3956d7556629" integrity sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw== @@ -15000,6 +15431,11 @@ strip-indent@^3.0.0: dependencies: min-indent "^1.0.0" +strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + strip-json-comments@~2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" @@ -15034,6 +15470,13 @@ stylehacks@^5.0.1: browserslist "^4.16.0" postcss-selector-parser "^6.0.4" +supports-color@8.1.1, supports-color@^8.0.0, supports-color@^8.1.0: + version "8.1.1" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== + dependencies: + has-flag "^4.0.0" + supports-color@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" @@ -15060,13 +15503,6 @@ supports-color@^7.0.0, supports-color@^7.1.0: dependencies: has-flag "^4.0.0" -supports-color@^8.0.0, supports-color@^8.1.0: - version "8.1.1" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" - integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== - dependencies: - has-flag "^4.0.0" - supports-hyperlinks@^2.0.0, supports-hyperlinks@^2.1.0: version "2.2.0" resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz#4f77b42488765891774b70c79babd87f9bd594bb" @@ -15299,7 +15735,7 @@ through2@^4.0.0: dependencies: readable-stream "3" -through@2, "through@>=2.2.7 <3", through@^2.3.4, through@^2.3.6: +through@2, "through@>=2.2.7 <3", through@^2.3.4, through@^2.3.6, through@^2.3.8: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= @@ -15467,7 +15903,7 @@ tslib@2.0.1: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.0.1.tgz#410eb0d113e5b6356490eec749603725b021b43e" integrity sha512-SgIkNheinmEBgx1IUNirK0TUD4X9yjjBRTqqjggWCU3pUEqIk3/Uwl3yRixYKT6WjQuGiwDv4NomL3wqRCj+CQ== -tslib@^1, tslib@^1.9.0, tslib@^1.9.3: +tslib@^1, tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: version "1.14.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== @@ -15482,6 +15918,13 @@ tslib@^2.0.3, tslib@^2.1.0, tslib@^2.2.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.0.tgz#803b8cdab3e12ba581a4ca41c8839bbb0dacb09e" integrity sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg== +tsutils@^3.21.0: + version "3.21.0" + resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" + integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== + dependencies: + tslib "^1.8.1" + tunnel-agent@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" @@ -15494,6 +15937,13 @@ tweetnacl@^0.14.3, tweetnacl@~0.14.0: resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= +type-check@^0.4.0, type-check@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" + integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== + dependencies: + prelude-ls "^1.2.1" + type-check@~0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" @@ -15918,6 +16368,11 @@ uuid@^8.3.0: resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== +v8-compile-cache@^2.0.3: + version "2.3.0" + resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" + integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA== + v8-to-istanbul@^7.0.0: version "7.1.2" resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-7.1.2.tgz#30898d1a7fa0c84d225a2c1434fb958f290883c1" @@ -16315,7 +16770,7 @@ windows-release@^3.1.0: dependencies: execa "^1.0.0" -word-wrap@~1.2.3: +word-wrap@^1.2.3, word-wrap@~1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== From 442d4ff3c985e9f67d50d333863d266d01426f9b Mon Sep 17 00:00:00 2001 From: Sebastian Rindom Date: Tue, 12 Oct 2021 22:10:36 +0200 Subject: [PATCH 25/43] fix: correct path --- .husky/pre-commit | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.husky/pre-commit b/.husky/pre-commit index 4b69f33f48..80160b183b 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,4 +1,4 @@ #!/bin/sh . "$(dirname "$0")/_/husky.sh" -lint-staged || node scripts/on-lint-error.js +node_modules/.bin/lint-staged || node scripts/on-lint-error.js From 43a2b07866aab722ddb8dc24fe33d4afb7df1d1c Mon Sep 17 00:00:00 2001 From: Sebastian Rindom Date: Tue, 12 Oct 2021 22:13:52 +0200 Subject: [PATCH 26/43] fix: example --- .eslintignore | 1 - packages/medusa/src/services/auth.js | 6 ++++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.eslintignore b/.eslintignore index 563f1ca420..7e100863a6 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,4 +1,3 @@ -/packages/medusa/src/services/auth.js /packages/medusa/src/services/cart.js /packages/medusa/src/services/claim-item.js /packages/medusa/src/services/claim.js diff --git a/packages/medusa/src/services/auth.js b/packages/medusa/src/services/auth.js index 3216886c5d..364524843d 100644 --- a/packages/medusa/src/services/auth.js +++ b/packages/medusa/src/services/auth.js @@ -3,7 +3,7 @@ import { BaseService } from "medusa-interfaces" /** * Can authenticate a user based on email password combination - * @implements BaseService + * @extends BaseService */ class AuthService extends BaseService { constructor({ userService, customerService }) { @@ -47,7 +47,9 @@ class AuthService extends BaseService { success: true, user, } - } catch (error) {} + } catch (error) { + // ignore + } } try { From 751d1edd62df8d48510748271ba33223110b109e Mon Sep 17 00:00:00 2001 From: Sebastian Rindom Date: Tue, 12 Oct 2021 22:34:09 +0200 Subject: [PATCH 27/43] fix: update circleci node --- .circleci/config.yml | 16 ++++++++-------- .eslintignore | 4 ++++ 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index a5ca486edf..8dd22fa444 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -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: diff --git a/.eslintignore b/.eslintignore index 7e100863a6..a800e6f6c3 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,3 +1,5 @@ +# FILES TODO + /packages/medusa/src/services/cart.js /packages/medusa/src/services/claim-item.js /packages/medusa/src/services/claim.js @@ -50,6 +52,8 @@ /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 From 06ee5df9a0d56d52ca89611905c202281e26dc73 Mon Sep 17 00:00:00 2001 From: Rachel Date: Wed, 13 Oct 2021 00:48:41 -0700 Subject: [PATCH 28/43] fix: make /packages/medusa/src/services/gift-card.js pass eslint (#533) --- .eslintignore | 1 - packages/medusa/src/services/gift-card.js | 19 +++++++++++-------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/.eslintignore b/.eslintignore index a800e6f6c3..0b416cc9f8 100644 --- a/.eslintignore +++ b/.eslintignore @@ -9,7 +9,6 @@ /packages/medusa/src/services/event-bus.js /packages/medusa/src/services/fulfillment-provider.js /packages/medusa/src/services/fulfillment.js -/packages/medusa/src/services/gift-card.js /packages/medusa/src/services/idempotency-key.js /packages/medusa/src/services/inventory.js /packages/medusa/src/services/line-item.js diff --git a/packages/medusa/src/services/gift-card.js b/packages/medusa/src/services/gift-card.js index 3762ce36f5..b96372d223 100644 --- a/packages/medusa/src/services/gift-card.js +++ b/packages/medusa/src/services/gift-card.js @@ -5,7 +5,7 @@ import { Brackets } from "typeorm" /** * Provides layer to manipulate gift cards. - * @implements BaseService + * @extends BaseService */ class GiftCardService extends BaseService { static Events = { @@ -101,7 +101,7 @@ class GiftCardService extends BaseService { .select(["gift_card.id"]) .where(where) .andWhere( - new Brackets(qb => { + new Brackets((qb) => { return qb .where(`gift_card.code ILIKE :q`, { q: `%${q}%` }) .orWhere(`display_id::varchar(255) ILIKE :dId`, { dId: `${q}` }) @@ -111,14 +111,14 @@ class GiftCardService extends BaseService { return giftCardRepo.findWithRelations( rels, - raw.map(i => i.id) + raw.map((i) => i.id) ) } return giftCardRepo.findWithRelations(rels, query) } async createTransaction(data) { - return this.atomicPhase_(async manager => { + return this.atomicPhase_(async (manager) => { const gctRepo = manager.getCustomRepository(this.giftCardTransactionRepo_) const created = gctRepo.create(data) const saved = await gctRepo.save(created) @@ -132,7 +132,7 @@ class GiftCardService extends BaseService { * @return {Promise} the result of the create operation */ async create(giftCard) { - return this.atomicPhase_(async manager => { + return this.atomicPhase_(async (manager) => { const giftCardRepo = manager.getCustomRepository(this.giftCardRepository_) if (!giftCard.region_id) { @@ -169,6 +169,7 @@ class GiftCardService extends BaseService { /** * Gets a gift card by id. * @param {string} giftCardId - id of gift card to retrieve + * @param {object} config - optional values to include with gift card query * @return {Promise} the gift card */ async retrieve(giftCardId, config = {}) { @@ -244,7 +245,7 @@ class GiftCardService extends BaseService { * @return {Promise} the result of the update operation */ async update(giftCardId, update) { - return this.atomicPhase_(async manager => { + return this.atomicPhase_(async (manager) => { const giftCardRepo = manager.getCustomRepository(this.giftCardRepository_) const giftCard = await this.retrieve(giftCardId) @@ -285,12 +286,14 @@ class GiftCardService extends BaseService { * @return {Promise} the result of the delete operation */ async delete(giftCardId) { - return this.atomicPhase_(async manager => { + return this.atomicPhase_(async (manager) => { const giftCardRepo = manager.getCustomRepository(this.giftCardRepository_) const giftCard = await giftCardRepo.findOne({ where: { id: giftCardId } }) - if (!giftCard) return Promise.resolve() + if (!giftCard) { + return Promise.resolve() + } await giftCardRepo.softRemove(giftCard) From c0e947f47a009dcdf189bd4564485385b3019045 Mon Sep 17 00:00:00 2001 From: Sebastian Mateos Nicolajsen <80953876+sebastiannicolajsen@users.noreply.github.com> Date: Wed, 13 Oct 2021 12:05:58 +0200 Subject: [PATCH 29/43] fix: update product images (#494) --- .../api/__tests__/admin/product.js | 23 +++++++++++++++++++ packages/medusa/src/services/product.js | 6 ++--- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/integration-tests/api/__tests__/admin/product.js b/integration-tests/api/__tests__/admin/product.js index 1e398b65c5..06ff71aee5 100644 --- a/integration-tests/api/__tests__/admin/product.js +++ b/integration-tests/api/__tests__/admin/product.js @@ -592,6 +592,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 +718,7 @@ describe("/admin/products", () => { ) }) }) + describe("testing for soft-deletion + uniqueness on handles, collection and variant properties", () => { beforeEach(async () => { try { diff --git a/packages/medusa/src/services/product.js b/packages/medusa/src/services/product.js index 45b7375af4..d46a5968c5 100644 --- a/packages/medusa/src/services/product.js +++ b/packages/medusa/src/services/product.js @@ -295,7 +295,7 @@ class ProductService extends BaseService { let product = productRepo.create(rest) - if (images && images.length) { + if (images) { product.images = await this.upsertImages_(images) } @@ -380,11 +380,11 @@ class ProductService extends BaseService { ...rest } = update - if (!product.thumbnail && !update.thumbnail && images && images.length) { + if (!product.thumbnail && !update.thumbnail && images?.length) { product.thumbnail = images[0] } - if (images && images.length) { + if (images) { product.images = await this.upsertImages_(images) } From 5ef2a3fbcb108c8d49b7754ea14ac890af643950 Mon Sep 17 00:00:00 2001 From: pKorsholm <88927411+pKorsholm@users.noreply.github.com> Date: Wed, 13 Oct 2021 16:01:59 +0200 Subject: [PATCH 30/43] feat: Product filtering (#439) --- .../admin/__snapshots__/auth.js.snap | 3 +- .../admin/__snapshots__/product.js.snap | 193 +++++++++++ .../api/__tests__/admin/product.js | 320 +++++++++++++++++- .../store/__snapshots__/auth.js.snap | 4 +- integration-tests/api/__tests__/store/cart.js | 18 +- integration-tests/api/helpers/cart-seeder.js | 22 ++ .../api/helpers/product-seeder.js | 72 ++++ packages/medusa-core-utils/src/validator.js | 29 ++ .../src/api/routes/admin/products/index.js | 21 +- .../routes/admin/products/list-products.js | 34 +- packages/medusa/src/repositories/product.ts | 22 +- 11 files changed, 707 insertions(+), 31 deletions(-) diff --git a/integration-tests/api/__tests__/admin/__snapshots__/auth.js.snap b/integration-tests/api/__tests__/admin/__snapshots__/auth.js.snap index 3a3a924b4b..0824e3c74c 100644 --- a/integration-tests/api/__tests__/admin/__snapshots__/auth.js.snap +++ b/integration-tests/api/__tests__/admin/__snapshots__/auth.js.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`/admin/discounts creates admin session correctly 1`] = ` +exports[`/admin/auth creates admin session correctly 1`] = ` Object { "api_token": "test_token", "created_at": Any, @@ -13,4 +13,3 @@ Object { "updated_at": Any, } `; - diff --git a/integration-tests/api/__tests__/admin/__snapshots__/product.js.snap b/integration-tests/api/__tests__/admin/__snapshots__/product.js.snap index 8fc7342918..0e103ae418 100644 --- a/integration-tests/api/__tests__/admin/__snapshots__/product.js.snap +++ b/integration-tests/api/__tests__/admin/__snapshots__/product.js.snap @@ -380,5 +380,198 @@ Array [ "weight": null, "width": null, }, + Object { + "collection": Any, + "collection_id": "test-collection1", + "created_at": Any, + "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, + "origin_country": null, + "profile_id": StringMatching /\\^sp_\\*/, + "status": "draft", + "subtitle": null, + "tags": Any, + "thumbnail": null, + "title": "Test product filtering 3", + "type": Any, + "type_id": "test-type", + "updated_at": Any, + "variants": Any, + "weight": null, + "width": null, + }, + Object { + "collection": Any, + "collection_id": "test-collection1", + "created_at": Any, + "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, + "origin_country": null, + "profile_id": StringMatching /\\^sp_\\*/, + "status": "proposed", + "subtitle": null, + "tags": Any, + "thumbnail": null, + "title": "Test product filtering 1", + "type": Any, + "type_id": "test-type", + "updated_at": Any, + "variants": Any, + "weight": null, + "width": null, + }, + Object { + "collection": Any, + "collection_id": "test-collection2", + "created_at": Any, + "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, + "origin_country": null, + "profile_id": StringMatching /\\^sp_\\*/, + "status": "published", + "subtitle": null, + "tags": Any, + "thumbnail": null, + "title": "Test product filtering 2", + "type": Any, + "type_id": "test-type", + "updated_at": Any, + "variants": Any, + "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, + "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, + "deleted_at": null, + "id": StringMatching /\\^opt_\\*/, + "metadata": null, + "product_id": StringMatching /\\^prod_\\*/, + "title": "Denominations", + "updated_at": Any, + }, + ], + "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, + "variants": Array [ + Object { + "allow_backorder": false, + "barcode": null, + "created_at": Any, + "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, + "deleted_at": null, + "id": StringMatching /\\^opt_\\*/, + "metadata": null, + "option_id": StringMatching /\\^opt_\\*/, + "updated_at": Any, + "value": "100", + "variant_id": StringMatching /\\^variant_\\*/, + }, + ], + "origin_country": null, + "prices": Array [ + Object { + "amount": 100, + "created_at": Any, + "currency_code": "usd", + "deleted_at": null, + "id": Any, + "region_id": null, + "sale_amount": null, + "updated_at": Any, + "variant_id": StringMatching /\\^variant_\\*/, + }, + ], + "product_id": StringMatching /\\^prod_\\*/, + "sku": null, + "title": "Test variant", + "upc": null, + "updated_at": Any, + "weight": null, + "width": null, + }, + ], + "weight": null, + "width": null, + }, ] `; diff --git a/integration-tests/api/__tests__/admin/product.js b/integration-tests/api/__tests__/admin/product.js index 06ff71aee5..c40f92a12b 100644 --- a/integration-tests/api/__tests__/admin/product.js +++ b/integration-tests/api/__tests__/admin/product.js @@ -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), + }, ]) }) }) diff --git a/integration-tests/api/__tests__/store/__snapshots__/auth.js.snap b/integration-tests/api/__tests__/store/__snapshots__/auth.js.snap index aaae35ba9c..c514df7ec3 100644 --- a/integration-tests/api/__tests__/store/__snapshots__/auth.js.snap +++ b/integration-tests/api/__tests__/store/__snapshots__/auth.js.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`/admin/discounts creates store session correctly 1`] = ` +exports[`/admin/auth creates store session correctly 1`] = ` Object { "billing_address_id": null, "created_at": Any, @@ -15,4 +15,4 @@ Object { "phone": "12345678", "updated_at": Any, } -`; +`; \ No newline at end of file diff --git a/integration-tests/api/__tests__/store/cart.js b/integration-tests/api/__tests__/store/cart.js index 00177015de..081e8b4466 100644 --- a/integration-tests/api/__tests__/store/cart.js +++ b/integration-tests/api/__tests__/store/cart.js @@ -152,16 +152,16 @@ describe("/store/carts", () => { expect.assertions(2) const api = useApi() - try { - await api.post("/store/carts/test-cart", { - discounts: [{ code: "CREATED" }], + let response = 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" + ) }) - } 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 () => { diff --git a/integration-tests/api/helpers/cart-seeder.js b/integration-tests/api/helpers/cart-seeder.js index f7e499368a..d4b054873f 100644 --- a/integration-tests/api/helpers/cart-seeder.js +++ b/integration-tests/api/helpers/cart-seeder.js @@ -108,6 +108,28 @@ module.exports = async (connection, data = {}) => { 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", diff --git a/integration-tests/api/helpers/product-seeder.js b/integration-tests/api/helpers/product-seeder.js index 0f93673c18..8cea9fadb6 100644 --- a/integration-tests/api/helpers/product-seeder.js +++ b/integration-tests/api/helpers/product-seeder.js @@ -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) } diff --git a/packages/medusa-core-utils/src/validator.js b/packages/medusa-core-utils/src/validator.js index 43fe68f5cd..243b19d20f 100644 --- a/packages/medusa-core-utils/src/validator.js +++ b/packages/medusa-core-utils/src/validator.js @@ -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 diff --git a/packages/medusa/src/api/routes/admin/products/index.js b/packages/medusa/src/api/routes/admin/products/index.js index 8125eebd52..e32e13d168 100644 --- a/packages/medusa/src/api/routes/admin/products/index.js +++ b/packages/medusa/src/api/routes/admin/products/index.js @@ -46,7 +46,11 @@ export default app => { ) route.get("/:id", middlewares.wrap(require("./get-product").default)) - route.get("/", middlewares.wrap(require("./list-products").default)) + route.get( + "/", + middlewares.normalizeQuery(), + middlewares.wrap(require("./list-products").default) + ) return app } @@ -121,3 +125,18 @@ export const allowedRelations = [ "type", "collection", ] + +export const filterableFields = [ + "id", + "status", + "collection_id", + "tags", + "title", + "description", + "handle", + "is_giftcard", + "type", + "created_at", + "updated_at", + "deleted_at", +] diff --git a/packages/medusa/src/api/routes/admin/products/list-products.js b/packages/medusa/src/api/routes/admin/products/list-products.js index bd4873a82d..ea309a43ab 100644 --- a/packages/medusa/src/api/routes/admin/products/list-products.js +++ b/packages/medusa/src/api/routes/admin/products/list-products.js @@ -1,6 +1,6 @@ import _ from "lodash" import { MedusaError, Validator } from "medusa-core-utils" -import { defaultFields, defaultRelations } from "./" +import { defaultFields, defaultRelations, filterableFields } from "./" /** * @oas [get] /products @@ -31,6 +31,17 @@ import { defaultFields, defaultRelations } from "./" * $ref: "#/components/schemas/product" */ export default async (req, res) => { + const schema = Validator.productFilter() + + const { value, error } = schema.validate(req.query) + + if (error) { + throw new MedusaError( + MedusaError.Types.INVALID_DATA, + JSON.stringify(error.details) + ) + } + try { const productService = req.scope.resolve("productService") @@ -53,21 +64,16 @@ export default async (req, res) => { expandFields = req.query.expand.split(",") } - if ("is_giftcard" in req.query) { - selector.is_giftcard = req.query.is_giftcard === "true" + for (const k of filterableFields) { + if (k in value) { + selector[k] = value[k] + } } - if ("status" in req.query) { - const schema = Validator.array() - .items( - Validator.string().valid("proposed", "draft", "published", "rejected") - ) - .single() - - const { value, error } = schema.validate(req.query.status) - - if (value && !error) { - selector.status = value + if (selector.status?.indexOf("null") > -1) { + selector.status.splice(selector.status.indexOf("null"), 1) + if (selector.status.length === 0) { + delete selector.status } } diff --git a/packages/medusa/src/repositories/product.ts b/packages/medusa/src/repositories/product.ts index c88f090fb3..f686ef80d2 100644 --- a/packages/medusa/src/repositories/product.ts +++ b/packages/medusa/src/repositories/product.ts @@ -15,7 +15,27 @@ export class ProductRepository extends Repository { if (Array.isArray(idsOrOptionsWithoutRelations)) { entities = await this.findByIds(idsOrOptionsWithoutRelations) } else { - entities = await this.find(idsOrOptionsWithoutRelations) + // Since tags are in a one-to-many realtion they cant be included in a + // regular query, to solve this add the join on tags seperately if + // the query exists + const tags = idsOrOptionsWithoutRelations.where.tags + delete idsOrOptionsWithoutRelations.where.tags + let qb = this.createQueryBuilder("product") + .select(["product.id"]) + .where(idsOrOptionsWithoutRelations.where) + .skip(idsOrOptionsWithoutRelations.skip) + .take(idsOrOptionsWithoutRelations.take) + + if (tags) { + qb = qb + .leftJoinAndSelect("product.tags", "tags") + .andWhere( + `tags.id IN (:...ids)`, { ids: tags._value} + ) + } + + entities = await qb + .getMany() } const entitiesIds = entities.map(({ id }) => id) From 8d7d0e4b051a394ada9f10bc9e287a0c6347331a Mon Sep 17 00:00:00 2001 From: Sebastian Rindom Date: Wed, 13 Oct 2021 16:12:11 +0200 Subject: [PATCH 31/43] fix: add prettier rules to eslint --- .eslintrc.js | 1 + jest-transformer.js | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 47bc35d527..c40421efa9 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -9,6 +9,7 @@ module.exports = { plugins: [`eslint-plugin-prettier`], extends: [`eslint:recommended`, `google`, `eslint-config-prettier`], rules: { + "prettier/prettier": `error`, curly: [2, `all`], "new-cap": `off`, "require-jsdoc": `off`, diff --git a/jest-transformer.js b/jest-transformer.js index a5f750c9d6..f6d0fb7336 100644 --- a/jest-transformer.js +++ b/jest-transformer.js @@ -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, -}); +}) From 7c0700e03f83feb88f29333fe87aa1edd3cd7db4 Mon Sep 17 00:00:00 2001 From: Sebastian Rindom Date: Wed, 13 Oct 2021 17:56:10 +0200 Subject: [PATCH 32/43] hotfix(medusa-plugin-restock-notification): add delay for event trigger (#544) * fix: add delay before triggering email * fix: non-blocking delay --- .../__tests__/restock-notification.js | 61 +++++++++++++++++-- .../src/services/restock-notification.js | 15 +++-- 2 files changed, 63 insertions(+), 13 deletions(-) diff --git a/packages/medusa-plugin-restock-notification/src/services/__tests__/restock-notification.js b/packages/medusa-plugin-restock-notification/src/services/__tests__/restock-notification.js index 328873b17b..caa4b924bc 100644 --- a/packages/medusa-plugin-restock-notification/src/services/__tests__/restock-notification.js +++ b/packages/medusa-plugin-restock-notification/src/services/__tests__/restock-notification.js @@ -135,6 +135,57 @@ describe("RestockNotificationService", () => { }) describe("triggerRestock", () => { + afterEach(() => { + jest.useRealTimers() + }) + + it("trigger delay default to 0", async () => { + const restockNotiService = new RestockNotificationService({ + manager: MockManager, + productVariantService: ProductVariantService, + restockNotificationModel: RestockNotificationModel, + eventBusService: EventBusService, + }) + + restockNotiService.restockExecute_ = jest.fn() + + jest.clearAllMocks() + jest.useFakeTimers() + + restockNotiService.triggerRestock("variant_test") + + jest.runAllTimers() + + expect(setTimeout).toHaveBeenCalledTimes(1) + expect(setTimeout).toHaveBeenCalledWith(expect.any(Function), 0) + }) + + it("trigger delay 10", async () => { + const restockNotiService = new RestockNotificationService( + { + manager: MockManager, + productVariantService: ProductVariantService, + restockNotificationModel: RestockNotificationModel, + eventBusService: EventBusService, + }, + { trigger_delay: 10 } + ) + + restockNotiService.restockExecute_ = jest.fn() + + jest.clearAllMocks() + jest.useFakeTimers() + + restockNotiService.triggerRestock("variant_test") + + jest.runAllTimers() + + expect(setTimeout).toHaveBeenCalledTimes(1) + expect(setTimeout).toHaveBeenCalledWith(expect.any(Function), 10) + }) + }) + + describe("restockExecute_", () => { const restockNotiService = new RestockNotificationService({ manager: MockManager, productVariantService: ProductVariantService, @@ -145,20 +196,20 @@ describe("RestockNotificationService", () => { it("non-existing noti does nothing", async () => { jest.clearAllMocks() - await expect(restockNotiService.triggerRestock("variant_test")).resolves + await expect(restockNotiService.restockExecute_("variant_test")).resolves }) it("existing noti but out of stock does nothing", async () => { jest.clearAllMocks() - await expect(restockNotiService.triggerRestock("variant_outofstock")) + await expect(restockNotiService.restockExecute_("variant_outofstock")) .resolves }) it("existing noti emits and deletes", async () => { jest.clearAllMocks() - await restockNotiService.triggerRestock("variant_1234") + await restockNotiService.restockExecute_("variant_1234") expect(EventBusService.emit).toHaveBeenCalledTimes(1) expect(EventBusService.emit).toHaveBeenCalledWith( @@ -187,7 +238,7 @@ describe("RestockNotificationService", () => { { inventory_required: 5 } ) - await service.triggerRestock("variant_1234") + await service.restockExecute_("variant_1234") expect(EventBusService.emit).toHaveBeenCalledTimes(1) expect(EventBusService.emit).toHaveBeenCalledWith( @@ -214,7 +265,7 @@ describe("RestockNotificationService", () => { { inventory_required: 5 } ) - await service.triggerRestock("variant_low_inventory") + await service.restockExecute_("variant_low_inventory") expect(EventBusService.emit).toHaveBeenCalledTimes(0) expect(RestockNotificationModel.delete).toHaveBeenCalledTimes(0) diff --git a/packages/medusa-plugin-restock-notification/src/services/restock-notification.js b/packages/medusa-plugin-restock-notification/src/services/restock-notification.js index 4244742379..b0de97286a 100644 --- a/packages/medusa-plugin-restock-notification/src/services/restock-notification.js +++ b/packages/medusa-plugin-restock-notification/src/services/restock-notification.js @@ -108,16 +108,15 @@ class RestockNotificationService extends BaseService { * and emits a restocked event to the event bus. After successful emission the * restock notification is deleted. * @param {string} variantId - the variant id to trigger restock for - * @return {Promise} The resulting restock notification + * @return The resulting restock notification */ - async triggerRestock(variantId) { - if (this.options_?.trigger_delay) { - await new Promise((resolve) => - setTimeout(resolve, this.options_.trigger_delay) - ) - } + triggerRestock(variantId) { + const delay = this.options_?.trigger_delay ?? 0 + setTimeout(() => this.restockExecute_(variantId), delay) + } - return this.atomicPhase_(async (manager) => { + async restockExecute_(variantId) { + return await this.atomicPhase_(async (manager) => { const restockRepo = manager.getRepository(this.restockNotificationModel_) const existing = await this.retrieve(variantId) From 10f1d9df5c184ff723c71dbc618bc574e94228d9 Mon Sep 17 00:00:00 2001 From: Luke Nguyen <59778930+mqnguyen5@users.noreply.github.com> Date: Wed, 13 Oct 2021 12:07:28 -0400 Subject: [PATCH 33/43] fix: Make packages/medusa/src/services/transaction.js pass linting (#534) --- .eslintignore | 1 - packages/medusa/src/services/transaction.js | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/.eslintignore b/.eslintignore index 0b416cc9f8..cbad3adfb9 100644 --- a/.eslintignore +++ b/.eslintignore @@ -31,7 +31,6 @@ /packages/medusa/src/services/swap.js /packages/medusa/src/services/system-payment-provider.js /packages/medusa/src/services/totals.js -/packages/medusa/src/services/transaction.js /packages/medusa/src/services/user.js /packages/medusa/src/subscribers/notification.js diff --git a/packages/medusa/src/services/transaction.js b/packages/medusa/src/services/transaction.js index 5af02ae3b0..7f57599ec6 100644 --- a/packages/medusa/src/services/transaction.js +++ b/packages/medusa/src/services/transaction.js @@ -1,9 +1,8 @@ import { BaseService } from "medusa-interfaces" import mongoose from "mongoose" -import _ from "lodash" class TransactionService extends BaseService { - constructor({}) { + constructor() { super() } From cf5b405a56082f621f5d48eeb9375aeaad5071bb Mon Sep 17 00:00:00 2001 From: Sergiy Fomin <65659492+FominSergiy@users.noreply.github.com> Date: Wed, 13 Oct 2021 13:45:50 -0400 Subject: [PATCH 34/43] fixed linting errors on packages/medusa/src/services/user.js #531 (#543) * fixed linting errors on packages/medusa/src/services/user.js #531 * fix: resolving a few issues as raised in the PR, issue: #531 Co-authored-by: Sergiy Fomin --- .eslintignore | 1 - .eslintrc.js | 1 - jest-transformer.js | 4 ++-- packages/medusa/src/services/gift-card.js | 19 ++++++++----------- packages/medusa/src/services/transaction.js | 3 ++- packages/medusa/src/services/user.js | 21 ++++++++++++++------- 6 files changed, 26 insertions(+), 23 deletions(-) diff --git a/.eslintignore b/.eslintignore index cbad3adfb9..745932b48d 100644 --- a/.eslintignore +++ b/.eslintignore @@ -31,7 +31,6 @@ /packages/medusa/src/services/swap.js /packages/medusa/src/services/system-payment-provider.js /packages/medusa/src/services/totals.js -/packages/medusa/src/services/user.js /packages/medusa/src/subscribers/notification.js /packages/medusa/src/subscribers/order.js diff --git a/.eslintrc.js b/.eslintrc.js index c40421efa9..47bc35d527 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -9,7 +9,6 @@ module.exports = { plugins: [`eslint-plugin-prettier`], extends: [`eslint:recommended`, `google`, `eslint-config-prettier`], rules: { - "prettier/prettier": `error`, curly: [2, `all`], "new-cap": `off`, "require-jsdoc": `off`, diff --git a/jest-transformer.js b/jest-transformer.js index f6d0fb7336..a5f750c9d6 100644 --- a/jest-transformer.js +++ b/jest-transformer.js @@ -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, -}) +}); diff --git a/packages/medusa/src/services/gift-card.js b/packages/medusa/src/services/gift-card.js index b96372d223..3762ce36f5 100644 --- a/packages/medusa/src/services/gift-card.js +++ b/packages/medusa/src/services/gift-card.js @@ -5,7 +5,7 @@ import { Brackets } from "typeorm" /** * Provides layer to manipulate gift cards. - * @extends BaseService + * @implements BaseService */ class GiftCardService extends BaseService { static Events = { @@ -101,7 +101,7 @@ class GiftCardService extends BaseService { .select(["gift_card.id"]) .where(where) .andWhere( - new Brackets((qb) => { + new Brackets(qb => { return qb .where(`gift_card.code ILIKE :q`, { q: `%${q}%` }) .orWhere(`display_id::varchar(255) ILIKE :dId`, { dId: `${q}` }) @@ -111,14 +111,14 @@ class GiftCardService extends BaseService { return giftCardRepo.findWithRelations( rels, - raw.map((i) => i.id) + raw.map(i => i.id) ) } return giftCardRepo.findWithRelations(rels, query) } async createTransaction(data) { - return this.atomicPhase_(async (manager) => { + return this.atomicPhase_(async manager => { const gctRepo = manager.getCustomRepository(this.giftCardTransactionRepo_) const created = gctRepo.create(data) const saved = await gctRepo.save(created) @@ -132,7 +132,7 @@ class GiftCardService extends BaseService { * @return {Promise} the result of the create operation */ async create(giftCard) { - return this.atomicPhase_(async (manager) => { + return this.atomicPhase_(async manager => { const giftCardRepo = manager.getCustomRepository(this.giftCardRepository_) if (!giftCard.region_id) { @@ -169,7 +169,6 @@ class GiftCardService extends BaseService { /** * Gets a gift card by id. * @param {string} giftCardId - id of gift card to retrieve - * @param {object} config - optional values to include with gift card query * @return {Promise} the gift card */ async retrieve(giftCardId, config = {}) { @@ -245,7 +244,7 @@ class GiftCardService extends BaseService { * @return {Promise} the result of the update operation */ async update(giftCardId, update) { - return this.atomicPhase_(async (manager) => { + return this.atomicPhase_(async manager => { const giftCardRepo = manager.getCustomRepository(this.giftCardRepository_) const giftCard = await this.retrieve(giftCardId) @@ -286,14 +285,12 @@ class GiftCardService extends BaseService { * @return {Promise} the result of the delete operation */ async delete(giftCardId) { - return this.atomicPhase_(async (manager) => { + return this.atomicPhase_(async manager => { const giftCardRepo = manager.getCustomRepository(this.giftCardRepository_) const giftCard = await giftCardRepo.findOne({ where: { id: giftCardId } }) - if (!giftCard) { - return Promise.resolve() - } + if (!giftCard) return Promise.resolve() await giftCardRepo.softRemove(giftCard) diff --git a/packages/medusa/src/services/transaction.js b/packages/medusa/src/services/transaction.js index 7f57599ec6..5af02ae3b0 100644 --- a/packages/medusa/src/services/transaction.js +++ b/packages/medusa/src/services/transaction.js @@ -1,8 +1,9 @@ import { BaseService } from "medusa-interfaces" import mongoose from "mongoose" +import _ from "lodash" class TransactionService extends BaseService { - constructor() { + constructor({}) { super() } diff --git a/packages/medusa/src/services/user.js b/packages/medusa/src/services/user.js index 52a55f6e01..940b49a7ce 100644 --- a/packages/medusa/src/services/user.js +++ b/packages/medusa/src/services/user.js @@ -6,7 +6,7 @@ import { BaseService } from "medusa-interfaces" /** * Provides layer to manipulate users. - * @implements BaseService + * @extends BaseService */ class UserService extends BaseService { static Events = { @@ -75,6 +75,7 @@ class UserService extends BaseService { * Gets a user by id. * Throws in case of DB Error and if user was not found. * @param {string} userId - the id of the user to get. + * @param {Object} config - query configs * @return {Promise} the user document. */ async retrieve(userId, config = {}) { @@ -99,6 +100,7 @@ class UserService extends BaseService { * Gets a user by api token. * Throws in case of DB Error and if user was not found. * @param {string} apiToken - the token of the user to get. + * @param {string[]} relations - relations to include with the user * @return {Promise} the user document. */ async retrieveByApiToken(apiToken, relations = []) { @@ -123,6 +125,7 @@ class UserService extends BaseService { * Gets a user by email. * Throws in case of DB Error and if user was not found. * @param {string} email - the email of the user to get. + * @param {Object} config - query config * @return {Promise} the user document. */ async retrieveByEmail(email, config = {}) { @@ -144,7 +147,7 @@ class UserService extends BaseService { /** * Hashes a password * @param {string} password - the value to hash - * @return hashed password + * @return {string} hashed password */ async hashPassword_(password) { const buf = await Scrypt.kdf(password, { logN: 1, r: 1, p: 1 }) @@ -155,6 +158,7 @@ class UserService extends BaseService { * Creates a user with username being validated. * Fails if email is not a valid format. * @param {object} user - the user to create + * @param {string} password - user's password to hash * @return {Promise} the result of create */ async create(user, password) { @@ -177,7 +181,8 @@ class UserService extends BaseService { /** * Updates a user. - * @param {object} user - the user to create + * @param {object} userId - id of the user to update + * @param {object} update - the values to be updated on the user * @return {Promise} the result of create */ async update(userId, update) { @@ -228,7 +233,9 @@ class UserService extends BaseService { // Should not fail, if user does not exist, since delete is idempotent const user = await userRepo.findOne({ where: { id: userId } }) - if (!user) return Promise.resolve() + if (!user) { + return Promise.resolve() + } await userRepo.softRemove(user) @@ -242,7 +249,7 @@ class UserService extends BaseService { * password does not work. * @param {string} userId - the userId to set password for * @param {string} password - the old password to set - * @returns {Promise} the result of the update operation + * @return {Promise} the result of the update operation */ async setPassword_(userId, password) { return this.atomicPhase_(async manager => { @@ -270,8 +277,8 @@ class UserService extends BaseService { * The token will be signed with the users current password hash as a secret * a long side a payload with userId and the expiry time for the token, which * is always 15 minutes. - * @param {User} user - the user to reset password for - * @returns {string} the generated JSON web token + * @param {string} userId - the id of the user to reset password for + * @return {string} the generated JSON web token */ async generateResetPasswordToken(userId) { const user = await this.retrieve(userId) From fc50aacde2e59fd3cc939dcdaa2630d788b4eca2 Mon Sep 17 00:00:00 2001 From: Kasper Fabricius Kristensen <45367945+kasperkristensen@users.noreply.github.com> Date: Wed, 13 Oct 2021 19:46:44 +0200 Subject: [PATCH 35/43] fix: options relations on GET variant (#532) --- .../__snapshots__/product-variants.js.snap | 24 ++++++ integration-tests/api/package.json | 6 +- integration-tests/api/yarn.lock | 84 +++++++++++-------- .../store/variants/__tests__/get-variant.js | 2 +- .../src/api/routes/store/variants/index.js | 2 +- 5 files changed, 78 insertions(+), 40 deletions(-) diff --git a/integration-tests/api/__tests__/store/__snapshots__/product-variants.js.snap b/integration-tests/api/__tests__/store/__snapshots__/product-variants.js.snap index 073655bb2a..b6850cd492 100644 --- a/integration-tests/api/__tests__/store/__snapshots__/product-variants.js.snap +++ b/integration-tests/api/__tests__/store/__snapshots__/product-variants.js.snap @@ -17,6 +17,18 @@ Object { "material": null, "metadata": null, "mid_code": null, + "options": Array [ + Object { + "created_at": "2021-10-13T08:02:41.668Z", + "deleted_at": null, + "id": "test-variant-option", + "metadata": null, + "option_id": "test-option", + "updated_at": "2021-10-13T08:02:41.668Z", + "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": "2021-10-13T08:02:41.493Z", + "deleted_at": null, + "id": "test-variant-option", + "metadata": null, + "option_id": "test-option", + "updated_at": "2021-10-13T08:02:41.493Z", + "value": "Default variant", + "variant_id": "test-variant", + }, + ], "origin_country": null, "prices": Array [ Object { diff --git a/integration-tests/api/package.json b/integration-tests/api/package.json index 7abd7c9b69..5093128d5a 100644 --- a/integration-tests/api/package.json +++ b/integration-tests/api/package.json @@ -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-1634111876218", + "medusa-interfaces": "1.1.21", "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.13", "jest": "^26.6.3" } } diff --git a/integration-tests/api/yarn.lock b/integration-tests/api/yarn.lock index 2ba33d481a..15959def5a 100644 --- a/integration-tests/api/yarn.lock +++ b/integration-tests/api/yarn.lock @@ -1223,10 +1223,10 @@ "@types/yargs" "^15.0.0" chalk "^4.0.0" -"@medusajs/medusa-cli@1.1.16-dev-1631630701835": - version "1.1.16-dev-1631630701835" - resolved "http://localhost:4873/@medusajs%2fmedusa-cli/-/medusa-cli-1.1.16-dev-1631630701835.tgz#7fcb95cb9a45e0367cc5becfff7f5d1533b46b5f" - integrity sha512-UomtR8B1lBFDb3h1y060fOcWcZi812Jwt8Kgjxqtpn+aRj6Bu7+I3WJGHBVSx4VnUBINSYbtiQMpEwqVGTCKnw== +"@medusajs/medusa-cli@^1.1.18": + version "1.1.19" + resolved "http://localhost:4873/@medusajs%2fmedusa-cli/-/medusa-cli-1.1.19.tgz#65507b9694f9b51b1fce3f2f4bd601075b53ce94" + integrity sha512-DuHJBPEhAGV5Mx0P5WTaVrbkLE7CeGdA+s2u6Wnp0cMPS3N6XDv3dBUHjHXiIEUoKHWZfDLyBKV3UUA3r+M6TQ== dependencies: "@babel/polyfill" "^7.8.7" "@babel/runtime" "^7.9.6" @@ -1244,8 +1244,8 @@ is-valid-path "^0.1.1" joi-objectid "^3.0.1" meant "^1.0.1" - medusa-core-utils "1.1.20-dev-1631630701835" - medusa-telemetry "0.0.3-dev-1631630701835" + medusa-core-utils "^0.1.27" + medusa-telemetry "^0.0.5" netrc-parser "^3.1.6" open "^8.0.6" ora "^5.4.1" @@ -1259,13 +1259,13 @@ winston "^3.3.3" yargs "^15.3.1" -"@medusajs/medusa@1.1.40-dev-1631630701835": - version "1.1.40-dev-1631630701835" - resolved "http://localhost:4873/@medusajs%2fmedusa/-/medusa-1.1.40-dev-1631630701835.tgz#fa67ceda5887fd31196b3bcfd3115a9e02d68448" - integrity sha512-svPsKonuBrwRgtYod7U7ho9bN84K7N/QorMJG9+wklEO4jp6zXG+U5DQcfVAKQ00cHHe50OcnfX1ZS0kVNovYw== +"@medusajs/medusa@1.1.41-dev-1634111876218": + version "1.1.41-dev-1634111876218" + resolved "http://localhost:4873/@medusajs%2fmedusa/-/medusa-1.1.41-dev-1634111876218.tgz#7338b3b94da7ab343e969b51ac4e808284581601" + integrity sha512-4ZqZRBHhXvhx5/jDwUH3HMvrvXA+Zb1mHhLrJSsPhze5xfJ0DfmY/UnK2dgWO8f6R3UHgCJ7YISN+rWxYJ75Ww== dependencies: "@hapi/joi" "^16.1.8" - "@medusajs/medusa-cli" "1.1.16-dev-1631630701835" + "@medusajs/medusa-cli" "^1.1.18" "@types/lodash" "^4.14.168" awilix "^4.2.3" body-parser "^1.19.0" @@ -1283,11 +1283,12 @@ glob "^7.1.6" ioredis "^4.17.3" ioredis-mock "^5.6.0" + iso8601-duration "^1.3.0" joi "^17.3.0" joi-objectid "^3.0.1" jsonwebtoken "^8.5.1" - medusa-core-utils "1.1.20-dev-1631630701835" - medusa-test-utils "1.1.23-dev-1631630701835" + medusa-core-utils "^1.1.22" + medusa-test-utils "^1.1.25" morgan "^1.9.1" multer "^1.4.2" passport "^0.4.0" @@ -1932,10 +1933,10 @@ babel-preset-jest@^26.6.2: babel-plugin-jest-hoist "^26.6.2" babel-preset-current-node-syntax "^1.0.0" -babel-preset-medusa-package@1.1.13-dev-1631630701835: - version "1.1.13-dev-1631630701835" - resolved "http://localhost:4873/babel-preset-medusa-package/-/babel-preset-medusa-package-1.1.13-dev-1631630701835.tgz#5b66b3738e4904e31b2db30a6ea8e68eb0f8f641" - integrity sha512-V7sXlktlvEON7FLhxe+Y3NVe8l8DQyB5oJTryG4Bhw8y1AaUFOiQ5Vat3XuoL3qRcUSVMGL4VHw0m0O78t0PuA== +babel-preset-medusa-package@1.1.13: + version "1.1.13" + resolved "https://registry.yarnpkg.com/babel-preset-medusa-package/-/babel-preset-medusa-package-1.1.13.tgz#9dc4e64e08436fb7b3536cef0f363a535e126474" + integrity sha512-Q9t06udxwMnfwyx7gyxoUKiZj/dtYSSXBtQ+K4ntY1hzMhOK2hBBInuiTgnLQS1cxc4j+FN2oYYPCpspX/acaw== dependencies: "@babel/plugin-proposal-class-properties" "^7.12.1" "@babel/plugin-proposal-decorators" "^7.12.1" @@ -4274,6 +4275,11 @@ isexe@^2.0.0: resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= +iso8601-duration@^1.3.0: + version "1.3.0" + resolved "http://localhost:4873/iso8601-duration/-/iso8601-duration-1.3.0.tgz#29d7b69e0574e4acdee50c5e5e09adab4137ba5a" + integrity sha512-K4CiUBzo3YeWk76FuET/dQPH03WE04R94feo5TSKQCXpoXQt9E4yx2CnY737QZnSAI3PI4WlKo/zfqizGx52QQ== + isobject@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" @@ -5104,25 +5110,33 @@ media-typer@0.3.0: resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= -medusa-core-utils@1.1.20-dev-1631630701835: - version "1.1.20-dev-1631630701835" - resolved "http://localhost:4873/medusa-core-utils/-/medusa-core-utils-1.1.20-dev-1631630701835.tgz#1fa7ccd2551b7891127d4f07f708029c585f4ea8" - integrity sha512-KKBo6W1QI47Ig3KMV4UXQnQN5JilMfjR6Cx7hDNj4frJoNiWa/YKDYqUr6SmY2+iJtKetnLkrKaPsDyyhZrxcw== +medusa-core-utils@^0.1.27: + version "0.1.39" + resolved "https://registry.yarnpkg.com/medusa-core-utils/-/medusa-core-utils-0.1.39.tgz#d57816c9bd43f9a92883650c1e66add1665291df" + integrity sha512-R8+U1ile7if+nR6Cjh5exunx0ETV0OfkWUUBUpz1KmHSDv0V0CcvQqU9lcZesPFDEbu3Y2iEjsCqidVA4nG2nQ== + dependencies: + "@hapi/joi" "^16.1.8" + joi-objectid "^3.0.1" + +medusa-core-utils@^1.1.20, medusa-core-utils@^1.1.22, medusa-core-utils@^1.1.23: + version "1.1.23" + resolved "https://registry.yarnpkg.com/medusa-core-utils/-/medusa-core-utils-1.1.23.tgz#1e9260d5066117350dbf56dc176dd25e488e90a1" + integrity sha512-3KnfbCGud09xYC7qrc+kw2XmBFa4UuGBOPT2IU3j70BjFhEU9ftD45oolKXNUziOZIIGRf/FIwGj+Lwgy6IAfA== dependencies: joi "^17.3.0" joi-objectid "^3.0.1" -medusa-interfaces@1.1.21-dev-1631630701835: - version "1.1.21-dev-1631630701835" - resolved "http://localhost:4873/medusa-interfaces/-/medusa-interfaces-1.1.21-dev-1631630701835.tgz#af29b2ef0c987bded1b2d295ac6cf39880af551e" - integrity sha512-rTASRjOdcS3J9fP95p9vJzCpatMpUhTum5ddfAA0s42pZx2gsPlf1f+rUSNz5QfeC5RdIEzRfOmAGfvMpAbYGw== +medusa-interfaces@1.1.21: + version "1.1.21" + resolved "https://registry.yarnpkg.com/medusa-interfaces/-/medusa-interfaces-1.1.21.tgz#ca86808e939b7ecc21a6d316008a4e41f163619f" + integrity sha512-mlHHoMIOFBc+Exs+uVIQsfeEP2C1Pi6IZHcpbm7O00tYBdQdqRjJre9+Z/I/Z37wt5IwA28/TIoVkYG71iQYxw== dependencies: - medusa-core-utils "1.1.20-dev-1631630701835" + medusa-core-utils "^1.1.20" -medusa-telemetry@0.0.3-dev-1631630701835: - version "0.0.3-dev-1631630701835" - resolved "http://localhost:4873/medusa-telemetry/-/medusa-telemetry-0.0.3-dev-1631630701835.tgz#d56c01d261fa30ccedc6d9976971b9744b9d8c0f" - integrity sha512-FS1L1DOIOSdRZgeIQWaM5nhFG5NtbnC/Pntfac51vQxLkzFuHy7ZEtg11CXKE+x6NWlqT1rqqgxq0EabFzEZzw== +medusa-telemetry@^0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/medusa-telemetry/-/medusa-telemetry-0.0.5.tgz#d7d08fca5cbecc0e853b4e0406194a92c5206caa" + integrity sha512-h7hP5Lc33OkFhMcvfrPcwINzMOuPoG8Vn8O6niKGFxF9RmmQnJgaAG1J43/Eq9ZWBrWi0n42XBttibKwCMViHw== dependencies: axios "^0.21.1" axios-retry "^3.1.9" @@ -5134,13 +5148,13 @@ medusa-telemetry@0.0.3-dev-1631630701835: remove-trailing-slash "^0.1.1" uuid "^8.3.2" -medusa-test-utils@1.1.23-dev-1631630701835: - version "1.1.23-dev-1631630701835" - resolved "http://localhost:4873/medusa-test-utils/-/medusa-test-utils-1.1.23-dev-1631630701835.tgz#8995d636caf2dea9ebb184f1e15b0c364c4d1b93" - integrity sha512-A8xRL+sZS22qXZSHpVfdV8f/egZxXs4iExRO2xUkTP6I/OgMhFBSg6nEd/DXVdVfpsHZCDEv8PA3ewaeAkoYhQ== +medusa-test-utils@^1.1.25: + version "1.1.26" + resolved "http://localhost:4873/medusa-test-utils/-/medusa-test-utils-1.1.26.tgz#badc7213e9acd332c2b4353602c5fc9d16f3c4e6" + integrity sha512-CtDD+5cLMqxuiJNtLljZF1btain7i64K6P3rbdef/kndPwjofBVUb3awoz4bieoZzJZF0GOn+p1kCZNsj2ggew== dependencies: "@babel/plugin-transform-classes" "^7.9.5" - medusa-core-utils "1.1.20-dev-1631630701835" + medusa-core-utils "^1.1.23" randomatic "^3.1.1" merge-descriptors@1.0.1: diff --git a/packages/medusa/src/api/routes/store/variants/__tests__/get-variant.js b/packages/medusa/src/api/routes/store/variants/__tests__/get-variant.js index 890ab5e078..eb16ea70d1 100644 --- a/packages/medusa/src/api/routes/store/variants/__tests__/get-variant.js +++ b/packages/medusa/src/api/routes/store/variants/__tests__/get-variant.js @@ -16,7 +16,7 @@ describe("Get variant by id", () => { it("calls get variant from variantSerice", () => { expect(ProductVariantServiceMock.retrieve).toHaveBeenCalledTimes(1) expect(ProductVariantServiceMock.retrieve).toHaveBeenCalledWith("1", { - relations: ["prices"], + relations: ["prices", "options"], }) }) diff --git a/packages/medusa/src/api/routes/store/variants/index.js b/packages/medusa/src/api/routes/store/variants/index.js index bc26ec1f55..b4ed48b30d 100644 --- a/packages/medusa/src/api/routes/store/variants/index.js +++ b/packages/medusa/src/api/routes/store/variants/index.js @@ -12,4 +12,4 @@ export default app => { return app } -export const defaultRelations = ["prices"] +export const defaultRelations = ["prices", "options"] From ba2d92341d7fa5cdd334fd9161f86da0c986b524 Mon Sep 17 00:00:00 2001 From: Sebastian Rindom Date: Wed, 13 Oct 2021 19:50:10 +0200 Subject: [PATCH 36/43] Revert "fixed linting errors on packages/medusa/src/services/user.js #531 (#543)" (#547) This reverts commit cf5b405a56082f621f5d48eeb9375aeaad5071bb. --- .eslintignore | 1 + .eslintrc.js | 1 + jest-transformer.js | 4 ++-- packages/medusa/src/services/gift-card.js | 19 +++++++++++-------- packages/medusa/src/services/transaction.js | 3 +-- packages/medusa/src/services/user.js | 21 +++++++-------------- 6 files changed, 23 insertions(+), 26 deletions(-) diff --git a/.eslintignore b/.eslintignore index 745932b48d..cbad3adfb9 100644 --- a/.eslintignore +++ b/.eslintignore @@ -31,6 +31,7 @@ /packages/medusa/src/services/swap.js /packages/medusa/src/services/system-payment-provider.js /packages/medusa/src/services/totals.js +/packages/medusa/src/services/user.js /packages/medusa/src/subscribers/notification.js /packages/medusa/src/subscribers/order.js diff --git a/.eslintrc.js b/.eslintrc.js index 47bc35d527..c40421efa9 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -9,6 +9,7 @@ module.exports = { plugins: [`eslint-plugin-prettier`], extends: [`eslint:recommended`, `google`, `eslint-config-prettier`], rules: { + "prettier/prettier": `error`, curly: [2, `all`], "new-cap": `off`, "require-jsdoc": `off`, diff --git a/jest-transformer.js b/jest-transformer.js index a5f750c9d6..f6d0fb7336 100644 --- a/jest-transformer.js +++ b/jest-transformer.js @@ -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, -}); +}) diff --git a/packages/medusa/src/services/gift-card.js b/packages/medusa/src/services/gift-card.js index 3762ce36f5..b96372d223 100644 --- a/packages/medusa/src/services/gift-card.js +++ b/packages/medusa/src/services/gift-card.js @@ -5,7 +5,7 @@ import { Brackets } from "typeorm" /** * Provides layer to manipulate gift cards. - * @implements BaseService + * @extends BaseService */ class GiftCardService extends BaseService { static Events = { @@ -101,7 +101,7 @@ class GiftCardService extends BaseService { .select(["gift_card.id"]) .where(where) .andWhere( - new Brackets(qb => { + new Brackets((qb) => { return qb .where(`gift_card.code ILIKE :q`, { q: `%${q}%` }) .orWhere(`display_id::varchar(255) ILIKE :dId`, { dId: `${q}` }) @@ -111,14 +111,14 @@ class GiftCardService extends BaseService { return giftCardRepo.findWithRelations( rels, - raw.map(i => i.id) + raw.map((i) => i.id) ) } return giftCardRepo.findWithRelations(rels, query) } async createTransaction(data) { - return this.atomicPhase_(async manager => { + return this.atomicPhase_(async (manager) => { const gctRepo = manager.getCustomRepository(this.giftCardTransactionRepo_) const created = gctRepo.create(data) const saved = await gctRepo.save(created) @@ -132,7 +132,7 @@ class GiftCardService extends BaseService { * @return {Promise} the result of the create operation */ async create(giftCard) { - return this.atomicPhase_(async manager => { + return this.atomicPhase_(async (manager) => { const giftCardRepo = manager.getCustomRepository(this.giftCardRepository_) if (!giftCard.region_id) { @@ -169,6 +169,7 @@ class GiftCardService extends BaseService { /** * Gets a gift card by id. * @param {string} giftCardId - id of gift card to retrieve + * @param {object} config - optional values to include with gift card query * @return {Promise} the gift card */ async retrieve(giftCardId, config = {}) { @@ -244,7 +245,7 @@ class GiftCardService extends BaseService { * @return {Promise} the result of the update operation */ async update(giftCardId, update) { - return this.atomicPhase_(async manager => { + return this.atomicPhase_(async (manager) => { const giftCardRepo = manager.getCustomRepository(this.giftCardRepository_) const giftCard = await this.retrieve(giftCardId) @@ -285,12 +286,14 @@ class GiftCardService extends BaseService { * @return {Promise} the result of the delete operation */ async delete(giftCardId) { - return this.atomicPhase_(async manager => { + return this.atomicPhase_(async (manager) => { const giftCardRepo = manager.getCustomRepository(this.giftCardRepository_) const giftCard = await giftCardRepo.findOne({ where: { id: giftCardId } }) - if (!giftCard) return Promise.resolve() + if (!giftCard) { + return Promise.resolve() + } await giftCardRepo.softRemove(giftCard) diff --git a/packages/medusa/src/services/transaction.js b/packages/medusa/src/services/transaction.js index 5af02ae3b0..7f57599ec6 100644 --- a/packages/medusa/src/services/transaction.js +++ b/packages/medusa/src/services/transaction.js @@ -1,9 +1,8 @@ import { BaseService } from "medusa-interfaces" import mongoose from "mongoose" -import _ from "lodash" class TransactionService extends BaseService { - constructor({}) { + constructor() { super() } diff --git a/packages/medusa/src/services/user.js b/packages/medusa/src/services/user.js index 940b49a7ce..52a55f6e01 100644 --- a/packages/medusa/src/services/user.js +++ b/packages/medusa/src/services/user.js @@ -6,7 +6,7 @@ import { BaseService } from "medusa-interfaces" /** * Provides layer to manipulate users. - * @extends BaseService + * @implements BaseService */ class UserService extends BaseService { static Events = { @@ -75,7 +75,6 @@ class UserService extends BaseService { * Gets a user by id. * Throws in case of DB Error and if user was not found. * @param {string} userId - the id of the user to get. - * @param {Object} config - query configs * @return {Promise} the user document. */ async retrieve(userId, config = {}) { @@ -100,7 +99,6 @@ class UserService extends BaseService { * Gets a user by api token. * Throws in case of DB Error and if user was not found. * @param {string} apiToken - the token of the user to get. - * @param {string[]} relations - relations to include with the user * @return {Promise} the user document. */ async retrieveByApiToken(apiToken, relations = []) { @@ -125,7 +123,6 @@ class UserService extends BaseService { * Gets a user by email. * Throws in case of DB Error and if user was not found. * @param {string} email - the email of the user to get. - * @param {Object} config - query config * @return {Promise} the user document. */ async retrieveByEmail(email, config = {}) { @@ -147,7 +144,7 @@ class UserService extends BaseService { /** * Hashes a password * @param {string} password - the value to hash - * @return {string} hashed password + * @return hashed password */ async hashPassword_(password) { const buf = await Scrypt.kdf(password, { logN: 1, r: 1, p: 1 }) @@ -158,7 +155,6 @@ class UserService extends BaseService { * Creates a user with username being validated. * Fails if email is not a valid format. * @param {object} user - the user to create - * @param {string} password - user's password to hash * @return {Promise} the result of create */ async create(user, password) { @@ -181,8 +177,7 @@ class UserService extends BaseService { /** * Updates a user. - * @param {object} userId - id of the user to update - * @param {object} update - the values to be updated on the user + * @param {object} user - the user to create * @return {Promise} the result of create */ async update(userId, update) { @@ -233,9 +228,7 @@ class UserService extends BaseService { // Should not fail, if user does not exist, since delete is idempotent const user = await userRepo.findOne({ where: { id: userId } }) - if (!user) { - return Promise.resolve() - } + if (!user) return Promise.resolve() await userRepo.softRemove(user) @@ -249,7 +242,7 @@ class UserService extends BaseService { * password does not work. * @param {string} userId - the userId to set password for * @param {string} password - the old password to set - * @return {Promise} the result of the update operation + * @returns {Promise} the result of the update operation */ async setPassword_(userId, password) { return this.atomicPhase_(async manager => { @@ -277,8 +270,8 @@ class UserService extends BaseService { * The token will be signed with the users current password hash as a secret * a long side a payload with userId and the expiry time for the token, which * is always 15 minutes. - * @param {string} userId - the id of the user to reset password for - * @return {string} the generated JSON web token + * @param {User} user - the user to reset password for + * @returns {string} the generated JSON web token */ async generateResetPasswordToken(userId) { const user = await this.retrieve(userId) From 7565d139d4febbcdcc19d590032939c6c8ce1bfd Mon Sep 17 00:00:00 2001 From: Sergiy Fomin <65659492+FominSergiy@users.noreply.github.com> Date: Wed, 13 Oct 2021 14:16:25 -0400 Subject: [PATCH 37/43] PR to make a fix to issue #531 -- previous reverted PR #543 (#548) * fixed linting errors on packages/medusa/src/services/user.js #531 fixing merge conflicts * fix: resolving a few issues as raised in the PR, issue: #531 Co-authored-by: Sergiy Fomin --- .eslintignore | 1 - packages/medusa/src/services/user.js | 21 ++++++++++++++------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/.eslintignore b/.eslintignore index cbad3adfb9..745932b48d 100644 --- a/.eslintignore +++ b/.eslintignore @@ -31,7 +31,6 @@ /packages/medusa/src/services/swap.js /packages/medusa/src/services/system-payment-provider.js /packages/medusa/src/services/totals.js -/packages/medusa/src/services/user.js /packages/medusa/src/subscribers/notification.js /packages/medusa/src/subscribers/order.js diff --git a/packages/medusa/src/services/user.js b/packages/medusa/src/services/user.js index 52a55f6e01..940b49a7ce 100644 --- a/packages/medusa/src/services/user.js +++ b/packages/medusa/src/services/user.js @@ -6,7 +6,7 @@ import { BaseService } from "medusa-interfaces" /** * Provides layer to manipulate users. - * @implements BaseService + * @extends BaseService */ class UserService extends BaseService { static Events = { @@ -75,6 +75,7 @@ class UserService extends BaseService { * Gets a user by id. * Throws in case of DB Error and if user was not found. * @param {string} userId - the id of the user to get. + * @param {Object} config - query configs * @return {Promise} the user document. */ async retrieve(userId, config = {}) { @@ -99,6 +100,7 @@ class UserService extends BaseService { * Gets a user by api token. * Throws in case of DB Error and if user was not found. * @param {string} apiToken - the token of the user to get. + * @param {string[]} relations - relations to include with the user * @return {Promise} the user document. */ async retrieveByApiToken(apiToken, relations = []) { @@ -123,6 +125,7 @@ class UserService extends BaseService { * Gets a user by email. * Throws in case of DB Error and if user was not found. * @param {string} email - the email of the user to get. + * @param {Object} config - query config * @return {Promise} the user document. */ async retrieveByEmail(email, config = {}) { @@ -144,7 +147,7 @@ class UserService extends BaseService { /** * Hashes a password * @param {string} password - the value to hash - * @return hashed password + * @return {string} hashed password */ async hashPassword_(password) { const buf = await Scrypt.kdf(password, { logN: 1, r: 1, p: 1 }) @@ -155,6 +158,7 @@ class UserService extends BaseService { * Creates a user with username being validated. * Fails if email is not a valid format. * @param {object} user - the user to create + * @param {string} password - user's password to hash * @return {Promise} the result of create */ async create(user, password) { @@ -177,7 +181,8 @@ class UserService extends BaseService { /** * Updates a user. - * @param {object} user - the user to create + * @param {object} userId - id of the user to update + * @param {object} update - the values to be updated on the user * @return {Promise} the result of create */ async update(userId, update) { @@ -228,7 +233,9 @@ class UserService extends BaseService { // Should not fail, if user does not exist, since delete is idempotent const user = await userRepo.findOne({ where: { id: userId } }) - if (!user) return Promise.resolve() + if (!user) { + return Promise.resolve() + } await userRepo.softRemove(user) @@ -242,7 +249,7 @@ class UserService extends BaseService { * password does not work. * @param {string} userId - the userId to set password for * @param {string} password - the old password to set - * @returns {Promise} the result of the update operation + * @return {Promise} the result of the update operation */ async setPassword_(userId, password) { return this.atomicPhase_(async manager => { @@ -270,8 +277,8 @@ class UserService extends BaseService { * The token will be signed with the users current password hash as a secret * a long side a payload with userId and the expiry time for the token, which * is always 15 minutes. - * @param {User} user - the user to reset password for - * @returns {string} the generated JSON web token + * @param {string} userId - the id of the user to reset password for + * @return {string} the generated JSON web token */ async generateResetPasswordToken(userId) { const user = await this.retrieve(userId) From f926e0245c8372b050c5b48a90ef1671ee9525f5 Mon Sep 17 00:00:00 2001 From: Ajordat Date: Wed, 13 Oct 2021 20:18:21 +0200 Subject: [PATCH 38/43] fix: make packages/medusa/src/services/region.js pass eslint (#542) * fix: make packages/medusa/src/services/region.js pass eslint * Fix intendation --- .eslintignore | 1 - packages/medusa/src/services/region.js | 17 ++++++++++------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/.eslintignore b/.eslintignore index 745932b48d..8111735959 100644 --- a/.eslintignore +++ b/.eslintignore @@ -22,7 +22,6 @@ /packages/medusa/src/services/product-variant.js /packages/medusa/src/services/product.js /packages/medusa/src/services/query-builder.js -/packages/medusa/src/services/region.js /packages/medusa/src/services/return-reason.js /packages/medusa/src/services/return.js /packages/medusa/src/services/shipping-option.js diff --git a/packages/medusa/src/services/region.js b/packages/medusa/src/services/region.js index 0994214958..bc2d5eab57 100644 --- a/packages/medusa/src/services/region.js +++ b/packages/medusa/src/services/region.js @@ -1,11 +1,10 @@ -import _ from "lodash" -import { Validator, MedusaError } from "medusa-core-utils" +import { MedusaError } from "medusa-core-utils" import { BaseService } from "medusa-interfaces" import { countries } from "../utils/countries" /** * Provides layer to manipulate regions. - * @implements BaseService + * @extends BaseService */ class RegionService extends BaseService { static Events = { @@ -83,7 +82,7 @@ class RegionService extends BaseService { /** * Creates a region. - * @param {Region} rawRegion - the unvalidated region + * @param {Region} regionObject - the unvalidated region * @return {Region} the newly created region */ async create(regionObject) { @@ -118,7 +117,7 @@ class RegionService extends BaseService { } if (metadata) { - regionObject.metadata = this.setMetadata_(region, metadata) + regionObject.metadata = this.setMetadata_(regionObject, metadata) } for (const [key, value] of Object.entries(validated)) { @@ -339,6 +338,7 @@ class RegionService extends BaseService { /** * Retrieves a region by its id. * @param {string} regionId - the id of the region to retrieve + * @param {object} config - configuration settings * @return {Region} the region */ async retrieve(regionId, config = {}) { @@ -361,7 +361,8 @@ class RegionService extends BaseService { /** * Lists all regions based on a query - * @param {object} listOptions - query object for find + * @param {object} selector - query object for find + * @param {object} config - configuration settings * @return {Promise} result of the find operation */ async list(selector = {}, config = { relations: [], skip: 0, take: 10 }) { @@ -382,7 +383,9 @@ class RegionService extends BaseService { const region = await regionRepo.findOne({ where: { id: regionId } }) - if (!region) return Promise.resolve() + if (!region) { + return Promise.resolve() + } await regionRepo.softRemove(region) From d9608e15e815898aa58cdf253fd46c3834ce3bda Mon Sep 17 00:00:00 2001 From: Sebastian Rindom Date: Wed, 13 Oct 2021 20:19:09 +0200 Subject: [PATCH 39/43] fix: autofix --- packages/medusa/src/services/region.js | 36 +++++++++++++------------- packages/medusa/src/services/user.js | 12 ++++----- 2 files changed, 23 insertions(+), 25 deletions(-) diff --git a/packages/medusa/src/services/region.js b/packages/medusa/src/services/region.js index bc2d5eab57..44d611ca0e 100644 --- a/packages/medusa/src/services/region.js +++ b/packages/medusa/src/services/region.js @@ -86,7 +86,7 @@ class RegionService extends BaseService { * @return {Region} the newly created region */ async create(regionObject) { - return this.atomicPhase_(async manager => { + return this.atomicPhase_(async (manager) => { const regionRepository = manager.getCustomRepository( this.regionRepository_ ) @@ -144,7 +144,7 @@ class RegionService extends BaseService { * @return {Promise} the result of the update operation */ async update(regionId, update) { - return this.atomicPhase_(async manager => { + return this.atomicPhase_(async (manager) => { const regionRepository = manager.getCustomRepository( this.regionRepository_ ) @@ -217,17 +217,17 @@ class RegionService extends BaseService { if (region.countries) { region.countries = await Promise.all( - region.countries.map(countryCode => + region.countries.map((countryCode) => this.validateCountry_(countryCode, id) ) - ).catch(err => { + ).catch((err) => { throw err }) } if (region.payment_providers) { region.payment_providers = await Promise.all( - region.payment_providers.map(async pId => { + region.payment_providers.map(async (pId) => { const pp = await ppRepository.findOne({ where: { id: pId } }) if (!pp) { throw new MedusaError( @@ -243,7 +243,7 @@ class RegionService extends BaseService { if (region.fulfillment_providers) { region.fulfillment_providers = await Promise.all( - region.fulfillment_providers.map(async fId => { + region.fulfillment_providers.map(async (fId) => { const fp = await fpRepository.findOne({ where: { id: fId } }) if (!fp) { throw new MedusaError( @@ -282,7 +282,7 @@ class RegionService extends BaseService { .withTransaction(this.transactionManager_) .retrieve(["currencies"]) - const storeCurrencies = store.currencies.map(curr => curr.code) + const storeCurrencies = store.currencies.map((curr) => curr.code) if (!storeCurrencies.includes(currencyCode.toLowerCase())) { throw new MedusaError( @@ -304,7 +304,7 @@ class RegionService extends BaseService { ) const countryCode = code.toUpperCase() - const validCountry = countries.find(c => c.alpha2 === countryCode) + const validCountry = countries.find((c) => c.alpha2 === countryCode) if (!validCountry) { throw new MedusaError( MedusaError.Types.INVALID_DATA, @@ -378,7 +378,7 @@ class RegionService extends BaseService { * @return {Promise} the result of the delete operation */ async delete(regionId) { - return this.atomicPhase_(async manager => { + return this.atomicPhase_(async (manager) => { const regionRepo = manager.getCustomRepository(this.regionRepository_) const region = await regionRepo.findOne({ where: { id: regionId } }) @@ -400,7 +400,7 @@ class RegionService extends BaseService { * @return {Promise} the result of the update operation */ async addCountry(regionId, code) { - return this.atomicPhase_(async manager => { + return this.atomicPhase_(async (manager) => { const regionRepo = manager.getCustomRepository(this.regionRepository_) const country = await this.validateCountry_(code, regionId) @@ -410,7 +410,7 @@ class RegionService extends BaseService { // Check if region already has country if ( region.countries && - region.countries.map(c => c.iso_2).includes(country.iso_2) + region.countries.map((c) => c.iso_2).includes(country.iso_2) ) { return region } @@ -437,7 +437,7 @@ class RegionService extends BaseService { * @return {Promise} the result of the update operation */ async removeCountry(regionId, code) { - return this.atomicPhase_(async manager => { + return this.atomicPhase_(async (manager) => { const regionRepo = manager.getCustomRepository(this.regionRepository_) const region = await this.retrieve(regionId, { relations: ["countries"] }) @@ -445,13 +445,13 @@ class RegionService extends BaseService { // Check if region contains country. If not, we simpy resolve if ( region.countries && - !region.countries.map(c => c.iso_2).includes(code) + !region.countries.map((c) => c.iso_2).includes(code) ) { return region } region.countries = region.countries.filter( - country => country.iso_2 !== code + (country) => country.iso_2 !== code ) const updated = await regionRepo.save(region) @@ -473,7 +473,7 @@ class RegionService extends BaseService { * @return {Promise} the result of the update operation */ async addPaymentProvider(regionId, providerId) { - return this.atomicPhase_(async manager => { + return this.atomicPhase_(async (manager) => { const regionRepo = manager.getCustomRepository(this.regionRepository_) const ppRepo = manager.getCustomRepository( this.paymentProviderRepository_ @@ -520,7 +520,7 @@ class RegionService extends BaseService { * @return {Promise} the result of the update operation */ async addFulfillmentProvider(regionId, providerId) { - return this.atomicPhase_(async manager => { + return this.atomicPhase_(async (manager) => { const regionRepo = manager.getCustomRepository(this.regionRepository_) const fpRepo = manager.getCustomRepository( this.fulfillmentProviderRepository_ @@ -564,7 +564,7 @@ class RegionService extends BaseService { * @return {Promise} the result of the update operation */ async removePaymentProvider(regionId, providerId) { - return this.atomicPhase_(async manager => { + return this.atomicPhase_(async (manager) => { const regionRepo = manager.getCustomRepository(this.regionRepository_) const region = await this.retrieve(regionId, { @@ -598,7 +598,7 @@ class RegionService extends BaseService { * @return {Promise} the result of the update operation */ async removeFulfillmentProvider(regionId, providerId) { - return this.atomicPhase_(async manager => { + return this.atomicPhase_(async (manager) => { const regionRepo = manager.getCustomRepository(this.regionRepository_) const region = await this.retrieve(regionId, { diff --git a/packages/medusa/src/services/user.js b/packages/medusa/src/services/user.js index 940b49a7ce..6a254f4dd2 100644 --- a/packages/medusa/src/services/user.js +++ b/packages/medusa/src/services/user.js @@ -48,9 +48,7 @@ class UserService extends BaseService { * @return {string} the validated email */ validateEmail_(email) { - const schema = Validator.string() - .email() - .required() + const schema = Validator.string().email().required() const { value, error } = schema.validate(email) if (error) { throw new MedusaError( @@ -162,7 +160,7 @@ class UserService extends BaseService { * @return {Promise} the result of create */ async create(user, password) { - return this.atomicPhase_(async manager => { + return this.atomicPhase_(async (manager) => { const userRepo = manager.getCustomRepository(this.userRepository_) const validatedEmail = this.validateEmail_(user.email) @@ -186,7 +184,7 @@ class UserService extends BaseService { * @return {Promise} the result of create */ async update(userId, update) { - return this.atomicPhase_(async manager => { + return this.atomicPhase_(async (manager) => { const userRepo = manager.getCustomRepository(this.userRepository_) const validatedId = this.validateId_(userId) @@ -227,7 +225,7 @@ class UserService extends BaseService { * @return {Promise} the result of the delete operation. */ async delete(userId) { - return this.atomicPhase_(async manager => { + return this.atomicPhase_(async (manager) => { const userRepo = manager.getCustomRepository(this.userRepository_) // Should not fail, if user does not exist, since delete is idempotent @@ -252,7 +250,7 @@ class UserService extends BaseService { * @return {Promise} the result of the update operation */ async setPassword_(userId, password) { - return this.atomicPhase_(async manager => { + return this.atomicPhase_(async (manager) => { const userRepo = manager.getCustomRepository(this.userRepository_) const user = await this.retrieve(userId) From ddeaf57f598b5bde8d048fdf63199ba8789348e0 Mon Sep 17 00:00:00 2001 From: Vladyslav Date: Thu, 14 Oct 2021 10:52:18 +0300 Subject: [PATCH 40/43] fix: make packages/medusa/src/services/claim.js pass eslint (#551) --- .eslintignore | 1 - packages/medusa/src/services/claim.js | 48 ++++++++++++--------------- 2 files changed, 22 insertions(+), 27 deletions(-) diff --git a/.eslintignore b/.eslintignore index 8111735959..5f029b0700 100644 --- a/.eslintignore +++ b/.eslintignore @@ -2,7 +2,6 @@ /packages/medusa/src/services/cart.js /packages/medusa/src/services/claim-item.js -/packages/medusa/src/services/claim.js /packages/medusa/src/services/customer.js /packages/medusa/src/services/discount.js /packages/medusa/src/services/draft-order.js diff --git a/packages/medusa/src/services/claim.js b/packages/medusa/src/services/claim.js index 9d60a28626..8e73dbf83e 100644 --- a/packages/medusa/src/services/claim.js +++ b/packages/medusa/src/services/claim.js @@ -1,7 +1,5 @@ -import _ from "lodash" -import { Validator, MedusaError } from "medusa-core-utils" +import { MedusaError } from "medusa-core-utils" import { BaseService } from "medusa-interfaces" -import { Brackets } from "typeorm" class ClaimService extends BaseService { static Events = { @@ -102,7 +100,7 @@ class ClaimService extends BaseService { } update(id, data) { - return this.atomicPhase_(async manager => { + return this.atomicPhase_(async (manager) => { const claimRepo = manager.getCustomRepository(this.claimRepository_) const claim = await this.retrieve(id, { relations: ["shipping_methods"] }) @@ -113,13 +111,7 @@ class ClaimService extends BaseService { ) } - const { - claim_items, - shipping_methods, - metadata, - fulfillment_status, - no_notification, - } = data + const { claim_items, shipping_methods, metadata, no_notification } = data if (metadata) { claim.metadata = this.setMetadata_(claim, metadata) @@ -183,9 +175,11 @@ class ClaimService extends BaseService { * Creates a Claim on an Order. Claims consists of items that are claimed and * optionally items to be sent as replacement for the claimed items. The * shipping address that the new items will be shipped to + * @param {Object} data - the object containing all data required to create a claim + * @return {Object} created claim */ create(data) { - return this.atomicPhase_(async manager => { + return this.atomicPhase_(async (manager) => { const claimRepo = manager.getCustomRepository(this.claimRepository_) const { @@ -257,8 +251,8 @@ class ClaimService extends BaseService { let toRefund = refund_amount if (type === "refund" && typeof refund_amount === "undefined") { - const lines = claim_items.map(ci => { - const orderItem = order.items.find(oi => oi.id === ci.item_id) + const lines = claim_items.map((ci) => { + const orderItem = order.items.find((oi) => oi.id === ci.item_id) return { ...orderItem, quantity: ci.quantity, @@ -274,7 +268,7 @@ class ClaimService extends BaseService { } const newItems = await Promise.all( - additional_items.map(i => + additional_items.map((i) => this.lineItemService_ .withTransaction(manager) .generate(i.variant_id, order.region_id, i.quantity) @@ -332,7 +326,7 @@ class ClaimService extends BaseService { await this.returnService_.withTransaction(manager).create({ order_id: order.id, claim_order_id: result.id, - items: claim_items.map(ci => ({ + items: claim_items.map((ci) => ({ item_id: ci.item_id, quantity: ci.quantity, metadata: ci.metadata, @@ -362,7 +356,7 @@ class ClaimService extends BaseService { ) { const { metadata, no_notification } = config - return this.atomicPhase_(async manager => { + return this.atomicPhase_(async (manager) => { const claim = await this.retrieve(id, { relations: [ "additional_items", @@ -429,7 +423,7 @@ class ClaimService extends BaseService { is_claim: true, no_notification: evaluatedNoNotification, }, - claim.additional_items.map(i => ({ + claim.additional_items.map((i) => ({ item_id: i.id, quantity: i.quantity, })), @@ -445,7 +439,7 @@ class ClaimService extends BaseService { for (const item of claim.additional_items) { const fulfillmentItem = successfullyFulfilled.find( - f => item.id === f.item_id + (f) => item.id === f.item_id ) if (fulfillmentItem) { @@ -485,7 +479,7 @@ class ClaimService extends BaseService { } async cancelFulfillment(fulfillmentId) { - return this.atomicPhase_(async manager => { + return this.atomicPhase_(async (manager) => { const canceled = await this.fulfillmentService_ .withTransaction(manager) .cancelFulfillment(fulfillmentId) @@ -508,7 +502,7 @@ class ClaimService extends BaseService { } async processRefund(id) { - return this.atomicPhase_(async manager => { + return this.atomicPhase_(async (manager) => { const claim = await this.retrieve(id, { relations: ["order", "order.payments"], }) @@ -560,7 +554,7 @@ class ClaimService extends BaseService { ) { const { metadata, no_notification } = config - return this.atomicPhase_(async manager => { + return this.atomicPhase_(async (manager) => { const claim = await this.retrieve(id, { relations: ["additional_items"], }) @@ -584,7 +578,7 @@ class ClaimService extends BaseService { claim.fulfillment_status = "shipped" for (const i of claim.additional_items) { - const shipped = shipment.items.find(si => si.item_id === i.id) + const shipped = shipment.items.find((si) => si.item_id === i.id) if (shipped) { const shippedQty = (i.shipped_quantity || 0) + shipped.quantity await this.lineItemService_.withTransaction(manager).update(i.id, { @@ -617,7 +611,7 @@ class ClaimService extends BaseService { } async cancel(id) { - return this.atomicPhase_(async manager => { + return this.atomicPhase_(async (manager) => { const claim = await this.retrieve(id, { relations: ["return_order", "fulfillments", "order", "order.refunds"], }) @@ -665,6 +659,7 @@ class ClaimService extends BaseService { /** * @param {Object} selector - the query object for find + * @param {Object} config - the config object containing query settings * @return {Promise} the result of the find operation */ async list( @@ -678,7 +673,8 @@ class ClaimService extends BaseService { /** * Gets an order by id. - * @param {string} orderId - id of order to retrieve + * @param {string} claimId - id of order to retrieve + * @param {Object} config - the config object containing query settings * @return {Promise} the order document */ async retrieve(claimId, config = {}) { @@ -717,7 +713,7 @@ class ClaimService extends BaseService { const keyPath = `metadata.${key}` return this.orderModel_ .updateOne({ _id: validatedId }, { $unset: { [keyPath]: "" } }) - .catch(err => { + .catch((err) => { throw new MedusaError(MedusaError.Types.DB_ERROR, err.message) }) } From fe599c709e1f7e0109ae69bd1d0efb396f959e6a Mon Sep 17 00:00:00 2001 From: Rachel Date: Thu, 14 Oct 2021 01:31:23 -0700 Subject: [PATCH 41/43] fix: make medusa/src/services/fulfillment.js pass eslint (#557) * fix: make /packages/medusa/src/services/gift-card.js pass eslint * fix: make medusa/src/services/fulfillment.js pass eslint * chore: restore eslintignore change * chore: clean up diffs --- .eslintignore | 1 - packages/medusa/src/services/fulfillment.js | 41 +++++++++++---------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/.eslintignore b/.eslintignore index 5f029b0700..1a96ca41aa 100644 --- a/.eslintignore +++ b/.eslintignore @@ -7,7 +7,6 @@ /packages/medusa/src/services/draft-order.js /packages/medusa/src/services/event-bus.js /packages/medusa/src/services/fulfillment-provider.js -/packages/medusa/src/services/fulfillment.js /packages/medusa/src/services/idempotency-key.js /packages/medusa/src/services/inventory.js /packages/medusa/src/services/line-item.js diff --git a/packages/medusa/src/services/fulfillment.js b/packages/medusa/src/services/fulfillment.js index ed0b4e4f80..b2cd6b71bc 100644 --- a/packages/medusa/src/services/fulfillment.js +++ b/packages/medusa/src/services/fulfillment.js @@ -1,10 +1,9 @@ -import _ from "lodash" import { BaseService } from "medusa-interfaces" import { MedusaError } from "medusa-core-utils" /** * Handles Fulfillments - * @implements BaseService + * @extends BaseService */ class FulfillmentService extends BaseService { constructor({ @@ -61,7 +60,7 @@ class FulfillmentService extends BaseService { } partitionItems_(shippingMethods, items) { - let partitioned = [] + const partitioned = [] // partition order items to their dedicated shipping method for (const method of shippingMethods) { const temp = { shipping_method: method } @@ -95,12 +94,12 @@ class FulfillmentService extends BaseService { async getFulfillmentItems_(order, items, transformer) { const toReturn = await Promise.all( items.map(async ({ item_id, quantity }) => { - const item = order.items.find(i => i.id === item_id) + const item = order.items.find((i) => i.id === item_id) return transformer(item, quantity) }) ) - return toReturn.filter(i => !!i) + return toReturn.filter((i) => !!i) } /** @@ -137,6 +136,7 @@ class FulfillmentService extends BaseService { /** * Retrieves a fulfillment by its id. * @param {string} id - the id of the fulfillment to retrieve + * @param {object} config - optional values to include with fulfillmentRepository query * @return {Fulfillment} the fulfillment */ async retrieve(id, config = {}) { @@ -165,11 +165,11 @@ class FulfillmentService extends BaseService { * those partitions. * @param {Order} order - order to create fulfillment for * @param {{ item_id: string, quantity: number}[]} itemsToFulfill - the items in the order to fulfill - * @param {object} metadata - potential metadata to add + * @param {object} custom - potential custom values to add * @return {Fulfillment[]} the created fulfillments */ async createFulfillment(order, itemsToFulfill, custom = {}) { - return this.atomicPhase_(async manager => { + return this.atomicPhase_(async (manager) => { const fulfillmentRepository = manager.getCustomRepository( this.fulfillmentRepository_ ) @@ -190,18 +190,19 @@ class FulfillmentService extends BaseService { const ful = fulfillmentRepository.create({ ...custom, provider_id: shipping_method.shipping_option.provider_id, - items: items.map(i => ({ item_id: i.id, quantity: i.quantity })), + items: items.map((i) => ({ item_id: i.id, quantity: i.quantity })), data: {}, }) - let result = await fulfillmentRepository.save(ful) + const result = await fulfillmentRepository.save(ful) - result.data = await this.fulfillmentProviderService_.createFulfillment( - shipping_method, - items, - { ...order }, - { ...result } - ) + result.data = + await this.fulfillmentProviderService_.createFulfillment( + shipping_method, + items, + { ...order }, + { ...result } + ) return fulfillmentRepository.save(result) }) @@ -220,7 +221,7 @@ class FulfillmentService extends BaseService { * */ cancelFulfillment(fulfillmentOrId) { - return this.atomicPhase_(async manager => { + return this.atomicPhase_(async (manager) => { let id = fulfillmentOrId if (typeof fulfillmentOrId === "object") { id = fulfillmentOrId.id @@ -260,9 +261,9 @@ class FulfillmentService extends BaseService { /** * Creates a shipment by marking a fulfillment as shipped. Adds - * tracking numbers and potentially more metadata. + * tracking links and potentially more metadata. * @param {Order} fulfillmentId - the fulfillment to ship - * @param {TrackingLink[]} trackingNumbers - tracking numbers for the shipment + * @param {TrackingLink[]} trackingLinks - tracking links for the shipment * @param {object} config - potential configuration settings, such as no_notification and metadata * @return {Fulfillment} the shipped fulfillment */ @@ -276,7 +277,7 @@ class FulfillmentService extends BaseService { ) { const { metadata, no_notification } = config - return this.atomicPhase_(async manager => { + return this.atomicPhase_(async (manager) => { const fulfillmentRepository = manager.getCustomRepository( this.fulfillmentRepository_ ) @@ -298,7 +299,7 @@ class FulfillmentService extends BaseService { const now = new Date() fulfillment.shipped_at = now - fulfillment.tracking_links = trackingLinks.map(tl => + fulfillment.tracking_links = trackingLinks.map((tl) => trackingLinkRepo.create(tl) ) From 1e13c831ab955d4221a887e921ee9d38b0b27225 Mon Sep 17 00:00:00 2001 From: Vladyslav Date: Thu, 14 Oct 2021 11:36:58 +0300 Subject: [PATCH 42/43] fix: make packages/medusa/src/services/discount.js pass eslint (#553) --- .eslintignore | 1 - packages/medusa/src/services/discount.js | 82 ++++++++++++------------ 2 files changed, 40 insertions(+), 43 deletions(-) diff --git a/.eslintignore b/.eslintignore index 1a96ca41aa..a3ff74ff07 100644 --- a/.eslintignore +++ b/.eslintignore @@ -3,7 +3,6 @@ /packages/medusa/src/services/cart.js /packages/medusa/src/services/claim-item.js /packages/medusa/src/services/customer.js -/packages/medusa/src/services/discount.js /packages/medusa/src/services/draft-order.js /packages/medusa/src/services/event-bus.js /packages/medusa/src/services/fulfillment-provider.js diff --git a/packages/medusa/src/services/discount.js b/packages/medusa/src/services/discount.js index 70f404f378..bb796208ce 100644 --- a/packages/medusa/src/services/discount.js +++ b/packages/medusa/src/services/discount.js @@ -1,14 +1,11 @@ -import _ from "lodash" -import randomize from "randomatic" import { BaseService } from "medusa-interfaces" import { Validator, MedusaError } from "medusa-core-utils" -import { MedusaErrorCodes } from "medusa-core-utils/dist/errors" import { parse, toSeconds } from "iso8601-duration" import { Brackets, ILike } from "typeorm" /** * Provides layer to manipulate discounts. - * @implements BaseService + * @implements {BaseService} */ class DiscountService extends BaseService { constructor({ @@ -17,7 +14,6 @@ class DiscountService extends BaseService { discountRuleRepository, giftCardRepository, totalsService, - productVariantService, productService, regionService, eventBusService, @@ -80,21 +76,13 @@ class DiscountService extends BaseService { id: Validator.string().optional(), description: Validator.string().optional(), type: Validator.string().required(), - value: Validator.number() - .min(0) - .required(), + value: Validator.number().min(0).required(), allocation: Validator.string().required(), valid_for: Validator.array().optional(), created_at: Validator.date().optional(), - updated_at: Validator.date() - .allow(null) - .optional(), - deleted_at: Validator.date() - .allow(null) - .optional(), - metadata: Validator.object() - .allow(null) - .optional(), + updated_at: Validator.date().allow(null).optional(), + deleted_at: Validator.date().allow(null).optional(), + metadata: Validator.object().allow(null).optional(), }) const { value, error } = schema.validate(discountRule) @@ -117,6 +105,7 @@ class DiscountService extends BaseService { /** * @param {Object} selector - the query object for find + * @param {Object} config - the config object containing query settings * @return {Promise} the result of the find operation */ async list(selector = {}, config = { relations: [], skip: 0, take: 10 }) { @@ -130,6 +119,7 @@ class DiscountService extends BaseService { /** * @param {Object} selector - the query object for find + * @param {Object} config - the config object containing query settings * @return {Promise} the result of the find operation */ async listAndCount( @@ -153,11 +143,11 @@ class DiscountService extends BaseService { delete where.code - query.where = qb => { + query.where = (qb) => { qb.where(where) qb.andWhere( - new Brackets(qb => { + new Brackets((qb) => { qb.where({ code: ILike(`%${q}%`) }) }) ) @@ -176,19 +166,19 @@ class DiscountService extends BaseService { * @return {Promise} the result of the create operation */ async create(discount) { - return this.atomicPhase_(async manager => { + return this.atomicPhase_(async (manager) => { const discountRepo = manager.getCustomRepository(this.discountRepository_) const ruleRepo = manager.getCustomRepository(this.discountRuleRepository_) if (discount.rule?.valid_for) { - discount.rule.valid_for = discount.rule.valid_for.map(id => ({ id })) + discount.rule.valid_for = discount.rule.valid_for.map((id) => ({ id })) } const validatedRule = this.validateDiscountRule_(discount.rule) if (discount.regions) { discount.regions = await Promise.all( - discount.regions.map(regionId => + discount.regions.map((regionId) => this.regionService_.withTransaction(manager).retrieve(regionId) ) ) @@ -209,6 +199,7 @@ class DiscountService extends BaseService { /** * Gets a discount by id. * @param {string} discountId - id of discount to retrieve + * @param {Object} config - the config object containing query settings * @return {Promise} the discount */ async retrieve(discountId, config = {}) { @@ -233,6 +224,7 @@ class DiscountService extends BaseService { /** * Gets a discount by discount code. * @param {string} discountCode - discount code of discount to retrieve + * @param {array} relations - list of relations * @return {Promise} the discount document */ async retrieveByCode(discountCode, relations = []) { @@ -269,7 +261,7 @@ class DiscountService extends BaseService { * @return {Promise} the result of the update operation */ async update(discountId, update) { - return this.atomicPhase_(async manager => { + return this.atomicPhase_(async (manager) => { const discountRepo = manager.getCustomRepository(this.discountRepository_) const discount = await this.retrieve(discountId) @@ -287,7 +279,7 @@ class DiscountService extends BaseService { if (regions) { discount.regions = await Promise.all( - regions.map(regionId => this.regionService_.retrieve(regionId)) + regions.map((regionId) => this.regionService_.retrieve(regionId)) ) } @@ -298,7 +290,9 @@ class DiscountService extends BaseService { if (rule) { discount.rule = this.validateDiscountRule_(rule) if (rule.valid_for) { - discount.rule.valid_for = discount.rule.valid_for.map(id => ({ id })) + discount.rule.valid_for = discount.rule.valid_for.map((id) => ({ + id, + })) } } @@ -314,11 +308,11 @@ class DiscountService extends BaseService { /** * Creates a dynamic code for a discount id. * @param {string} discountId - the id of the discount to create a code for - * @param {string} code - the code to identify the discount by + * @param {Object} data - the object containing a code to identify the discount by * @return {Promise} the newly created dynamic code */ async createDynamicCode(discountId, data) { - return this.atomicPhase_(async manager => { + return this.atomicPhase_(async (manager) => { const discountRepo = manager.getCustomRepository(this.discountRepository_) const discount = await this.retrieve(discountId) @@ -367,13 +361,15 @@ class DiscountService extends BaseService { * @return {Promise} the newly created dynamic code */ async deleteDynamicCode(discountId, code) { - return this.atomicPhase_(async manager => { + return this.atomicPhase_(async (manager) => { const discountRepo = manager.getCustomRepository(this.discountRepository_) const discount = await discountRepo.findOne({ where: { parent_discount_id: discountId, code }, }) - if (!discount) return Promise.resolve() + if (!discount) { + return Promise.resolve() + } await discountRepo.softRemove(discount) @@ -388,7 +384,7 @@ class DiscountService extends BaseService { * @return {Promise} the result of the update operation */ async addValidProduct(discountId, productId) { - return this.atomicPhase_(async manager => { + return this.atomicPhase_(async (manager) => { const discountRuleRepo = manager.getCustomRepository( this.discountRuleRepository_ ) @@ -399,7 +395,7 @@ class DiscountService extends BaseService { const { rule } = discount - const exists = rule.valid_for.find(p => p.id === productId) + const exists = rule.valid_for.find((p) => p.id === productId) // If product is already present, we return early if (exists) { return rule @@ -421,7 +417,7 @@ class DiscountService extends BaseService { * @return {Promise} the result of the update operation */ async removeValidProduct(discountId, productId) { - return this.atomicPhase_(async manager => { + return this.atomicPhase_(async (manager) => { const discountRuleRepo = manager.getCustomRepository( this.discountRuleRepository_ ) @@ -432,13 +428,13 @@ class DiscountService extends BaseService { const { rule } = discount - const exists = rule.valid_for.find(p => p.id === productId) + const exists = rule.valid_for.find((p) => p.id === productId) // If product is not present, we return early if (!exists) { return rule } - rule.valid_for = rule.valid_for.filter(p => p.id !== productId) + rule.valid_for = rule.valid_for.filter((p) => p.id !== productId) const updated = await discountRuleRepo.save(rule) return updated @@ -452,14 +448,14 @@ class DiscountService extends BaseService { * @return {Promise} the result of the update operation */ async addRegion(discountId, regionId) { - return this.atomicPhase_(async manager => { + return this.atomicPhase_(async (manager) => { const discountRepo = manager.getCustomRepository(this.discountRepository_) const discount = await this.retrieve(discountId, { relations: ["regions"], }) - const exists = discount.regions.find(r => r.id === regionId) + const exists = discount.regions.find((r) => r.id === regionId) // If region is already present, we return early if (exists) { return discount @@ -481,20 +477,20 @@ class DiscountService extends BaseService { * @return {Promise} the result of the update operation */ async removeRegion(discountId, regionId) { - return this.atomicPhase_(async manager => { + return this.atomicPhase_(async (manager) => { const discountRepo = manager.getCustomRepository(this.discountRepository_) const discount = await this.retrieve(discountId, { relations: ["regions"], }) - const exists = discount.regions.find(r => r.id === regionId) + const exists = discount.regions.find((r) => r.id === regionId) // If region is not present, we return early if (!exists) { return discount } - discount.regions = discount.regions.filter(r => r.id !== regionId) + discount.regions = discount.regions.filter((r) => r.id !== regionId) const updated = await discountRepo.save(discount) return updated @@ -507,12 +503,14 @@ class DiscountService extends BaseService { * @return {Promise} the result of the delete operation */ async delete(discountId) { - return this.atomicPhase_(async manager => { + return this.atomicPhase_(async (manager) => { const discountRepo = manager.getCustomRepository(this.discountRepository_) const discount = await discountRepo.findOne({ where: { id: discountId } }) - if (!discount) return Promise.resolve() + if (!discount) { + return Promise.resolve() + } await discountRepo.softRemove(discount) @@ -522,7 +520,7 @@ class DiscountService extends BaseService { /** * Decorates a discount. - * @param {Discount} discount - the discount to decorate. + * @param {string} discountId - id of discount to decorate * @param {string[]} fields - the fields to include. * @param {string[]} expandFields - fields to expand. * @return {Discount} return the decorated discount. From a1ada9309388bba629626650938370ffb05cdb40 Mon Sep 17 00:00:00 2001 From: saurabh042 Date: Thu, 14 Oct 2021 14:19:57 +0530 Subject: [PATCH 43/43] chore/lint : Make packages/medusa/src/services/order.js pass linting #515 (#556) --- .eslintignore | 1 - packages/medusa/src/services/order.js | 119 +++++++++++++------------- 2 files changed, 61 insertions(+), 59 deletions(-) diff --git a/.eslintignore b/.eslintignore index a3ff74ff07..1faf09169d 100644 --- a/.eslintignore +++ b/.eslintignore @@ -13,7 +13,6 @@ /packages/medusa/src/services/note.js /packages/medusa/src/services/notification.js /packages/medusa/src/services/oauth.js -/packages/medusa/src/services/order.js /packages/medusa/src/services/payment-provider.js /packages/medusa/src/services/product-collection.js /packages/medusa/src/services/product-variant.js diff --git a/packages/medusa/src/services/order.js b/packages/medusa/src/services/order.js index c63e3d2cdb..07e1a96901 100644 --- a/packages/medusa/src/services/order.js +++ b/packages/medusa/src/services/order.js @@ -178,6 +178,7 @@ class OrderService extends BaseService { /** * @param {Object} selector - the query object for find + * @param {Object} config - the config to be used for find * @return {Promise} the result of the find operation */ async list( @@ -187,9 +188,8 @@ class OrderService extends BaseService { const orderRepo = this.manager_.getCustomRepository(this.orderRepository_) const query = this.buildQuery_(selector, config) - const { select, relations, totalsToSelect } = this.transformQueryForTotals_( - config - ) + const { select, relations, totalsToSelect } = + this.transformQueryForTotals_(config) if (select && select.length) { query.select = select @@ -201,7 +201,7 @@ class OrderService extends BaseService { const raw = await orderRepo.find(query) - return raw.map(r => this.decorateTotals_(r, totalsToSelect)) + return raw.map((r) => this.decorateTotals_(r, totalsToSelect)) } async listAndCount( @@ -231,11 +231,11 @@ class OrderService extends BaseService { }, } - query.where = qb => { + query.where = (qb) => { qb.where(where) qb.andWhere( - new Brackets(qb => { + new Brackets((qb) => { qb.where(`shipping_address.first_name ILIKE :qfn`, { qfn: `%${q}%`, }) @@ -246,20 +246,19 @@ class OrderService extends BaseService { } } - const { select, relations, totalsToSelect } = this.transformQueryForTotals_( - config - ) + const { select, relations, totalsToSelect } = + this.transformQueryForTotals_(config) if (select && select.length) { query.select = select } - let rels = relations + const rels = relations delete query.relations const raw = await orderRepo.findWithRelations(rels, query) const count = await orderRepo.count(query) - const orders = raw.map(r => this.decorateTotals_(r, totalsToSelect)) + const orders = raw.map((r) => this.decorateTotals_(r, totalsToSelect)) return [orders, count] } @@ -289,7 +288,7 @@ class OrderService extends BaseService { "swaps.additional_items.refundable", ] - const totalsToSelect = select.filter(v => totalFields.includes(v)) + const totalsToSelect = select.filter((v) => totalFields.includes(v)) if (totalsToSelect.length > 0) { const relationSet = new Set(relations) relationSet.add("items") @@ -305,7 +304,7 @@ class OrderService extends BaseService { relationSet.add("region") relations = [...relationSet] - select = select.filter(v => !totalFields.includes(v)) + select = select.filter((v) => !totalFields.includes(v)) } return { @@ -318,15 +317,15 @@ class OrderService extends BaseService { /** * Gets an order by id. * @param {string} orderId - id of order to retrieve + * @param {Object} config - config of order to retrieve * @return {Promise} the order document */ async retrieve(orderId, config = {}) { const orderRepo = this.manager_.getCustomRepository(this.orderRepository_) const validatedId = this.validateId_(orderId) - const { select, relations, totalsToSelect } = this.transformQueryForTotals_( - config - ) + const { select, relations, totalsToSelect } = + this.transformQueryForTotals_(config) const query = { where: { id: validatedId }, @@ -357,14 +356,14 @@ class OrderService extends BaseService { /** * Gets an order by cart id. * @param {string} cartId - cart id to find order + * @param {Object} config - the config to be used to find order * @return {Promise} the order document */ async retrieveByCartId(cartId, config = {}) { const orderRepo = this.manager_.getCustomRepository(this.orderRepository_) - const { select, relations, totalsToSelect } = this.transformQueryForTotals_( - config - ) + const { select, relations, totalsToSelect } = + this.transformQueryForTotals_(config) const query = { where: { cart_id: cartId }, @@ -397,7 +396,7 @@ class OrderService extends BaseService { * @return {Promise} the order document */ async existsByCartId(cartId) { - const order = await this.retrieveByCartId(cartId).catch(_ => undefined) + const order = await this.retrieveByCartId(cartId).catch((_) => undefined) if (!order) { return false } @@ -409,7 +408,7 @@ class OrderService extends BaseService { * @return {Promise} the result of the find operation */ async completeOrder(orderId) { - return this.atomicPhase_(async manager => { + return this.atomicPhase_(async (manager) => { const order = await this.retrieve(orderId) if (order.status === "canceled") { @@ -428,7 +427,7 @@ class OrderService extends BaseService { } ) - await completeOrderJob.finished().catch(error => { + await completeOrderJob.finished().catch((error) => { throw error }) @@ -445,7 +444,7 @@ class OrderService extends BaseService { * @return {Promise} resolves to the creation result. */ async createFromCart(cartId) { - return this.atomicPhase_(async manager => { + return this.atomicPhase_(async (manager) => { const cart = await this.cartService_ .withTransaction(manager) .retrieve(cartId, { @@ -619,6 +618,7 @@ class OrderService extends BaseService { * @param {string} fulfillmentId - the fulfillment that has now been shipped * @param {TrackingLink[]} trackingLinks - array of tracking numebers * associated with the shipment + * @param {Object} config - the config of the order that has been shipped * @param {Dictionary} metadata - optional metadata to add to * the fulfillment * @return {order} the resulting order following the update. @@ -634,7 +634,7 @@ class OrderService extends BaseService { ) { const { metadata, no_notification } = config - return this.atomicPhase_(async manager => { + return this.atomicPhase_(async (manager) => { const order = await this.retrieve(orderId, { relations: ["items"] }) const shipment = await this.fulfillmentService_.retrieve(fulfillmentId) @@ -666,7 +666,7 @@ class OrderService extends BaseService { order.fulfillment_status = "shipped" for (const item of order.items) { - const shipped = shipmentRes.items.find(si => si.item_id === item.id) + const shipped = shipmentRes.items.find((si) => si.item_id === item.id) if (shipped) { const shippedQty = (item.shipped_quantity || 0) + shipped.quantity if (shippedQty !== item.quantity) { @@ -700,11 +700,11 @@ class OrderService extends BaseService { /** * Creates an order - * @param {object} order - the order to create + * @param {object} data - the data to create an order * @return {Promise} resolves to the creation result. */ async create(data) { - return this.atomicPhase_(async manager => { + return this.atomicPhase_(async (manager) => { const orderRepo = manager.getCustomRepository(this.orderRepository_) const order = orderRepo.create(data) const result = await orderRepo.save(order) @@ -720,7 +720,7 @@ class OrderService extends BaseService { /** * Updates the order's billing address. - * @param {string} orderId - the id of the order to update + * @param {object} order - the order to update * @param {object} address - the value to set the billing address to * @return {Promise} the result of the update operation */ @@ -755,7 +755,7 @@ class OrderService extends BaseService { /** * Updates the order's shipping address. - * @param {string} orderId - the id of the order to update + * @param {object} order - the order to update * @param {object} address - the value to set the shipping address to * @return {Promise} the result of the update operation */ @@ -787,7 +787,7 @@ class OrderService extends BaseService { } async addShippingMethod(orderId, optionId, data, config = {}) { - return this.atomicPhase_(async manager => { + return this.atomicPhase_(async (manager) => { const order = await this.retrieve(orderId, { select: ["subtotal"], relations: [ @@ -845,7 +845,7 @@ class OrderService extends BaseService { * @return {Promise} resolves to the update result. */ async update(orderId, update) { - return this.atomicPhase_(async manager => { + return this.atomicPhase_(async (manager) => { const order = await this.retrieve(orderId) if (order.status === "canceled") { @@ -874,13 +874,7 @@ class OrderService extends BaseService { ) } - const { - metadata, - items, - billing_address, - shipping_address, - ...rest - } = update + const { ...rest } = update if ("metadata" in update) { order.metadata = this.setMetadata_(order, update.metadata) @@ -932,7 +926,7 @@ class OrderService extends BaseService { * @return {Promise} result of the update operation. */ async cancel(orderId) { - return this.atomicPhase_(async manager => { + return this.atomicPhase_(async (manager) => { const order = await this.retrieve(orderId, { relations: [ "fulfillments", @@ -952,16 +946,16 @@ class OrderService extends BaseService { } const throwErrorIf = (arr, pred, type) => - arr?.filter(pred).find(_ => { + arr?.filter(pred).find((_) => { throw new MedusaError( MedusaError.Types.NOT_ALLOWED, `All ${type} must be canceled before canceling an order` ) }) - const notCanceled = o => !o.canceled_at + const notCanceled = (o) => !o.canceled_at throwErrorIf(order.fulfillments, notCanceled, "fulfillments") - throwErrorIf(order.returns, r => r.status !== "canceled", "returns") + throwErrorIf(order.returns, (r) => r.status !== "canceled", "returns") throwErrorIf(order.swaps, notCanceled, "swaps") throwErrorIf(order.claims, notCanceled, "claims") @@ -1000,7 +994,7 @@ class OrderService extends BaseService { * @return {Promise} result of the update operation. */ async capturePayment(orderId) { - return this.atomicPhase_(async manager => { + return this.atomicPhase_(async (manager) => { const orderRepo = manager.getCustomRepository(this.orderRepository_) const order = await this.retrieve(orderId, { relations: ["payments"] }) @@ -1017,7 +1011,7 @@ class OrderService extends BaseService { const result = await this.paymentProviderService_ .withTransaction(manager) .capturePayment(p) - .catch(err => { + .catch((err) => { this.eventBus_ .withTransaction(manager) .emit(OrderService.Events.PAYMENT_CAPTURE_FAILED, { @@ -1039,7 +1033,7 @@ class OrderService extends BaseService { } order.payments = payments - order.payment_status = payments.every(p => p.captured_at !== null) + order.payment_status = payments.every((p) => p.captured_at !== null) ? "captured" : "requires_action" @@ -1095,6 +1089,8 @@ class OrderService extends BaseService { * we need to partition the order items, such that they can be sent * to their respective fulfillment provider. * @param {string} orderId - id of order to cancel. + * @param {Object} itemsToFulfill - items to fulfil. + * @param {Object} config - the config to cancel. * @return {Promise} result of the update operation. */ async createFulfillment( @@ -1107,7 +1103,7 @@ class OrderService extends BaseService { ) { const { metadata, no_notification } = config - return this.atomicPhase_(async manager => { + return this.atomicPhase_(async (manager) => { // NOTE: we are telling the service to calculate all totals for us which // will add to what is fetched from the database. We want this to happen // so that we get all order details. These will thereafter be forwarded @@ -1169,7 +1165,7 @@ class OrderService extends BaseService { // Update all line items to reflect fulfillment for (const item of order.items) { const fulfillmentItem = successfullyFulfilled.find( - f => item.id === f.item_id + (f) => item.id === f.item_id ) if (fulfillmentItem) { @@ -1217,10 +1213,10 @@ class OrderService extends BaseService { /** * Cancels a fulfillment (if related to an order) * @param {string} fulfillmentId - the ID of the fulfillment to cancel - * @returns updated order + * @return {Promise} updated order */ async cancelFulfillment(fulfillmentId) { - return this.atomicPhase_(async manager => { + return this.atomicPhase_(async (manager) => { const canceled = await this.fulfillmentService_ .withTransaction(manager) .cancelFulfillment(fulfillmentId) @@ -1264,12 +1260,12 @@ class OrderService extends BaseService { async getFulfillmentItems_(order, items, transformer) { const toReturn = await Promise.all( items.map(async ({ item_id, quantity }) => { - const item = order.items.find(i => i.id.equals(item_id)) + const item = order.items.find((i) => i.id.equals(item_id)) return transformer(item, quantity) }) ) - return toReturn.filter(i => !!i) + return toReturn.filter((i) => !!i) } /** @@ -1279,7 +1275,7 @@ class OrderService extends BaseService { * @return {Promise} the result of the update operation */ async archive(orderId) { - return this.atomicPhase_(async manager => { + return this.atomicPhase_(async (manager) => { const order = await this.retrieve(orderId) if (order.status !== ("completed" || "refunded")) { @@ -1298,6 +1294,12 @@ class OrderService extends BaseService { /** * Refunds a given amount back to the customer. + * @param {string} orderId - id of the order to refund. + * @param {float} refundAmount - the amount to refund. + * @param {string} reason - the reason to refund. + * @param {string} note - note for refund. + * @param {Object} config - the config for refund. + * @return {Promise} the result of the refund operation. */ async createRefund( orderId, @@ -1310,7 +1312,7 @@ class OrderService extends BaseService { ) { const { no_notification } = config - return this.atomicPhase_(async manager => { + return this.atomicPhase_(async (manager) => { const order = await this.retrieve(orderId, { select: ["refundable_amount", "total", "refunded_total"], relations: ["payments"], @@ -1380,7 +1382,7 @@ class OrderService extends BaseService { } if (totalsFields.includes("items.refundable")) { - order.items = order.items.map(i => ({ + order.items = order.items.map((i) => ({ ...i, refundable: this.totalsService_.getLineItemRefund(order, { ...i, @@ -1395,7 +1397,7 @@ class OrderService extends BaseService { order.swaps.length ) { for (const s of order.swaps) { - s.additional_items = s.additional_items.map(i => ({ + s.additional_items = s.additional_items.map((i) => ({ ...i, refundable: this.totalsService_.getLineItemRefund(order, { ...i, @@ -1418,10 +1420,11 @@ class OrderService extends BaseService { * mismatches. * @param {string} orderId - the order to return. * @param {object} receivedReturn - the received return + * @param {float} customRefundAmount - the custom refund amount return * @return {Promise} the result of the update operation */ async registerReturnReceived(orderId, receivedReturn, customRefundAmount) { - return this.atomicPhase_(async manager => { + return this.atomicPhase_(async (manager) => { const order = await this.retrieve(orderId, { select: ["total", "refunded_total", "refundable_amount"], relations: ["items", "returns", "payments"], @@ -1441,7 +1444,7 @@ class OrderService extends BaseService { ) } - let refundAmount = customRefundAmount || receivedReturn.refund_amount + const refundAmount = customRefundAmount || receivedReturn.refund_amount const orderRepo = manager.getCustomRepository(this.orderRepository_) @@ -1510,7 +1513,7 @@ class OrderService extends BaseService { const keyPath = `metadata.${key}` return this.orderModel_ .updateOne({ _id: validatedId }, { $unset: { [keyPath]: "" } }) - .catch(err => { + .catch((err) => { throw new MedusaError(MedusaError.Types.DB_ERROR, err.message) }) }