chore: fix yarn.lock conflicts

This commit is contained in:
olivermrbl committed 2022-07-05 10:17:09 +02:00
commit 915359d84b
400 files changed
+16918 -15217

No files matched your search

+8
View File
@@ -0,0 +1,8 @@
# Changesets
Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
with multi-package repos, or single-package repos to help you version and publish your code. You can
find the full documentation for it [in our repository](https://github.com/changesets/changesets)
We have a quick list of common questions to get you started engaging with this project in
[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)
+11
View File
@@ -0,0 +1,11 @@
{
"$schema": "https://unpkg.com/@changesets/config@2.0.0/schema.json",
"changelog": ["@changesets/changelog-github", { "repo": "medusajs/medusa" }],
"commit": false,
"fixed": [],
"linked": [],
"access": "public",
"baseBranch": "master",
"updateInternalDependencies": "patch",
"ignore": []
}
+1 -1
View File
@@ -29,7 +29,7 @@ module.exports = {
files: [`*.ts`],
parser: `@typescript-eslint/parser`,
plugins: [`@typescript-eslint/eslint-plugin`],
extends: [`plugin:@typescript-eslint/recommended`],
extends: [`plugin:@typescript-eslint/recommended`, "prettier"],
rules: {
"valid-jsdoc": [
"error",
+20
View File
@@ -0,0 +1,20 @@
#.kodiak.toml
version = 1
[merge]
automerge_label = "automerge"
require_automerge_label = true
blocking_labels = ["on hold", "wip", "blocked"]
method = "squash"
delete_branch_on_merge = true
optimistic_updates = false
prioritize_ready_to_merge = true
notify_on_conflict = true
[merge.message]
title = "pull_request_title"
body = "pull_request_body"
include_coauthors= true
include_pr_number = true
body_type = "markdown"
strip_html_comments = true
+42
View File
@@ -0,0 +1,42 @@
name: "Setup test env"
description: "Setup test environment for actions"
inputs:
node-version:
description: "Node version"
required: false
default: "14"
cache-extension:
description: "Extension for fetching cached dependencies"
required: true
runs:
using: "composite"
steps:
- name: Cancel Previous Runs
uses: styfle/cancel-workflow-action@0.9.1
with:
access_token: ${{ github.token }}
- name: Setup Node.js environment
uses: actions/setup-node@v2.4.1
with:
node-version: ${{ inputs.node-version }}
cache: "yarn"
- name: Bootstrap packages
uses: ./.github/actions/cache-bootstrap
with:
extension: ${{ inputs.cache-extension }}
- name: Build Packages
shell: "bash"
run: yarn build
- name: Install dev cli
shell: "bash"
run: sudo npm i -g medusa-dev-cli
- name: Set path to medusa repo
shell: "bash"
run: medusa-dev --set-path-to-repo $(pwd)
+33
View File
@@ -0,0 +1,33 @@
name: "Test server"
description: "Test the currently running medusa server to see if a user has been created and that the server is seeded"
inputs:
email:
description: "email of user to log in"
required: false
default: "test@test.com"
password:
description: "password of user to log in"
required: false
default: "password"
pathToSeedData:
description: "path to seed data"
required: false
default: "../cli-test/data/seed.json"
runs:
using: "composite"
steps:
- name: Wait for live server response
shell: "bash"
run: ./integration-tests/scripts/cli/wait-for-server-live.sh
- name: Log in with user
shell: "bash"
run: ./integration-tests/scripts/cli/login.sh ${{ inputs.email }} ${{ inputs.password }}
- name: GetProducts
shell: "bash"
run: ./integration-tests/scripts/cli/get-products.sh ${{ inputs.pathToSeedData }}
- name: Kill server
shell: "bash"
run: kill -9 $(lsof -t -i :9000)
+12 -3
View File
@@ -1,9 +1,9 @@
name: Medusa Pipeline
on:
on:
pull_request:
paths-ignore:
- 'docs/**'
- 'www/**'
- "docs/**"
- "www/**"
jobs:
unit-tests:
@@ -43,6 +43,15 @@ jobs:
runs-on: ubuntu-latest
services:
redis:
image: redis
options: >-
--health-cmd "redis-cli ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 6379:6379
postgres:
image: postgres
env:
+133
View File
@@ -0,0 +1,133 @@
# Inspired from https://github.com/Shopify/quilt/blob/main/.github/workflows/snapit.yml
name: Snapshot This
on:
issue_comment:
types:
- created
concurrency: ${{ github.workflow }}-${{ github.ref }}
jobs:
snapshot:
name: Snapshot Release
if: |
github.event.issue.pull_request &&
github.event.comment.body == '/snapshot-this'
runs-on: ubuntu-latest
steps:
- name: Validate pull request
uses: actions/github-script@v6
with:
script: |
try {
// Add a rocket reaction to the comment
await github.rest.reactions.createForIssueComment({
...context.repo,
comment_id: context.payload.comment.id,
content: 'rocket',
})
// Only allow comment creators who have "write" permissions to repo
const actorPermission = (await github.rest.repos.getCollaboratorPermissionLevel({
...context.repo,
username: context.actor
})).data.permission
const isPermitted = ['write', 'admin'].includes(actorPermission)
if (!isPermitted) {
const errorMessage = 'Only users with write permission to the respository can run /snapshot-this'
await github.rest.issues.createComment({
...context.repo,
issue_number: context.issue.number,
body: errorMessage,
})
core.setFailed(errorMessage)
return;
}
const pullRequest = await github.rest.pulls.get({
...context.repo,
pull_number: context.issue.number,
})
// Pull request from fork
if (context.payload.repository.full_name !== pullRequest.data.head.repo.full_name) {
const errorMessage = '`/snapshot-this` is not supported on pull requests from forked repositories.'
await github.rest.issues.createComment({
...context.repo,
issue_number: context.issue.number,
body: errorMessage,
})
core.setFailed(errorMessage)
}
} catch (err) {
core.setFailed(`Request failed with error ${err}`)
}
- name: Checkout pull request branch
uses: actions/checkout@v2
with:
ref: ${{ format('refs/pull/{0}/merge', github.event.issue.number) }}
# Because changeset entries are consumed and removed on the
# 'changeset-release/main' branch, we need to reset the files
# so the following 'changeset version --snapshot' command will
# regenerate the package version bumps with the snapshot releases
- name: Reset changeset entries on changeset-release/main branch
run: |
if [[ $(git branch --show-current) == 'changeset-release/main' ]]; then
git checkout origin/main -- .changeset
fi
- name: Setup Node.js
uses: actions/setup-node@v2
with:
node-version: 16.x
cache: "yarn"
- name: Bootstrap packages
uses: ./.github/actions/cache-bootstrap
with:
extension: snapshot-this
- name: Create an .npmrc
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
run: |
cat << EOF > "$HOME/.npmrc"
//registry.npmjs.org/:_authToken=$NPM_TOKEN
EOF
- name: Create and publish snapshot release
uses: actions/github-script@v6
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
script: |
await exec.exec('yarn run changeset version --snapshot snapshot')
const {stdout} = await exec.getExecOutput('yarn run release:snapshot')
const newTags = Array
.from(stdout.matchAll(/New tag:\s+([^\s\n]+)/g))
.map(([_, tag]) => tag)
if (newTags.length) {
const multiple = newTags.length > 1
const body = (
`#### :rocket: A snapshot release has been made for this PR\n\n` +
`Test the snapshot${multiple ? 's' : ''} by updating your \`package.json\` ` +
`with the newly published version${multiple ? 's' : ''}:\n` +
newTags.map(tag => (
'```sh\n' +
`yarn add ${tag}\n` +
'```'
)).join('\n') +
`\n\n> Latest commit: ${context.sha}`
)
await github.rest.issues.createComment({
...context.repo,
issue_number: context.issue.number,
body,
})
await github.rest.reactions.createForIssueComment({
...context.repo,
comment_id: context.payload.comment.id,
content: 'hooray',
})
}
@@ -0,0 +1,100 @@
name: CLI Pipeline
on: [pull_request]
jobs:
test-cli-with-database:
env:
REDIS_URL: redis://localhost:6379
DATABASE_URL: "postgres://postgres:postgres@localhost/cli-test"
strategy:
matrix:
db: [sqlite, postgres]
services:
redis:
image: redis
# Set health checks to wait until redis has started
options: >-
--health-cmd "redis-cli ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 6379:6379
postgres:
image: postgres
env:
POSTGRES_PASSWORD: postgres
POSTGRES_USER: postgres
POSTGRES_DB: cli-test
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432:5432
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2.3.5
with:
fetch-depth: 0
- name: Setup development server
uses: ./.github/actions/setup-server
with:
cache-extension: "cli-test"
- name: Install Medusa cli
run: yarn global add @medusajs/medusa-cli
- name: Create Medusa project
run: |
medusa new cli-test
working-directory: ..
- name: Install postgres config
if: matrix.db == 'postgres'
run: |
curl \
https://raw.githubusercontent.com/medusajs/medusa-starter-default/feat/postgres-config/medusa-config.js \
--output medusa-config.js
working-directory: ../cli-test
- name: run medusa dev
run: medusa-dev --force-install
working-directory: ../cli-test
- name: Run migrations
run: medusa migrations run
working-directory: ../cli-test
- name: Seed db
run: yarn seed
working-directory: ../cli-test
- name: Create admin user
run: medusa user -e test@test.com -p password -i admin_123
working-directory: ../cli-test
########################## Test medusa develop ###############################
- name: Run development server
run: medusa develop > /dev/null 2>&1 &
working-directory: ../cli-test
- name: Testing development server
uses: ./.github/actions/test-server
########################### Test medusa start ################################
- name: Starting medusa
run: medusa start > /dev/null 2>&1 &
working-directory: ../cli-test
- name: Testing server
uses: ./.github/actions/test-server
+11 -12
View File
@@ -49,7 +49,6 @@ A basic Medusa server installed with the `medusa new` command has dependencies s
"medusa-interfaces": "^1.3.0",
"medusa-payment-manual": "^1.0.16",
"medusa-payment-stripe": "^1.1.38",
"mongoose": "^5.13.3",
"typeorm": "^0.2.36"
},
"devDependencies": {
@@ -169,10 +168,10 @@ const plugins = [
{
resolve: `medusa-plugin-custom`,
options: {
name: 'My Store'
}
}
];
name: "My Store",
},
},
]
```
Then, you can have access to your plugin configuration in the constructor of services in your plugin:
@@ -195,11 +194,11 @@ export default (rootDirectory, options) => {
router.get("/hello-world", (req, res) => {
res.json({
message: `Welcome to ${options.name ? options.name : 'Medusa'}!`
message: `Welcome to ${options.name ? options.name : "Medusa"}!`,
})
})
return router;
return router
}
```
@@ -249,10 +248,10 @@ const plugins = [
resolve: `medusa-plugin-custom`,
//if your plugin has configurations
options: {
name: 'My Store'
}
}
];
name: "My Store",
},
},
]
```
:::note
@@ -269,7 +268,7 @@ npm run start
## NPM Ignore File
Not all files that you use while developing your plugin are necessary to be published.
Not all files that you use while developing your plugin are necessary to be published.
For example, the files you add in the `src` directory are compiled to a `dist` directory before publishing. Then, when a developer installs your plugin, they’ll just be using the files under the `dist` directory.
@@ -189,20 +189,20 @@ Object {
"collections": Array [
Object {
"created_at": Any<String>,
"handle": "test-collection",
"id": "test-collection",
"handle": "test-collection2",
"id": "test-collection2",
"products": Array [
Object {
"collection_id": "test-collection",
"collection_id": "test-collection2",
"created_at": Any<String>,
"deleted_at": null,
"description": "test-product-description",
"discountable": true,
"external_id": null,
"handle": "test-product",
"handle": "test-product_filtering_2",
"height": null,
"hs_code": null,
"id": "test-product",
"id": "test-product_filtering_2",
"is_giftcard": false,
"length": null,
"material": null,
@@ -210,44 +210,17 @@ Object {
"mid_code": null,
"origin_country": null,
"profile_id": StringMatching /\\^sp_\\*/,
"status": "draft",
"status": "published",
"subtitle": null,
"thumbnail": null,
"title": "Test product",
"type_id": "test-type",
"updated_at": Any<String>,
"weight": null,
"width": null,
},
Object {
"collection_id": "test-collection",
"created_at": Any<String>,
"deleted_at": null,
"description": "test-product-description1",
"discountable": true,
"external_id": null,
"handle": "test-product1",
"height": null,
"hs_code": null,
"id": "test-product1",
"is_giftcard": false,
"length": null,
"material": null,
"metadata": null,
"mid_code": null,
"origin_country": null,
"profile_id": StringMatching /\\^sp_\\*/,
"status": "draft",
"subtitle": null,
"thumbnail": null,
"title": "Test product1",
"title": "Test product filtering 2",
"type_id": "test-type",
"updated_at": Any<String>,
"weight": null,
"width": null,
},
],
"title": "Test collection",
"title": "Test collection 2",
"updated_at": Any<String>,
},
Object {
@@ -315,20 +288,20 @@ Object {
},
Object {
"created_at": Any<String>,
"handle": "test-collection2",
"id": "test-collection2",
"handle": "test-collection",
"id": "test-collection",
"products": Array [
Object {
"collection_id": "test-collection2",
"collection_id": "test-collection",
"created_at": Any<String>,
"deleted_at": null,
"description": "test-product-description",
"discountable": true,
"external_id": null,
"handle": "test-product_filtering_2",
"handle": "test-product",
"height": null,
"hs_code": null,
"id": "test-product_filtering_2",
"id": "test-product",
"is_giftcard": false,
"length": null,
"material": null,
@@ -336,17 +309,44 @@ Object {
"mid_code": null,
"origin_country": null,
"profile_id": StringMatching /\\^sp_\\*/,
"status": "published",
"status": "draft",
"subtitle": null,
"thumbnail": null,
"title": "Test product filtering 2",
"title": "Test product",
"type_id": "test-type",
"updated_at": Any<String>,
"weight": null,
"width": null,
},
Object {
"collection_id": "test-collection",
"created_at": Any<String>,
"deleted_at": null,
"description": "test-product-description1",
"discountable": true,
"external_id": null,
"handle": "test-product1",
"height": null,
"hs_code": null,
"id": "test-product1",
"is_giftcard": false,
"length": null,
"material": null,
"metadata": null,
"mid_code": null,
"origin_country": null,
"profile_id": StringMatching /\\^sp_\\*/,
"status": "draft",
"subtitle": null,
"thumbnail": null,
"title": "Test product1",
"type_id": "test-type",
"updated_at": Any<String>,
"weight": null,
"width": null,
},
],
"title": "Test collection 2",
"title": "Test collection",
"updated_at": Any<String>,
},
],
@@ -12,13 +12,13 @@ Array [
"created_at": Any<String>,
"id": "tag3",
"updated_at": Any<String>,
"value": "123",
"value": "1235",
},
Object {
"created_at": Any<String>,
"id": "tag4",
"updated_at": Any<String>,
"value": "123",
"value": "1234",
},
]
`;
@@ -35,13 +35,13 @@ Array [
"created_at": Any<String>,
"id": "tag3",
"updated_at": Any<String>,
"value": "123",
"value": "1235",
},
Object {
"created_at": Any<String>,
"id": "tag4",
"updated_at": Any<String>,
"value": "123",
"value": "1234",
},
]
`;
@@ -79,6 +79,8 @@ Array [
"allow_backorder": false,
"barcode": "test-barcode",
"calculated_price": null,
"calculated_price_incl_tax": null,
"calculated_tax": null,
"created_at": Any<String>,
"deleted_at": null,
"ean": "test-ean",
@@ -105,6 +107,8 @@ Array [
],
"origin_country": null,
"original_price": null,
"original_price_incl_tax": null,
"original_tax": null,
"prices": Array [
Object {
"amount": 100,
@@ -123,6 +127,7 @@ Array [
],
"product_id": StringMatching /\\^test-\\*/,
"sku": "test-sku",
"tax_rates": null,
"title": "Test variant",
"upc": "test-upc",
"updated_at": Any<String>,
@@ -133,6 +138,8 @@ Array [
"allow_backorder": false,
"barcode": null,
"calculated_price": null,
"calculated_price_incl_tax": null,
"calculated_tax": null,
"created_at": Any<String>,
"deleted_at": null,
"ean": "test-ean2",
@@ -159,6 +166,8 @@ Array [
],
"origin_country": null,
"original_price": null,
"original_price_incl_tax": null,
"original_tax": null,
"prices": Array [
Object {
"amount": 100,
@@ -177,6 +186,7 @@ Array [
],
"product_id": StringMatching /\\^test-\\*/,
"sku": "test-sku2",
"tax_rates": null,
"title": "Test variant rank (2)",
"upc": "test-upc2",
"updated_at": Any<String>,
@@ -187,6 +197,8 @@ Array [
"allow_backorder": false,
"barcode": "test-barcode 1",
"calculated_price": null,
"calculated_price_incl_tax": null,
"calculated_tax": null,
"created_at": Any<String>,
"deleted_at": null,
"ean": "test-ean1",
@@ -213,6 +225,8 @@ Array [
],
"origin_country": null,
"original_price": null,
"original_price_incl_tax": null,
"original_tax": null,
"prices": Array [
Object {
"amount": 100,
@@ -231,6 +245,7 @@ Array [
],
"product_id": StringMatching /\\^test-\\*/,
"sku": "test-sku1",
"tax_rates": null,
"title": "Test variant rank (1)",
"upc": "test-upc1",
"updated_at": Any<String>,
@@ -241,6 +256,8 @@ Array [
"allow_backorder": false,
"barcode": "test-barcode-sale",
"calculated_price": null,
"calculated_price_incl_tax": null,
"calculated_tax": null,
"created_at": Any<String>,
"deleted_at": null,
"ean": "test-ean-sale",
@@ -267,6 +284,8 @@ Array [
],
"origin_country": null,
"original_price": null,
"original_price_incl_tax": null,
"original_tax": null,
"prices": Array [
Object {
"amount": 1000,
@@ -285,6 +304,7 @@ Array [
],
"product_id": StringMatching /\\^test-\\*/,
"sku": "test-sku-sale",
"tax_rates": null,
"title": "Test variant",
"upc": "test-upc-sale",
"updated_at": Any<String>,
@@ -353,6 +373,8 @@ Array [
"allow_backorder": false,
"barcode": null,
"calculated_price": null,
"calculated_price_incl_tax": null,
"calculated_tax": null,
"created_at": Any<String>,
"deleted_at": null,
"ean": "test-ean4",
@@ -379,6 +401,8 @@ Array [
],
"origin_country": null,
"original_price": null,
"original_price_incl_tax": null,
"original_tax": null,
"prices": Array [
Object {
"amount": 100,
@@ -397,6 +421,7 @@ Array [
],
"product_id": StringMatching /\\^test-\\*/,
"sku": "test-sku4",
"tax_rates": null,
"title": "Test variant rank (2)",
"upc": "test-upc4",
"updated_at": Any<String>,
@@ -407,6 +432,8 @@ Array [
"allow_backorder": false,
"barcode": null,
"calculated_price": null,
"calculated_price_incl_tax": null,
"calculated_tax": null,
"created_at": Any<String>,
"deleted_at": null,
"ean": "test-ean3",
@@ -433,6 +460,8 @@ Array [
],
"origin_country": null,
"original_price": null,
"original_price_incl_tax": null,
"original_tax": null,
"prices": Array [
Object {
"amount": 100,
@@ -451,6 +480,7 @@ Array [
],
"product_id": StringMatching /\\^test-\\*/,
"sku": "test-sku3",
"tax_rates": null,
"title": "Test variant rank (2)",
"upc": "test-upc3",
"updated_at": Any<String>,
@@ -609,6 +639,8 @@ Array [
"allow_backorder": false,
"barcode": null,
"calculated_price": null,
"calculated_price_incl_tax": null,
"calculated_tax": null,
"created_at": Any<String>,
"deleted_at": null,
"ean": null,
@@ -635,6 +667,8 @@ Array [
],
"origin_country": null,
"original_price": null,
"original_price_incl_tax": null,
"original_tax": null,
"prices": Array [
Object {
"amount": 100,
@@ -653,6 +687,7 @@ Array [
],
"product_id": StringMatching /\\^prod_\\*/,
"sku": null,
"tax_rates": null,
"title": "Test variant",
"upc": null,
"updated_at": Any<String>,
@@ -769,6 +804,8 @@ Object {
"allow_backorder": false,
"barcode": null,
"calculated_price": null,
"calculated_price_incl_tax": null,
"calculated_tax": null,
"created_at": Any<String>,
"deleted_at": null,
"ean": null,
@@ -805,6 +842,8 @@ Object {
],
"origin_country": null,
"original_price": null,
"original_price_incl_tax": null,
"original_tax": null,
"prices": Array [
Object {
"amount": 100,
@@ -851,6 +890,7 @@ Object {
],
"product_id": StringMatching /\\^prod_\\*/,
"sku": null,
"tax_rates": null,
"title": "Test variant",
"upc": null,
"updated_at": Any<String>,
@@ -933,6 +973,8 @@ Object {
"allow_backorder": false,
"barcode": "test-barcode",
"calculated_price": null,
"calculated_price_incl_tax": null,
"calculated_tax": null,
"created_at": Any<String>,
"deleted_at": null,
"ean": "test-ean",
@@ -959,6 +1001,8 @@ Object {
],
"origin_country": null,
"original_price": null,
"original_price_incl_tax": null,
"original_tax": null,
"prices": Array [
Object {
"amount": 75,
@@ -977,6 +1021,7 @@ Object {
],
"product_id": "test-product",
"sku": "test-sku",
"tax_rates": null,
"title": "Test variant",
"upc": "test-upc",
"updated_at": Any<String>,
@@ -176,20 +176,14 @@ describe("/admin/collections", () => {
expect(response.data).toMatchSnapshot({
collections: [
{
id: "test-collection",
handle: "test-collection",
title: "Test collection",
id: "test-collection2",
handle: "test-collection2",
title: "Test collection 2",
created_at: expect.any(String),
updated_at: expect.any(String),
products: [
{
collection_id: "test-collection",
created_at: expect.any(String),
updated_at: expect.any(String),
profile_id: expect.stringMatching(/^sp_*/),
},
{
collection_id: "test-collection",
collection_id: "test-collection2",
created_at: expect.any(String),
updated_at: expect.any(String),
profile_id: expect.stringMatching(/^sp_*/),
@@ -218,14 +212,20 @@ describe("/admin/collections", () => {
],
},
{
id: "test-collection2",
handle: "test-collection2",
title: "Test collection 2",
id: "test-collection",
handle: "test-collection",
title: "Test collection",
created_at: expect.any(String),
updated_at: expect.any(String),
products: [
{
collection_id: "test-collection2",
collection_id: "test-collection",
created_at: expect.any(String),
updated_at: expect.any(String),
profile_id: expect.stringMatching(/^sp_*/),
},
{
collection_id: "test-collection",
created_at: expect.any(String),
updated_at: expect.any(String),
profile_id: expect.stringMatching(/^sp_*/),
@@ -353,6 +353,84 @@ describe("/admin/draft-orders", () => {
)
})
it("creates a draft order with discount and free shipping along the line item", async () => {
const api = useApi()
const payload = {
email: "oli@test.dk",
shipping_address: "oli-shipping",
discounts: [{ code: "TEST" }, { code: "free-shipping"}],
items: [
{
variant_id: "test-variant",
quantity: 2,
metadata: {},
},
],
region_id: "test-region",
customer_id: "oli-test",
shipping_methods: [
{
option_id: "test-option",
},
],
}
const response = await api
.post("/admin/draft-orders", payload, {
headers: {
Authorization: "Bearer test_token",
},
})
.catch((err) => {
console.log(err)
})
const created = await api
.get(`/admin/draft-orders/${response.data.draft_order.id}`, {
headers: {
Authorization: "Bearer test_token",
},
})
.catch((err) => {
console.log(err)
})
const draftOrder = created.data.draft_order
const lineItemId = draftOrder.cart.items[0].id
expect(response.status).toEqual(200)
expect(draftOrder.cart.items).toEqual(
expect.arrayContaining([
expect.objectContaining({
variant_id: "test-variant",
unit_price: 8000,
quantity: 2,
adjustments: expect.arrayContaining([
expect.objectContaining({
item_id: lineItemId,
amount: 1600,
description: "discount",
discount_id: "test-discount",
}),
]),
}),
])
)
// Check that discounts are applied
expect(draftOrder.cart.discounts).toEqual(
expect.arrayContaining([
expect.objectContaining({
code: "TEST",
}),
expect.objectContaining({
code: "free-shipping",
})
])
)
})
it("creates a draft order with created shipping address", async () => {
const api = useApi()
@@ -89,11 +89,10 @@ describe("/admin/product-tags", () => {
updated_at: expect.any(String),
}
expect(res.data.product_tags.map((pt) => pt.value)).toEqual([
"123",
"123",
"123",
])
expect(res.data.product_tags.length).toEqual(3)
expect(res.data.product_tags.map((pt) => pt.value)).toEqual(
expect.arrayContaining(["123", "1235", "1234"])
)
expect(res.data.product_tags).toMatchSnapshot([
tagMatch,
+160 -51
View File
@@ -6,7 +6,11 @@ const { initDb, useDb } = require("../../../helpers/use-db")
const adminSeeder = require("../../helpers/admin-seeder")
const productSeeder = require("../../helpers/product-seeder")
const { ProductVariant, ProductOptionValue, MoneyAmount } = require("@medusajs/medusa")
const {
ProductVariant,
ProductOptionValue,
MoneyAmount,
} = require("@medusajs/medusa")
const priceListSeeder = require("../../helpers/price-list-seeder")
jest.setTimeout(50000)
@@ -18,7 +22,7 @@ describe("/admin/products", () => {
beforeAll(async () => {
const cwd = path.resolve(path.join(__dirname, "..", ".."))
dbConnection = await initDb({ cwd })
medusaProcess = await setupServer({ cwd })
medusaProcess = await setupServer({ cwd, verbose: false })
})
afterAll(async () => {
@@ -232,6 +236,7 @@ describe("/admin/products", () => {
})
expect(response.status).toEqual(200)
expect(response.data.count).toEqual(1)
expect(response.data.products).toEqual([
expect.objectContaining({
id: "test-product_filtering_4",
@@ -256,6 +261,36 @@ describe("/admin/products", () => {
expect(response.data.products.length).toEqual(2)
})
it("returns a list of products with free text query including variant prices", async () => {
const api = useApi()
const response = await api
.get("/admin/products?q=test+product1", {
headers: {
Authorization: "Bearer test_token",
},
})
.catch((err) => {
console.log(err)
})
const expectedVariantPrices = response.data.products[0].variants
.map((v) => v.prices)
.flat(1)
expect(response.status).toEqual(200)
expect(expectedVariantPrices).toEqual(
expect.arrayContaining([
expect.objectContaining({
id: "test-price4",
}),
expect.objectContaining({
id: "test-price3",
}),
])
)
})
it("returns a list of products with free text query and offset", async () => {
const api = useApi()
@@ -1388,7 +1423,7 @@ describe("/admin/products", () => {
})
describe("GET /admin/products/:id/variants", () => {
beforeEach(async() => {
beforeEach(async () => {
try {
await productSeeder(dbConnection)
await adminSeeder(dbConnection)
@@ -1398,12 +1433,12 @@ describe("/admin/products", () => {
}
})
afterEach(async() => {
afterEach(async () => {
const db = useDb()
await db.teardown()
})
it('should return the variants related to the requested product', async () => {
it("should return the variants related to the requested product", async () => {
const api = useApi()
const res = await api
@@ -1420,10 +1455,22 @@ describe("/admin/products", () => {
expect(res.data.variants.length).toBe(4)
expect(res.data.variants).toEqual(
expect.arrayContaining([
expect.objectContaining({ id: "test-variant", product_id: "test-product" }),
expect.objectContaining({ id: "test-variant_1", product_id: "test-product" }),
expect.objectContaining({ id: "test-variant_2", product_id: "test-product" }),
expect.objectContaining({ id: "test-variant-sale", product_id: "test-product" }),
expect.objectContaining({
id: "test-variant",
product_id: "test-product",
}),
expect.objectContaining({
id: "test-variant_1",
product_id: "test-product",
}),
expect.objectContaining({
id: "test-variant_2",
product_id: "test-product",
}),
expect.objectContaining({
id: "test-variant-sale",
product_id: "test-product",
}),
])
)
})
@@ -1812,16 +1859,22 @@ describe("/admin/products", () => {
expect(optValPost).toEqual(undefined)
// Validate that the option still exists in the DB with deleted_at
const optValDeleted = await dbConnection.manager.findOne(ProductOptionValue, {
variant_id: "test-variant_2",
}, {
withDeleted: true,
})
const optValDeleted = await dbConnection.manager.findOne(
ProductOptionValue,
{
variant_id: "test-variant_2",
},
{
withDeleted: true,
}
)
expect(optValDeleted).toEqual(expect.objectContaining({
deleted_at: expect.any(Date),
variant_id: "test-variant_2",
}))
expect(optValDeleted).toEqual(
expect.objectContaining({
deleted_at: expect.any(Date),
variant_id: "test-variant_2",
})
)
})
it("successfully deletes a product and any option value associated with one of its variants", async () => {
@@ -1854,16 +1907,22 @@ describe("/admin/products", () => {
expect(optValPost).toEqual(undefined)
// Validate that the option still exists in the DB with deleted_at
const optValDeleted = await dbConnection.manager.findOne(ProductOptionValue, {
variant_id: "test-variant_2",
}, {
withDeleted: true,
})
const optValDeleted = await dbConnection.manager.findOne(
ProductOptionValue,
{
variant_id: "test-variant_2",
},
{
withDeleted: true,
}
)
expect(optValDeleted).toEqual(expect.objectContaining({
deleted_at: expect.any(Date),
variant_id: "test-variant_2",
}))
expect(optValDeleted).toEqual(
expect.objectContaining({
deleted_at: expect.any(Date),
variant_id: "test-variant_2",
})
)
})
it("successfully deletes a product variant and its associated prices", async () => {
@@ -1889,26 +1948,29 @@ describe("/admin/products", () => {
expect(response.status).toEqual(200)
// Validate that the price was deleted
const pricePost = await dbConnection.manager.findOne(
MoneyAmount,
{
id: "test-price",
}
)
const pricePost = await dbConnection.manager.findOne(MoneyAmount, {
id: "test-price",
})
expect(pricePost).toEqual(undefined)
// Validate that the price still exists in the DB with deleted_at
const optValDeleted = await dbConnection.manager.findOne(MoneyAmount, {
id: "test-price",
}, {
withDeleted: true,
})
const optValDeleted = await dbConnection.manager.findOne(
MoneyAmount,
{
id: "test-price",
},
{
withDeleted: true,
}
)
expect(optValDeleted).toEqual(expect.objectContaining({
deleted_at: expect.any(Date),
id: "test-price",
}))
expect(optValDeleted).toEqual(
expect.objectContaining({
deleted_at: expect.any(Date),
id: "test-price",
})
)
})
it("successfully deletes a product and any prices associated with one of its variants", async () => {
@@ -1938,16 +2000,22 @@ describe("/admin/products", () => {
expect(pricePost).toEqual(undefined)
// Validate that the price still exists in the DB with deleted_at
const optValDeleted = await dbConnection.manager.findOne(MoneyAmount, {
id: "test-price",
}, {
withDeleted: true,
})
const optValDeleted = await dbConnection.manager.findOne(
MoneyAmount,
{
id: "test-price",
},
{
withDeleted: true,
}
)
expect(optValDeleted).toEqual(expect.objectContaining({
deleted_at: expect.any(Date),
id: "test-price",
}))
expect(optValDeleted).toEqual(
expect.objectContaining({
deleted_at: expect.any(Date),
id: "test-price",
})
)
})
it("successfully creates product with soft-deleted product handle and deletes it again", async () => {
@@ -2182,4 +2250,45 @@ describe("/admin/products", () => {
)
})
})
describe("GET /admin/products/tag-usage", () => {
beforeEach(async () => {
try {
await productSeeder(dbConnection)
await adminSeeder(dbConnection)
} catch (err) {
console.log(err)
throw err
}
})
afterEach(async () => {
const db = useDb()
await db.teardown()
})
it("successfully gets the tags usage", async () => {
const api = useApi()
const res = await api
.get("/admin/products/tag-usage", {
headers: {
Authorization: "Bearer test_token",
},
})
.catch((err) => {
console.log(err)
})
expect(res.status).toEqual(200)
expect(res.data.tags.length).toEqual(3)
expect(res.data.tags).toEqual(
expect.arrayContaining([
{ id: "tag1", usage_count: "2", value: "123" },
{ id: "tag3", usage_count: "2", value: "1235" },
{ id: "tag4", usage_count: "1", value: "1234" },
])
)
})
})
})
@@ -1,39 +1,142 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`/admin/batch GET /admin/batch lists batch jobs created by the user 1`] = `
exports[`/admin/batch-jobs GET /admin/batch-jobs lists batch jobs created by the user 1`] = `
Object {
"batch_jobs": Array [
Object {
"canceled_at": null,
"completed_at": "2022-06-27T22:00:00.000Z",
"confirmed_at": null,
"context": Object {},
"created_at": Any<String>,
"created_by": "admin_user",
"deleted_at": null,
"dry_run": false,
"failed_at": null,
"id": "job_5",
"pre_processed_at": null,
"processing_at": null,
"result": null,
"status": "completed",
"type": "product-export",
"updated_at": Any<String>,
},
Object {
"canceled_at": null,
"completed_at": null,
"confirmed_at": null,
"context": Object {},
"created_at": Any<String>,
"created_by": "admin_user",
"deleted_at": null,
"dry_run": false,
"failed_at": null,
"id": "job_3",
"pre_processed_at": null,
"processing_at": null,
"result": null,
"status": "created",
"type": "batch_2",
"type": "product-export",
"updated_at": Any<String>,
},
Object {
"canceled_at": null,
"completed_at": null,
"confirmed_at": null,
"context": Object {},
"created_at": Any<String>,
"created_by": "admin_user",
"deleted_at": null,
"dry_run": false,
"failed_at": null,
"id": "job_2",
"pre_processed_at": null,
"processing_at": null,
"result": null,
"status": "created",
"type": "batch_2",
"type": "product-export",
"updated_at": Any<String>,
},
Object {
"canceled_at": null,
"completed_at": null,
"confirmed_at": null,
"context": Object {},
"created_at": Any<String>,
"created_by": "admin_user",
"deleted_at": null,
"dry_run": false,
"failed_at": null,
"id": "job_1",
"pre_processed_at": null,
"processing_at": null,
"result": null,
"status": "created",
"type": "batch_1",
"type": "product-export",
"updated_at": Any<String>,
},
],
"count": 4,
"limit": 10,
"offset": 0,
}
`;
exports[`/admin/batch-jobs GET /admin/batch-jobs lists batch jobs created by the user and where completed_at is null 1`] = `
Object {
"batch_jobs": Array [
Object {
"canceled_at": null,
"completed_at": null,
"confirmed_at": null,
"context": Object {},
"created_at": Any<String>,
"created_by": "admin_user",
"deleted_at": null,
"dry_run": false,
"failed_at": null,
"id": "job_3",
"pre_processed_at": null,
"processing_at": null,
"result": null,
"status": "created",
"type": "product-export",
"updated_at": Any<String>,
},
Object {
"canceled_at": null,
"completed_at": null,
"confirmed_at": null,
"context": Object {},
"created_at": Any<String>,
"created_by": "admin_user",
"deleted_at": null,
"dry_run": false,
"failed_at": null,
"id": "job_2",
"pre_processed_at": null,
"processing_at": null,
"result": null,
"status": "created",
"type": "product-export",
"updated_at": Any<String>,
},
Object {
"canceled_at": null,
"completed_at": null,
"confirmed_at": null,
"context": Object {},
"created_at": Any<String>,
"created_by": "admin_user",
"deleted_at": null,
"dry_run": false,
"failed_at": null,
"id": "job_1",
"pre_processed_at": null,
"processing_at": null,
"result": null,
"status": "created",
"type": "product-export",
"updated_at": Any<String>,
},
],
@@ -42,3 +145,64 @@ Object {
"offset": 0,
}
`;
exports[`/admin/batch-jobs POST /admin/batch-jobs/ Creates a batch job 1`] = `
Object {
"canceled_at": null,
"completed_at": null,
"confirmed_at": null,
"context": Object {
"list_config": Object {
"order": Object {
"created_at": "DESC",
},
"relations": Array [
"variants",
"variants.prices",
"variants.options",
"images",
"options",
"tags",
"type",
"collection",
"variants.prices.region",
],
"skip": 0,
"take": 50,
},
},
"created_at": Any<String>,
"created_by": "admin_user",
"deleted_at": null,
"dry_run": false,
"failed_at": null,
"id": Any<String>,
"pre_processed_at": null,
"processing_at": null,
"result": null,
"status": "created",
"type": "product-export",
"updated_at": Any<String>,
}
`;
exports[`/admin/batch-jobs POST /admin/batch-jobs/:id/cancel Cancels batch job created by the user 1`] = `
Object {
"canceled_at": Any<String>,
"completed_at": null,
"confirmed_at": null,
"context": Object {},
"created_at": Any<String>,
"created_by": "admin_user",
"deleted_at": null,
"dry_run": false,
"failed_at": null,
"id": "job_1",
"pre_processed_at": null,
"processing_at": null,
"result": null,
"status": "canceled",
"type": "product-export",
"updated_at": Any<String>,
}
`;
+263 -30
View File
@@ -5,6 +5,8 @@ const { useApi } = require("../../../helpers/use-api")
const { initDb, useDb } = require("../../../helpers/use-db")
const adminSeeder = require("../../helpers/admin-seeder")
const userSeeder = require("../../helpers/user-seeder")
const { simpleBatchJobFactory } = require("../../factories")
jest.setTimeout(50000)
@@ -15,14 +17,53 @@ const adminReqConfig = {
},
}
describe("/admin/batch", () => {
const setupJobDb = async (dbConnection) => {
try {
await adminSeeder(dbConnection)
await userSeeder(dbConnection)
await simpleBatchJobFactory(dbConnection, {
id: "job_1",
type: "product-export",
created_by: "admin_user",
})
await simpleBatchJobFactory(dbConnection, {
id: "job_2",
type: "product-export",
created_by: "admin_user",
})
await simpleBatchJobFactory(dbConnection, {
id: "job_3",
type: "product-export",
created_by: "admin_user",
})
await simpleBatchJobFactory(dbConnection, {
id: "job_4",
type: "product-export",
status: "awaiting_confirmation",
created_by: "member-user",
})
await simpleBatchJobFactory(dbConnection, {
id: "job_5",
type: "product-export",
status: "completed",
completed_at: "2022-06-27T22:00:00.000Z",
created_by: "admin_user",
})
} catch (err) {
console.log(err)
throw err
}
}
describe("/admin/batch-jobs", () => {
let medusaProcess
let dbConnection
beforeAll(async () => {
const cwd = path.resolve(path.join(__dirname, "..", ".."))
dbConnection = await initDb({ cwd })
medusaProcess = await setupServer({ cwd, verbose: false })
medusaProcess = await setupServer({ cwd })
})
afterAll(async () => {
@@ -32,34 +73,9 @@ describe("/admin/batch", () => {
medusaProcess.kill()
})
describe("GET /admin/batch", () => {
describe("GET /admin/batch-jobs", () => {
beforeEach(async () => {
try {
await simpleBatchJobFactory(dbConnection, {
id: "job_1",
type: "batch_1",
created_by: "admin_user",
})
await simpleBatchJobFactory(dbConnection, {
id: "job_2",
type: "batch_2",
created_by: "admin_user",
})
await simpleBatchJobFactory(dbConnection, {
id: "job_3",
type: "batch_2",
created_by: "admin_user",
})
await simpleBatchJobFactory(dbConnection, {
id: "job_4",
type: "batch_1",
created_by: "not_this_user",
})
await adminSeeder(dbConnection)
} catch (err) {
console.log(err)
throw err
}
await setupJobDb(dbConnection)
})
afterEach(async () => {
@@ -69,26 +85,243 @@ describe("/admin/batch", () => {
it("lists batch jobs created by the user", async () => {
const api = useApi()
const response = await api.get("/admin/batch", adminReqConfig)
const response = await api.get("/admin/batch-jobs", adminReqConfig)
expect(response.status).toEqual(200)
expect(response.data.batch_jobs.length).toEqual(4)
expect(response.data).toMatchSnapshot({
batch_jobs: [
{
id: "job_5",
created_at: expect.any(String),
updated_at: expect.any(String),
created_by: "admin_user",
},
{
id: "job_3",
created_at: expect.any(String),
updated_at: expect.any(String),
created_by: "admin_user",
},
{
id: "job_2",
created_at: expect.any(String),
updated_at: expect.any(String),
created_by: "admin_user",
},
{
id: "job_1",
created_at: expect.any(String),
updated_at: expect.any(String),
created_by: "admin_user",
},
],
})
})
it("lists batch jobs created by the user and where completed_at is null ", async () => {
const api = useApi()
const response = await api.get(
"/admin/batch-jobs?completed_at=null",
adminReqConfig
)
expect(response.status).toEqual(200)
expect(response.data.batch_jobs.length).toEqual(3)
expect(response.data).toMatchSnapshot({
batch_jobs: [
{
id: "job_3",
created_at: expect.any(String),
updated_at: expect.any(String),
created_by: "admin_user",
},
{
id: "job_2",
created_at: expect.any(String),
updated_at: expect.any(String),
created_by: "admin_user",
},
{
id: "job_1",
created_at: expect.any(String),
updated_at: expect.any(String),
created_by: "admin_user",
},
],
})
})
})
describe("GET /admin/batch-jobs/:id", () => {
beforeEach(async () => {
await setupJobDb(dbConnection)
})
afterEach(async () => {
const db = useDb()
await db.teardown()
})
it("return batch job created by the user", async () => {
const api = useApi()
const response = await api.get("/admin/batch-jobs/job_1", adminReqConfig)
expect(response.status).toEqual(200)
expect(response.data.batch_job).toEqual(
expect.objectContaining({
created_at: expect.any(String),
updated_at: expect.any(String),
created_by: "admin_user",
})
)
})
it("should fail on batch job created by other user", async () => {
const api = useApi()
await api.get("/admin/batch-jobs/job_4", adminReqConfig).catch((err) => {
expect(err.response.status).toEqual(400)
expect(err.response.data.type).toEqual("not_allowed")
expect(err.response.data.message).toEqual(
"Cannot access a batch job that does not belong to the logged in user"
)
})
})
})
describe("POST /admin/batch-jobs/", () => {
beforeEach(async () => {
await setupJobDb(dbConnection)
})
afterEach(async () => {
const db = useDb()
await db.teardown()
})
it("Creates a batch job", async () => {
const api = useApi()
const response = await api.post(
"/admin/batch-jobs",
{
type: "product-export",
context: {},
},
adminReqConfig
)
expect(response.status).toEqual(201)
expect(response.data.batch_job).toMatchSnapshot({
created_by: "admin_user",
status: "created",
id: expect.any(String),
created_at: expect.any(String),
updated_at: expect.any(String),
})
})
})
describe("POST /admin/batch-jobs/:id/confirm", () => {
beforeEach(async () => {
await setupJobDb(dbConnection)
})
afterEach(async () => {
const db = useDb()
await db.teardown()
})
it("Fails to confirm a batch job created by a different user", async () => {
const api = useApi()
const jobId = "job_4"
api
.post(`/admin/batch-jobs/${jobId}/confirm`, {}, adminReqConfig)
.catch((err) => {
expect(err.response.status).toEqual(400)
expect(err.response.data.type).toEqual("not_allowed")
expect(err.response.data.message).toEqual(
"Cannot access a batch job that does not belong to the logged in user"
)
})
})
})
describe("POST /admin/batch-jobs/:id/cancel", () => {
beforeEach(async () => {
try {
await setupJobDb(dbConnection)
await simpleBatchJobFactory(dbConnection, {
id: "job_complete",
type: "product-export",
created_by: "admin_user",
completed_at: new Date(),
})
} catch (e) {
console.log(e)
throw e
}
})
afterEach(async () => {
const db = useDb()
await db.teardown()
})
it("Cancels batch job created by the user", async () => {
const api = useApi()
const jobId = "job_1"
const response = await api.post(
`/admin/batch-jobs/${jobId}/cancel`,
{},
adminReqConfig
)
expect(response.status).toEqual(200)
expect(response.data.batch_job).toMatchSnapshot({
created_at: expect.any(String),
updated_at: expect.any(String),
canceled_at: expect.any(String),
status: "canceled",
})
})
it("Fails to cancel a batch job created by a different user", async () => {
expect.assertions(3)
const api = useApi()
const jobId = "job_4"
api
.post(`/admin/batch-jobs/${jobId}/cancel`, {}, adminReqConfig)
.catch((err) => {
expect(err.response.status).toEqual(400)
expect(err.response.data.type).toEqual("not_allowed")
expect(err.response.data.message).toEqual(
"Cannot access a batch job that does not belong to the logged in user"
)
})
})
it("Fails to cancel a batch job that is already complete", async () => {
expect.assertions(3)
const api = useApi()
const jobId = "job_complete"
await api
.post(`/admin/batch-jobs/${jobId}/cancel`, {}, adminReqConfig)
.catch((err) => {
expect(err.response.status).toEqual(400)
expect(err.response.data.type).toEqual("not_allowed")
expect(err.response.data.message).toEqual(
"Cannot cancel completed batch job"
)
})
})
})
})
@@ -0,0 +1,251 @@
const path = require("path")
const fs = require("fs/promises")
import { sep, resolve } from "path"
const setupServer = require("../../../../helpers/setup-server")
const { useApi } = require("../../../../helpers/use-api")
const { initDb, useDb } = require("../../../../helpers/use-db")
const adminSeeder = require("../../../helpers/admin-seeder")
const userSeeder = require("../../../helpers/user-seeder")
const orderSeeder = require("../../../helpers/order-seeder")
const adminReqConfig = {
headers: {
Authorization: "Bearer test_token",
},
}
jest.setTimeout(1000000)
describe("Batchjob with type order-export", () => {
let medusaProcess
let dbConnection
let exportFilePath = ""
let topDir = ""
beforeAll(async () => {
const cwd = path.resolve(path.join(__dirname, "..", "..", ".."))
dbConnection = await initDb({ cwd })
medusaProcess = await setupServer({
cwd,
redisUrl: "redis://127.0.0.1:6379",
uploadDir: __dirname,
verbose: false,
})
})
afterAll(async () => {
if (topDir !== "") {
await fs.rm(resolve(__dirname, topDir), { recursive: true })
}
const db = useDb()
await db.shutdown()
medusaProcess.kill()
})
beforeEach(async () => {
try {
await adminSeeder(dbConnection)
await userSeeder(dbConnection)
await orderSeeder(dbConnection)
} catch (e) {
console.log(e)
throw e
}
})
afterEach(async () => {
const db = useDb()
await db.teardown()
const isFileExists = (await fs.stat(exportFilePath))?.isFile()
if (isFileExists) {
const [, relativeRoot] = exportFilePath.replace(__dirname, "").split(sep)
if ((await fs.stat(resolve(__dirname, relativeRoot)))?.isDirectory()) {
topDir = relativeRoot
}
await fs.unlink(exportFilePath)
}
})
it("Should export a file containing all orders", async () => {
jest.setTimeout(1000000)
const api = useApi()
const batchPayload = {
type: "order-export",
context: {},
}
const batchJobRes = await api.post(
"/admin/batch-jobs",
batchPayload,
adminReqConfig
)
const batchJobId = batchJobRes.data.batch_job.id
expect(batchJobId).toBeTruthy()
// Pull to check the status until it is completed
let batchJob
let shouldContinuePulling = true
while (shouldContinuePulling) {
const res = await api.get(
`/admin/batch-jobs/${batchJobId}`,
adminReqConfig
)
batchJob = res.data.batch_job
shouldContinuePulling = !(
batchJob.status === "completed" || batchJob.status === "failed"
)
if (shouldContinuePulling) {
await new Promise((resolve, _) => {
setTimeout(resolve, 1000)
})
}
}
expect(batchJob.status).toBe("completed")
expect(batchJob.status).toBe("completed")
exportFilePath = path.resolve(__dirname, batchJob.result.file_key)
const isFileExists = (await fs.stat(exportFilePath)).isFile()
expect(isFileExists).toBeTruthy()
const fileSize = (await fs.stat(exportFilePath)).size
expect(batchJob.result?.file_size).toBe(fileSize)
const data = (await fs.readFile(exportFilePath)).toString()
const [, ...lines] = data.split("\r\n").filter((l) => l)
expect(lines.length).toBe(6)
const csvLine = lines[0].split(";")
expect(csvLine[0]).toBe("discount-order")
expect(csvLine[1]).toBe("6")
expect(csvLine[14]).toBe("fulfilled")
expect(csvLine[15]).toBe("captured")
expect(csvLine[16]).toBe("8000")
})
it("Should export a file containing a limited number of orders", async () => {
jest.setTimeout(1000000)
const api = useApi()
const batchPayload = {
type: "order-export",
context: { batch_size: 3 },
}
const batchJobRes = await api.post(
"/admin/batch-jobs",
batchPayload,
adminReqConfig
)
const batchJobId = batchJobRes.data.batch_job.id
expect(batchJobId).toBeTruthy()
// Pull to check the status until it is completed
let batchJob
let shouldContinuePulling = true
while (shouldContinuePulling) {
const res = await api.get(
`/admin/batch-jobs/${batchJobId}`,
adminReqConfig
)
batchJob = res.data.batch_job
shouldContinuePulling = !(
batchJob.status === "completed" || batchJob.status === "failed"
)
if (shouldContinuePulling) {
await new Promise((resolve, _) => {
setTimeout(resolve, 1000)
})
}
}
exportFilePath = path.resolve(__dirname, batchJob.result.file_key)
const isFileExists = (await fs.stat(exportFilePath)).isFile()
expect(isFileExists).toBeTruthy()
const data = (await fs.readFile(exportFilePath)).toString()
const [, ...lines] = data.split("\r\n").filter((l) => l)
expect(lines.length).toBe(3)
})
it("Should export a file with orders from a single customer", async () => {
jest.setTimeout(1000000)
const api = useApi()
const batchPayload = {
type: "order-export",
context: { filterable_fields: { email: "test@email.com" } },
}
const batchJobRes = await api.post(
"/admin/batch-jobs",
batchPayload,
adminReqConfig
)
const batchJobId = batchJobRes.data.batch_job.id
expect(batchJobId).toBeTruthy()
// Pull to check the status until it is completed
let batchJob
let shouldContinuePulling = true
while (shouldContinuePulling) {
const res = await api.get(
`/admin/batch-jobs/${batchJobId}`,
adminReqConfig
)
batchJob = res.data.batch_job
shouldContinuePulling = !(
batchJob.status === "completed" || batchJob.status === "failed"
)
if (shouldContinuePulling) {
await new Promise((resolve, _) => {
setTimeout(resolve, 1000)
})
}
}
expect(batchJob.status).toBe("completed")
exportFilePath = path.resolve(__dirname, batchJob.result.file_key)
const isFileExists = (await fs.stat(exportFilePath)).isFile()
expect(isFileExists).toBeTruthy()
const data = (await fs.readFile(exportFilePath)).toString()
const [, ...lines] = data.split("\r\n").filter((l) => l)
expect(lines.length).toBe(1)
const csvLine = lines[0].split(";")
expect(csvLine[0]).toBe("test-order")
expect(csvLine[6]).toBe("test@email.com")
})
})
@@ -0,0 +1,237 @@
const path = require("path")
const fs = require("fs/promises")
import { sep, resolve } from "path"
const setupServer = require("../../../../helpers/setup-server")
const { useApi } = require("../../../../helpers/use-api")
const { initDb, useDb } = require("../../../../helpers/use-db")
const adminSeeder = require("../../../helpers/admin-seeder")
const userSeeder = require("../../../helpers/user-seeder")
const productSeeder = require("../../../helpers/product-seeder")
const adminReqConfig = {
headers: {
Authorization: "Bearer test_token",
},
}
jest.setTimeout(1000000)
describe("Batch job of product-export type", () => {
let medusaProcess
let dbConnection
let exportFilePath = ""
let topDir = ""
beforeAll(async () => {
const cwd = path.resolve(path.join(__dirname, "..", "..", ".."))
dbConnection = await initDb({ cwd })
medusaProcess = await setupServer({
cwd,
redisUrl: "redis://127.0.0.1:6379",
uploadDir: __dirname,
verbose: false,
})
})
afterAll(async () => {
if (topDir !== "") {
await fs.rm(resolve(__dirname, topDir), { recursive: true })
}
const db = useDb()
await db.shutdown()
medusaProcess.kill()
})
beforeEach(async () => {
try {
await productSeeder(dbConnection)
await adminSeeder(dbConnection)
await userSeeder(dbConnection)
} catch (e) {
console.log(e)
throw e
}
})
afterEach(async () => {
const db = useDb()
await db.teardown()
const isFileExists = (await fs.stat(exportFilePath))?.isFile()
if (isFileExists) {
const [, relativeRoot] = exportFilePath.replace(__dirname, "").split(sep)
if ((await fs.stat(resolve(__dirname, relativeRoot)))?.isDirectory()) {
topDir = relativeRoot
}
await fs.unlink(exportFilePath)
}
})
it("should export a csv file containing the expected products", async () => {
jest.setTimeout(1000000)
const api = useApi()
const productPayload = {
title: "Test export product",
description: "test-product-description",
type: { value: "test-type" },
images: ["test-image.png", "test-image-2.png"],
collection_id: "test-collection",
tags: [{ value: "123" }, { value: "456" }],
options: [{ title: "size" }, { title: "color" }],
variants: [
{
title: "Test variant",
inventory_quantity: 10,
sku: "test-variant-sku-product-export",
prices: [
{
currency_code: "usd",
amount: 100,
},
{
currency_code: "eur",
amount: 45,
},
{
currency_code: "dkk",
amount: 30,
},
],
options: [{ value: "large" }, { value: "green" }],
},
],
}
const createProductRes = await api.post(
"/admin/products",
productPayload,
adminReqConfig
)
const productId = createProductRes.data.product.id
const variantId = createProductRes.data.product.variants[0].id
const batchPayload = {
type: "product-export",
context: {
filterable_fields: {
title: "Test export product",
},
},
}
const batchJobRes = await api.post(
"/admin/batch-jobs",
batchPayload,
adminReqConfig
)
const batchJobId = batchJobRes.data.batch_job.id
expect(batchJobId).toBeTruthy()
// Pull to check the status until it is completed
let batchJob
let shouldContinuePulling = true
while (shouldContinuePulling) {
const res = await api.get(
`/admin/batch-jobs/${batchJobId}`,
adminReqConfig
)
await new Promise((resolve, _) => {
setTimeout(resolve, 1000)
})
batchJob = res.data.batch_job
shouldContinuePulling = !(
batchJob.status === "completed" || batchJob.status === "failed"
)
}
expect(batchJob.status).toBe("completed")
exportFilePath = path.resolve(__dirname, batchJob.result.file_key)
const isFileExists = (await fs.stat(exportFilePath)).isFile()
expect(isFileExists).toBeTruthy()
const fileSize = (await fs.stat(exportFilePath)).size
expect(batchJob.result?.file_size).toBe(fileSize)
const data = (await fs.readFile(exportFilePath)).toString()
const [, ...lines] = data.split("\r\n").filter((l) => l)
expect(lines.length).toBe(1)
const lineColumn = lines[0].split(";")
expect(lineColumn[0]).toBe(productId)
expect(lineColumn[2]).toBe(productPayload.title)
expect(lineColumn[4]).toBe(productPayload.description)
expect(lineColumn[23]).toBe(variantId)
expect(lineColumn[24]).toBe(productPayload.variants[0].title)
expect(lineColumn[25]).toBe(productPayload.variants[0].sku)
})
it("should export a csv file containing a limited number of products", async () => {
jest.setTimeout(1000000)
const api = useApi()
const batchPayload = {
type: "product-export",
context: {
batch_size: 1,
filterable_fields: { collection_id: "test-collection" },
order: "created_at",
},
}
const batchJobRes = await api.post(
"/admin/batch-jobs",
batchPayload,
adminReqConfig
)
const batchJobId = batchJobRes.data.batch_job.id
expect(batchJobId).toBeTruthy()
// Pull to check the status until it is completed
let batchJob
let shouldContinuePulling = true
while (shouldContinuePulling) {
const res = await api.get(
`/admin/batch-jobs/${batchJobId}`,
adminReqConfig
)
await new Promise((resolve, _) => {
setTimeout(resolve, 1000)
})
batchJob = res.data.batch_job
shouldContinuePulling = !(
batchJob.status === "completed" || batchJob.status === "failed"
)
}
expect(batchJob.status).toBe("completed")
exportFilePath = path.resolve(__dirname, batchJob.result.file_key)
const isFileExists = (await fs.stat(exportFilePath)).isFile()
expect(isFileExists).toBeTruthy()
const data = (await fs.readFile(exportFilePath)).toString()
const [, ...lines] = data.split("\r\n").filter((l) => l)
expect(lines.length).toBe(4)
const csvLine = lines[0].split(";")
expect(csvLine[0]).toBe("test-product")
})
})
@@ -6,6 +6,8 @@ Object {
"allow_backorder": false,
"barcode": "test-barcode",
"calculated_price": null,
"calculated_price_incl_tax": null,
"calculated_tax": null,
"created_at": Any<String>,
"deleted_at": null,
"ean": "test-ean",
@@ -32,6 +34,8 @@ Object {
],
"origin_country": null,
"original_price": null,
"original_price_incl_tax": null,
"original_tax": null,
"prices": Array [
Object {
"amount": 100,
@@ -51,6 +55,7 @@ Object {
"product": Any<Object>,
"product_id": "test-product",
"sku": "test-sku",
"tax_rates": null,
"title": "Test variant",
"upc": "test-upc",
"updated_at": Any<String>,
@@ -66,7 +71,9 @@ Object {
"allow_backorder": false,
"barcode": "test-barcode",
"calculated_price": 80,
"calculated_price_incl_tax": null,
"calculated_price_type": "sale",
"calculated_tax": null,
"created_at": Any<String>,
"deleted_at": null,
"ean": "test-ean",
@@ -93,6 +100,8 @@ Object {
],
"origin_country": null,
"original_price": 100,
"original_price_incl_tax": null,
"original_tax": null,
"prices": Array [
Object {
"amount": 100,
@@ -137,6 +146,7 @@ Object {
"product": Any<Object>,
"product_id": "test-product",
"sku": "test-sku",
"tax_rates": null,
"title": "Test variant",
"upc": "test-upc",
"updated_at": Any<String>,
@@ -153,6 +163,8 @@ Object {
"allow_backorder": false,
"barcode": null,
"calculated_price": null,
"calculated_price_incl_tax": null,
"calculated_tax": null,
"created_at": Any<String>,
"deleted_at": null,
"ean": null,
@@ -179,6 +191,8 @@ Object {
],
"origin_country": null,
"original_price": null,
"original_price_incl_tax": null,
"original_tax": null,
"prices": Array [
Object {
"amount": 100,
@@ -198,6 +212,7 @@ Object {
"product": Any<Object>,
"product_id": Any<String>,
"sku": null,
"tax_rates": null,
"title": "test2",
"upc": null,
"updated_at": Any<String>,
@@ -214,7 +229,9 @@ Object {
"allow_backorder": false,
"barcode": "test-barcode",
"calculated_price": 80,
"calculated_price_incl_tax": 80,
"calculated_price_type": "sale",
"calculated_tax": 0,
"created_at": Any<String>,
"deleted_at": null,
"ean": "test-ean",
@@ -241,6 +258,8 @@ Object {
],
"origin_country": null,
"original_price": 100,
"original_price_incl_tax": 100,
"original_tax": 0,
"prices": Array [
Object {
"amount": 100,
@@ -285,6 +304,13 @@ Object {
"product": Any<Object>,
"product_id": "test-product",
"sku": "test-sku",
"tax_rates": Array [
Object {
"code": "default",
"name": "default",
"rate": 0,
},
],
"title": "Test variant",
"upc": "test-upc",
"updated_at": Any<String>,
@@ -301,6 +327,8 @@ Object {
"allow_backorder": false,
"barcode": "test-barcode",
"calculated_price": null,
"calculated_price_incl_tax": null,
"calculated_tax": null,
"created_at": Any<String>,
"deleted_at": null,
"ean": "test-ean",
@@ -327,6 +355,8 @@ Object {
],
"origin_country": null,
"original_price": null,
"original_price_incl_tax": null,
"original_tax": null,
"prices": Array [
Object {
"amount": 100,
@@ -346,6 +376,7 @@ Object {
"product": Any<Object>,
"product_id": "test-product",
"sku": "test-sku",
"tax_rates": null,
"title": "Test variant",
"upc": "test-upc",
"updated_at": Any<String>,
@@ -131,6 +131,8 @@ Object {
"allow_backorder": false,
"barcode": "test-barcode",
"calculated_price": null,
"calculated_price_incl_tax": null,
"calculated_tax": null,
"created_at": Any<String>,
"deleted_at": null,
"ean": "test-ean",
@@ -157,6 +159,8 @@ Object {
],
"origin_country": null,
"original_price": null,
"original_price_incl_tax": null,
"original_tax": null,
"prices": Array [
Object {
"amount": 100,
@@ -200,6 +204,7 @@ Object {
],
"product_id": "test-product",
"sku": "test-sku",
"tax_rates": null,
"title": "Test variant",
"upc": "test-upc",
"updated_at": Any<String>,
@@ -210,6 +215,8 @@ Object {
"allow_backorder": false,
"barcode": null,
"calculated_price": null,
"calculated_price_incl_tax": null,
"calculated_tax": null,
"created_at": Any<String>,
"deleted_at": null,
"ean": "test-ean2",
@@ -236,6 +243,8 @@ Object {
],
"origin_country": null,
"original_price": null,
"original_price_incl_tax": null,
"original_tax": null,
"prices": Array [
Object {
"amount": 100,
@@ -279,6 +288,7 @@ Object {
],
"product_id": "test-product",
"sku": "test-sku2",
"tax_rates": null,
"title": "Test variant rank (2)",
"upc": "test-upc2",
"updated_at": Any<String>,
@@ -289,6 +299,8 @@ Object {
"allow_backorder": false,
"barcode": "test-barcode 1",
"calculated_price": null,
"calculated_price_incl_tax": null,
"calculated_tax": null,
"created_at": Any<String>,
"deleted_at": null,
"ean": "test-ean1",
@@ -315,6 +327,8 @@ Object {
],
"origin_country": null,
"original_price": null,
"original_price_incl_tax": null,
"original_tax": null,
"prices": Array [
Object {
"amount": 100,
@@ -358,6 +372,7 @@ Object {
],
"product_id": "test-product",
"sku": "test-sku1",
"tax_rates": null,
"title": "Test variant rank (1)",
"upc": "test-upc1",
"updated_at": Any<String>,
@@ -385,6 +400,8 @@ Object {
"allow_backorder": false,
"barcode": null,
"calculated_price": null,
"calculated_price_incl_tax": null,
"calculated_tax": null,
"created_at": Any<String>,
"deleted_at": null,
"ean": null,
@@ -399,6 +416,8 @@ Object {
"mid_code": null,
"origin_country": null,
"original_price": null,
"original_price_incl_tax": null,
"original_tax": null,
"prices": Array [
Object {
"amount": 100,
@@ -417,6 +436,7 @@ Object {
],
"product_id": Any<String>,
"sku": null,
"tax_rates": null,
"title": "test-variant",
"upc": null,
"updated_at": Any<String>,
@@ -58,6 +58,21 @@ describe("/store/customers", () => {
expect(response.data.customer).not.toHaveProperty("password_hash")
})
it("normalizes email", async () => {
const api = useApi()
const response = await api.post("/store/customers", {
first_name: "James",
last_name: "Bond",
email: "James@Bond.com",
password: "test",
})
expect(response.status).toEqual(200)
expect(response.data.customer).not.toHaveProperty("password_hash")
expect(response.data.customer.email).toEqual("james@bond.com")
})
it("responds 422 on duplicate", async () => {
const api = useApi()
@@ -15,7 +15,7 @@ describe("/store/variants", () => {
beforeAll(async () => {
const cwd = path.resolve(path.join(__dirname, "..", ".."))
dbConnection = await initDb({ cwd })
medusaProcess = await setupServer({ cwd })
medusaProcess = await setupServer({ cwd, verbose: false })
})
afterAll(async () => {
@@ -0,0 +1,237 @@
const path = require("path")
const setupServer = require("../../../helpers/setup-server")
const { useApi } = require("../../../helpers/use-api")
const { useDb, initDb } = require("../../../helpers/use-db")
const {
simpleDiscountFactory,
simpleRegionFactory,
simpleProductFactory,
simpleProductTaxRateFactory,
} = require("../../factories")
const adminSeeder = require("../../helpers/admin-seeder")
jest.setTimeout(30000)
describe("Cart Totals Calculations", () => {
let medusaProcess
let dbConnection
beforeAll(async () => {
const cwd = path.resolve(path.join(__dirname, "..", ".."))
dbConnection = await initDb({ cwd })
medusaProcess = await setupServer({ cwd })
})
afterAll(async () => {
const db = useDb()
await db.shutdown()
medusaProcess.kill()
})
beforeEach(async () => {
try {
await adminSeeder(dbConnection)
} catch (err) {
console.log(err)
throw err
}
})
afterEach(async () => {
const db = useDb()
await db.teardown()
})
it("sets correct line item totals for a cart with item of price 100 and tax rate 10", async () => {
const api = useApi()
const region = await simpleRegionFactory(dbConnection)
const product = await simpleProductFactory(dbConnection)
await simpleProductTaxRateFactory(dbConnection, {
product_id: product.id,
rate: {
region_id: region.id,
rate: 10,
},
})
// create cart
const { cart } = await api
.post("/store/carts", {
region_id: region.id,
})
.then((res) => res.data)
// add product to cart
const res = await api.post(`/store/carts/${cart.id}/line-items`, {
variant_id: product.variants[0].id,
quantity: 1,
})
expect(res.data.cart.items[0].unit_price).toEqual(100)
expect(res.data.cart.items[0].quantity).toEqual(1)
expect(res.data.cart.items[0].subtotal).toEqual(100)
expect(res.data.cart.items[0].tax_total).toEqual(10)
expect(res.data.cart.items[0].total).toEqual(110)
expect(res.data.cart.items[0].original_total).toEqual(110)
expect(res.data.cart.items[0].original_tax_total).toEqual(10)
expect(res.data.cart.items[0].discount_total).toEqual(0)
expect(res.data.cart.items[0].gift_card_total).toEqual(0)
})
it("sets correct line item totals for a cart with item of price 100; tax rate 10; discount 10", async () => {
const api = useApi()
const region = await simpleRegionFactory(dbConnection)
const product = await simpleProductFactory(dbConnection)
const discount = await simpleDiscountFactory(dbConnection, {
regions: [region.id],
type: "percentage",
value: 10,
})
await simpleProductTaxRateFactory(dbConnection, {
product_id: product.id,
rate: {
region_id: region.id,
rate: 10,
},
})
const { cart } = await api
.post("/store/carts", {
region_id: region.id,
})
.then((res) => res.data)
await api.post(`/store/carts/${cart.id}/line-items`, {
variant_id: product.variants[0].id,
quantity: 1,
})
const res = await api.post(`/store/carts/${cart.id}`, {
discounts: [
{
code: discount.code,
},
],
})
expect(res.data.cart.items[0].unit_price).toEqual(100)
expect(res.data.cart.items[0].quantity).toEqual(1)
expect(res.data.cart.items[0].subtotal).toEqual(100)
expect(res.data.cart.items[0].tax_total).toEqual(9)
expect(res.data.cart.items[0].total).toEqual(99)
expect(res.data.cart.items[0].original_total).toEqual(110)
expect(res.data.cart.items[0].original_tax_total).toEqual(10)
expect(res.data.cart.items[0].discount_total).toEqual(10)
expect(res.data.cart.items[0].gift_card_total).toEqual(0)
})
it("doesn't include taxes in !automatic_taxes regions", async () => {
const api = useApi()
const region = await simpleRegionFactory(dbConnection, {
automatic_taxes: false,
})
const product = await simpleProductFactory(dbConnection)
const discount = await simpleDiscountFactory(dbConnection, {
regions: [region.id],
type: "percentage",
value: 10,
})
await simpleProductTaxRateFactory(dbConnection, {
product_id: product.id,
rate: {
region_id: region.id,
rate: 10,
},
})
const { cart } = await api
.post("/store/carts", {
region_id: region.id,
})
.then((res) => res.data)
await api.post(`/store/carts/${cart.id}/line-items`, {
variant_id: product.variants[0].id,
quantity: 1,
})
const res = await api.post(`/store/carts/${cart.id}`, {
discounts: [
{
code: discount.code,
},
],
})
expect(res.data.cart.items[0].unit_price).toEqual(100)
expect(res.data.cart.items[0].quantity).toEqual(1)
expect(res.data.cart.items[0].subtotal).toEqual(100)
expect(res.data.cart.items[0].tax_total).toEqual(0)
expect(res.data.cart.items[0].total).toEqual(90)
expect(res.data.cart.items[0].original_total).toEqual(100)
expect(res.data.cart.items[0].original_tax_total).toEqual(0)
expect(res.data.cart.items[0].discount_total).toEqual(10)
expect(res.data.cart.items[0].gift_card_total).toEqual(0)
})
it("includes taxes in !automatic_taxes regions when forced", async () => {
const api = useApi()
const region = await simpleRegionFactory(dbConnection, {
automatic_taxes: false,
})
const product = await simpleProductFactory(dbConnection)
const discount = await simpleDiscountFactory(dbConnection, {
regions: [region.id],
type: "percentage",
value: 10,
})
await simpleProductTaxRateFactory(dbConnection, {
product_id: product.id,
rate: {
region_id: region.id,
rate: 10,
},
})
const { cart } = await api
.post("/store/carts", {
region_id: region.id,
})
.then((res) => res.data)
await api.post(`/store/carts/${cart.id}/line-items`, {
variant_id: product.variants[0].id,
quantity: 1,
})
await api.post(`/store/carts/${cart.id}`, {
discounts: [
{
code: discount.code,
},
],
})
const res = await api.post(`/store/carts/${cart.id}/taxes`)
expect(res.data.cart.items[0].unit_price).toEqual(100)
expect(res.data.cart.items[0].quantity).toEqual(1)
expect(res.data.cart.items[0].subtotal).toEqual(100)
expect(res.data.cart.items[0].tax_total).toEqual(9)
expect(res.data.cart.items[0].total).toEqual(99)
expect(res.data.cart.items[0].original_total).toEqual(110)
expect(res.data.cart.items[0].original_tax_total).toEqual(10)
expect(res.data.cart.items[0].discount_total).toEqual(10)
expect(res.data.cart.items[0].gift_card_total).toEqual(0)
})
})
@@ -0,0 +1,109 @@
const path = require("path")
const setupServer = require("../../../helpers/setup-server")
const { useApi } = require("../../../helpers/use-api")
const { useDb, initDb } = require("../../../helpers/use-db")
const {
simpleRegionFactory,
simpleProductFactory,
simpleShippingTaxRateFactory,
simpleShippingOptionFactory,
} = require("../../factories")
const adminSeeder = require("../../helpers/admin-seeder")
jest.setTimeout(30000)
describe("Shipping Options Totals Calculations", () => {
let medusaProcess
let dbConnection
beforeAll(async () => {
const cwd = path.resolve(path.join(__dirname, "..", ".."))
dbConnection = await initDb({ cwd })
medusaProcess = await setupServer({ cwd })
})
afterAll(async () => {
const db = useDb()
await db.shutdown()
medusaProcess.kill()
})
beforeEach(async () => {
try {
await adminSeeder(dbConnection)
} catch (err) {
console.log(err)
throw err
}
})
afterEach(async () => {
const db = useDb()
await db.teardown()
})
it("admin gets correct shipping prices", async () => {
const api = useApi()
const region = await simpleRegionFactory(dbConnection, {
tax_rate: 25,
})
const so = await simpleShippingOptionFactory(dbConnection, {
region_id: region.id,
price: 100,
})
await simpleShippingTaxRateFactory(dbConnection, {
shipping_option_id: so.id,
rate: {
region_id: region.id,
rate: 10,
},
})
const res = await api.get(`/admin/shipping-options`, {
headers: {
Authorization: `Bearer test_token`,
},
})
expect(res.data.shipping_options).toEqual([
expect.objectContaining({
id: so.id,
amount: 100,
price_incl_tax: 110,
}),
])
})
it("gets correct shipping prices", async () => {
const api = useApi()
const region = await simpleRegionFactory(dbConnection, {
tax_rate: 25,
})
const so = await simpleShippingOptionFactory(dbConnection, {
region_id: region.id,
price: 100,
})
await simpleShippingTaxRateFactory(dbConnection, {
shipping_option_id: so.id,
rate: {
region_id: region.id,
rate: 10,
},
})
const res = await api.get(`/store/shipping-options?region_id=${region.id}`)
expect(res.data.shipping_options).toEqual([
expect.objectContaining({
id: so.id,
amount: 100,
price_incl_tax: 110,
}),
])
})
})
+2
View File
@@ -1,5 +1,6 @@
export * from "./simple-payment-factory"
export * from "./simple-batch-job-factory"
export * from "./simple-discount-factory"
export * from "./simple-order-factory"
export * from "./simple-cart-factory"
export * from "./simple-region-factory"
@@ -13,3 +14,4 @@ export * from "./simple-shipping-option-factory"
export * from "./simple-shipping-method-factory"
export * from "./simple-product-type-tax-rate-factory"
export * from "./simple-price-list-factory"
export * from "./simple-batch-job-factory"
@@ -7,6 +7,8 @@ export type BatchJobFactoryData = {
status?: BatchJobStatus
created_by?: string
context?: Record<string, unknown>
awaiting_confirmation_at?: Date | string
completed_at?: Date | string
}
export const simpleBatchJobFactory = async (
@@ -15,13 +17,14 @@ export const simpleBatchJobFactory = async (
): Promise<BatchJob> => {
const manager = connection.manager
const job = manager.create(BatchJob, {
const job = manager.create<BatchJob>(BatchJob, {
id: data.id,
status: data.status ?? BatchJobStatus.CREATED,
completed_at: data.completed_at ?? null,
type: data.type ?? "test-job",
created_by: data.created_by ?? null,
context: data.context ?? {},
})
return await manager.save(job)
return await manager.save<BatchJob>(job)
}
@@ -119,7 +119,15 @@ module.exports = async (connection, data = {}) => {
type: "percentage",
})
const d = manager.create(Discount, {
await manager.insert(DiscountRule, {
id: "free-shipping-rule",
description: "Free shipping rule",
type: "free_shipping",
value: 100,
allocation: "total",
})
const testDiscount = manager.create(Discount, {
id: "test-discount",
code: "TEST",
is_dynamic: false,
@@ -127,7 +135,15 @@ module.exports = async (connection, data = {}) => {
rule_id: "discount_rule_id",
})
d.regions = [
const freeShippingDiscount = manager.create(Discount, {
id: "free-shipping-discount",
code: "free-shipping",
is_dynamic: false,
is_disabled: false,
rule_id: "free-shipping-rule",
})
testDiscount.regions = [
{
id: "test-region",
name: "Test Region",
@@ -136,7 +152,17 @@ module.exports = async (connection, data = {}) => {
},
]
await manager.save(d)
freeShippingDiscount.regions = [
{
id: "test-region",
name: "Test Region",
currency_code: "usd",
tax_rate: 0,
},
]
await manager.save(testDiscount)
await manager.save(freeShippingDiscount)
await manager.query(
`UPDATE "country" SET region_id='test-region' WHERE iso_2 = 'us'`
@@ -50,14 +50,14 @@ module.exports = async (connection, data = {}) => {
const tag3 = await manager.create(ProductTag, {
id: "tag3",
value: "123",
value: "1235",
})
await manager.save(tag3)
const tag4 = await manager.create(ProductTag, {
id: "tag4",
value: "123",
value: "1234",
})
await manager.save(tag4)
+3 -3
View File
@@ -5,10 +5,10 @@ const workerId = parseInt(process.env.JEST_WORKER_ID || "1")
module.exports = {
plugins: [],
projectConfig: {
// redis_url: REDIS_URL,
redis_url: process.env.REDIS_URL,
database_url: `postgres://${DB_USERNAME}:${DB_PASSWORD}@localhost/medusa-integration-${workerId}`,
database_type: "postgres",
jwt_secret: 'test',
cookie_secret: 'test'
jwt_secret: "test",
cookie_secret: "test",
},
}
+3 -3
View File
@@ -8,16 +8,16 @@
"build": "babel src -d dist --extensions \".ts,.js\""
},
"dependencies": {
"@medusajs/medusa": "1.3.0-dev-1652704115624",
"@medusajs/medusa": "1.3.2-dev-1655728455189",
"faker": "^5.5.3",
"medusa-interfaces": "1.3.0-dev-1652704115624",
"medusa-interfaces": "1.3.0-dev-1655728455189",
"typeorm": "^0.2.31"
},
"devDependencies": {
"@babel/cli": "^7.12.10",
"@babel/core": "^7.12.10",
"@babel/node": "^7.12.10",
"babel-preset-medusa-package": "1.1.19-dev-1652704115624",
"babel-preset-medusa-package": "1.1.19-dev-1655728455189",
"jest": "^26.6.3"
}
}
@@ -0,0 +1,69 @@
import { AbstractFileService } from "@medusajs/medusa"
import stream from "stream"
import * as fs from "fs"
import * as path from "path"
export default class LocalFileService extends AbstractFileService {
// eslint-disable-next-line no-empty-pattern
constructor({}, options) {
super({})
this.upload_dir_ =
process.env.UPLOAD_DIR ?? options.upload_dir ?? "uploads/images"
if (!fs.existsSync(this.upload_dir_)) {
fs.mkdirSync(this.upload_dir_)
}
}
async upload(file) {
const uploadPath = path.join(
this.upload_dir_,
path.dirname(file.originalname)
)
if (!fs.existsSync(uploadPath)) {
fs.mkdirSync(uploadPath, { recursive: true })
}
const filePath = path.resolve(this.upload_dir_, file.originalname)
fs.writeFile(filePath, "", (error) => {
if (error) {
throw error
}
})
return { url: filePath }
}
async delete({ name }) {
return new Promise((resolve, _) => {
const path = resolve(this.upload_dir_, name)
fs.unlink(path, (err) => {
if (err) {
throw err
}
resolve("file unlinked")
})
})
}
async getUploadStreamDescriptor({ name, ext }) {
const fileKey = `${name}-${Date.now()}.${ext}`
const filePath = path.resolve(this.upload_dir_, fileKey)
const isFileExists = fs.existsSync(filePath)
if (!isFileExists) {
await this.upload({ originalname: fileKey })
}
const pass = new stream.PassThrough()
pass.pipe(fs.createWriteStream(filePath))
return {
writeStream: pass,
promise: Promise.resolve(),
url: `${this.upload_dir_}/${fileKey}`,
fileKey,
}
}
}
+74 -35
View File
@@ -1327,10 +1327,10 @@
semver "^7.3.5"
tar "^6.1.11"
"@medusajs/medusa-cli@1.3.0-dev-1652704115624":
version "1.3.0-dev-1652704115624"
resolved "http://localhost:4873/@medusajs%2fmedusa-cli/-/medusa-cli-1.3.0-dev-1652704115624.tgz#9841fcc6123cd9c72d544d48316d66cf49ad8dc0"
integrity sha512-Lhk6pdvgv4UrLLUY/aYuty3TKfgDlvSUfmG4cTgZXbLehnRSbLelPcX+YKCtiM4d0NYfgF364H7p0NAMuCkBIg==
"@medusajs/medusa-cli@1.3.0-dev-1655728455189":
version "1.3.0-dev-1655728455189"
resolved "http://localhost:4873/@medusajs%2fmedusa-cli/-/medusa-cli-1.3.0-dev-1655728455189.tgz#1d6bf606fbd96167faf0824d221646f664e06d66"
integrity sha512-wMDVBN6X6cG6Ni/0/H5K54Hs3RmYLiGvI81VqWQtisjia0T98Hf8MuhxlJ4kS3ny34r5M7Qjs+KCoiospgzbqA==
dependencies:
"@babel/polyfill" "^7.8.7"
"@babel/runtime" "^7.9.6"
@@ -1348,8 +1348,8 @@
is-valid-path "^0.1.1"
joi-objectid "^3.0.1"
meant "^1.0.1"
medusa-core-utils "1.1.31-dev-1652704115624"
medusa-telemetry "0.0.11-dev-1652704115624"
medusa-core-utils "1.1.31-dev-1655728455189"
medusa-telemetry "0.0.11-dev-1655728455189"
netrc-parser "^3.1.6"
open "^8.0.6"
ora "^5.4.1"
@@ -1363,13 +1363,13 @@
winston "^3.3.3"
yargs "^15.3.1"
"@medusajs/medusa@1.3.0-dev-1652704115624":
version "1.3.0-dev-1652704115624"
resolved "http://localhost:4873/@medusajs%2fmedusa/-/medusa-1.3.0-dev-1652704115624.tgz#040eede718d0eec6b01b4471e9af8495314c3ff0"
integrity sha512-x2Wg7lP5A25NMENqcZoC00O46Y3ojLaSnxa9L6E3u375nYWa99uxyT8ckXhklVqzhM3iBtReb/20H4r2+niwmw==
"@medusajs/medusa@1.3.2-dev-1655728455189":
version "1.3.2-dev-1655728455189"
resolved "http://localhost:4873/@medusajs%2fmedusa/-/medusa-1.3.2-dev-1655728455189.tgz#c3c189fa7aebf94117349067acc0b0a0bc537a28"
integrity sha512-oYMihSFpQKrAru2vRnCU9q6A6j6Zgq0/6153m+vibhrbuvW8NZUJSoRjEEA925c8Yprr4xWwFhYghB0jUjIAPA==
dependencies:
"@hapi/joi" "^16.1.8"
"@medusajs/medusa-cli" "1.3.0-dev-1652704115624"
"@medusajs/medusa-cli" "1.3.0-dev-1655728455189"
"@types/lodash" "^4.14.168"
awilix "^4.2.3"
body-parser "^1.19.0"
@@ -1392,10 +1392,12 @@
joi "^17.3.0"
joi-objectid "^3.0.1"
jsonwebtoken "^8.5.1"
medusa-core-utils "1.1.31-dev-1652704115624"
medusa-test-utils "1.1.37-dev-1652704115624"
medusa-core-utils "1.1.31-dev-1655728455189"
medusa-test-utils "1.1.37-dev-1655728455189"
morgan "^1.9.1"
multer "^1.4.2"
node-schedule "^2.1.0"
papaparse "^5.3.2"
passport "^0.4.0"
passport-http-bearer "^1.0.1"
passport-jwt "^4.0.0"
@@ -2039,10 +2041,10 @@ babel-preset-jest@^26.6.2:
babel-plugin-jest-hoist "^26.6.2"
babel-preset-current-node-syntax "^1.0.0"
babel-preset-medusa-package@1.1.19-dev-1652704115624:
version "1.1.19-dev-1652704115624"
resolved "http://localhost:4873/babel-preset-medusa-package/-/babel-preset-medusa-package-1.1.19-dev-1652704115624.tgz#fa584e39e7c0a1b808af25953f81653252225be7"
integrity sha512-Nv8Si592nO+UZmMQuOGCs1pFs5ShIsCKclbqBybahjGZIPPD6bVNaz9dujSaZnlxvA9a3gn4dvuh3H8IRitUug==
babel-preset-medusa-package@1.1.19-dev-1655728455189:
version "1.1.19-dev-1655728455189"
resolved "http://localhost:4873/babel-preset-medusa-package/-/babel-preset-medusa-package-1.1.19-dev-1655728455189.tgz#a7884d6869c9ac7adb23c5789d6dc858614381c7"
integrity sha512-i8JKgbu59S+WU58tRV5iJtJ9UnJnpOPwEtU9b7WVz9X5LLDeUk6Mmhcb6XvTuYCfdC8bXJ/Hk8NlqETIZw1lPQ==
dependencies:
"@babel/plugin-proposal-class-properties" "^7.12.1"
"@babel/plugin-proposal-decorators" "^7.12.1"
@@ -2768,6 +2770,14 @@ cron-parser@^2.13.0:
is-nan "^1.3.0"
moment-timezone "^0.5.31"
cron-parser@^3.5.0:
version "3.5.0"
resolved "http://localhost:4873/cron-parser/-/cron-parser-3.5.0.tgz#b1a9da9514c0310aa7ef99c2f3f1d0f8c235257c"
integrity sha512-wyVZtbRs6qDfFd8ap457w3XVntdvqcwBGxBoTvJQH9KGVKL/fB+h2k3C8AqiVxvUQKN1Ps/Ns46CNViOpVDhfQ==
dependencies:
is-nan "^1.3.2"
luxon "^1.26.0"
cross-spawn@^6.0.0, cross-spawn@^6.0.5:
version "6.0.5"
resolved "http://localhost:4873/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4"
@@ -4198,7 +4208,7 @@ is-lambda@^1.0.1:
resolved "http://localhost:4873/is-lambda/-/is-lambda-1.0.1.tgz#3d9877899e6a53efc0160504cde15f82e6f061d5"
integrity sha1-PZh3iZ5qU+/AFgUEzeFfgubwYdU=
is-nan@^1.3.0:
is-nan@^1.3.0, is-nan@^1.3.2:
version "1.3.2"
resolved "http://localhost:4873/is-nan/-/is-nan-1.3.2.tgz#043a54adea31748b55b6cd4e09aadafa69bd9e1d"
integrity sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==
@@ -5083,6 +5093,11 @@ logform@^2.3.2, logform@^2.4.0:
safe-stable-stringify "^2.3.1"
triple-beam "^1.3.0"
long-timeout@0.1.1:
version "0.1.1"
resolved "http://localhost:4873/long-timeout/-/long-timeout-0.1.1.tgz#9721d788b47e0bcb5a24c2e2bee1a0da55dab514"
integrity sha512-BFRuQUqc7x2NWxfJBCyUrN8iYUYznzL9JROmRz1gZ6KlOIgmoD+njPVbb+VNn2nGMKggMsK79iUNErillsrx7w==
lower-case@^2.0.2:
version "2.0.2"
resolved "http://localhost:4873/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28"
@@ -5097,6 +5112,11 @@ lru-cache@^6.0.0:
dependencies:
yallist "^4.0.0"
luxon@^1.26.0:
version "1.28.0"
resolved "http://localhost:4873/luxon/-/luxon-1.28.0.tgz#e7f96daad3938c06a62de0fb027115d251251fbf"
integrity sha512-TfTiyvZhwBYM/7QdAVDh+7dBTBA29v4ik0Ce9zda3Mnf8on1S5KJI8P2jKFZ8+5C0jhmr0KwJEO/Wdpm0VeWJQ==
make-dir@^2.0.0, make-dir@^2.1.0:
version "2.1.0"
resolved "http://localhost:4873/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5"
@@ -5168,23 +5188,23 @@ media-typer@0.3.0:
resolved "http://localhost:4873/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=
medusa-core-utils@1.1.31-dev-1652704115624:
version "1.1.31-dev-1652704115624"
resolved "http://localhost:4873/medusa-core-utils/-/medusa-core-utils-1.1.31-dev-1652704115624.tgz#cda24aa1a292a1bd0a770774aae9a970fc7ab963"
integrity sha512-evkWva10x6JaHBex5S8Zi9Ae/ZStG858nb18w5ITxbCHWvGYq6/VyLpo4vB7I6wuU3z6kDTMd/yyvsVXv33O2g==
medusa-core-utils@1.1.31-dev-1655728455189:
version "1.1.31-dev-1655728455189"
resolved "http://localhost:4873/medusa-core-utils/-/medusa-core-utils-1.1.31-dev-1655728455189.tgz#d1765c3aa5ff294722a44c7f5b90dacb185f03ee"
integrity sha512-hsyo22Hp5A18+qA5DhfQRvY5eAM3IpzxuuY/SVk50Pxrf+r3U4aWmL4uX0qGpQeKWv6gOLmPxt+OyJIDUPNrwg==
dependencies:
joi "^17.3.0"
joi-objectid "^3.0.1"
medusa-interfaces@1.3.0-dev-1652704115624:
version "1.3.0-dev-1652704115624"
resolved "http://localhost:4873/medusa-interfaces/-/medusa-interfaces-1.3.0-dev-1652704115624.tgz#02dbbda562f99e7de96e7e6657a6af2a20855e36"
integrity sha512-aSm6gYWF0gPHoIswHllB/YrGwkrYRr4ZrBmWh3QSulDYnjmtyBFHKkatrC1pscAEFG/5h4d1LEENUHYrBQ4tQg==
medusa-interfaces@1.3.0-dev-1655728455189:
version "1.3.0-dev-1655728455189"
resolved "http://localhost:4873/medusa-interfaces/-/medusa-interfaces-1.3.0-dev-1655728455189.tgz#4eaa0dc41671bb24777b3630c8640124c4ca9f24"
integrity sha512-lcYVYQUg5ImtXLtR4pHHOqXi1q1kl46Gi9rgjAUWVvldnssVDnGThjgnVCFeCkDz+qMqn7lxTHoZXaDIneadlA==
medusa-telemetry@0.0.11-dev-1652704115624:
version "0.0.11-dev-1652704115624"
resolved "http://localhost:4873/medusa-telemetry/-/medusa-telemetry-0.0.11-dev-1652704115624.tgz#4de0f4a66a9f8bb4f61d8b7c24aa9cbd719039a7"
integrity sha512-sfCzUM4mlXpBJVaqAycKAmeEWalzyg18+gUo+As/mxoEUIUR5XtpZ5Bdx484/2GUvRP8OdOYC1uPpbuTZbLXcw==
medusa-telemetry@0.0.11-dev-1655728455189:
version "0.0.11-dev-1655728455189"
resolved "http://localhost:4873/medusa-telemetry/-/medusa-telemetry-0.0.11-dev-1655728455189.tgz#552ba1e2038641321f152b5ba7df0d88f29c26bf"
integrity sha512-MCTDiUg62y2xmRAFeOZHdaKXd2VhVw/9JE8Ewm9IY8QWSkumQOMKVPUAfdgra8EYsp3TD7LixWRlPB2TTn7DKg==
dependencies:
axios "^0.21.1"
axios-retry "^3.1.9"
@@ -5196,13 +5216,13 @@ medusa-telemetry@0.0.11-dev-1652704115624:
remove-trailing-slash "^0.1.1"
uuid "^8.3.2"
medusa-test-utils@1.1.37-dev-1652704115624:
version "1.1.37-dev-1652704115624"
resolved "http://localhost:4873/medusa-test-utils/-/medusa-test-utils-1.1.37-dev-1652704115624.tgz#7118753a4afd1c6ed6f1d1ceb75fdf9bfb65afa3"
integrity sha512-OnQEA/1jj4jnQGukMtbcqg2HWlKve0BqzeS8T5O2VDL5zldFG9SXFF3LupC51nh96u97dcKZk5A6JL8oHwonog==
medusa-test-utils@1.1.37-dev-1655728455189:
version "1.1.37-dev-1655728455189"
resolved "http://localhost:4873/medusa-test-utils/-/medusa-test-utils-1.1.37-dev-1655728455189.tgz#d6283c0075a69dee988d437f5aedfbba334aa847"
integrity sha512-SHkPjpiC6A37Duat9HrWPz+tBbZL50zDEXr/sjDiF0N8cEYNIxVRz2Y2/kbbKegSG3UTcckk5VUtxP17LiQKBw==
dependencies:
"@babel/plugin-transform-classes" "^7.9.5"
medusa-core-utils "1.1.31-dev-1652704115624"
medusa-core-utils "1.1.31-dev-1655728455189"
randomatic "^3.1.1"
merge-descriptors@1.0.1:
@@ -5544,6 +5564,15 @@ node-releases@^2.0.3:
resolved "http://localhost:4873/node-releases/-/node-releases-2.0.4.tgz#f38252370c43854dc48aa431c766c6c398f40476"
integrity sha512-gbMzqQtTtDz/00jQzZ21PQzdI9PyLYqUSvD0p3naOhX4odFji0ZxYdnVwPTxmSwkmxhcFImpozceidSG+AgoPQ==
node-schedule@^2.1.0:
version "2.1.0"
resolved "http://localhost:4873/node-schedule/-/node-schedule-2.1.0.tgz#068ae38d7351c330616f7fe7cdb05036f977cbaf"
integrity sha512-nl4JTiZ7ZQDc97MmpTq9BQjYhq7gOtoh7SiPH069gBFBj0PzD8HI7zyFs6rzqL8Y5tTiEEYLxgtbx034YPrbyQ==
dependencies:
cron-parser "^3.5.0"
long-timeout "0.1.1"
sorted-array-functions "^1.3.0"
nopt@^5.0.0:
version "5.0.0"
resolved "http://localhost:4873/nopt/-/nopt-5.0.0.tgz#530942bb58a512fccafe53fe210f13a25355dc88"
@@ -5815,6 +5844,11 @@ packet-reader@1.0.0:
resolved "http://localhost:4873/packet-reader/-/packet-reader-1.0.0.tgz#9238e5480dedabacfe1fe3f2771063f164157d74"
integrity sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==
papaparse@^5.3.2:
version "5.3.2"
resolved "http://localhost:4873/papaparse/-/papaparse-5.3.2.tgz#d1abed498a0ee299f103130a6109720404fbd467"
integrity sha512-6dNZu0Ki+gyV0eBsFKJhYr+MdQYAzFUGlBMNj3GNrmHxmz1lfRa24CjFObPXtjcetlOv5Ad299MhIK0znp3afw==
parse-json@^5.0.0:
version "5.2.0"
resolved "http://localhost:4873/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd"
@@ -6760,6 +6794,11 @@ socks@^2.6.2:
ip "^1.1.5"
smart-buffer "^4.2.0"
sorted-array-functions@^1.3.0:
version "1.3.0"
resolved "http://localhost:4873/sorted-array-functions/-/sorted-array-functions-1.3.0.tgz#8605695563294dffb2c9796d602bd8459f7a0dd5"
integrity sha512-2sqgzeFlid6N4Z2fUQ1cvFmTOLRi/sEDzSQ0OKYchqgoPmQBVyM3959qYx3fpS6Esef80KjmpgPeEr028dP3OA==
source-map-resolve@^0.5.0:
version "0.5.3"
resolved "http://localhost:4873/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a"
+20 -16
View File
@@ -5,26 +5,30 @@ const importFrom = require("import-from")
module.exports = {
bootstrapApp: async ({ cwd } = {}) => {
const app = express()
try {
const app = express()
const loaders = importFrom(
cwd || process.cwd(),
"@medusajs/medusa/dist/loaders"
).default
const loaders = importFrom(
cwd || process.cwd(),
"@medusajs/medusa/dist/loaders"
).default
const { container, dbConnection } = await loaders({
directory: path.resolve(cwd || process.cwd()),
expressApp: app,
isTest: false,
})
const { container, dbConnection } = await loaders({
directory: path.resolve(cwd || process.cwd()),
expressApp: app,
isTest: false,
})
const PORT = await getPort()
const PORT = await getPort()
return {
container,
db: dbConnection,
app,
port: PORT,
return {
container,
db: dbConnection,
app,
port: PORT,
}
} catch (e) {
console.log(e)
}
},
}
+10 -1
View File
@@ -2,9 +2,15 @@ const path = require("path")
const { spawn } = require("child_process")
const { setPort } = require("./use-api")
module.exports = ({ cwd, verbose }) => {
module.exports = ({ cwd, redisUrl, uploadDir, verbose, env }) => {
const serverPath = path.join(__dirname, "test-server.js")
// in order to prevent conflicts in redis, use a different db for each worker
// same fix as for databases (works with up to 15)
// redis dbs are 0-indexed and jest worker ids are indexed from 1
const workerId = parseInt(process.env.JEST_WORKER_ID || "1")
const redisUrlWithDatabase = `${redisUrl}/${workerId - 1}`
return new Promise((resolve, reject) => {
const medusaProcess = spawn("node", [path.resolve(serverPath)], {
cwd,
@@ -13,6 +19,9 @@ module.exports = ({ cwd, verbose }) => {
NODE_ENV: "development",
JWT_SECRET: "test",
COOKIE_SECRET: "test",
REDIS_URL: redisUrl ? redisUrlWithDatabase : undefined, // If provided, will use a real instance, otherwise a fake instance
UPLOAD_DIR: uploadDir, // If provided, will be used for the fake local file service
...env,
},
stdio: verbose
? ["inherit", "inherit", "inherit", "ipc"]
@@ -0,0 +1,28 @@
const setupServer = require("./setup-server")
const { initDb } = require("./use-db")
const startServerWithEnvironment = async ({ cwd, verbose, env }) => {
if (env) {
Object.entries(env).forEach(([key, value]) => {
process.env[key] = value
})
}
const dbConnection = await initDb({
cwd,
})
Object.entries(env).forEach(([key, value]) => {
delete process.env[key]
})
const medusaProcess = await setupServer({
cwd,
verbose,
env,
})
return [medusaProcess, dbConnection]
}
export default startServerWithEnvironment
+51 -2
View File
@@ -79,6 +79,19 @@ const instance = DbTestUtil
module.exports = {
initDb: async function ({ cwd }) {
const configPath = path.resolve(path.join(cwd, `medusa-config.js`))
const { projectConfig, featureFlags } = require(configPath)
const featureFlagsLoader = require(path.join(
cwd,
`node_modules`,
`@medusajs`,
`medusa`,
`dist`,
`loaders`,
`feature-flags`
)).default
const featureFlagsRouter = featureFlagsLoader({ featureFlags })
const modelsLoader = require(path.join(
cwd,
@@ -89,9 +102,9 @@ module.exports = {
`loaders`,
`models`
)).default
const entities = modelsLoader({}, { register: false })
const { projectConfig } = require(configPath)
if (projectConfig.database_type === "sqlite") {
connectionType = "sqlite"
const dbConnection = await createConnection({
@@ -108,12 +121,48 @@ module.exports = {
await dbFactory.createFromTemplate(databaseName)
// get migraitons with enabled featureflags
const migrationDir = path.resolve(
path.join(
cwd,
`node_modules`,
`@medusajs`,
`medusa`,
`dist`,
`migrations`,
`*.js`
)
)
const { getEnabledMigrations } = require(path.join(
cwd,
`node_modules`,
`@medusajs`,
`medusa`,
`dist`,
`commands`,
`utils`,
`get-migrations`
))
const enabledMigrations = await getEnabledMigrations(
[migrationDir],
(flag) => featureFlagsRouter.isFeatureEnabled(flag)
)
const enabledEntities = entities.filter(
(e) => typeof e.isFeatureEnabled === "undefined" || e.isFeatureEnabled()
)
const dbConnection = await createConnection({
type: "postgres",
url: DB_URL,
entities,
entities: enabledEntities,
migrations: enabledMigrations,
})
await dbConnection.runMigrations()
instance.setDb(dbConnection)
return dbConnection
}
+21 -3
View File
@@ -31,10 +31,28 @@ class DatabaseFactory {
`@medusajs`,
`medusa`,
`dist`,
`migrations`
`migrations`,
`*.js`
)
)
const { getEnabledMigrations } = require(path.join(
cwd,
`node_modules`,
`@medusajs`,
`medusa`,
`dist`,
`commands`,
`utils`,
`get-migrations`
))
// filter migrations to only include those that dont have feature flags
const enabledMigrations = await getEnabledMigrations(
[migrationDir],
(flag) => false
)
await dropDatabase(
{
databaseName: this.templateDbName,
@@ -51,7 +69,7 @@ class DatabaseFactory {
type: "postgres",
name: "templateConnection",
url: `${DB_URL}/${this.templateDbName}`,
migrations: [`${migrationDir}/*.js`],
migrations: enabledMigrations,
})
await templateDbConnection.runMigrations()
@@ -92,7 +110,7 @@ class DatabaseFactory {
}
async destroy() {
let connection = await this.getMasterConnection()
const connection = await this.getMasterConnection()
await connection.query(`DROP DATABASE IF EXISTS "${this.templateDbName}";`)
await connection.close()
+24
View File
@@ -0,0 +1,24 @@
#!/bin/bash
seedJson=$(pwd)/$1
res=$(curl -s 'http://localhost:9000/store/products' | \
python3 -c "
import sys, json;
loadedProducts = json.load(sys.stdin)['products'];
seededProducts = json.load(open(\""$seedJson"\"))['products'];
result = set([product['title'] for product in loadedProducts]) == set([product['title'] for product in seededProducts])
print(result)
")
if [[ "$res" != "True" ]] ; then
echo "Seed failed, products are not equal"
exit 1
else
exit 0
fi
+15
View File
@@ -0,0 +1,15 @@
#!/bin/bash
status_code=$(curl \
-X POST\
-H "Content-Type: application/json"\
-d '{"email":"'$1'", "password":"'$2'"}'\
--write-out %{http_code}\
http://localhost:9000/admin/auth)
if [[ "$status_code" -ne 200 ]] ; then
echo "Site status changed to $status_code"
exit 1
else
exit 0
fi
+29
View File
@@ -0,0 +1,29 @@
#!/bin/bash
for i in {1..6}
do
echo $i
status_code=$(curl \
-X GET \
--write-out %{http_code} \
--silent\
--output /dev/null\
http://localhost:9000/store/products)
echo $status_code
if [[ "$status_code" -ne 000 ]] ; then
echo "exiting"
exit 0
else
sleep 5
fi
done
echo $status_code
if [[ "$status_code" = 000 ]] ; then
echo "Site status changed to $status_code"
exit 1
else
exit 0
fi
+4 -2
View File
@@ -40,7 +40,6 @@
"lerna": "^3.22.1",
"lint-staged": "^11.2.3",
"microbundle": "^0.13.3",
"mongoose": "^5.10.15",
"pg-god": "^1.0.11",
"prettier": "^2.1.1",
"resolve-cwd": "^3.0.0",
@@ -67,9 +66,12 @@
"test:integration:api": "jest --config=integration-tests/jest.config.js --projects=integration-tests/api",
"test:integration:plugins": "jest --config=integration-tests/jest.config.js --projects=integration-tests/plugins",
"test:fixtures": "jest --config=docs-util/jest.config.js --runInBand",
"generate:services": "typedoc --options typedoc.services.js"
"generate:services": "typedoc --options typedoc.services.js",
"release:snapshot": "changeset publish --no-git-tags --snapshot --tag snapshot"
},
"dependencies": {
"@changesets/changelog-github": "^0.4.5",
"@changesets/cli": "^2.23.0",
"global": "^4.4.0",
"import-from": "^3.0.0",
"oas-normalize": "^5.0.1",
+11
View File
@@ -3,6 +3,17 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.3.1](https://github.com/medusajs/medusa/compare/@medusajs/medusa-cli@1.3.0...@medusajs/medusa-cli@1.3.1) (2022-07-05)
### Features
* **medusa-cli:** Allow to revert migrations from the CLI ([#1353](https://github.com/medusajs/medusa/issues/1353)) ([012513b](https://github.com/medusajs/medusa/commit/012513b6a1e90169e9e0e53f7a59841a34fbaeb3))
# [1.3.0](https://github.com/medusajs/medusa/compare/@medusajs/medusa-cli@1.2.1...@medusajs/medusa-cli@1.3.0) (2022-05-01)
**Note:** Version bump only for package @medusajs/medusa-cli
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@medusajs/medusa-cli",
"version": "1.3.0",
"version": "1.3.1",
"description": "Command Line interface for Medusa Commerce",
"main": "dist/index.js",
"bin": {
+2 -2
View File
@@ -166,11 +166,11 @@ function buildLocalCommands(cli, isLocalProject) {
})
.command({
command: `migrations [action]`,
desc: `Migrate the database to the most recent version.`,
desc: `Manage migrations from the core and your own project`,
builder: {
action: {
demand: true,
choices: ["run", "show"],
choices: ["run", "revert", "show"],
},
},
handler: handlerP(
+16
View File
@@ -3,6 +3,22 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [0.0.26](https://github.com/medusajs/medusa/compare/medusa-dev-cli@0.0.24...medusa-dev-cli@0.0.26) (2022-07-05)
**Note:** Version bump only for package medusa-dev-cli
## [0.0.25](https://github.com/medusajs/medusa/compare/medusa-dev-cli@0.0.24...medusa-dev-cli@0.0.25) (2022-07-05)
**Note:** Version bump only for package medusa-dev-cli
## [0.0.24](https://github.com/medusajs/medusa/compare/medusa-dev-cli@0.0.23...medusa-dev-cli@0.0.24) (2021-12-08)
**Note:** Version bump only for package medusa-dev-cli
+5 -4
View File
@@ -1,7 +1,7 @@
{
"name": "medusa-dev-cli",
"description": "CLI helpers for contributors working on Medusa",
"version": "0.0.24",
"version": "0.0.26",
"author": "Sebastian Rindom <skrindom@gmail.com>",
"bin": {
"medusa-dev": "./dist/index.js"
@@ -16,6 +16,7 @@
"fs-extra": "^9.0.1",
"got": "^11.8.5",
"is-absolute": "^1.0.0",
"jest": "^25.5.2",
"lodash": "^4.17.21",
"signal-exit": "^3.0.3",
"verdaccio": "^4.10.0",
@@ -39,10 +40,10 @@
"directory": "packages/medusa-dev-cli"
},
"scripts": {
"build": "babel src --out-dir dist --ignore \"**/__tests__\"",
"prepare": "cross-env NODE_ENV=production npm run build",
"test": "echo \"Error: no test specified\" && exit 1",
"watch": "babel -w src --out-dir dist --ignore \"**/__tests__\""
"build": "babel src --out-dir dist",
"test": "jest",
"watch": "babel -w src --out-dir dist"
},
"engines": {
"node": ">=12.13.0"
+64 -43
View File
@@ -1,12 +1,12 @@
#!/usr/bin/env node
const Configstore = require(`configstore`);
const pkg = require(`../package.json`);
const _ = require(`lodash`);
const path = require(`path`);
const os = require(`os`);
const watch = require(`./watch`);
const { getVersionInfo } = require(`./utils/version`);
const Configstore = require(`configstore`)
const pkg = require(`../package.json`)
const _ = require(`lodash`)
const path = require(`path`)
const os = require(`os`)
const watch = require(`./watch`)
const { getVersionInfo } = require(`./utils/version`)
const argv = require(`yargs`)
.usage(`Usage: medusa-dev [options]`)
.alias(`q`, `quiet`)
@@ -19,7 +19,7 @@ const argv = require(`yargs`)
.nargs(`p`, 1)
.describe(
`p`,
`Set path to Medusa repository.
`Set path to medusa repository.
You typically only need to configure this once.`
)
.nargs(`force-install`, 0)
@@ -27,6 +27,11 @@ You typically only need to configure this once.`
`force-install`,
`Disables copying files into node_modules and forces usage of local npm repository.`
)
.nargs(`external-registry`, 0)
.describe(
`external-registry`,
`Run 'yarn add' commands without the --registry flag.`
)
.alias(`C`, `copy-all`)
.nargs(`C`, 0)
.describe(
@@ -39,87 +44,101 @@ You typically only need to configure this once.`
.alias(`h`, `help`)
.nargs(`v`, 0)
.alias(`v`, `version`)
.describe(`v`, `Print the currently installed version of Medusa Dev CLI`)
.argv;
.describe(`v`, `Print the currently installed version of Medusa Dev CLI`).argv
if (argv.version) {
console.log(getVersionInfo());
process.exit();
console.log(getVersionInfo())
process.exit()
}
const conf = new Configstore(pkg.name);
const conf = new Configstore(pkg.name)
const fs = require(`fs-extra`);
const fs = require(`fs-extra`)
let pathToRepo = argv.setPathToRepo;
let pathToRepo = argv.setPathToRepo
if (pathToRepo) {
if (pathToRepo.includes(`~`)) {
pathToRepo = path.join(os.homedir(), pathToRepo.split(`~`).pop());
pathToRepo = path.join(os.homedir(), pathToRepo.split(`~`).pop())
}
conf.set(`medusa-location`, path.resolve(pathToRepo));
process.exit();
conf.set(`medusa-location`, path.resolve(pathToRepo))
process.exit()
}
const havePackageJsonFile = fs.existsSync(`package.json`);
const havePackageJsonFile = fs.existsSync(`package.json`)
if (!havePackageJsonFile) {
console.error(`Current folder must have a package.json file!`);
process.exit();
console.error(`Current folder must have a package.json file!`)
process.exit()
}
const medusaLocation = conf.get(`medusa-location`);
const medusaLocation = conf.get(`medusa-location`)
if (!medusaLocation) {
console.error(
`
You haven't set the path yet to your cloned
version of medusa. Do so now by running:
medusa-dev --set-path-to-repo /path/to/my/cloned/version/medusa
`
);
process.exit();
)
process.exit()
}
// get list of packages from monorepo
const monoRepoPackages = [];
const packageNameToPath = new Map()
const monoRepoPackages = fs
.readdirSync(path.join(medusaLocation, `packages`))
.map((dirName) => {
try {
const localPkg = JSON.parse(
fs.readFileSync(
path.join(medusaLocation, `packages`, dirName, `package.json`)
)
)
const pkgsDirs = fs.readdirSync(path.join(medusaLocation, `packages`));
for (const dir of pkgsDirs) {
const pack = JSON.parse(
fs.readFileSync(path.join(medusaLocation, `packages`, dir, `package.json`))
);
monoRepoPackages.push(pack.name);
}
if (localPkg?.name) {
packageNameToPath.set(
localPkg.name,
path.join(medusaLocation, `packages`, dirName)
)
return localPkg.name
}
} catch (error) {
// fallback to generic one
}
const localPkg = JSON.parse(fs.readFileSync(`package.json`));
packageNameToPath.set(
dirName,
path.join(medusaLocation, `packages`, dirName)
)
return dirName
})
const localPkg = JSON.parse(fs.readFileSync(`package.json`))
// intersect dependencies with monoRepoPackages to get list of packages that are used
const localPackages = _.intersection(
monoRepoPackages,
Object.keys(_.merge({}, localPkg.dependencies, localPkg.devDependencies))
);
)
if (!argv.packages && _.isEmpty(localPackages)) {
console.error(
`
You haven't got any medusa dependencies into your current package.json
You probably want to pass in a list of packages to start
developing on! For example:
medusa-dev --packages @medusajs/medusa
medusa-dev --packages medusa medusa-js
If you prefer to place them in your package.json dependencies instead,
medusa-dev will pick them up.
`
);
)
if (!argv.forceInstall) {
process.exit();
process.exit()
} else {
console.log(
`Continuing other dependencies installation due to "--forceInstall" flag`
);
)
}
}
@@ -129,4 +148,6 @@ watch(medusaLocation, argv.packages, {
scanOnce: argv.scanOnce,
forceInstall: argv.forceInstall,
monoRepoPackages,
});
packageNameToPath,
externalRegistry: argv.externalRegistry,
})
@@ -1,23 +1,23 @@
const startVerdaccio = require(`verdaccio`).default;
const startVerdaccio = require(`verdaccio`).default
const fs = require(`fs-extra`);
const _ = require(`lodash`);
const fs = require(`fs-extra`)
const _ = require(`lodash`)
let VerdaccioInitPromise = null;
let VerdaccioInitPromise = null
const { verdaccioConfig } = require(`./verdaccio-config`);
const { publishPackage } = require(`./publish-package`);
const { installPackages } = require(`./install-packages`);
const { verdaccioConfig } = require(`./verdaccio-config`)
const { publishPackage } = require(`./publish-package`)
const { installPackages } = require(`./install-packages`)
const startServer = () => {
if (VerdaccioInitPromise) {
return VerdaccioInitPromise;
return VerdaccioInitPromise
}
console.log(`Starting local verdaccio server`);
console.log(`Starting local verdaccio server`)
// clear storage
fs.removeSync(verdaccioConfig.storage);
fs.removeSync(verdaccioConfig.storage)
VerdaccioInitPromise = new Promise((resolve) => {
startVerdaccio(
@@ -29,47 +29,49 @@ const startServer = () => {
(webServer, addr, pkgName, pkgVersion) => {
// console.log(webServer)
webServer.listen(addr.port || addr.path, addr.host, () => {
console.log(`Started local verdaccio server`);
console.log(`Started local verdaccio server`)
resolve();
});
resolve()
})
}
);
});
)
})
return VerdaccioInitPromise;
};
return VerdaccioInitPromise
}
exports.startVerdaccio = startServer;
exports.startVerdaccio = startServer
exports.publishPackagesLocallyAndInstall = async ({
packagesToPublish,
localPackages,
root,
packageNameToPath,
ignorePackageJSONChanges,
yarnWorkspaceRoot,
externalRegistry,
}) => {
await startServer();
await startServer()
const versionPostFix = Date.now();
const versionPostFix = Date.now()
const newlyPublishedPackageVersions = {};
const newlyPublishedPackageVersions = {}
for (const packageName of packagesToPublish) {
newlyPublishedPackageVersions[packageName] = await publishPackage({
packageName,
packagesToPublish,
root,
packageNameToPath,
versionPostFix,
ignorePackageJSONChanges,
});
})
}
const packagesToInstall = _.intersection(packagesToPublish, localPackages);
const packagesToInstall = _.intersection(packagesToPublish, localPackages)
await installPackages({
packagesToInstall,
yarnWorkspaceRoot,
newlyPublishedPackageVersions,
});
};
externalRegistry,
})
}
@@ -8,10 +8,11 @@ const installPackages = async ({
packagesToInstall,
yarnWorkspaceRoot,
newlyPublishedPackageVersions,
externalRegistry,
}) => {
console.log(
`Installing packages from local registry:\n${packagesToInstall
.map(packageAndVersion => ` - ${packageAndVersion}`)
.map((packageAndVersion) => ` - ${packageAndVersion}`)
.join(`\n`)}`
)
let installCmd
@@ -80,22 +81,22 @@ const installPackages = async ({
process.exit(1)
}
const handleDeps = deps => {
const handleDeps = (deps) => {
if (!deps) {
return false
}
let changed = false
Object.keys(deps).forEach(depName => {
Object.keys(deps).forEach((depName) => {
if (packagesToInstall.includes(depName)) {
deps[depName] = `gatsby-dev`
deps[depName] = `medusa-dev`
changed = true
}
})
return changed
}
Object.keys(workspacesLayout).forEach(workspaceName => {
Object.keys(workspacesLayout).forEach((workspaceName) => {
const { location } = workspacesLayout[workspaceName]
const pkgJsonPath = path.join(yarnWorkspaceRoot, location, `package.json`)
if (!fs.existsSync(pkgJsonPath)) {
@@ -109,7 +110,7 @@ const installPackages = async ({
changed |= handleDeps(pkg.peerDependencies)
if (changed) {
console.log(`Changing deps in ${pkgJsonPath} to use @gatsby-dev`)
console.log(`Changing deps in ${pkgJsonPath} to use @medusa-dev`)
fs.outputJSONSync(pkgJsonPath, pkg, {
spaces: 2,
})
@@ -118,24 +119,28 @@ const installPackages = async ({
// package.json files are changed - so we just want to install
// using verdaccio registry
installCmd = [
`yarn`,
[`install`, `--registry=${registryUrl}`, `--ignore-engines`],
]
const yarnCommands = [`install`]
if (!externalRegistry) {
yarnCommands.push(`--registry=${registryUrl}`)
}
installCmd = [`yarn`, yarnCommands]
} else {
installCmd = [
`yarn`,
[
`add`,
...packagesToInstall.map(packageName => {
const packageVersion = newlyPublishedPackageVersions[packageName]
return `${packageName}@${packageVersion}`
}),
`--registry=${registryUrl}`,
`--exact`,
`--ignore-engines`,
],
const yarnCommands = [
`add`,
...packagesToInstall.map((packageName) => {
const packageVersion = newlyPublishedPackageVersions[packageName]
return `${packageName}@${packageVersion}`
}),
`--exact`,
]
if (!externalRegistry) {
yarnCommands.push(`--registry=${registryUrl}`)
}
installCmd = [`yarn`, yarnCommands]
}
try {
@@ -1,18 +1,18 @@
const fs = require(`fs-extra`);
const path = require(`path`);
const fs = require(`fs-extra`)
const path = require(`path`)
const { promisifiedSpawn } = require(`../utils/promisified-spawn`);
const { registryUrl } = require(`./verdaccio-config`);
const { promisifiedSpawn } = require(`../utils/promisified-spawn`)
const { registryUrl } = require(`./verdaccio-config`)
const NPMRCContent = `${registryUrl.replace(
/https?:/g,
``
)}/:_authToken="medusa-dev"`;
)}/:_authToken="medusa-dev"`
const {
getMonorepoPackageJsonPath,
} = require(`../utils/get-monorepo-package-json-path`);
const { registerCleanupTask } = require(`./cleanup-tasks`);
} = require(`../utils/get-monorepo-package-json-path`)
const { registerCleanupTask } = require(`./cleanup-tasks`)
/**
* Edit package.json to:
@@ -27,7 +27,7 @@ const adjustPackageJson = ({
versionPostFix,
packagesToPublish,
ignorePackageJSONChanges,
root,
packageNameToPath,
}) => {
// we need to check if package depend on any other package to will be published and
// adjust version selector to point to dev version of package so local registry is used
@@ -36,10 +36,10 @@ const adjustPackageJson = ({
const monorepoPKGjsonString = fs.readFileSync(
monoRepoPackageJsonPath,
`utf-8`
);
const monorepoPKGjson = JSON.parse(monorepoPKGjsonString);
)
const monorepoPKGjson = JSON.parse(monorepoPKGjsonString)
monorepoPKGjson.version = `${monorepoPKGjson.version}-dev-${versionPostFix}`;
monorepoPKGjson.version = `${monorepoPKGjson.version}-dev-${versionPostFix}`
packagesToPublish.forEach((packageThatWillBePublished) => {
if (
monorepoPKGjson.dependencies &&
@@ -49,37 +49,37 @@ const adjustPackageJson = ({
fs.readFileSync(
getMonorepoPackageJsonPath({
packageName: packageThatWillBePublished,
root,
packageNameToPath,
}),
`utf-8`
)
).version;
).version
monorepoPKGjson.dependencies[
packageThatWillBePublished
] = `${currentVersion}-dev-${versionPostFix}`;
] = `${currentVersion}-dev-${versionPostFix}`
}
});
})
const temporaryMonorepoPKGjsonString = JSON.stringify(monorepoPKGjson);
const temporaryMonorepoPKGjsonString = JSON.stringify(monorepoPKGjson)
const unignorePackageJSONChanges = ignorePackageJSONChanges(packageName, [
monorepoPKGjsonString,
temporaryMonorepoPKGjsonString,
]);
])
// change version and dependency versions
fs.outputFileSync(monoRepoPackageJsonPath, temporaryMonorepoPKGjsonString);
fs.outputFileSync(monoRepoPackageJsonPath, temporaryMonorepoPKGjsonString)
return {
newPackageVersion: monorepoPKGjson.version,
unadjustPackageJson: registerCleanupTask(() => {
// restore original package.json
fs.outputFileSync(monoRepoPackageJsonPath, monorepoPKGjsonString);
unignorePackageJSONChanges();
fs.outputFileSync(monoRepoPackageJsonPath, monorepoPKGjsonString)
unignorePackageJSONChanges()
}),
};
};
}
}
/**
* Anonymous publishing require dummy .npmrc
@@ -88,38 +88,38 @@ const adjustPackageJson = ({
* This is not verdaccio restriction.
*/
const createTemporaryNPMRC = ({ pathToPackage }) => {
const NPMRCPath = path.join(pathToPackage, `.npmrc`);
fs.outputFileSync(NPMRCPath, NPMRCContent);
const NPMRCPath = path.join(pathToPackage, `.npmrc`)
fs.outputFileSync(NPMRCPath, NPMRCContent)
return registerCleanupTask(() => {
fs.removeSync(NPMRCPath);
});
};
fs.removeSync(NPMRCPath)
})
}
const publishPackage = async ({
packageName,
packagesToPublish,
root,
versionPostFix,
ignorePackageJSONChanges,
packageNameToPath,
}) => {
const monoRepoPackageJsonPath = getMonorepoPackageJsonPath({
packageName,
root,
});
packageNameToPath,
})
const { unadjustPackageJson, newPackageVersion } = adjustPackageJson({
monoRepoPackageJsonPath,
packageName,
root,
packageNameToPath,
versionPostFix,
packagesToPublish,
ignorePackageJSONChanges,
});
})
const pathToPackage = path.dirname(monoRepoPackageJsonPath);
const pathToPackage = path.dirname(monoRepoPackageJsonPath)
const uncreateTemporaryNPMRC = createTemporaryNPMRC({ pathToPackage });
const uncreateTemporaryNPMRC = createTemporaryNPMRC({ pathToPackage })
// npm publish
const publishCmd = [
@@ -128,26 +128,26 @@ const publishPackage = async ({
{
cwd: pathToPackage,
},
];
]
console.log(
`Publishing ${packageName}@${newPackageVersion} to local registry`
);
)
try {
await promisifiedSpawn(publishCmd);
await promisifiedSpawn(publishCmd)
console.log(
`Published ${packageName}@${newPackageVersion} to local registry`
);
)
} catch (e) {
console.error(`Failed to publish ${packageName}@${newPackageVersion}`, e);
process.exit(1);
console.error(`Failed to publish ${packageName}@${newPackageVersion}`, e)
process.exit(1)
}
uncreateTemporaryNPMRC();
unadjustPackageJson();
uncreateTemporaryNPMRC()
unadjustPackageJson()
return newPackageVersion;
};
return newPackageVersion
}
exports.publishPackage = publishPackage;
exports.publishPackage = publishPackage
@@ -1,5 +1,15 @@
const { getDependantPackages } = require(`../get-dependant-packages`)
function createMockPackageNameToPath(packageNames) {
const packageNameToPath = new Map()
for (const packageName of packageNames) {
packageNameToPath.set(packageName, `/test/${packageName}`)
}
return packageNameToPath
}
describe(`getDependantPackages`, () => {
it(`handles deep dependency chains`, () => {
const packagesToPublish = getDependantPackages({
@@ -9,6 +19,13 @@ describe(`getDependantPackages`, () => {
"package-a-dep1-dep1": new Set([`package-a-dep1`]),
"not-related": new Set([`also-not-related`]),
},
packageNameToPath: createMockPackageNameToPath([
`package-a`,
`package-a-dep1`,
`package-a-dep1-dep1`,
`not-related`,
`also-not-related`,
]),
})
expect(packagesToPublish).toEqual(
@@ -23,6 +40,10 @@ describe(`getDependantPackages`, () => {
"package-a": new Set([`package-b`]),
"package-b": new Set([`package-a`]),
},
packageNameToPath: createMockPackageNameToPath([
`package-a`,
`package-b`,
]),
})
expect(packagesToPublish).toEqual(new Set([`package-a`, `package-b`]))
})
@@ -43,15 +43,25 @@ jest.doMock(
describe(`traversePackageDeps`, () => {
it(`handles deep dependency chains`, () => {
const monoRepoPackages = [
`package-a`,
`package-a-dep1`,
`package-a-dep1-dep1`,
`package-not-used`,
]
const packageNameToPath = new Map()
for (const packageName of monoRepoPackages) {
packageNameToPath.set(
packageName,
path.join(...`<monorepo-path>/packages/${packageName}`.split(`/`))
)
}
const { seenPackages, depTree } = traversePackagesDeps({
root: `<monorepo-path>`,
packages: [`package-a`, `doesnt-exist`],
monoRepoPackages: [
`package-a`,
`package-a-dep1`,
`package-a-dep1-dep1`,
`package-not-used`,
],
monoRepoPackages,
packageNameToPath,
})
expect(seenPackages).toEqual([
@@ -1,9 +1,9 @@
const fs = require(`fs-extra`);
const _ = require(`lodash`);
const fs = require(`fs-extra`)
const _ = require(`lodash`)
const {
getMonorepoPackageJsonPath,
} = require(`./get-monorepo-package-json-path`);
const got = require(`got`);
} = require(`./get-monorepo-package-json-path`)
const got = require(`got`)
function difference(object, base) {
function changes(object, base) {
@@ -12,11 +12,11 @@ function difference(object, base) {
result[key] =
_.isObject(value) && _.isObject(base[key])
? changes(value, base[key])
: value;
: value
}
});
})
}
return changes(object, base);
return changes(object, base)
}
/**
@@ -24,33 +24,33 @@ function difference(object, base) {
* It will skip dependencies that are removed in monorepo package.
*
* If local package is not installed, it will check unpkg.com.
* This allow medusa-dev to skip publishing unnecesairly and
* This allow gatsby-dev to skip publishing unnecesairly and
* let install packages from public npm repository if nothing changed.
*/
exports.checkDepsChanges = async ({
newPath,
packageName,
monoRepoPackages,
root,
isInitialScan,
ignoredPackageJSON,
packageNameToPath,
}) => {
let localPKGjson;
let packageNotInstalled = false;
let localPKGjson
let packageNotInstalled = false
try {
localPKGjson = JSON.parse(fs.readFileSync(newPath, `utf-8`));
localPKGjson = JSON.parse(fs.readFileSync(newPath, `utf-8`))
} catch {
packageNotInstalled = true;
packageNotInstalled = true
// there is no local package - so we still need to install deps
// this is nice because devs won't need to do initial package installation - we can handle this.
if (!isInitialScan) {
console.log(
`'${packageName}' doesn't seem to be installed. Restart medusa-dev to publish it`
);
`'${packageName}' doesn't seem to be installed. Restart gatsby-dev to publish it`
)
return {
didDepsChanged: false,
packageNotInstalled,
};
}
}
// if package is not installed, we will do http GET request to
@@ -60,37 +60,33 @@ exports.checkDepsChanges = async ({
// this allow us to not publish to local repository
// and save some time/work
try {
const response = await got(
`https://unpkg.com/${packageName}/package.json`
);
const version = getPackageVersion(packageName)
const url = `https://unpkg.com/${packageName}@${version}/package.json`
const response = await got(url)
if (response?.statusCode !== 200) {
throw new Error(`No response or non 200 code`);
throw new Error(`No response or non 200 code for ${url}`)
}
localPKGjson = JSON.parse(response.body);
} catch {
localPKGjson = JSON.parse(response.body)
} catch (e) {
console.log(
`'${packageName}' doesn't seem to be installed and is not published on NPM.`
);
`'${packageName}' doesn't seem to be installed and is not published on NPM. Error: ${e.message}`
)
return {
didDepsChanged: true,
packageNotInstalled,
};
}
}
}
const monoDir = packageName.startsWith("@medusajs")
? packageName.split("/")[1]
: packageName;
const monoRepoPackageJsonPath = getMonorepoPackageJsonPath({
packageName: monoDir,
root,
});
packageName,
packageNameToPath,
})
const monorepoPKGjsonString = fs.readFileSync(
monoRepoPackageJsonPath,
`utf-8`
);
const monorepoPKGjson = JSON.parse(monorepoPKGjsonString);
)
const monorepoPKGjson = JSON.parse(monorepoPKGjsonString)
if (ignoredPackageJSON.has(packageName)) {
if (ignoredPackageJSON.get(packageName).includes(monorepoPKGjsonString)) {
// we are in middle of publishing and content of package.json is one set during publish process,
@@ -98,44 +94,44 @@ exports.checkDepsChanges = async ({
return {
didDepsChanged: false,
packageNotInstalled,
};
}
}
}
if (!monorepoPKGjson.dependencies) monorepoPKGjson.dependencies = {};
if (!localPKGjson.dependencies) localPKGjson.dependencies = {};
if (!monorepoPKGjson.dependencies) monorepoPKGjson.dependencies = {}
if (!localPKGjson.dependencies) localPKGjson.dependencies = {}
const areDepsEqual = _.isEqual(
monorepoPKGjson.dependencies,
localPKGjson.dependencies
);
)
if (!areDepsEqual) {
const diff = difference(
monorepoPKGjson.dependencies,
localPKGjson.dependencies
);
)
const diff2 = difference(
localPKGjson.dependencies,
monorepoPKGjson.dependencies
);
)
let needPublishing = false;
let isPublishing = false;
let needPublishing = false
let isPublishing = false
const depChangeLog = _.uniq(Object.keys({ ...diff, ...diff2 }))
.reduce((acc, key) => {
if (monorepoPKGjson.dependencies[key] === `medusa-dev`) {
if (monorepoPKGjson.dependencies[key] === `gatsby-dev`) {
// if we are in middle of publishing to local repository - ignore
isPublishing = true;
return acc;
isPublishing = true
return acc
}
if (localPKGjson.dependencies[key] === `medusa-dev`) {
if (localPKGjson.dependencies[key] === `gatsby-dev`) {
// monorepo packages will restore version, but after installation
// in local site - it will use `medusa-dev` dist tag - we need
// in local site - it will use `gatsby-dev` dist tag - we need
// to ignore changes that
return acc;
return acc
}
if (
@@ -143,48 +139,55 @@ exports.checkDepsChanges = async ({
monorepoPKGjson.dependencies[key]
) {
// Check only for version changes in packages
// that are not from medusa repo.
// Changes in medusa packages will be copied over
// that are not from gatsby repo.
// Changes in gatsby packages will be copied over
// from monorepo - and if those contain other dependency
// changes - they will be covered
if (!monoRepoPackages.includes(key)) {
acc.push(
` - '${key}' changed version from ${localPKGjson.dependencies[key]} to ${monorepoPKGjson.dependencies[key]}`
);
needPublishing = true;
)
needPublishing = true
}
} else if (monorepoPKGjson.dependencies[key]) {
acc.push(
` - '${key}@${monorepoPKGjson.dependencies[key]}' was added`
);
needPublishing = true;
acc.push(` - '${key}@${monorepoPKGjson.dependencies[key]}' was added`)
needPublishing = true
} else {
acc.push(` - '${key}@${localPKGjson.dependencies[key]}' was removed`);
acc.push(` - '${key}@${localPKGjson.dependencies[key]}' was removed`)
// this doesn't need publishing really, so will skip this
}
return acc;
return acc
}, [])
.join(`\n`);
.join(`\n`)
if (!isPublishing && depChangeLog.length > 0) {
console.log(`Dependencies of '${packageName}' changed:\n${depChangeLog}`);
console.log(`Dependencies of '${packageName}' changed:\n${depChangeLog}`)
if (isInitialScan) {
console.log(
`Will ${!needPublishing ? `not ` : ``}publish to local npm registry.`
);
)
} else {
console.warn(
`Installation of dependencies after initial scan is not implemented`
);
)
}
return {
didDepsChanged: needPublishing,
packageNotInstalled,
};
}
}
}
return {
didDepsChanged: false,
packageNotInstalled,
};
};
}
}
function getPackageVersion(packageName) {
const projectPackageJson = JSON.parse(
fs.readFileSync(`./package.json`, `utf-8`)
)
const { dependencies = {}, devDependencies = {} } = projectPackageJson
const version = dependencies[packageName] || devDependencies[packageName]
return version || `latest`
}
@@ -1,11 +1,4 @@
const path = require(`path`);
const path = require(`path`)
exports.getMonorepoPackageJsonPath = ({ packageName, root }) => {
let dirName = packageName;
if (packageName.startsWith("@medusajs")) {
const [, directory] = packageName.split("/");
dirName = directory;
}
return path.join(root, `packages`, dirName, `package.json`);
};
exports.getMonorepoPackageJsonPath = ({ packageName, packageNameToPath }) =>
path.join(packageNameToPath.get(packageName), `package.json`)
@@ -1,10 +1,17 @@
const _ = require(`lodash`);
const path = require(`path`);
const _ = require(`lodash`)
const path = require(`path`)
/**
* @typedef {Object} TraversePackagesDepsReturn
* @property {Object} depTree Lookup table to check dependants for given package.
* Used to determine which packages need to be published.
* @example
* ```
* {
* "medusa-cli": Set(["medusa"]),
* "medusa-telemetry": Set(["medusa", "medusa-cli"]),
* }
* ```
*/
/**
@@ -24,53 +31,58 @@ const path = require(`path`);
* @return {TraversePackagesDepsReturn}
*/
const traversePackagesDeps = ({
root,
packages,
monoRepoPackages,
seenPackages = [...packages],
depTree = {},
packageNameToPath,
}) => {
packages.forEach((p) => {
if (p.startsWith("@medusajs")) {
p = p.split("/")[1];
}
let pkgJson;
let pkgJson
try {
pkgJson = require(path.join(root, `packages`, p, `package.json`));
} catch {
console.error(`"${p}" package doesn't exist in monorepo.`);
const packageRoot = packageNameToPath.get(p)
if (packageRoot) {
pkgJson = require(path.join(packageRoot, `package.json`))
} else {
console.error(`"${p}" package doesn't exist in monorepo.`)
// remove from seenPackages
seenPackages = seenPackages.filter((seenPkg) => seenPkg !== p)
return
}
} catch (e) {
console.error(`"${p}" package doesn't exist in monorepo.`, e)
// remove from seenPackages
seenPackages = seenPackages.filter((seenPkg) => seenPkg !== p);
return;
seenPackages = seenPackages.filter((seenPkg) => seenPkg !== p)
return
}
const fromMonoRepo = _.intersection(
Object.keys({ ...pkgJson.dependencies }),
monoRepoPackages
);
)
fromMonoRepo.forEach((pkgName) => {
depTree[pkgName] = (depTree[pkgName] || new Set()).add(p);
});
depTree[pkgName] = (depTree[pkgName] || new Set()).add(p)
})
// only traverse not yet seen packages to avoid infinite loops
const newPackages = _.difference(fromMonoRepo, seenPackages);
const newPackages = _.difference(fromMonoRepo, seenPackages)
if (newPackages.length) {
newPackages.forEach((depFromMonorepo) => {
seenPackages.push(depFromMonorepo);
});
seenPackages.push(depFromMonorepo)
})
traversePackagesDeps({
root,
packages: fromMonoRepo,
monoRepoPackages,
seenPackages,
depTree,
});
packageNameToPath,
})
}
});
return { seenPackages, depTree };
};
})
return { seenPackages, depTree }
}
exports.traversePackagesDeps = traversePackagesDeps;
exports.traversePackagesDeps = traversePackagesDeps
+151 -133
View File
@@ -1,27 +1,27 @@
const chokidar = require(`chokidar`);
const _ = require(`lodash`);
const del = require(`del`);
const fs = require(`fs-extra`);
const path = require(`path`);
const findWorkspaceRoot = require(`find-yarn-workspace-root`);
const chokidar = require(`chokidar`)
const _ = require(`lodash`)
const del = require(`del`)
const fs = require(`fs-extra`)
const path = require(`path`)
const findWorkspaceRoot = require(`find-yarn-workspace-root`)
const { publishPackagesLocallyAndInstall } = require(`./local-npm-registry`);
const { checkDepsChanges } = require(`./utils/check-deps-changes`);
const { getDependantPackages } = require(`./utils/get-dependant-packages`);
const { publishPackagesLocallyAndInstall } = require(`./local-npm-registry`)
const { checkDepsChanges } = require(`./utils/check-deps-changes`)
const { getDependantPackages } = require(`./utils/get-dependant-packages`)
const {
setDefaultSpawnStdio,
promisifiedSpawn,
} = require(`./utils/promisified-spawn`);
const { traversePackagesDeps } = require(`./utils/traverse-package-deps`);
} = require(`./utils/promisified-spawn`)
const { traversePackagesDeps } = require(`./utils/traverse-package-deps`)
let numCopied = 0;
let numCopied = 0
const quit = () => {
console.log(`Copied ${numCopied} files`);
process.exit();
};
console.log(`Copied ${numCopied} files`)
process.exit()
}
const MAX_COPY_RETRIES = 3;
const MAX_COPY_RETRIES = 3
/*
* non-existent packages break on('ready')
@@ -30,76 +30,89 @@ const MAX_COPY_RETRIES = 3;
async function watch(
root,
packages,
{ scanOnce, quiet, forceInstall, monoRepoPackages, localPackages }
{
scanOnce,
quiet,
forceInstall,
monoRepoPackages,
localPackages,
packageNameToPath,
externalRegistry,
}
) {
setDefaultSpawnStdio(quiet ? `ignore` : `inherit`);
setDefaultSpawnStdio(quiet ? `ignore` : `inherit`)
// determine if in yarn workspace - if in workspace, force using verdaccio
// as current logic of copying files will not work correctly.
const yarnWorkspaceRoot = findWorkspaceRoot();
const yarnWorkspaceRoot = findWorkspaceRoot()
if (yarnWorkspaceRoot && process.env.NODE_ENV !== `test`) {
console.log(`Yarn workspace found.`);
forceInstall = true;
console.log(`Yarn workspace found.`)
forceInstall = true
}
let afterPackageInstallation = false;
let queuedCopies = [];
let afterPackageInstallation = false
let queuedCopies = []
const realCopyPath = (arg) => {
const { oldPath, newPath, quiet, resolve, reject, retry = 0 } = arg;
const { oldPath, newPath, quiet, resolve, reject, retry = 0 } = arg
fs.copy(oldPath, newPath, (err) => {
if (err) {
if (retry >= MAX_COPY_RETRIES) {
console.error(err);
reject(err);
return;
console.error(err)
reject(err)
return
} else {
setTimeout(
() => realCopyPath({ ...arg, retry: retry + 1 }),
500 * Math.pow(2, retry)
);
return;
)
return
}
}
// When the medusa binary is copied over, it is not setup with the executable
// permissions that it is given when installed via yarn.
// This fixes the issue where after running meduas-dev, running `yarn medusa develop`
// This fixes the issue where after running medusa-dev, running `yarn medusa develop`
// fails with a permission issue.
if (/(bin\/meduas.js|medusa(-cli)?\/cli.js)$/.test(newPath)) {
fs.chmodSync(newPath, `0755`);
// @fixes https://github.com/medusajs/medusa/issues/18809
// Binary files we target:
// - medusa/bin/medusa.js
// -medusa/cli.js
// -medusa-cli/cli.js
if (/(bin\/medusa.js|medusa(-cli)?\/cli.js)$/.test(newPath)) {
fs.chmodSync(newPath, `0755`)
}
numCopied += 1;
numCopied += 1
if (!quiet) {
console.log(`Copied ${oldPath} to ${newPath}`);
console.log(`Copied ${oldPath} to ${newPath}`)
}
resolve();
});
};
resolve()
})
}
const copyPath = (oldPath, newPath, quiet, packageName) =>
new Promise((resolve, reject) => {
const argObj = { oldPath, newPath, quiet, packageName, resolve, reject };
const argObj = { oldPath, newPath, quiet, packageName, resolve, reject }
if (afterPackageInstallation) {
realCopyPath(argObj);
realCopyPath(argObj)
} else {
queuedCopies.push(argObj);
queuedCopies.push(argObj)
}
});
})
const runQueuedCopies = () => {
afterPackageInstallation = true;
queuedCopies.forEach((argObj) => realCopyPath(argObj));
queuedCopies = [];
};
afterPackageInstallation = true
queuedCopies.forEach((argObj) => realCopyPath(argObj))
queuedCopies = []
}
const clearJSFilesFromNodeModules = async () => {
const packagesToClear = queuedCopies.reduce((acc, { packageName }) => {
if (packageName) {
acc.add(packageName);
acc.add(packageName)
}
return acc;
}, new Set());
return acc
}, new Set())
await Promise.all(
[...packagesToClear].map(
@@ -110,65 +123,64 @@ async function watch(
`!node_modules/${packageToClear}/src/**/*.{js,js.map}`,
])
)
);
};
)
}
// check packages deps and if they depend on other packages from monorepo
// add them to packages list
const { seenPackages, depTree } = traversePackagesDeps({
root,
packages: _.uniq(localPackages),
monoRepoPackages,
});
packageNameToPath,
})
const allPackagesToWatch = packages
? _.intersection(packages, seenPackages)
: seenPackages;
: seenPackages
const ignoredPackageJSON = new Map();
const ignoredPackageJSON = new Map()
const ignorePackageJSONChanges = (packageName, contentArray) => {
ignoredPackageJSON.set(packageName, contentArray);
ignoredPackageJSON.set(packageName, contentArray)
return () => {
ignoredPackageJSON.delete(packageName);
};
};
ignoredPackageJSON.delete(packageName)
}
}
if (forceInstall) {
try {
if (allPackagesToWatch.length > 0) {
await publishPackagesLocallyAndInstall({
packagesToPublish: allPackagesToWatch,
root,
packageNameToPath,
localPackages,
ignorePackageJSONChanges,
yarnWorkspaceRoot,
});
externalRegistry,
})
} else {
// run `yarn`
const yarnInstallCmd = [`yarn`];
const yarnInstallCmd = [`yarn`]
console.log(`Installing packages from public NPM registry`);
await promisifiedSpawn(yarnInstallCmd);
console.log(`Installation complete`);
console.log(`Installing packages from public NPM registry`)
await promisifiedSpawn(yarnInstallCmd)
console.log(`Installation complete`)
}
} catch (e) {
console.log(e);
console.log(e)
}
process.exit();
process.exit()
}
if (allPackagesToWatch.length === 0) {
console.error(`There are no packages to watch.`);
return;
console.error(`There are no packages to watch.`)
return
}
const cleanToWatch = allPackagesToWatch.map((pkgName) => {
if (pkgName.startsWith(`@medusajs`)) {
return pkgName.split("/")[1];
}
return pkgName;
});
const allPackagesIgnoringThemesToWatch = allPackagesToWatch.filter(
(pkgName) => !pkgName.startsWith(`medusa-theme`)
)
const ignored = [
/[/\\]node_modules[/\\]/i,
@@ -178,63 +190,70 @@ async function watch(
/[/\\]__mocks__[/\\]/i,
/\.npmrc/i,
].concat(
cleanToWatch.map((p) => new RegExp(`${p}[\\/\\\\]src[\\/\\\\]`, `i`))
);
allPackagesIgnoringThemesToWatch.map(
(p) => new RegExp(`${p}[\\/\\\\]src[\\/\\\\]`, `i`)
)
)
const watchers = _.uniq(
cleanToWatch
.map((p) => path.join(root, `/packages/`, p))
allPackagesToWatch
.map((p) => path.join(packageNameToPath.get(p)))
.filter((p) => fs.existsSync(p))
);
)
let allCopies = [];
const packagesToPublish = new Set();
let isInitialScan = true;
let isPublishing = false;
let allCopies = []
const packagesToPublish = new Set()
let isInitialScan = true
let isPublishing = false
const waitFor = new Set();
let anyPackageNotInstalled = false;
const watchEvents = [`change`, `add`];
const waitFor = new Set()
let anyPackageNotInstalled = false
const watchEvents = [`change`, `add`]
const packagePathMatchingEntries = Array.from(packageNameToPath.entries())
chokidar
.watch(watchers, {
ignored: [(filePath) => _.some(ignored, (reg) => reg.test(filePath))],
})
.on(`all`, async (event, filePath) => {
if (!watchEvents.includes(event)) {
return;
return
}
const [pack] = filePath
.split(/packages[/\\]/)
.pop()
.split(/[/\\]/);
// match against paths
let packageName
const sourcePkg = JSON.parse(
fs.readFileSync(path.join(root, `/packages/`, pack, `package.json`))
);
const packageName = sourcePkg.name;
for (const [_packageName, packagePath] of packagePathMatchingEntries) {
const relativeToThisPackage = path.relative(packagePath, filePath)
if (!relativeToThisPackage.startsWith(`..`)) {
packageName = _packageName
break
}
}
const prefix = path.join(root, `/packages/`, pack);
if (!packageName) {
return
}
const prefix = packageNameToPath.get(packageName)
// Copy it over local version.
// Don't copy over the medusa bin file as that breaks the NPM symlink.
if (_.includes(filePath, `dist/medusa-cli.js`)) {
return;
return
}
const relativePackageFile = path.relative(prefix, filePath);
const relativePackageFile = path.relative(prefix, filePath)
const newPath = path.join(
`./node_modules/${packageName}`,
relativePackageFile
);
)
if (relativePackageFile === `package.json`) {
// package.json files will change during publish to adjust version of package (and dependencies), so ignore
// changes during this process
if (isPublishing) {
return;
return
}
// Compare dependencies with local version
@@ -243,10 +262,10 @@ async function watch(
newPath,
packageName,
monoRepoPackages,
root,
packageNameToPath,
isInitialScan,
ignoredPackageJSON,
});
})
if (isInitialScan) {
// normally checkDepsChanges would be sync,
@@ -255,21 +274,19 @@ async function watch(
// keep track of it to make sure all of it
// finish before installing
waitFor.add(didDepsChangedPromise);
waitFor.add(didDepsChangedPromise)
}
const {
didDepsChanged,
packageNotInstalled,
} = await didDepsChangedPromise;
const { didDepsChanged, packageNotInstalled } =
await didDepsChangedPromise
if (packageNotInstalled) {
anyPackageNotInstalled = true;
anyPackageNotInstalled = true
}
if (didDepsChanged) {
if (isInitialScan) {
waitFor.delete(didDepsChangedPromise);
waitFor.delete(didDepsChangedPromise)
// handle dependency change only in initial scan - this is for sure doable to
// handle this in watching mode correctly - but for the sake of shipping
// this I limit more work/time consuming edge cases.
@@ -287,66 +304,67 @@ async function watch(
}).forEach((packageToPublish) => {
// scheduling publish - we will publish when `ready` is emitted
// as we can do single publish then
packagesToPublish.add(packageToPublish);
});
packagesToPublish.add(packageToPublish)
})
}
}
// don't ever copy package.json as this will mess up any future dependency
// changes checks
return;
return
}
const localCopies = [copyPath(filePath, newPath, quiet, packageName)];
const localCopies = [copyPath(filePath, newPath, quiet, packageName)]
// If this is from "cache-dir" also copy it into the site's .cache
if (_.includes(filePath, `cache-dir`)) {
const newCachePath = path.join(
`.cache/`,
path.relative(path.join(prefix, `cache-dir`), filePath)
);
localCopies.push(copyPath(filePath, newCachePath, quiet));
)
localCopies.push(copyPath(filePath, newCachePath, quiet))
}
allCopies = allCopies.concat(localCopies);
allCopies = allCopies.concat(localCopies)
})
.on(`ready`, async () => {
// wait for all async work needed to be done
// before publishing / installing
await Promise.all(Array.from(waitFor));
await Promise.all(Array.from(waitFor))
if (isInitialScan) {
isInitialScan = false;
isInitialScan = false
if (packagesToPublish.size > 0) {
isPublishing = true;
isPublishing = true
await publishPackagesLocallyAndInstall({
packagesToPublish: Array.from(packagesToPublish),
root,
packageNameToPath,
localPackages,
ignorePackageJSONChanges,
});
packagesToPublish.clear();
isPublishing = false;
externalRegistry,
})
packagesToPublish.clear()
isPublishing = false
} else if (anyPackageNotInstalled) {
// run `yarn`
const yarnInstallCmd = [`yarn`];
const yarnInstallCmd = [`yarn`]
console.log(`Installing packages from public NPM registry`);
await promisifiedSpawn(yarnInstallCmd);
console.log(`Installation complete`);
console.log(`Installing packages from public NPM registry`)
await promisifiedSpawn(yarnInstallCmd)
console.log(`Installation complete`)
}
await clearJSFilesFromNodeModules();
runQueuedCopies();
await clearJSFilesFromNodeModules()
runQueuedCopies()
}
// all files watched, quit once all files are copied if necessary
Promise.all(allCopies).then(() => {
if (scanOnce) {
quit();
quit()
}
});
});
})
})
}
module.exports = watch;
module.exports = watch
+17
View File
@@ -3,6 +3,23 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.0.7](https://github.com/medusajs/medusa/compare/medusa-file-minio@1.0.6...medusa-file-minio@1.0.7) (2022-07-05)
### Bug Fixes
* **medusa-file-spaces,medusa-file-s3,medusa-file-minio:** Add options to super call in file plugins ([#1714](https://github.com/medusajs/medusa/issues/1714)) ([a5f717b](https://github.com/medusajs/medusa/commit/a5f717be5ae1954f3dbf1e7b2edb35d11088a8c8))
### Features
* **medusa:** Delete and download url endpoints ([#1705](https://github.com/medusajs/medusa/issues/1705)) ([cc29b64](https://github.com/medusajs/medusa/commit/cc29b641c9358415b46179371988e7ddc11d2664))
* **medusa:** Extend file-service interface + move to core ([#1577](https://github.com/medusajs/medusa/issues/1577)) ([8e42d37](https://github.com/medusajs/medusa/commit/8e42d37e84e80c003b9c0311117ab8a8871aa61b))
## [1.0.6](https://github.com/medusajs/medusa/compare/medusa-file-minio@1.0.4...medusa-file-minio@1.0.6) (2022-06-19)
**Note:** Version bump only for package medusa-file-minio
+1 -1
View File
@@ -13,4 +13,4 @@ Learn more about how you can use this plugin in the [documentation](https://docs
access_key_id: "YOUR-ACCESS-KEY",
secret_access_key: "YOUR-SECRET-KEY",
}
```
```
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "medusa-file-minio",
"version": "1.0.6",
"version": "1.0.7",
"description": "MinIO server file connector for Medusa",
"main": "index.js",
"repository": {
@@ -1,10 +1,11 @@
import fs from "fs"
import { AbstractFileService } from '@medusajs/medusa'
import aws from "aws-sdk"
import { FileService } from "medusa-interfaces"
import fs from "fs"
class MinioService extends FileService {
class MinioService extends AbstractFileService {
constructor({}, options) {
super()
super({}, options)
this.bucket_ = options.bucket
this.accessKeyId_ = options.access_key_id
@@ -15,14 +16,14 @@ class MinioService extends FileService {
}
upload(file) {
aws.config.setPromisesDependency()
aws.config.setPromisesDependency(null)
aws.config.update({
accessKeyId: this.accessKeyId_,
secretAccessKey: this.secretAccessKey_,
endpoint: this.endpoint_,
s3ForcePathStyle: this.s3ForcePathStyle_,
signatureVersion: this.signatureVersion_,
})
}, true)
const s3 = new aws.S3()
const params = {
@@ -46,14 +47,14 @@ class MinioService extends FileService {
}
delete(file) {
aws.config.setPromisesDependency()
aws.config.setPromisesDependency(null)
aws.config.update({
accessKeyId: this.accessKeyId_,
secretAccessKey: this.secretAccessKey_,
endpoint: this.endpoint_,
s3ForcePathStyle: this.s3ForcePathStyle_,
signatureVersion: this.signatureVersion_,
})
}, true)
const s3 = new aws.S3()
const params = {
@@ -71,6 +72,18 @@ class MinioService extends FileService {
})
})
}
async getUploadStreamDescriptor(fileData) {
throw new Error("Method not implemented.")
}
async getDownloadStream(fileData) {
throw new Error("Method not implemented.")
}
async getPresignedDownloadUrl(fileData) {
throw new Error("Method not implemented.")
}
}
export default MinioService
+16
View File
@@ -3,6 +3,22 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.1.3](https://github.com/medusajs/medusa/compare/medusa-file-s3@1.1.2...medusa-file-s3@1.1.3) (2022-07-05)
### Bug Fixes
* **medusa-file-spaces,medusa-file-s3,medusa-file-minio:** Add options to super call in file plugins ([#1714](https://github.com/medusajs/medusa/issues/1714)) ([a5f717b](https://github.com/medusajs/medusa/commit/a5f717be5ae1954f3dbf1e7b2edb35d11088a8c8))
### Features
* **medusa:** Extend file-service interface + move to core ([#1577](https://github.com/medusajs/medusa/issues/1577)) ([8e42d37](https://github.com/medusajs/medusa/commit/8e42d37e84e80c003b9c0311117ab8a8871aa61b))
## [1.1.2](https://github.com/medusajs/medusa/compare/medusa-file-s3@1.1.0...medusa-file-s3@1.1.2) (2022-06-19)
**Note:** Version bump only for package medusa-file-s3
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "medusa-file-s3",
"version": "1.1.2",
"version": "1.1.3",
"description": "AWS s3 file connector for Medusa",
"main": "index.js",
"repository": {
+19 -7
View File
@@ -1,10 +1,10 @@
import fs from "fs"
import aws from "aws-sdk"
import { FileService } from "medusa-interfaces"
import { AbstractFileService } from '@medusajs/medusa'
class S3Service extends FileService {
class S3Service extends AbstractFileService {
constructor({}, options) {
super()
super({}, options)
this.bucket_ = options.bucket
this.s3Url_ = options.s3_url
@@ -15,13 +15,13 @@ class S3Service extends FileService {
}
upload(file) {
aws.config.setPromisesDependency()
aws.config.setPromisesDependency(null)
aws.config.update({
accessKeyId: this.accessKeyId_,
secretAccessKey: this.secretAccessKey_,
region: this.region_,
endpoint: this.endpoint_,
})
}, true)
const s3 = new aws.S3()
var params = {
@@ -44,13 +44,13 @@ class S3Service extends FileService {
}
delete(file) {
aws.config.setPromisesDependency()
aws.config.setPromisesDependency(null)
aws.config.update({
accessKeyId: this.accessKeyId_,
secretAccessKey: this.secretAccessKey_,
region: this.region_,
endpoint: this.endpoint_,
})
}, true)
const s3 = new aws.S3()
var params = {
@@ -68,6 +68,18 @@ class S3Service extends FileService {
})
})
}
async getUploadStreamDescriptor(fileData) {
throw new Error("Method not implemented.")
}
async getDownloadStream(fileData) {
throw new Error("Method not implemented.")
}
async getPresignedDownloadUrl(fileData) {
throw new Error("Method not implemented.")
}
}
export default S3Service
+18
View File
@@ -3,6 +3,24 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.2.3](https://github.com/medusajs/medusa/compare/medusa-file-spaces@1.2.2...medusa-file-spaces@1.2.3) (2022-07-05)
### Bug Fixes
* **medusa-file-spaces,medusa-file-s3,medusa-file-minio:** Add options to super call in file plugins ([#1714](https://github.com/medusajs/medusa/issues/1714)) ([a5f717b](https://github.com/medusajs/medusa/commit/a5f717be5ae1954f3dbf1e7b2edb35d11088a8c8))
### Features
* **medusa:** Add batch strategy for order exports ([#1603](https://github.com/medusajs/medusa/issues/1603)) ([bf47d1a](https://github.com/medusajs/medusa/commit/bf47d1aecd74f4489667609444a8b09393e894d3))
* **medusa:** Extend file-service interface + move to core ([#1577](https://github.com/medusajs/medusa/issues/1577)) ([8e42d37](https://github.com/medusajs/medusa/commit/8e42d37e84e80c003b9c0311117ab8a8871aa61b))
* **medusa-file-spaces:** DigitalOcean fileservice streaming ([#1585](https://github.com/medusajs/medusa/issues/1585)) ([abaf10b](https://github.com/medusajs/medusa/commit/abaf10b31d1e9a60710da87cac5c9c869195660d)), closes [#1583](https://github.com/medusajs/medusa/issues/1583) [#1580](https://github.com/medusajs/medusa/issues/1580) [#1582](https://github.com/medusajs/medusa/issues/1582) [#1583](https://github.com/medusajs/medusa/issues/1583) [#1580](https://github.com/medusajs/medusa/issues/1580) [#1582](https://github.com/medusajs/medusa/issues/1582)
## [1.2.2](https://github.com/medusajs/medusa/compare/medusa-file-spaces@1.2.0...medusa-file-spaces@1.2.2) (2022-06-19)
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "medusa-file-spaces",
"version": "1.2.2",
"version": "1.2.3",
"description": "Digital Ocean Spaces file connector for Medusa",
"main": "index.js",
"repository": {
@@ -1,11 +1,12 @@
import fs from "fs"
import { AbstractFileService } from "@medusajs/medusa"
import aws from "aws-sdk"
import fs from "fs"
import { parse } from "path"
import { FileService } from "medusa-interfaces"
import stream from "stream"
class DigitalOceanService extends FileService {
class DigitalOceanService extends AbstractFileService {
constructor({}, options) {
super()
super({}, options)
this.bucket_ = options.bucket
this.spacesUrl_ = options.spaces_url?.replace(/\/$/, "")
@@ -16,18 +17,12 @@ class DigitalOceanService extends FileService {
}
upload(file) {
aws.config.setPromisesDependency()
aws.config.update({
accessKeyId: this.accessKeyId_,
secretAccessKey: this.secretAccessKey_,
region: this.region_,
endpoint: this.endpoint_,
})
this.updateAwsConfig()
const parsedFilename = parse(file.originalname)
const fileKey = `${parsedFilename.name}-${Date.now()}${parsedFilename.ext}`
const s3 = new aws.S3()
var params = {
const params = {
ACL: "public-read",
Bucket: this.bucket_,
Body: fs.createReadStream(file.path),
@@ -51,16 +46,10 @@ class DigitalOceanService extends FileService {
}
delete(file) {
aws.config.setPromisesDependency()
aws.config.update({
accessKeyId: this.accessKeyId_,
secretAccessKey: this.secretAccessKey_,
region: this.region_,
endpoint: this.endpoint_,
})
this.updateAwsConfig()
const s3 = new aws.S3()
var params = {
const params = {
Bucket: this.bucket_,
Key: `${file}`,
}
@@ -75,6 +64,71 @@ class DigitalOceanService extends FileService {
})
})
}
async getUploadStreamDescriptor(fileData) {
this.updateAwsConfig()
const pass = new stream.PassThrough()
const fileKey = `${fileData.name}.${fileData.ext}`
const params = {
ACL: fileData.acl ?? "private",
Bucket: this.bucket_,
Body: pass,
Key: fileKey,
}
const s3 = new aws.S3()
return {
writeStream: pass,
promise: s3.upload(params).promise(),
url: `${this.spacesUrl_}/${fileKey}`,
fileKey,
}
}
async getDownloadStream(fileData) {
this.updateAwsConfig()
const s3 = new aws.S3()
const params = {
Bucket: this.bucket_,
Key: `${fileData.fileKey}`,
}
return s3.getObject(params).createReadStream()
}
async getPresignedDownloadUrl(fileData) {
this.updateAwsConfig({
signatureVersion: "v4",
})
const s3 = new aws.S3()
const params = {
Bucket: this.bucket_,
Key: `${fileData.fileKey}`,
Expires: 60, // 60 seconds
}
return await s3.getSignedUrlPromise("getObject", params)
}
updateAwsConfig(additionalConfiguration = {}) {
aws.config.setPromisesDependency(null)
aws.config.update(
{
accessKeyId: this.accessKeyId_,
secretAccessKey: this.secretAccessKey_,
region: this.region_,
endpoint: this.endpoint_,
...additionalConfiguration,
},
true
)
}
}
export default DigitalOceanService
@@ -3,6 +3,18 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.3.3](https://github.com/medusajs/medusa/compare/medusa-fulfillment-webshipper@1.3.2...medusa-fulfillment-webshipper@1.3.3) (2022-07-05)
### Bug Fixes
* **webshipper:** allow cancelling WS orders with error status ([#1755](https://github.com/medusajs/medusa/issues/1755)) ([1d3032d](https://github.com/medusajs/medusa/commit/1d3032dc671b477654afd13a218caa772454bde0))
* **webshipper:** only add invoices if invoice generator produces a file ([#1749](https://github.com/medusajs/medusa/issues/1749)) ([c0e18d4](https://github.com/medusajs/medusa/commit/c0e18d473c050d143f67ed2c76fa9ec9414daf1a))
## [1.3.2](https://github.com/medusajs/medusa/compare/medusa-fulfillment-webshipper@1.3.0...medusa-fulfillment-webshipper@1.3.2) (2022-06-19)
**Note:** Version bump only for package medusa-fulfillment-webshipper
@@ -1,6 +1,6 @@
# medusa-fulfillment-webshipper
Adds Webshipper as a fulfilment provider in Medusa Commerce.
Adds Webshipper as a fulfilment provider in Medusa Commerce.
On each new fulfillment an order is created in Webshipper. The plugin listens for shipment events and updated the shipment accordingly.
A webhook listener is exposed at `/webshipper/shipments` to listen for shipment creations. You must create this webhook in Webshipper to have Medusa listen for shipment events.
@@ -13,4 +13,5 @@ A webhook listener is exposed at `/webshipper/shipments` to listen for shipment
order_channel_id: [the channel id to register orders on] (required)
webhook_secret: [the webhook secret used to listen for shipments] (required)
coo_countries: [an array of countries in which a Certificate of Origin will be attached] (default: "all")
delete_on_cancel [determines whether Webshipper orders are deleted when a Medusa fulfillment is canceled] (default: false)
```
@@ -1,6 +1,6 @@
{
"name": "medusa-fulfillment-webshipper",
"version": "1.3.2",
"version": "1.3.3",
"description": "Webshipper Fulfillment provider for Medusa",
"main": "index.js",
"repository": {
@@ -18,7 +18,7 @@
"@babel/plugin-transform-runtime": "^7.7.6",
"@babel/preset-env": "^7.7.5",
"@babel/runtime": "^7.9.6",
"@medusajs/medusa": "^1.3.2",
"@medusajs/medusa": "^1.3.3",
"client-sessions": "^0.8.0",
"cross-env": "^5.2.1",
"eslint": "^6.8.0",
@@ -287,19 +287,21 @@ class WebshipperFulfillmentService extends FulfillmentService {
fulfillmentItems
)
invoice = await this.client_.documents
.create({
type: "documents",
attributes: {
document_size: this.options_.document_size || "A4",
document_format: "PDF",
base64: base64Invoice,
document_type: "invoice",
},
})
.catch((err) => {
throw err
})
if (base64Invoice) {
invoice = await this.client_.documents
.create({
type: "documents",
attributes: {
document_size: this.options_.document_size || "A4",
document_format: "PDF",
base64: base64Invoice,
document_type: "invoice",
},
})
.catch((err) => {
throw err
})
}
const cooCountries = this.options_.coo_countries
if (
@@ -621,19 +623,17 @@ class WebshipperFulfillmentService extends FulfillmentService {
return Promise.resolve()
}
if (order) {
if (
order.data.attributes.status !== "pending" &&
order.data.attributes.status !== "missing_rate"
) {
if (order.data.attributes.status === "cancelled") {
return Promise.resolve(order)
}
throw new Error("Cannot cancel order")
}
if (this.options_.delete_on_cancel) {
return await this.client_.orders.delete(data.id)
}
return this.client_.orders.delete(data.id)
return await this.client_.orders.update(data.id, {
id: data.id,
type: "orders",
attributes: {
status: "cancelled",
},
})
}
}
@@ -83,6 +83,16 @@ class Webshipper {
},
}).then(({ data }) => data)
},
update: async (id, data) => {
const path = `/v2/orders/${id}`
return this.client_({
method: "PATCH",
url: path,
data: {
data,
},
}).then(({ data }) => data)
},
delete: async (id) => {
const path = `/v2/orders/${id}`
return this.client_({
+11
View File
@@ -3,6 +3,17 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.3.1](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.3.0...medusa-interfaces@1.3.1) (2022-07-05)
### Bug Fixes
* **medusa:** Remove deps `mongoose` + `mongodb` ([#1218](https://github.com/medusajs/medusa/issues/1218)) ([c76e23e](https://github.com/medusajs/medusa/commit/c76e23e84dd8cb08c3c709f9f95c4c17b9685439))
# [1.3.0](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.2.1...medusa-interfaces@1.3.0) (2022-05-01)
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "medusa-interfaces",
"version": "1.3.0",
"version": "1.3.1",
"description": "Core interfaces for Medusa",
"main": "dist/index.js",
"repository": {
@@ -1,153 +0,0 @@
import mongoose from "mongoose"
/**
* Interface for data models. The default data layer uses an internal mongoose
* model and is as such compatible with MongoDB.
* @interface
*/
class BaseModel {
constructor() {
/** @const the underlying mongoose model used for queries */
this.mongooseModel_ = this.createMongooseModel_()
}
/**
* Returns the model schema. The child class must implement the static schema
* property.
* @return {string} the models schema
*/
getSchema() {
if (!this.constructor.schema) {
throw new Error("Schema not defined")
}
return this.constructor.schema
}
/**
* Returns the model name. The child class must implement the static modelName
* property.
* @return {string} the name of the model
*/
getModelName() {
if (!this.constructor.modelName) {
throw new Error("Every model must have a static modelName property")
}
return this.constructor.modelName
}
/**
* Returns the schema options defined in child class.
* @return {object} the schema options
*/
getSchemaOptions() {
if (!this.constructor.schemaOptions) {
return {}
}
return this.constructor.schemaOptions
}
/**
* @private
* Creates a mongoose model based on schema, schema options and model name.
* @return {Mongooose.Model} the mongoose model
*/
createMongooseModel_() {
const schema = this.getSchema()
const options = this.getSchemaOptions()
const mongooseSchema = new mongoose.Schema(schema, options)
return mongoose.model(this.getModelName(), mongooseSchema)
}
/**
*/
startSession() {
return this.mongooseModel_.startSession()
}
/**
* Queries the mongoose model via the mongoose's findOne.
* @param query {object} a mongoose selector query
* @param options {?object=} mongoose options
* @return {?mongoose.Document} the retreived mongoose document or null.
*/
findOne(query, options = {}) {
return this.mongooseModel_.findOne(query, options).lean()
}
/**
* Queries the mongoose model via the mongoose's find.
* @param query {object} a mongoose selector query
* @param options {?object=} mongoose options
* @return {Array<mongoose.Document>} the retreived mongoose documents or
* an empty array
*/
find(query, options, offset, limit) {
return this.mongooseModel_
.find(query, options)
.skip(offset)
.limit(limit)
.lean()
}
count() {
return this.mongooseModel_.count({})
}
/**
* Update a model via the mongoose model's updateOne.
* @param query {object} a mongoose selector query
* @param update {object} mongoose update object
* @param options {?object=} mongoose options
* @return {object} mongoose result
*/
updateOne(query, update, options = {}) {
options.new = true
return this.mongooseModel_.findOneAndUpdate(query, update, options).lean()
}
/**
* Update a model via the mongoose model's update.
* @param query {object} a mongoose selector query
* @param update {object} mongoose update object
* @param options {?object=} mongoose options
* @return {object} mongoose result
*/
update(query, update, options) {
return this.mongooseModel_.update(query, update, options)
}
/**
* Creates a document in the mongoose model's collection via create.
* @param object {object} the value of the document to be created
* @param options {?object=} mongoose options
* @return {object} mongoose result
*/
create(object, options) {
return this.mongooseModel_.create(object, options)
}
/**
* Deletes a document in the mongoose model's collection
* @param query {object} the value of the document to be created
* @param options {?object=} mongoose options
* @return {object} mongoose result
*/
deleteOne(query, options) {
return this.mongooseModel_.deleteOne(query, options)
}
/**
* Deletes many document in the mongoose model's collection
* @param query {object} the value of the document to be created
* @param options {?object=} mongoose options
* @return {object} mongoose result
*/
delete(query, options) {
return this.mongooseModel_.deleteMany(query, options)
}
}
export default BaseModel
+2 -3
View File
@@ -1,8 +1,7 @@
export { default as BaseService } from "./base-service"
export { default as BaseModel } from "./base-model"
export { default as PaymentService } from "./payment-service"
export { default as FulfillmentService } from "./fulfillment-service"
export { default as FileService } from "./file-service"
export { default as FulfillmentService } from "./fulfillment-service"
export { default as NotificationService } from "./notification-service"
export { default as OauthService } from "./oauth-service"
export { default as PaymentService } from "./payment-service"
export { default as SearchService } from "./search-service"
+17
View File
@@ -3,6 +3,23 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.2.3](https://github.com/medusajs/medusa/compare/@medusajs/medusa-js@1.2.2...@medusajs/medusa-js@1.2.3) (2022-07-05)
### Bug Fixes
* **medusa-js:** Fix `stringifyNullProperties` util ([#1766](https://github.com/medusajs/medusa/issues/1766)) ([7bee57f](https://github.com/medusajs/medusa/commit/7bee57f7c55e15b6a6c847dfda433e67f258ef8e))
### Features
* **medusa-js:** Create utils to stringify null values and respect object types ([#1748](https://github.com/medusajs/medusa/issues/1748)) ([fc1cbe7](https://github.com/medusajs/medusa/commit/fc1cbe72c7b0cd2879a8b112a6f63fa94c728a19))
* **medusa,medusa-js,medusa-react:** Add BatchJob API support in `medusa-js` + `medusa-react` ([#1704](https://github.com/medusajs/medusa/issues/1704)) ([7302d76](https://github.com/medusajs/medusa/commit/7302d76e12683c989f340d2fcfaf4338dca6554a))
## [1.2.2](https://github.com/medusajs/medusa/compare/@medusajs/medusa-js@1.2.0...@medusajs/medusa-js@1.2.2) (2022-06-19)
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@medusajs/medusa-js",
"version": "1.2.2",
"version": "1.2.3",
"description": "Client for Medusa Commerce Rest API",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
@@ -15,7 +15,7 @@
"author": "Oliver Juhl",
"license": "MIT",
"dependencies": {
"@medusajs/medusa": "^1.3.2",
"@medusajs/medusa": "^1.3.3",
"axios": "^0.24.0",
"form-data": "^4.0.0",
"qs": "^6.10.3",
@@ -0,0 +1,60 @@
import {
AdminBatchJobListRes,
AdminBatchJobRes,
AdminGetBatchParams,
AdminPostBatchesReq,
} from "@medusajs/medusa"
import qs from "qs"
import { ResponsePromise } from "../../typings"
import BaseResource from "../base"
import { stringifyNullProperties } from "../../utils"
class AdminBatchJobsResource extends BaseResource {
create(
payload: AdminPostBatchesReq,
customHeaders: Record<string, any> = {}
): ResponsePromise<AdminBatchJobRes> {
const path = `/admin/batch-jobs`
return this.client.request("POST", path, payload, {}, customHeaders)
}
list(
query?: AdminGetBatchParams,
customHeaders: Record<string, any> = {}
): ResponsePromise<AdminBatchJobListRes> {
let path = `/admin/batch-jobs`
if (query) {
const queryString = qs.stringify(stringifyNullProperties(query))
path = `/admin/batch-jobs?${queryString}`
}
return this.client.request("GET", path, {}, {}, customHeaders)
}
cancel(
batchJobId: string,
customHeaders: Record<string, any> = {}
): ResponsePromise<AdminBatchJobRes> {
const path = `/admin/batch-jobs/${batchJobId}/cancel`
return this.client.request("POST", path, {}, {}, customHeaders)
}
confirm(
batchJobId: string,
customHeaders: Record<string, any> = {}
): ResponsePromise<AdminBatchJobRes> {
const path = `/admin/batch-jobs/${batchJobId}/confirm`
return this.client.request("POST", path, {}, {}, customHeaders)
}
retrieve(
batchJobId: string,
customHeaders: Record<string, any> = {}
): ResponsePromise<AdminBatchJobRes> {
const path = `/admin/batch-jobs/${batchJobId}`
return this.client.request("GET", path, {}, {}, customHeaders)
}
}
export default AdminBatchJobsResource
+17 -15
View File
@@ -1,33 +1,35 @@
import BaseResource from "../base"
import AdminAuthResource from "./auth"
import AdminCustomersResource from "./customers"
import AdminCustomerGroupsResource from "./customer-groups"
import AdminDiscountsResource from "./discounts"
import AdminBatchJobsResource from "./batch-jobs"
import CollectionsResource from "./collections"
import AdminCustomerGroupsResource from "./customer-groups"
import AdminCustomersResource from "./customers"
import AdminDiscountsResource from "./discounts"
import AdminDraftOrdersResource from "./draft-orders"
import AdminGiftCardsResource from "./gift-cards"
import AdminInvitesResource from "./invites"
import AdminNotesResource from "./notes"
import AdminProductsResource from "./products"
import AdminProductTypesResource from "./product-types"
import AdminUsersResource from "./users"
import AdminReturnsResource from "./returns"
import AdminNotificationsResource from "./notifications"
import AdminOrdersResource from "./orders"
import AdminPriceListResource from "./price-lists"
import AdminProductTagsResource from "./product-tags"
import AdminProductTypesResource from "./product-types"
import AdminProductsResource from "./products"
import AdminRegionsResource from "./regions"
import AdminReturnReasonsResource from "./return-reasons"
import AdminVariantsResource from "./variants"
import AdminSwapsResource from "./swaps"
import AdminTaxRatesResource from "./tax-rates"
import AdminReturnsResource from "./returns"
import AdminShippingOptionsResource from "./shipping-options"
import AdminShippingProfilesResource from "./shipping-profiles"
import AdminStoresResource from "./store"
import AdminShippingOptionsResource from "./shipping-options"
import AdminRegionsResource from "./regions"
import AdminNotificationsResource from "./notifications"
import AdminSwapsResource from "./swaps"
import AdminTaxRatesResource from "./tax-rates"
import AdminUploadsResource from "./uploads"
import AdminProductTagsResource from "./product-tags"
import AdminPriceListResource from "./price-lists"
import AdminUsersResource from "./users"
import AdminVariantsResource from "./variants"
class Admin extends BaseResource {
public auth = new AdminAuthResource(this.client)
public batchJobs = new AdminBatchJobsResource(this.client)
public customers = new AdminCustomersResource(this.client)
public customerGroups = new AdminCustomerGroupsResource(this.client)
public discounts = new AdminDiscountsResource(this.client)
@@ -1,14 +1,21 @@
import { AdminUploadRes, IAdminPostUploadsFile } from "@medusajs/medusa"
import {
AdminDeleteUploadsReq,
IAdminPostUploadsFileReq,
AdminDeleteUploadsRes,
AdminPostUploadsDownloadUrlReq,
AdminUploadsDownloadUrlRes,
AdminUploadsRes,
} from "@medusajs/medusa"
import FormData from "form-data"
import { ResponsePromise } from "../../typings"
import BaseResource from "../base"
import FormData from "form-data"
class AdminUploadsResource extends BaseResource {
private headers = {
"Content-Type": "multipart/form-data",
}
create(file: IAdminPostUploadsFile): ResponsePromise<AdminUploadRes> {
create(file: IAdminPostUploadsFileReq): ResponsePromise<AdminUploadsRes> {
const path = `/admin/uploads`
const payload = new FormData()
@@ -16,6 +23,24 @@ class AdminUploadsResource extends BaseResource {
return this.client.request("POST", path, payload, {}, this.headers)
}
delete(
payload: AdminDeleteUploadsReq,
customHeaders: Record<string, any> = {}
): ResponsePromise<AdminDeleteUploadsRes> {
const path = `/admin/uploads`
return this.client.request("DELETE", path, payload, {}, customHeaders)
}
getPresignedDownloadUrl(
payload: AdminPostUploadsDownloadUrlReq,
customHeaders: Record<string, any> = {}
): ResponsePromise<AdminUploadsDownloadUrlRes> {
const path = `/admin/uploads/download-url`
return this.client.request("POST", path, payload, {}, customHeaders)
}
}
export default AdminUploadsResource
+21
View File
@@ -0,0 +1,21 @@
export function stringifyNullProperties<T extends object>(input: T): T {
const convertProperties = (obj: T) => {
const res = {} as T
Object.keys(obj).reduce((acc: T, key: string) => {
if (obj[key] === null) {
acc[key] = "null"
} else if (typeof obj[key] === "object") {
acc[key] = convertProperties(obj[key])
} else {
acc[key] = obj[key]
}
return acc
}, res)
return res
}
return convertProperties(input)
}
@@ -0,0 +1,31 @@
import { stringifyNullProperties } from "../../src/utils"
describe("stringifyNullProperties", () => {
test("returns empty object on no props", () => {
const result = stringifyNullProperties({})
expect(result).toEqual({})
})
test("successfully stringifies null property", () => {
const result = stringifyNullProperties({ test: null })
expect(result).toEqual({ test: "null" })
})
test("successfully stringifies nested null property", () => {
const result = stringifyNullProperties({
test: { test_2: { test_3: null } },
another_test: "test",
})
expect(result).toEqual({
test: { test_2: { test_3: "null" } },
another_test: "test",
})
})
test("successfully stringifies string property", () => {
const result = stringifyNullProperties({
test: "test",
})
expect(result).toEqual({ test: "test" })
})
})
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.2.3](https://github.com/medusajs/medusa/compare/medusa-payment-paypal@1.2.2...medusa-payment-paypal@1.2.3) (2022-07-05)
**Note:** Version bump only for package medusa-payment-paypal
## [1.2.2](https://github.com/medusajs/medusa/compare/medusa-payment-paypal@1.2.0...medusa-payment-paypal@1.2.2) (2022-06-19)
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "medusa-payment-paypal",
"version": "1.2.2",
"version": "1.2.3",
"description": "Paypal Payment provider for Meduas Commerce",
"main": "index.js",
"repository": {
@@ -26,7 +26,7 @@
"cross-env": "^5.2.1",
"eslint": "^6.8.0",
"jest": "^25.5.2",
"medusa-interfaces": "^1.3.0",
"medusa-interfaces": "^1.3.1",
"medusa-test-utils": "^1.1.37"
},
"scripts": {
@@ -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.41](https://github.com/medusajs/medusa/compare/medusa-payment-stripe@1.1.40...medusa-payment-stripe@1.1.41) (2022-07-05)
**Note:** Version bump only for package medusa-payment-stripe
## [1.1.40](https://github.com/medusajs/medusa/compare/medusa-payment-stripe@1.1.38...medusa-payment-stripe@1.1.40) (2022-06-19)
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "medusa-payment-stripe",
"version": "1.1.40",
"version": "1.1.41",
"description": "Stripe Payment provider for Meduas Commerce",
"main": "index.js",
"repository": {
-13
View File
@@ -1,13 +0,0 @@
{
"plugins": [
"@babel/plugin-proposal-class-properties",
"@babel/plugin-transform-instanceof",
"@babel/plugin-transform-classes"
],
"presets": ["@babel/preset-env"],
"env": {
"test": {
"plugins": ["@babel/plugin-transform-runtime"]
}
}
}
-15
View File
@@ -1,15 +0,0 @@
/lib
node_modules
.DS_store
.env*
/*.js
!index.js
/dist
/api
/services
/models
/subscribers
/loaders
@@ -1,8 +0,0 @@
/lib
node_modules
.DS_store
.env*
/*.js
!index.js
yarn.lock
/src
-222
View File
@@ -1,222 +0,0 @@
# Change Log
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.2.0](https://github.com/medusajs/medusa/compare/medusa-plugin-add-ons@1.1.37...medusa-plugin-add-ons@1.2.0) (2022-05-01)
**Note:** Version bump only for package medusa-plugin-add-ons
## [1.1.37](https://github.com/medusajs/medusa/compare/medusa-plugin-add-ons@1.1.36...medusa-plugin-add-ons@1.1.37) (2022-01-11)
**Note:** Version bump only for package medusa-plugin-add-ons
## [1.1.36](https://github.com/medusajs/medusa/compare/medusa-plugin-add-ons@1.1.35...medusa-plugin-add-ons@1.1.36) (2021-12-29)
**Note:** Version bump only for package medusa-plugin-add-ons
## [1.1.35](https://github.com/medusajs/medusa/compare/medusa-plugin-add-ons@1.1.34...medusa-plugin-add-ons@1.1.35) (2021-12-17)
**Note:** Version bump only for package medusa-plugin-add-ons
## [1.1.34](https://github.com/medusajs/medusa/compare/medusa-plugin-add-ons@1.1.33...medusa-plugin-add-ons@1.1.34) (2021-12-08)
**Note:** Version bump only for package medusa-plugin-add-ons
## [1.1.33](https://github.com/medusajs/medusa/compare/medusa-plugin-add-ons@1.1.32...medusa-plugin-add-ons@1.1.33) (2021-11-23)
**Note:** Version bump only for package medusa-plugin-add-ons
## [1.1.32](https://github.com/medusajs/medusa/compare/medusa-plugin-add-ons@1.1.31...medusa-plugin-add-ons@1.1.32) (2021-11-22)
**Note:** Version bump only for package medusa-plugin-add-ons
## [1.1.31](https://github.com/medusajs/medusa/compare/medusa-plugin-add-ons@1.1.30...medusa-plugin-add-ons@1.1.31) (2021-11-19)
**Note:** Version bump only for package medusa-plugin-add-ons
## [1.1.30](https://github.com/medusajs/medusa/compare/medusa-plugin-add-ons@1.1.29...medusa-plugin-add-ons@1.1.30) (2021-11-19)
**Note:** Version bump only for package medusa-plugin-add-ons
## [1.1.29](https://github.com/medusajs/medusa/compare/medusa-plugin-add-ons@1.1.28...medusa-plugin-add-ons@1.1.29) (2021-10-18)
**Note:** Version bump only for package medusa-plugin-add-ons
## [1.1.28](https://github.com/medusajs/medusa/compare/medusa-plugin-add-ons@1.1.27...medusa-plugin-add-ons@1.1.28) (2021-10-18)
**Note:** Version bump only for package medusa-plugin-add-ons
## [1.1.27](https://github.com/medusajs/medusa/compare/medusa-plugin-add-ons@1.1.25...medusa-plugin-add-ons@1.1.27) (2021-10-18)
**Note:** Version bump only for package medusa-plugin-add-ons
## [1.1.26](https://github.com/medusajs/medusa/compare/medusa-plugin-add-ons@1.1.25...medusa-plugin-add-ons@1.1.26) (2021-10-18)
**Note:** Version bump only for package medusa-plugin-add-ons
## [1.1.25](https://github.com/medusajs/medusa/compare/medusa-plugin-add-ons@1.1.24...medusa-plugin-add-ons@1.1.25) (2021-09-15)
**Note:** Version bump only for package medusa-plugin-add-ons
## [1.1.24](https://github.com/medusajs/medusa/compare/medusa-plugin-add-ons@1.1.23...medusa-plugin-add-ons@1.1.24) (2021-09-14)
**Note:** Version bump only for package medusa-plugin-add-ons
## [1.1.23](https://github.com/medusajs/medusa/compare/medusa-plugin-add-ons@1.1.22...medusa-plugin-add-ons@1.1.23) (2021-08-05)
**Note:** Version bump only for package medusa-plugin-add-ons
## [1.1.22](https://github.com/medusajs/medusa/compare/medusa-plugin-add-ons@1.1.21...medusa-plugin-add-ons@1.1.22) (2021-07-26)
**Note:** Version bump only for package medusa-plugin-add-ons
## [1.1.21](https://github.com/medusajs/medusa/compare/medusa-plugin-add-ons@1.1.19...medusa-plugin-add-ons@1.1.21) (2021-07-15)
**Note:** Version bump only for package medusa-plugin-add-ons
## [1.1.20](https://github.com/medusajs/medusa/compare/medusa-plugin-add-ons@1.1.19...medusa-plugin-add-ons@1.1.20) (2021-07-15)
**Note:** Version bump only for package medusa-plugin-add-ons
## [1.1.19](https://github.com/medusajs/medusa/compare/medusa-plugin-add-ons@1.1.18...medusa-plugin-add-ons@1.1.19) (2021-07-02)
**Note:** Version bump only for package medusa-plugin-add-ons
## [1.1.18](https://github.com/medusajs/medusa/compare/medusa-plugin-add-ons@1.1.17...medusa-plugin-add-ons@1.1.18) (2021-06-22)
### Bug Fixes
- release assist ([668e8a7](https://github.com/medusajs/medusa/commit/668e8a740200847fc2a41c91d2979097f1392532))
## [1.1.17](https://github.com/medusajs/medusa/compare/medusa-plugin-add-ons@1.1.16...medusa-plugin-add-ons@1.1.17) (2021-06-09)
**Note:** Version bump only for package medusa-plugin-add-ons
## [1.1.16](https://github.com/medusajs/medusa/compare/medusa-plugin-add-ons@1.1.15...medusa-plugin-add-ons@1.1.16) (2021-06-09)
**Note:** Version bump only for package medusa-plugin-add-ons
## [1.1.15](https://github.com/medusajs/medusa/compare/medusa-plugin-add-ons@1.1.14...medusa-plugin-add-ons@1.1.15) (2021-06-09)
**Note:** Version bump only for package medusa-plugin-add-ons
## [1.1.14](https://github.com/medusajs/medusa/compare/medusa-plugin-add-ons@1.1.13...medusa-plugin-add-ons@1.1.14) (2021-06-09)
**Note:** Version bump only for package medusa-plugin-add-ons
## [1.1.13](https://github.com/medusajs/medusa/compare/medusa-plugin-add-ons@1.1.12...medusa-plugin-add-ons@1.1.13) (2021-06-08)
**Note:** Version bump only for package medusa-plugin-add-ons
## [1.1.12](https://github.com/medusajs/medusa/compare/medusa-plugin-add-ons@1.1.9...medusa-plugin-add-ons@1.1.12) (2021-04-28)
**Note:** Version bump only for package medusa-plugin-add-ons
## [1.1.11](https://github.com/medusajs/medusa/compare/medusa-plugin-add-ons@1.1.10...medusa-plugin-add-ons@1.1.11) (2021-04-20)
**Note:** Version bump only for package medusa-plugin-add-ons
## [1.1.10](https://github.com/medusajs/medusa/compare/medusa-plugin-add-ons@1.1.9...medusa-plugin-add-ons@1.1.10) (2021-04-20)
**Note:** Version bump only for package medusa-plugin-add-ons
## [1.1.9](https://github.com/medusajs/medusa/compare/medusa-plugin-add-ons@1.1.8...medusa-plugin-add-ons@1.1.9) (2021-04-13)
### Bug Fixes
- merge develop ([2982a8e](https://github.com/medusajs/medusa/commit/2982a8e682e90beb4549d969d9d3b04d78a46a2d))
- merge develop ([a468c45](https://github.com/medusajs/medusa/commit/a468c451e82c68f41b5005a2e480057f6124aaa6))
## [1.1.8](https://github.com/medusajs/medusa/compare/medusa-plugin-add-ons@1.1.7...medusa-plugin-add-ons@1.1.8) (2021-04-13)
**Note:** Version bump only for package medusa-plugin-add-ons
## [1.1.7](https://github.com/medusajs/medusa/compare/medusa-plugin-add-ons@1.1.6...medusa-plugin-add-ons@1.1.7) (2021-03-30)
**Note:** Version bump only for package medusa-plugin-add-ons
## [1.1.6](https://github.com/medusajs/medusa/compare/medusa-plugin-add-ons@1.1.5...medusa-plugin-add-ons@1.1.6) (2021-03-17)
**Note:** Version bump only for package medusa-plugin-add-ons
## [1.1.5](https://github.com/medusajs/medusa/compare/medusa-plugin-add-ons@1.1.3...medusa-plugin-add-ons@1.1.5) (2021-03-17)
**Note:** Version bump only for package medusa-plugin-add-ons
## [1.1.4](https://github.com/medusajs/medusa/compare/medusa-plugin-add-ons@1.1.3...medusa-plugin-add-ons@1.1.4) (2021-03-17)
**Note:** Version bump only for package medusa-plugin-add-ons
## [1.1.3](https://github.com/medusajs/medusa/compare/medusa-plugin-add-ons@1.1.2...medusa-plugin-add-ons@1.1.3) (2021-02-17)
**Note:** Version bump only for package medusa-plugin-add-ons
## [1.1.2](https://github.com/medusajs/medusa/compare/medusa-plugin-add-ons@1.1.1...medusa-plugin-add-ons@1.1.2) (2021-02-03)
**Note:** Version bump only for package medusa-plugin-add-ons
## [1.1.1](https://github.com/medusajs/medusa/compare/medusa-plugin-add-ons@1.1.0...medusa-plugin-add-ons@1.1.1) (2021-01-27)
**Note:** Version bump only for package medusa-plugin-add-ons
# [1.1.0](https://github.com/medusajs/medusa/compare/medusa-plugin-add-ons@1.0.8...medusa-plugin-add-ons@1.1.0) (2021-01-26)
**Note:** Version bump only for package medusa-plugin-add-ons
## [1.0.8](https://github.com/medusajs/medusa/compare/medusa-plugin-add-ons@1.0.7...medusa-plugin-add-ons@1.0.8) (2020-12-17)
**Note:** Version bump only for package medusa-plugin-add-ons
## [1.0.7](https://github.com/medusajs/medusa/compare/medusa-plugin-add-ons@1.0.6...medusa-plugin-add-ons@1.0.7) (2020-11-24)
**Note:** Version bump only for package medusa-plugin-add-ons
## [1.0.6](https://github.com/medusajs/medusa/compare/medusa-plugin-add-ons@1.0.5...medusa-plugin-add-ons@1.0.6) (2020-10-19)
**Note:** Version bump only for package medusa-plugin-add-ons
## [1.0.5](https://github.com/medusajs/medusa/compare/medusa-plugin-add-ons@1.0.4...medusa-plugin-add-ons@1.0.5) (2020-10-12)
**Note:** Version bump only for package medusa-plugin-add-ons
## [1.0.4](https://github.com/medusajs/medusa/compare/medusa-plugin-add-ons@1.0.3...medusa-plugin-add-ons@1.0.4) (2020-10-09)
**Note:** Version bump only for package medusa-plugin-add-ons
## [1.0.3](https://github.com/medusajs/medusa/compare/medusa-plugin-add-ons@1.0.2...medusa-plugin-add-ons@1.0.3) (2020-10-05)
### Bug Fixes
- **medusa-plugin-add-ons:** fixes add-on decorator ([5071f36](https://github.com/medusajs/medusa/commit/5071f362e4e140d11a1342a5058e8ad2efaa1ed4))
- **medusa-plugin-addon:** Fixes admin endpoints, Adds flag to avoid merging add-on line-items ([d8483cd](https://github.com/medusajs/medusa/commit/d8483cd1352ecc587112723786b7c31882f9416e))
## [1.0.2](https://github.com/medusajs/medusa/compare/medusa-plugin-add-ons@1.0.1...medusa-plugin-add-ons@1.0.2) (2020-09-09)
**Note:** Version bump only for package medusa-plugin-add-ons
## [1.0.1](https://github.com/medusajs/medusa/compare/medusa-plugin-add-ons@1.0.0...medusa-plugin-add-ons@1.0.1) (2020-09-09)
**Note:** Version bump only for package medusa-plugin-add-ons
# 1.0.0 (2020-09-09)
### Features
- **plugins:** Adds add-on plugin ([3de1e6d](https://github.com/medusajs/medusa/commit/3de1e6dd4ad4a2a48d4d8116ebdd011efce2b22a))
-1
View File
@@ -1 +0,0 @@
// noop
@@ -1,44 +0,0 @@
{
"name": "medusa-plugin-add-ons",
"version": "1.2.0",
"description": "Add-on plugin for Medusa Commerce",
"main": "index.js",
"repository": {
"type": "git",
"url": "https://github.com/medusajs/medusa",
"directory": "packages/medusa-plugin-add-ons"
},
"author": "Oliver Juhl",
"license": "AGPL-3.0-or-later",
"devDependencies": {
"@babel/cli": "^7.7.5",
"@babel/core": "^7.7.5",
"@babel/node": "^7.7.4",
"@babel/plugin-proposal-class-properties": "^7.7.4",
"@babel/plugin-transform-classes": "^7.9.5",
"@babel/plugin-transform-instanceof": "^7.8.3",
"@babel/plugin-transform-runtime": "^7.7.6",
"@babel/preset-env": "^7.7.5",
"@babel/register": "^7.7.4",
"@babel/runtime": "^7.9.6",
"client-sessions": "^0.8.0",
"cross-env": "^7.0.2",
"eslint": "^6.8.0",
"jest": "^25.5.2",
"medusa-test-utils": "^1.1.37"
},
"scripts": {
"build": "babel src -d . --ignore **/__tests__",
"prepare": "cross-env NODE_ENV=production npm run build",
"watch": "babel -w src --out-dir . --ignore **/__tests__",
"test": "jest"
},
"dependencies": {
"body-parser": "^1.19.0",
"cors": "^2.8.5",
"express": "^4.17.1",
"medusa-core-utils": "^1.1.31",
"redis": "^3.0.2"
},
"gitHead": "81a7ff73d012fda722f6e9ef0bd9ba0232d37808"
}
@@ -1,12 +0,0 @@
import { Router } from "express"
import admin from "./routes/admin"
import store from "./routes/store"
export default (rootDirectory) => {
const app = Router()
store(app, rootDirectory)
admin(app, rootDirectory)
return app
}
@@ -1 +0,0 @@
export default (fn) => (...args) => fn(...args).catch(args[2])
@@ -1,5 +0,0 @@
import { default as wrap } from "./await-middleware"
export default {
wrap,
}
@@ -1,29 +0,0 @@
import { Validator, MedusaError } from "medusa-core-utils"
export default async (req, res) => {
const schema = Validator.object().keys({
name: Validator.string().required(),
prices: Validator.array()
.items({
currency_code: Validator.string(),
amount: Validator.number(),
})
.required(),
valid_for: Validator.array().items(),
metadata: Validator.object().optional(),
})
const { value, error } = schema.validate(req.body)
if (error) {
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
}
try {
const addOnService = req.scope.resolve("addOnService")
const addOn = await addOnService.create(value)
res.status(200).json({ addOn })
} catch (err) {
throw err
}
}
@@ -1,16 +0,0 @@
export default async (req, res) => {
const { id } = req.params
const addOnService = req.scope.resolve("addOnService")
try {
await addOnService.delete(id)
res.status(200).send({
id,
object: "addOn",
deleted: true,
})
} catch (err) {
throw err
}
}
Loaded 100 of 400 files, more files were not shown because too many files have changed in this diff. Show more