chore: merge release

This commit is contained in:
Sebastian Rindom committed 2021-11-20 15:59:31 +01:00
commit 59761163c2
565 files changed
+38806 -16346

No files matched your search

+147 -4
View File
@@ -3,13 +3,156 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.1.54](https://github.com/medusajs/medusa/compare/@medusajs/medusa@1.1.53...@medusajs/medusa@1.1.54) (2021-11-11)
## [1.1.55](https://github.com/medusajs/medusa/compare/@medusajs/medusa@1.1.52...@medusajs/medusa@1.1.55) (2021-11-19)
**Note:** Version bump only for package @medusajs/medusa
### Bug Fixes
## [1.1.53](https://github.com/medusajs/medusa/compare/@medusajs/medusa@1.1.52...@medusajs/medusa@1.1.53) (2021-11-09)
- /api/routes/store/auth pass linting ([#645](https://github.com/medusajs/medusa/issues/645)) ([40ad748](https://github.com/medusajs/medusa/commit/40ad748cc3c38bad97690b83c3b4b19c6d6365c3))
- /api/routes/store/customers pass linting ([#646](https://github.com/medusajs/medusa/issues/646)) ([7cc94dc](https://github.com/medusajs/medusa/commit/7cc94dc479ee26df2d5a031bfeb9eb3709d03776))
- add product count to storefront ([#719](https://github.com/medusajs/medusa/issues/719)) ([45b344f](https://github.com/medusajs/medusa/commit/45b344fbe1a570620c4e47cf959bdb605fffddef))
- adds options to default relations in storefront product endpoints ([#712](https://github.com/medusajs/medusa/issues/712)) ([e82737a](https://github.com/medusajs/medusa/commit/e82737a03545f69a833d8329f52daef39cfca46d))
- api/routes/store/products pass linting ([#644](https://github.com/medusajs/medusa/issues/644)) ([2966242](https://github.com/medusajs/medusa/commit/2966242bc812f9e75f0bb9cca5662b66836fc6d8))
- delete region causes cascade error ([#672](https://github.com/medusajs/medusa/issues/672)) ([0a65eca](https://github.com/medusajs/medusa/commit/0a65eca3de4f339178315e88b4220bd92ebe2736))
- lint in packages/medusa/src/services/swap.js pass linting ([#587](https://github.com/medusajs/medusa/issues/587)) ([3156a0d](https://github.com/medusajs/medusa/commit/3156a0de31749c1f32d5bdfe2cf8f1a8044bb42e))
- make /api/routes/admin/gift-cards,routes/store/gift-cards,/routes/store/orders pass linting ([#643](https://github.com/medusajs/medusa/issues/643)) ([60e9432](https://github.com/medusajs/medusa/commit/60e943260b804efe1dec3e139e47180322e5b52e))
- make /swaps pass eslint ([#665](https://github.com/medusajs/medusa/issues/665)) ([623d5f2](https://github.com/medusajs/medusa/commit/623d5f2b6d3881e2334fa48be80928ab9340a7ce))
- make api/routes/admin/apps pass eslint ([#641](https://github.com/medusajs/medusa/issues/641)) ([c9234c2](https://github.com/medusajs/medusa/commit/c9234c2343e19b32b335b93f4fa53e54398a5fe6))
- make api/routes/store/swaps pass eslint ([#678](https://github.com/medusajs/medusa/issues/678)) ([d9ee127](https://github.com/medusajs/medusa/commit/d9ee1272841ef047cceb6047fc34b8e42b5f580f))
- make discounts pass eslint ([3df2b42](https://github.com/medusajs/medusa/commit/3df2b4279f363e746f3514d8d8aa78b0e8fa3906))
- make it possible to update order shipping address ([#668](https://github.com/medusajs/medusa/issues/668)) ([e0fa06f](https://github.com/medusajs/medusa/commit/e0fa06fe964b8000ca539efa875ed6c322f6d57b))
- make packages/medusa/src/api/routes/admin/auth and 3 others pass eslint ([#666](https://github.com/medusajs/medusa/issues/666)) ([95f80f8](https://github.com/medusajs/medusa/commit/95f80f8a6635d1cba74d3c7ccbf794809af2d0bb))
- make packages/medusa/src/api/routes/admin/notes pass eslint ([#690](https://github.com/medusajs/medusa/issues/690)) ([f011662](https://github.com/medusajs/medusa/commit/f01166272db7dc2d1b783223a7ea5c9e662a028b))
- make packages/medusa/src/api/routes/admin/orders pass eslint ([#649](https://github.com/medusajs/medusa/issues/649)) ([dc88210](https://github.com/medusajs/medusa/commit/dc8821095c65e73569bdda1c4a24762f7eb746fe))
- make packages/medusa/src/api/routes/admin/return-reasons pass eslint ([#654](https://github.com/medusajs/medusa/issues/654)) ([a8fc89a](https://github.com/medusajs/medusa/commit/a8fc89af066836224e4f760115d13ba85f89a359))
- make packages/medusa/src/api/routes/admin/returns pass eslint ([#653](https://github.com/medusajs/medusa/issues/653)) ([2f7deea](https://github.com/medusajs/medusa/commit/2f7deea1d0309b5a78d66b0dd0d3c2204717c02b))
- make packages/medusa/src/api/routes/admin/shipping-options pass eslint ([#674](https://github.com/medusajs/medusa/issues/674)) ([bd86d3c](https://github.com/medusajs/medusa/commit/bd86d3c995b27bae5c8d97a21eb900b6dd112068))
- make packages/medusa/src/api/routes/admin/shipping-profiles pass eslint ([#642](https://github.com/medusajs/medusa/issues/642)) ([ce4d361](https://github.com/medusajs/medusa/commit/ce4d3616d733b5472ebfc00b49ab17c5aa53daef))
- make packages/medusa/src/api/routes/admin/store pass eslint ([#686](https://github.com/medusajs/medusa/issues/686)) ([7a230ec](https://github.com/medusajs/medusa/commit/7a230ec7e96cae6628f9b68a41dc6da89559dfe9))
- make packages/medusa/src/api/routes/admin/upload pass eslint ([#627](https://github.com/medusajs/medusa/issues/627)) ([5fbce42](https://github.com/medusajs/medusa/commit/5fbce429489cba79340ae738c54a533bdc627a28))
- make packages/medusa/src/api/routes/admin/users pass eslint ([a6a612c](https://github.com/medusajs/medusa/commit/a6a612c941924722c51e389359393436655cfc65))
- make packages/medusa/src/api/routes/store/carts pass eslint ([#652](https://github.com/medusajs/medusa/issues/652)) ([1ba63cc](https://github.com/medusajs/medusa/commit/1ba63cce08d03edd47b3e651d60a34cf58d5c1e3))
- make packages/medusa/src/api/routes/store/shipping-options pass linting ([#648](https://github.com/medusajs/medusa/issues/648)) ([5cb1b56](https://github.com/medusajs/medusa/commit/5cb1b5687ee80448dfecd0c8ba683ccdfc6900fc))
- make packages/medusa/src/services/cart.js pass eslint ([#700](https://github.com/medusajs/medusa/issues/700)) ([0caba0d](https://github.com/medusajs/medusa/commit/0caba0d49113210af4f6d507e273e8b451a5eaaf))
- Make packages/medusa/src/services/claim-item.js pass linting ([#626](https://github.com/medusajs/medusa/issues/626)) ([0d30369](https://github.com/medusajs/medusa/commit/0d3036978a8203f9fb6e64a2bc82a19ef697db05))
- Make packages/medusa/src/services/oauth.js pass linting ([#604](https://github.com/medusajs/medusa/issues/604)) ([14608b0](https://github.com/medusajs/medusa/commit/14608b0f682e858ee38e4b996693962a25116749))
- make packages/medusa/src/services/payment-provider.js pass eslint ([#549](https://github.com/medusajs/medusa/issues/549)) ([caf4357](https://github.com/medusajs/medusa/commit/caf4357c4d1a2199ab1399dfabd18876dd0642c7)), closes [#692](https://github.com/medusajs/medusa/issues/692)
- Make packages/medusa/src/services/product-variant.js pass linting ([#693](https://github.com/medusajs/medusa/issues/693)) ([a351398](https://github.com/medusajs/medusa/commit/a3513983790eaf78285d32f1797d1d11eb446e5e))
- make packages/medusa/src/services/shipping-profile.js pass eslint ([#582](https://github.com/medusajs/medusa/issues/582)) ([2c415ea](https://github.com/medusajs/medusa/commit/2c415ea520ed700a0a474111b83021a699fbc851))
- make packages/medusa/src/services/totals.js pass eslint ([23e83af](https://github.com/medusajs/medusa/commit/23e83aff1cd858401fba42668f641270128068e1))
- make variants pass eslint ([a1e446c](https://github.com/medusajs/medusa/commit/a1e446c1214a79a799da46b3d1bdd62c64b99ebf))
- make variants pass eslint ([#647](https://github.com/medusajs/medusa/issues/647)) ([f4307c7](https://github.com/medusajs/medusa/commit/f4307c702a6c1420d1e344adc8b5ed880d9f1f8d))
- **medusa:** add total count for list queries in product ([#710](https://github.com/medusajs/medusa/issues/710)) ([109d400](https://github.com/medusajs/medusa/commit/109d4007204964f0a89759f48a6e2d5afc0b1dd9))
- Prepare routes for linting ([#603](https://github.com/medusajs/medusa/issues/603)) ([3e68069](https://github.com/medusajs/medusa/commit/3e68069a8959a2bf75f8639389cdd35b6a8058e7))
- release ([5fa4848](https://github.com/medusajs/medusa/commit/5fa4848b17a9dd432c2361e5d9485950494a2bc6))
- updates documentation and fixes script blockers ([#765](https://github.com/medusajs/medusa/issues/765)) ([3ea6aea](https://github.com/medusajs/medusa/commit/3ea6aea5beb2a50d1a3345fa3833eadb32d0a346))
**Note:** Version bump only for package @medusajs/medusa
### Features
- admin api - allow customers to be created and updated with metadata ([#824](https://github.com/medusajs/medusa/issues/824)) ([2d9879e](https://github.com/medusajs/medusa/commit/2d9879ea090898c08ad86a0ebc44d5b8965b5166))
- Algolia plugin for medusa ([#718](https://github.com/medusajs/medusa/issues/718)) ([8ce9b20](https://github.com/medusajs/medusa/commit/8ce9b20222e1f4db75f730898549f0ed09eb1574))
- Allow retrieval of soft-deleted products ([#723](https://github.com/medusajs/medusa/issues/723)) ([1e50aee](https://github.com/medusajs/medusa/commit/1e50aee4feb55092560dd4a9c51a0671363e8576))
- **medusa:** adds collection endpoints to storefront ([#711](https://github.com/medusajs/medusa/issues/711)) ([5812756](https://github.com/medusajs/medusa/commit/58127564d7110c674fb14fdd97fbc080afba156d))
- Typescript for API layer ([#817](https://github.com/medusajs/medusa/issues/817)) ([373532e](https://github.com/medusajs/medusa/commit/373532ecbc8196f47e71af95a8cf82a14a4b1f9e))
## [1.1.54](https://github.com/medusajs/medusa/compare/@medusajs/medusa@1.1.52...@medusajs/medusa@1.1.54) (2021-11-19)
### Bug Fixes
- /api/routes/store/auth pass linting ([#645](https://github.com/medusajs/medusa/issues/645)) ([40ad748](https://github.com/medusajs/medusa/commit/40ad748cc3c38bad97690b83c3b4b19c6d6365c3))
- /api/routes/store/customers pass linting ([#646](https://github.com/medusajs/medusa/issues/646)) ([7cc94dc](https://github.com/medusajs/medusa/commit/7cc94dc479ee26df2d5a031bfeb9eb3709d03776))
- add product count to storefront ([#719](https://github.com/medusajs/medusa/issues/719)) ([45b344f](https://github.com/medusajs/medusa/commit/45b344fbe1a570620c4e47cf959bdb605fffddef))
- adds options to default relations in storefront product endpoints ([#712](https://github.com/medusajs/medusa/issues/712)) ([e82737a](https://github.com/medusajs/medusa/commit/e82737a03545f69a833d8329f52daef39cfca46d))
- api/routes/store/products pass linting ([#644](https://github.com/medusajs/medusa/issues/644)) ([2966242](https://github.com/medusajs/medusa/commit/2966242bc812f9e75f0bb9cca5662b66836fc6d8))
- delete region causes cascade error ([#672](https://github.com/medusajs/medusa/issues/672)) ([0a65eca](https://github.com/medusajs/medusa/commit/0a65eca3de4f339178315e88b4220bd92ebe2736))
- lint in packages/medusa/src/services/swap.js pass linting ([#587](https://github.com/medusajs/medusa/issues/587)) ([3156a0d](https://github.com/medusajs/medusa/commit/3156a0de31749c1f32d5bdfe2cf8f1a8044bb42e))
- make /api/routes/admin/gift-cards,routes/store/gift-cards,/routes/store/orders pass linting ([#643](https://github.com/medusajs/medusa/issues/643)) ([60e9432](https://github.com/medusajs/medusa/commit/60e943260b804efe1dec3e139e47180322e5b52e))
- make /swaps pass eslint ([#665](https://github.com/medusajs/medusa/issues/665)) ([623d5f2](https://github.com/medusajs/medusa/commit/623d5f2b6d3881e2334fa48be80928ab9340a7ce))
- make api/routes/admin/apps pass eslint ([#641](https://github.com/medusajs/medusa/issues/641)) ([c9234c2](https://github.com/medusajs/medusa/commit/c9234c2343e19b32b335b93f4fa53e54398a5fe6))
- make api/routes/store/swaps pass eslint ([#678](https://github.com/medusajs/medusa/issues/678)) ([d9ee127](https://github.com/medusajs/medusa/commit/d9ee1272841ef047cceb6047fc34b8e42b5f580f))
- make discounts pass eslint ([3df2b42](https://github.com/medusajs/medusa/commit/3df2b4279f363e746f3514d8d8aa78b0e8fa3906))
- make it possible to update order shipping address ([#668](https://github.com/medusajs/medusa/issues/668)) ([e0fa06f](https://github.com/medusajs/medusa/commit/e0fa06fe964b8000ca539efa875ed6c322f6d57b))
- make packages/medusa/src/api/routes/admin/auth and 3 others pass eslint ([#666](https://github.com/medusajs/medusa/issues/666)) ([95f80f8](https://github.com/medusajs/medusa/commit/95f80f8a6635d1cba74d3c7ccbf794809af2d0bb))
- make packages/medusa/src/api/routes/admin/notes pass eslint ([#690](https://github.com/medusajs/medusa/issues/690)) ([f011662](https://github.com/medusajs/medusa/commit/f01166272db7dc2d1b783223a7ea5c9e662a028b))
- make packages/medusa/src/api/routes/admin/orders pass eslint ([#649](https://github.com/medusajs/medusa/issues/649)) ([dc88210](https://github.com/medusajs/medusa/commit/dc8821095c65e73569bdda1c4a24762f7eb746fe))
- make packages/medusa/src/api/routes/admin/return-reasons pass eslint ([#654](https://github.com/medusajs/medusa/issues/654)) ([a8fc89a](https://github.com/medusajs/medusa/commit/a8fc89af066836224e4f760115d13ba85f89a359))
- make packages/medusa/src/api/routes/admin/returns pass eslint ([#653](https://github.com/medusajs/medusa/issues/653)) ([2f7deea](https://github.com/medusajs/medusa/commit/2f7deea1d0309b5a78d66b0dd0d3c2204717c02b))
- make packages/medusa/src/api/routes/admin/shipping-options pass eslint ([#674](https://github.com/medusajs/medusa/issues/674)) ([bd86d3c](https://github.com/medusajs/medusa/commit/bd86d3c995b27bae5c8d97a21eb900b6dd112068))
- make packages/medusa/src/api/routes/admin/shipping-profiles pass eslint ([#642](https://github.com/medusajs/medusa/issues/642)) ([ce4d361](https://github.com/medusajs/medusa/commit/ce4d3616d733b5472ebfc00b49ab17c5aa53daef))
- make packages/medusa/src/api/routes/admin/store pass eslint ([#686](https://github.com/medusajs/medusa/issues/686)) ([7a230ec](https://github.com/medusajs/medusa/commit/7a230ec7e96cae6628f9b68a41dc6da89559dfe9))
- make packages/medusa/src/api/routes/admin/upload pass eslint ([#627](https://github.com/medusajs/medusa/issues/627)) ([5fbce42](https://github.com/medusajs/medusa/commit/5fbce429489cba79340ae738c54a533bdc627a28))
- make packages/medusa/src/api/routes/admin/users pass eslint ([a6a612c](https://github.com/medusajs/medusa/commit/a6a612c941924722c51e389359393436655cfc65))
- make packages/medusa/src/api/routes/store/carts pass eslint ([#652](https://github.com/medusajs/medusa/issues/652)) ([1ba63cc](https://github.com/medusajs/medusa/commit/1ba63cce08d03edd47b3e651d60a34cf58d5c1e3))
- make packages/medusa/src/api/routes/store/shipping-options pass linting ([#648](https://github.com/medusajs/medusa/issues/648)) ([5cb1b56](https://github.com/medusajs/medusa/commit/5cb1b5687ee80448dfecd0c8ba683ccdfc6900fc))
- make packages/medusa/src/services/cart.js pass eslint ([#700](https://github.com/medusajs/medusa/issues/700)) ([0caba0d](https://github.com/medusajs/medusa/commit/0caba0d49113210af4f6d507e273e8b451a5eaaf))
- Make packages/medusa/src/services/claim-item.js pass linting ([#626](https://github.com/medusajs/medusa/issues/626)) ([0d30369](https://github.com/medusajs/medusa/commit/0d3036978a8203f9fb6e64a2bc82a19ef697db05))
- Make packages/medusa/src/services/oauth.js pass linting ([#604](https://github.com/medusajs/medusa/issues/604)) ([14608b0](https://github.com/medusajs/medusa/commit/14608b0f682e858ee38e4b996693962a25116749))
- make packages/medusa/src/services/payment-provider.js pass eslint ([#549](https://github.com/medusajs/medusa/issues/549)) ([caf4357](https://github.com/medusajs/medusa/commit/caf4357c4d1a2199ab1399dfabd18876dd0642c7)), closes [#692](https://github.com/medusajs/medusa/issues/692)
- Make packages/medusa/src/services/product-variant.js pass linting ([#693](https://github.com/medusajs/medusa/issues/693)) ([a351398](https://github.com/medusajs/medusa/commit/a3513983790eaf78285d32f1797d1d11eb446e5e))
- make packages/medusa/src/services/shipping-profile.js pass eslint ([#582](https://github.com/medusajs/medusa/issues/582)) ([2c415ea](https://github.com/medusajs/medusa/commit/2c415ea520ed700a0a474111b83021a699fbc851))
- make packages/medusa/src/services/totals.js pass eslint ([23e83af](https://github.com/medusajs/medusa/commit/23e83aff1cd858401fba42668f641270128068e1))
- make variants pass eslint ([a1e446c](https://github.com/medusajs/medusa/commit/a1e446c1214a79a799da46b3d1bdd62c64b99ebf))
- make variants pass eslint ([#647](https://github.com/medusajs/medusa/issues/647)) ([f4307c7](https://github.com/medusajs/medusa/commit/f4307c702a6c1420d1e344adc8b5ed880d9f1f8d))
- **medusa:** add total count for list queries in product ([#710](https://github.com/medusajs/medusa/issues/710)) ([109d400](https://github.com/medusajs/medusa/commit/109d4007204964f0a89759f48a6e2d5afc0b1dd9))
- Prepare routes for linting ([#603](https://github.com/medusajs/medusa/issues/603)) ([3e68069](https://github.com/medusajs/medusa/commit/3e68069a8959a2bf75f8639389cdd35b6a8058e7))
- updates documentation and fixes script blockers ([#765](https://github.com/medusajs/medusa/issues/765)) ([3ea6aea](https://github.com/medusajs/medusa/commit/3ea6aea5beb2a50d1a3345fa3833eadb32d0a346))
### Features
- admin api - allow customers to be created and updated with metadata ([#824](https://github.com/medusajs/medusa/issues/824)) ([2d9879e](https://github.com/medusajs/medusa/commit/2d9879ea090898c08ad86a0ebc44d5b8965b5166))
- Algolia plugin for medusa ([#718](https://github.com/medusajs/medusa/issues/718)) ([8ce9b20](https://github.com/medusajs/medusa/commit/8ce9b20222e1f4db75f730898549f0ed09eb1574))
- Allow retrieval of soft-deleted products ([#723](https://github.com/medusajs/medusa/issues/723)) ([1e50aee](https://github.com/medusajs/medusa/commit/1e50aee4feb55092560dd4a9c51a0671363e8576))
- **medusa:** adds collection endpoints to storefront ([#711](https://github.com/medusajs/medusa/issues/711)) ([5812756](https://github.com/medusajs/medusa/commit/58127564d7110c674fb14fdd97fbc080afba156d))
- Typescript for API layer ([#817](https://github.com/medusajs/medusa/issues/817)) ([373532e](https://github.com/medusajs/medusa/commit/373532ecbc8196f47e71af95a8cf82a14a4b1f9e))
## [1.1.53](https://github.com/medusajs/medusa/compare/@medusajs/medusa@1.1.52...@medusajs/medusa@1.1.53) (2021-11-19)
### Bug Fixes
- /api/routes/store/auth pass linting ([#645](https://github.com/medusajs/medusa/issues/645)) ([40ad748](https://github.com/medusajs/medusa/commit/40ad748cc3c38bad97690b83c3b4b19c6d6365c3))
- /api/routes/store/customers pass linting ([#646](https://github.com/medusajs/medusa/issues/646)) ([7cc94dc](https://github.com/medusajs/medusa/commit/7cc94dc479ee26df2d5a031bfeb9eb3709d03776))
- add product count to storefront ([#719](https://github.com/medusajs/medusa/issues/719)) ([45b344f](https://github.com/medusajs/medusa/commit/45b344fbe1a570620c4e47cf959bdb605fffddef))
- adds options to default relations in storefront product endpoints ([#712](https://github.com/medusajs/medusa/issues/712)) ([e82737a](https://github.com/medusajs/medusa/commit/e82737a03545f69a833d8329f52daef39cfca46d))
- api/routes/store/products pass linting ([#644](https://github.com/medusajs/medusa/issues/644)) ([2966242](https://github.com/medusajs/medusa/commit/2966242bc812f9e75f0bb9cca5662b66836fc6d8))
- delete region causes cascade error ([#672](https://github.com/medusajs/medusa/issues/672)) ([0a65eca](https://github.com/medusajs/medusa/commit/0a65eca3de4f339178315e88b4220bd92ebe2736))
- lint in packages/medusa/src/services/swap.js pass linting ([#587](https://github.com/medusajs/medusa/issues/587)) ([3156a0d](https://github.com/medusajs/medusa/commit/3156a0de31749c1f32d5bdfe2cf8f1a8044bb42e))
- make /api/routes/admin/gift-cards,routes/store/gift-cards,/routes/store/orders pass linting ([#643](https://github.com/medusajs/medusa/issues/643)) ([60e9432](https://github.com/medusajs/medusa/commit/60e943260b804efe1dec3e139e47180322e5b52e))
- make /swaps pass eslint ([#665](https://github.com/medusajs/medusa/issues/665)) ([623d5f2](https://github.com/medusajs/medusa/commit/623d5f2b6d3881e2334fa48be80928ab9340a7ce))
- make api/routes/admin/apps pass eslint ([#641](https://github.com/medusajs/medusa/issues/641)) ([c9234c2](https://github.com/medusajs/medusa/commit/c9234c2343e19b32b335b93f4fa53e54398a5fe6))
- make api/routes/store/swaps pass eslint ([#678](https://github.com/medusajs/medusa/issues/678)) ([d9ee127](https://github.com/medusajs/medusa/commit/d9ee1272841ef047cceb6047fc34b8e42b5f580f))
- make discounts pass eslint ([3df2b42](https://github.com/medusajs/medusa/commit/3df2b4279f363e746f3514d8d8aa78b0e8fa3906))
- make it possible to update order shipping address ([#668](https://github.com/medusajs/medusa/issues/668)) ([e0fa06f](https://github.com/medusajs/medusa/commit/e0fa06fe964b8000ca539efa875ed6c322f6d57b))
- make packages/medusa/src/api/routes/admin/auth and 3 others pass eslint ([#666](https://github.com/medusajs/medusa/issues/666)) ([95f80f8](https://github.com/medusajs/medusa/commit/95f80f8a6635d1cba74d3c7ccbf794809af2d0bb))
- make packages/medusa/src/api/routes/admin/notes pass eslint ([#690](https://github.com/medusajs/medusa/issues/690)) ([f011662](https://github.com/medusajs/medusa/commit/f01166272db7dc2d1b783223a7ea5c9e662a028b))
- make packages/medusa/src/api/routes/admin/orders pass eslint ([#649](https://github.com/medusajs/medusa/issues/649)) ([dc88210](https://github.com/medusajs/medusa/commit/dc8821095c65e73569bdda1c4a24762f7eb746fe))
- make packages/medusa/src/api/routes/admin/return-reasons pass eslint ([#654](https://github.com/medusajs/medusa/issues/654)) ([a8fc89a](https://github.com/medusajs/medusa/commit/a8fc89af066836224e4f760115d13ba85f89a359))
- make packages/medusa/src/api/routes/admin/returns pass eslint ([#653](https://github.com/medusajs/medusa/issues/653)) ([2f7deea](https://github.com/medusajs/medusa/commit/2f7deea1d0309b5a78d66b0dd0d3c2204717c02b))
- make packages/medusa/src/api/routes/admin/shipping-options pass eslint ([#674](https://github.com/medusajs/medusa/issues/674)) ([bd86d3c](https://github.com/medusajs/medusa/commit/bd86d3c995b27bae5c8d97a21eb900b6dd112068))
- make packages/medusa/src/api/routes/admin/shipping-profiles pass eslint ([#642](https://github.com/medusajs/medusa/issues/642)) ([ce4d361](https://github.com/medusajs/medusa/commit/ce4d3616d733b5472ebfc00b49ab17c5aa53daef))
- make packages/medusa/src/api/routes/admin/store pass eslint ([#686](https://github.com/medusajs/medusa/issues/686)) ([7a230ec](https://github.com/medusajs/medusa/commit/7a230ec7e96cae6628f9b68a41dc6da89559dfe9))
- make packages/medusa/src/api/routes/admin/upload pass eslint ([#627](https://github.com/medusajs/medusa/issues/627)) ([5fbce42](https://github.com/medusajs/medusa/commit/5fbce429489cba79340ae738c54a533bdc627a28))
- make packages/medusa/src/api/routes/admin/users pass eslint ([a6a612c](https://github.com/medusajs/medusa/commit/a6a612c941924722c51e389359393436655cfc65))
- make packages/medusa/src/api/routes/store/carts pass eslint ([#652](https://github.com/medusajs/medusa/issues/652)) ([1ba63cc](https://github.com/medusajs/medusa/commit/1ba63cce08d03edd47b3e651d60a34cf58d5c1e3))
- make packages/medusa/src/api/routes/store/shipping-options pass linting ([#648](https://github.com/medusajs/medusa/issues/648)) ([5cb1b56](https://github.com/medusajs/medusa/commit/5cb1b5687ee80448dfecd0c8ba683ccdfc6900fc))
- make packages/medusa/src/services/cart.js pass eslint ([#700](https://github.com/medusajs/medusa/issues/700)) ([0caba0d](https://github.com/medusajs/medusa/commit/0caba0d49113210af4f6d507e273e8b451a5eaaf))
- Make packages/medusa/src/services/claim-item.js pass linting ([#626](https://github.com/medusajs/medusa/issues/626)) ([0d30369](https://github.com/medusajs/medusa/commit/0d3036978a8203f9fb6e64a2bc82a19ef697db05))
- Make packages/medusa/src/services/oauth.js pass linting ([#604](https://github.com/medusajs/medusa/issues/604)) ([14608b0](https://github.com/medusajs/medusa/commit/14608b0f682e858ee38e4b996693962a25116749))
- make packages/medusa/src/services/payment-provider.js pass eslint ([#549](https://github.com/medusajs/medusa/issues/549)) ([caf4357](https://github.com/medusajs/medusa/commit/caf4357c4d1a2199ab1399dfabd18876dd0642c7)), closes [#692](https://github.com/medusajs/medusa/issues/692)
- Make packages/medusa/src/services/product-variant.js pass linting ([#693](https://github.com/medusajs/medusa/issues/693)) ([a351398](https://github.com/medusajs/medusa/commit/a3513983790eaf78285d32f1797d1d11eb446e5e))
- make packages/medusa/src/services/shipping-profile.js pass eslint ([#582](https://github.com/medusajs/medusa/issues/582)) ([2c415ea](https://github.com/medusajs/medusa/commit/2c415ea520ed700a0a474111b83021a699fbc851))
- make packages/medusa/src/services/totals.js pass eslint ([23e83af](https://github.com/medusajs/medusa/commit/23e83aff1cd858401fba42668f641270128068e1))
- make variants pass eslint ([a1e446c](https://github.com/medusajs/medusa/commit/a1e446c1214a79a799da46b3d1bdd62c64b99ebf))
- make variants pass eslint ([#647](https://github.com/medusajs/medusa/issues/647)) ([f4307c7](https://github.com/medusajs/medusa/commit/f4307c702a6c1420d1e344adc8b5ed880d9f1f8d))
- **medusa:** add total count for list queries in product ([#710](https://github.com/medusajs/medusa/issues/710)) ([109d400](https://github.com/medusajs/medusa/commit/109d4007204964f0a89759f48a6e2d5afc0b1dd9))
- Prepare routes for linting ([#603](https://github.com/medusajs/medusa/issues/603)) ([3e68069](https://github.com/medusajs/medusa/commit/3e68069a8959a2bf75f8639389cdd35b6a8058e7))
- updates documentation and fixes script blockers ([#765](https://github.com/medusajs/medusa/issues/765)) ([3ea6aea](https://github.com/medusajs/medusa/commit/3ea6aea5beb2a50d1a3345fa3833eadb32d0a346))
### Features
- admin api - allow customers to be created and updated with metadata ([#824](https://github.com/medusajs/medusa/issues/824)) ([2d9879e](https://github.com/medusajs/medusa/commit/2d9879ea090898c08ad86a0ebc44d5b8965b5166))
- Algolia plugin for medusa ([#718](https://github.com/medusajs/medusa/issues/718)) ([8ce9b20](https://github.com/medusajs/medusa/commit/8ce9b20222e1f4db75f730898549f0ed09eb1574))
- Allow retrieval of soft-deleted products ([#723](https://github.com/medusajs/medusa/issues/723)) ([1e50aee](https://github.com/medusajs/medusa/commit/1e50aee4feb55092560dd4a9c51a0671363e8576))
- **medusa:** adds collection endpoints to storefront ([#711](https://github.com/medusajs/medusa/issues/711)) ([5812756](https://github.com/medusajs/medusa/commit/58127564d7110c674fb14fdd97fbc080afba156d))
- Typescript for API layer ([#817](https://github.com/medusajs/medusa/issues/817)) ([373532e](https://github.com/medusajs/medusa/commit/373532ecbc8196f47e71af95a8cf82a14a4b1f9e))
## [1.1.52](https://github.com/medusajs/medusa/compare/@medusajs/medusa@1.1.51...@medusajs/medusa@1.1.52) (2021-11-09)
Regular → Executable
View File
File mode changed.
+1
View File
@@ -0,0 +1 @@
declare module "medusa-interfaces"
+14 -9
View File
@@ -1,6 +1,6 @@
{
"name": "@medusajs/medusa",
"version": "1.1.54",
"version": "1.1.55",
"description": "E-commerce for JAMstack",
"main": "dist/index.js",
"bin": {
@@ -20,21 +20,24 @@
"@babel/cli": "^7.14.3",
"@babel/core": "^7.14.3",
"@babel/preset-typescript": "^7.13.0",
"babel-preset-medusa-package": "^1.1.18",
"@types/express": "^4.17.13",
"@types/jsonwebtoken": "^8.5.5",
"babel-preset-medusa-package": "^1.1.17",
"cross-env": "^5.2.1",
"eslint": "^6.8.0",
"eslint": "^7.32.0",
"jest": "^25.5.2",
"medusa-interfaces": "^1.1.27",
"medusa-interfaces": "^1.1.29",
"nodemon": "^2.0.1",
"prettier": "^1.19.1",
"sqlite3": "^5.0.2",
"supertest": "^4.0.2"
"supertest": "^4.0.2",
"typescript": "^4.4.4"
},
"scripts": {
"start": "nodemon --watch plugins/ --watch src/ --exec babel-node src/app.js",
"watch": "babel -w src --out-dir dist/ --ignore **/__tests__ --extensions \".ts,.js\"",
"watch": "tsc --build --watch",
"prepare": "cross-env NODE_ENV=production npm run build",
"build": "babel src -d dist --ignore **/__tests__ --extensions \".ts,.js\"",
"build": "tsc --build",
"serve": "node dist/app.js",
"test": "jest",
"test:unit": "jest"
@@ -51,6 +54,8 @@
"body-parser": "^1.19.0",
"bull": "^3.12.1",
"chokidar": "^3.4.2",
"class-transformer": "^0.4.0",
"class-validator": "^0.13.1",
"connect-redis": "^5.0.0",
"cookie-parser": "^1.4.4",
"core-js": "^3.6.5",
@@ -67,8 +72,8 @@
"joi": "^17.3.0",
"joi-objectid": "^3.0.1",
"jsonwebtoken": "^8.5.1",
"medusa-core-utils": "^1.1.26",
"medusa-test-utils": "^1.1.29",
"medusa-core-utils": "^1.1.28",
"medusa-test-utils": "^1.1.31",
"morgan": "^1.9.1",
"multer": "^1.4.2",
"passport": "^0.4.0",
+18 -1
View File
@@ -1,7 +1,7 @@
import { Router } from "express"
import errorHandler from "./middlewares/error-handler"
import admin from "./routes/admin"
import store from "./routes/store"
import errorHandler from "./middlewares/error-handler"
// guaranteed to get dependencies
export default (container, config) => {
@@ -14,3 +14,20 @@ export default (container, config) => {
return app
}
export * from "./routes/admin/notifications"
export * from "./routes/admin/store"
export * from "./routes/admin/variants"
export * from "./routes/store/auth"
export * from "./routes/store/carts"
export * from "./routes/store/collections"
export * from "./routes/store/customers"
export * from "./routes/store/gift-cards"
export * from "./routes/store/orders"
export * from "./routes/store/products"
export * from "./routes/store/regions"
export * from "./routes/store/return-reasons"
export * from "./routes/store/returns"
export * from "./routes/store/shipping-options"
export * from "./routes/store/swaps"
export * from "./routes/store/variants"
@@ -1 +1,3 @@
export default fn => (...args) => fn(...args).catch(args[2])
export default (fn) =>
(...args) =>
fn(...args).catch(args[2])
@@ -1,25 +0,0 @@
import { MedusaError, Validator } from "medusa-core-utils"
export default async (req, res) => {
const schema = Validator.object().keys({
application_name: Validator.string().required(),
state: Validator.string().required(),
code: Validator.string().required(),
})
const { value, error } = schema.validate(req.body)
if (error) {
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
}
try {
const oauthService = req.scope.resolve("oauthService")
const data = await oauthService.generateToken(
value.application_name,
value.code,
value.state
)
res.status(200).json({ apps: data })
} catch (err) {
throw err
}
}
@@ -0,0 +1,63 @@
import { IsNotEmpty, IsString } from "class-validator"
import { OauthService } from "../../../../services"
import { validator } from "../../../../utils/validator"
/**
* @oas [post] /apps
* operationId: "PostApps"
* summary: "Generates a token for an application."
* description: "Generates a token for an application."
* x-authenticated: true
* requestBody:
* content:
* application/json:
* schema:
* required:
* - application_name
* - state
* - code
* properties:
* application_name:
* type: string
* description: Name of the application for the token to be generated for.
* state:
* type: string
* description: State of the application.
* code:
* type: string
* description: The code for the generated token.
* tags:
* - Apps
* responses:
* "200":
* description: OK
* content:
* application/json:
* schema:
* properties:
* apps:
* $ref: "#/components/schemas/OAuth"
*/
export default async (req, res) => {
const validated = await validator(AdminPostAppsReq, req.body)
const oauthService: OauthService = req.scope.resolve("oauthService")
const data = await oauthService.generateToken(
validated.application_name,
validated.code,
validated.state
)
res.status(200).json({ apps: data })
}
export class AdminPostAppsReq {
@IsString()
@IsNotEmpty()
application_name: string
@IsString()
@IsNotEmpty()
state: string
@IsString()
@IsNotEmpty()
code: string
}
@@ -1,9 +1,10 @@
import { Router } from "express"
import { Oauth } from "../../../.."
import middlewares from "../../../middlewares"
const route = Router()
export default app => {
export default (app) => {
app.use("/apps", route)
route.get("/", middlewares.wrap(require("./list").default))
@@ -14,3 +15,11 @@ export default app => {
return app
}
export type AdminAppsRes = {
apps: Oauth
}
export type AdminAppsListRes = {
apps: Oauth[]
}
@@ -1,12 +0,0 @@
import { MedusaError, Validator } from "medusa-core-utils"
export default async (req, res) => {
try {
const oauthService = req.scope.resolve("oauthService")
const data = await oauthService.list({})
res.status(200).json({ apps: data })
} catch (err) {
throw err
}
}
@@ -0,0 +1,26 @@
import { OauthService } from "../../../../services"
/**
* @oas [get] /apps
* operationId: "GetApps"
* summary: "List applications"
* description: "Retrieve a list of applications."
* x-authenticated: true
* tags:
* - Apps
* responses:
* "200":
* description: OK
* content:
* application/json:
* schema:
* properties:
* collection:
* $ref: "#/components/schemas/OAuth"
*/
export default async (req, res) => {
const oauthService: OauthService = req.scope.resolve("oauthService")
const data = await oauthService.list({})
res.status(200).json({ apps: data })
}
@@ -1,53 +0,0 @@
import _ from "lodash"
import jwt from "jsonwebtoken"
import { Validator } from "medusa-core-utils"
import config from "../../../../config"
/**
* @oas [post] /auth
* operationId: "PostAuth"
* summary: "Authenticate a User"
* description: "Logs a User in and authorizes them to manage Store settings."
* parameters:
* - (body) email=* {string} The User's email.
* - (body) password=* {string} The User's password.
* tags:
* - Auth
* responses:
* "200":
* description: OK
* content:
* application/json:
* schema:
* properties:
* user:
* $ref: "#/components/schemas/user"
*/
export default async (req, res) => {
const { body } = req
const schema = Validator.object().keys({
email: Validator.string().required(),
password: Validator.string().required(),
})
const { value, error } = schema.validate(body)
if (error) {
throw error
}
const authService = req.scope.resolve("authService")
const result = await authService.authenticate(value.email, value.password)
if (!result.success) {
res.sendStatus(401)
return
}
// Add JWT to cookie
req.session.jwt = jwt.sign({ userId: result.user.id }, config.jwtSecret, {
expiresIn: "24h",
})
const cleanRes = _.omit(result.user, ["password_hash"])
res.json({ user: cleanRes })
}
@@ -0,0 +1,67 @@
import _ from "lodash"
import jwt from "jsonwebtoken"
import config from "../../../../config"
import { validator } from "../../../../utils/validator"
import { IsEmail, IsNotEmpty, IsString } from "class-validator"
import AuthService from "../../../../services/auth"
import { MedusaError } from "medusa-core-utils"
/**
* @oas [post] /auth
* operationId: "PostAuth"
* summary: "Authenticate a User"
* x-authenticated: false
* description: "Logs a User in and authorizes them to manage Store settings."
* parameters:
* - (body) email=* {string} The User's email.
* - (body) password=* {string} The User's password.
* tags:
* - Auth
* responses:
* "200":
* description: OK
* content:
* application/json:
* schema:
* properties:
* user:
* $ref: "#/components/schemas/user"
*/
export default async (req, res) => {
if (!config.jwtSecret) {
throw new MedusaError(
MedusaError.Types.NOT_FOUND,
"Please configure jwtSecret in your environment"
)
}
const validated = await validator(AdminPostAuthReq, req.body)
const authService: AuthService = req.scope.resolve("authService")
const result = await authService.authenticate(
validated.email,
validated.password
)
if (result.success && result.user) {
// Add JWT to cookie
req.session.jwt = jwt.sign({ userId: result.user.id }, config.jwtSecret, {
expiresIn: "24h",
})
const cleanRes = _.omit(result.user, ["password_hash"])
res.json({ user: cleanRes })
} else {
res.sendStatus(401)
}
}
export class AdminPostAuthReq {
@IsEmail()
@IsNotEmpty()
email: string
@IsString()
@IsNotEmpty()
password: string
}
@@ -1,9 +1,8 @@
import _ from "lodash"
/**
* @oas [get] /auth
* operationId: "DeleteAuth"
* summary: "Delete Session"
* x-authenticated: true
* description: "Deletes the current session for the logged in user."
* tags:
* - Auth
@@ -1,9 +1,11 @@
import _ from "lodash"
import UserService from "../../../../services/user"
/**
* @oas [get] /auth
* operationId: "GetAuth"
* summary: "Get Session"
* x-authenticated: true
* description: "Gets the currently logged in User."
* tags:
* - Auth
@@ -18,9 +20,13 @@ import _ from "lodash"
* $ref: "#/components/schemas/user"
*/
export default async (req, res) => {
const userService = req.scope.resolve("userService")
const user = await userService.retrieve(req.user.userId)
try {
const userService: UserService = req.scope.resolve("userService")
const user = await userService.retrieve(req.user.userId)
const cleanRes = _.omit(user, ["password_hash"])
res.status(200).json({ user: cleanRes })
const cleanRes = _.omit(user, ["password_hash"])
res.status(200).json({ user: cleanRes })
} catch (err) {
res.sendStatus(400)
}
}
@@ -1,9 +1,10 @@
import { Router } from "express"
import { User } from "../../../.."
import middlewares from "../../../middlewares"
const route = Router()
export default app => {
export default (app) => {
app.use("/auth", route)
route.get(
@@ -21,3 +22,11 @@ export default app => {
return app
}
export type AdminAuthRes = {
user: Omit<User, "password_hash">
}
export * from "./create-session"
export * from "./delete-session"
export * from "./get-session"
@@ -59,7 +59,9 @@ describe("POST /admin/collections", () => {
it("returns error details", () => {
expect(subject.body.type).toEqual("invalid_data")
expect(subject.body.message[0].message).toEqual(`"title" is required`)
expect(subject.body.message).toEqual(
"title should not be empty, title must be a string"
)
})
})
})
@@ -22,7 +22,7 @@ describe("GET /admin/collections", () => {
})
it("calls product collection service list", () => {
expect(ProductCollectionServiceMock.list).toHaveBeenCalledTimes(1)
expect(ProductCollectionServiceMock.listAndCount).toHaveBeenCalledTimes(1)
})
})
})
@@ -1,10 +1,12 @@
import { MedusaError, Validator } from "medusa-core-utils"
import { IsNotEmpty, IsObject, IsOptional, IsString } from "class-validator"
import ProductCollectionService from "../../../../services/product-collection"
import { validator } from "../../../../utils/validator"
/**
* @oas [post] /collections
* operationId: "PostCollections"
* summary: "Create a Product Collection"
* description: "Creates a Product Collection."
* x-authenticated: true
* requestBody:
* content:
* application/json:
@@ -34,29 +36,28 @@ import { MedusaError, Validator } from "medusa-core-utils"
* $ref: "#/components/schemas/product_collection"
*/
export default async (req, res) => {
const schema = Validator.object().keys({
title: Validator.string().required(),
handle: Validator.string()
.optional()
.allow(""),
metadata: Validator.object().optional(),
})
const validated = await validator(AdminPostCollectionsReq, req.body)
const { value, error } = schema.validate(req.body)
if (error) {
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
}
const productCollectionService: ProductCollectionService = req.scope.resolve(
"productCollectionService"
)
try {
const productCollectionService = req.scope.resolve(
"productCollectionService"
)
const created = await productCollectionService.create(validated)
const collection = await productCollectionService.retrieve(created.id)
const created = await productCollectionService.create(value)
const collection = await productCollectionService.retrieve(created.id)
res.status(200).json({ collection })
}
res.status(200).json({ collection })
} catch (err) {
throw err
}
export class AdminPostCollectionsReq {
@IsString()
@IsNotEmpty()
title: string
@IsString()
@IsOptional()
handle?: string
@IsObject()
@IsOptional()
metadata?: object
}
@@ -1,8 +1,11 @@
import ProductCollectionService from "../../../../services/product-collection"
/**
* @oas [delete] /collections/{id}
* operationId: "DeleteCollectionsCollection"
* summary: "Delete a Product Collection"
* description: "Deletes a Product Collection."
* x-authenticated: true
* parameters:
* - (path) id=* {string} The id of the Collection.
* tags:
@@ -26,18 +29,14 @@
export default async (req, res) => {
const { id } = req.params
try {
const productCollectionService = req.scope.resolve(
"productCollectionService"
)
await productCollectionService.delete(id)
const productCollectionService: ProductCollectionService = req.scope.resolve(
"productCollectionService"
)
await productCollectionService.delete(id)
res.json({
id,
object: "product-collection",
deleted: true,
})
} catch (err) {
throw err
}
res.json({
id,
object: "product-collection",
deleted: true,
})
}
@@ -0,0 +1,31 @@
import ProductCollectionService from "../../../../services/product-collection"
/**
* @oas [get] /collections/{id}
* operationId: "GetCollectionsCollection"
* summary: "Retrieve a Product Collection"
* description: "Retrieves a Product Collection."
* x-authenticated: true
* parameters:
* - (path) id=* {string} The id of the Product Collection
* tags:
* - Collection
* responses:
* "200":
* description: OK
* content:
* application/json:
* schema:
* properties:
* collection:
* $ref: "#/components/schemas/product_collection"
*/
export default async (req, res) => {
const { id } = req.params
const productCollectionService: ProductCollectionService = req.scope.resolve(
"productCollectionService"
)
const collection = await productCollectionService.retrieve(id)
res.status(200).json({ collection })
}
@@ -1,21 +0,0 @@
import { Router } from "express"
import middlewares from "../../../middlewares"
const route = Router()
export default app => {
app.use("/collections", route)
route.post("/", middlewares.wrap(require("./create-collection").default))
route.post("/:id", middlewares.wrap(require("./update-collection").default))
route.delete("/:id", middlewares.wrap(require("./delete-collection").default))
route.get("/:id", middlewares.wrap(require("./get-collection").default))
route.get("/", middlewares.wrap(require("./list-collections").default))
return app
}
export const defaultFields = ["id", "title", "handle"]
export const defaultRelations = ["products"]
@@ -0,0 +1,40 @@
import { Router } from "express"
import { ProductCollection } from "../../../.."
import { DeleteResponse, PaginatedResponse } from "../../../../types/common"
import middlewares from "../../../middlewares"
import "reflect-metadata"
const route = Router()
export default (app) => {
app.use("/collections", route)
route.post("/", middlewares.wrap(require("./create-collection").default))
route.post("/:id", middlewares.wrap(require("./update-collection").default))
route.delete("/:id", middlewares.wrap(require("./delete-collection").default))
route.get("/:id", middlewares.wrap(require("./get-collection").default))
route.get("/", middlewares.wrap(require("./list-collections").default))
return app
}
export const defaultAdminCollectionsFields = ["id", "title", "handle"]
export const defaultAdminCollectionsRelations = ["products"]
export type AdminCollectionsListRes = PaginatedResponse & {
collections: ProductCollection[]
}
export type AdminCollectionsDeleteRes = DeleteResponse
export type AdminCollectionsRes = {
collection: ProductCollection
}
export * from "./create-collection"
export * from "./delete-collection"
export * from "./get-collection"
export * from "./list-collections"
export * from "./update-collection"
@@ -1,47 +0,0 @@
import { defaultFields, defaultRelations } from "."
/**
* @oas [get] /collections
* operationId: "GetCollections"
* summary: "List Product Collections"
* description: "Retrieve a list of Product Collection."
* tags:
* - Collection
* responses:
* "200":
* description: OK
* content:
* application/json:
* schema:
* properties:
* collection:
* $ref: "#/components/schemas/product_collection"
*/
export default async (req, res) => {
try {
const selector = {}
const limit = parseInt(req.query.limit) || 10
const offset = parseInt(req.query.offset) || 0
const productCollectionService = req.scope.resolve(
"productCollectionService"
)
const listConfig = {
select: defaultFields,
relations: defaultRelations,
skip: offset,
take: limit,
}
const collections = await productCollectionService.list(
selector,
listConfig
)
res.status(200).json({ collections })
} catch (err) {
throw err
}
}
@@ -0,0 +1,67 @@
import { Type } from "class-transformer"
import { IsNumber, IsOptional } from "class-validator"
import {
defaultAdminCollectionsFields,
defaultAdminCollectionsRelations,
} from "."
import ProductCollectionService from "../../../../services/product-collection"
import { validator } from "../../../../utils/validator"
/**
* @oas [get] /collections
* operationId: "GetCollections"
* summary: "List Product Collections"
* description: "Retrieve a list of Product Collection."
* x-authenticated: true
* parameters:
* - (path) limit {string} The number of collections to return.
* - (path) offset {string} The offset of collections to return.
* tags:
* - Collection
* responses:
* "200":
* description: OK
* content:
* application/json:
* schema:
* properties:
* collection:
* $ref: "#/components/schemas/product_collection"
*/
export default async (req, res) => {
const validated = await validator(AdminGetCollectionsParams, req.query)
const productCollectionService: ProductCollectionService = req.scope.resolve(
"productCollectionService"
)
const listConfig = {
select: defaultAdminCollectionsFields,
relations: defaultAdminCollectionsRelations,
skip: validated.offset,
take: validated.limit,
}
const [collections, count] = await productCollectionService.listAndCount(
{},
listConfig
)
res.status(200).json({
collections,
count,
offset: validated.offset,
limit: validated.limit,
})
}
export class AdminGetCollectionsParams {
@IsNumber()
@IsOptional()
@Type(() => Number)
limit = 10
@IsNumber()
@IsOptional()
@Type(() => Number)
offset = 0
}
@@ -1,10 +1,12 @@
import { MedusaError, Validator } from "medusa-core-utils"
import { IsObject, IsOptional, IsString } from "class-validator"
import ProductCollectionService from "../../../../services/product-collection"
import { validator } from "../../../../utils/validator"
/**
* @oas [post] /collections/{id}
* operationId: "PostCollectionsCollection"
* summary: "Update a Product Collection"
* description: "Updates a Product Collection."
* x-authenticated: true
* parameters:
* - (path) id=* {string} The id of the Collection.
* requestBody:
@@ -36,27 +38,27 @@ import { MedusaError, Validator } from "medusa-core-utils"
export default async (req, res) => {
const { id } = req.params
const schema = Validator.object().keys({
title: Validator.string().optional(),
handle: Validator.string().optional(),
metadata: Validator.object().optional(),
})
const validated = await validator(AdminPostCollectionsCollectionReq, req.body)
const productCollectionService: ProductCollectionService = req.scope.resolve(
"productCollectionService"
)
const { value, error } = schema.validate(req.body)
if (error) {
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
}
const updated = await productCollectionService.update(id, validated)
const collection = await productCollectionService.retrieve(updated.id)
try {
const productCollectionService = req.scope.resolve(
"productCollectionService"
)
res.status(200).json({ collection })
}
const updated = await productCollectionService.update(id, value)
const collection = await productCollectionService.retrieve(updated.id)
export class AdminPostCollectionsCollectionReq {
@IsString()
@IsOptional()
title?: string
res.status(200).json({ collection })
} catch (err) {
throw err
}
@IsString()
@IsOptional()
handle?: string
@IsObject()
@IsOptional()
metadata?: object
}
@@ -1,47 +0,0 @@
import { Validator, MedusaError } from "medusa-core-utils"
/**
* @oas [post] /customers
* operationId: "PostCustomers"
* summary: "Create a Customer"
* description: "Creates a Customer."
* parameters:
* - (body) email=* {string} The Customer's email address.
* - (body) first_name=* {string} The Customer's first name.
* - (body) last_name=* {string} The Customer's last name.
* - (body) phone {string} The Customer's phone number.
* tags:
* - Customer
* responses:
* 200:
* description: OK
* content:
* application/json:
* schema:
* properties:
* customer:
* $ref: "#/components/schemas/customer"
*/
export default async (req, res) => {
const schema = Validator.object().keys({
email: Validator.string()
.email()
.required(),
first_name: Validator.string().required(),
last_name: Validator.string().required(),
password: Validator.string().required(),
phone: Validator.string().optional(),
})
const { value, error } = schema.validate(req.body)
if (error) {
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
}
try {
const customerService = req.scope.resolve("customerService")
const customer = await customerService.create(value)
res.status(201).json({ customer })
} catch (err) {
throw err
}
}
@@ -0,0 +1,57 @@
import { IsEmail, IsObject, IsOptional, IsString } from "class-validator"
import { CustomerService } from "../../../../services"
import { validator } from "../../../../utils/validator"
/**
* @oas [post] /customers
* operationId: "PostCustomers"
* summary: "Create a Customer"
* description: "Creates a Customer."
* x-authenticated: true
* parameters:
* - (body) email=* {string} The Customer's email address.
* - (body) first_name=* {string} The Customer's first name.
* - (body) last_name=* {string} The Customer's last name.
* - (body) phone {string} The Customer's phone number.
* - (body) metadata {object} Metadata for the customer.
* tags:
* - Customer
* responses:
* 200:
* description: OK
* content:
* application/json:
* schema:
* properties:
* customer:
* $ref: "#/components/schemas/customer"
*/
export default async (req, res) => {
const validated = await validator(AdminPostCustomersReq, req.bodyn)
const customerService: CustomerService = req.scope.resolve("customerService")
const customer = await customerService.create(validated)
res.status(201).json({ customer })
}
export class AdminPostCustomersReq {
@IsEmail()
email: string
@IsString()
first_name: string
@IsString()
last_name: string
@IsString()
password: string
@IsString()
@IsOptional()
phone?: string
@IsObject()
@IsOptional()
metadata?: object
}
@@ -1,8 +1,11 @@
import CustomerService from "../../../../services/customer"
/**
* @oas [get] /customers/{id}
* operationId: "GetCustomersCustomer"
* summary: "Retrieve a Customer"
* description: "Retrieves a Customer."
* x-authenticated: true
* parameters:
* - (path) id=* {string} The id of the Customer.
* tags:
@@ -19,14 +22,10 @@
*/
export default async (req, res) => {
const { id } = req.params
try {
const customerService = req.scope.resolve("customerService")
const customer = await customerService.retrieve(id, {
relations: ["orders", "shipping_addresses"],
})
const customerService: CustomerService = req.scope.resolve("customerService")
const customer = await customerService.retrieve(id, {
relations: ["orders", "shipping_addresses"],
})
res.json({ customer })
} catch (err) {
throw err
}
res.json({ customer })
}
@@ -1,15 +0,0 @@
import { Router } from "express"
import middlewares from "../../../middlewares"
const route = Router()
export default app => {
app.use("/customers", route)
route.get("/", middlewares.wrap(require("./list-customers").default))
route.get("/:id", middlewares.wrap(require("./get-customer").default))
route.post("/", middlewares.wrap(require("./create-customer").default))
route.post("/:id", middlewares.wrap(require("./update-customer").default))
return app
}
@@ -0,0 +1,32 @@
import { Router } from "express"
import { Customer } from "../../../.."
import { DeleteResponse, PaginatedResponse } from "../../../../types/common"
import middlewares from "../../../middlewares"
const route = Router()
export default (app) => {
app.use("/customers", route)
route.get("/", middlewares.wrap(require("./list-customers").default))
route.get("/:id", middlewares.wrap(require("./get-customer").default))
route.post("/", middlewares.wrap(require("./create-customer").default))
route.post("/:id", middlewares.wrap(require("./update-customer").default))
return app
}
export type AdminCustomersRes = {
customer: Customer
}
export type AdminCustomersDeleteRes = DeleteResponse
export type AdminCustomersListRes = PaginatedResponse & {
customers: Customer[]
}
export * from "./create-customer"
export * from "./get-customer"
export * from "./list-customers"
export * from "./update-customer"
@@ -1,51 +0,0 @@
/**
* @oas [get] /customers
* operationId: "GetCustomers"
* summary: "List Customers"
* description: "Retrieves a list of Customers."
* tags:
* - Customer
* responses:
* 200:
* description: OK
* content:
* application/json:
* schema:
* properties:
* customer:
* $ref: "#/components/schemas/customer"
*/
export default async (req, res) => {
try {
const customerService = req.scope.resolve("customerService")
const limit = parseInt(req.query.limit) || 50
const offset = parseInt(req.query.offset) || 0
const selector = {}
if ("q" in req.query) {
selector.q = req.query.q
}
let expandFields = []
if ("expand" in req.query) {
expandFields = req.query.expand.split(",")
}
const listConfig = {
relations: expandFields.length ? expandFields : [],
skip: offset,
take: limit,
}
const [customers, count] = await customerService.listAndCount(
selector,
listConfig
)
res.json({ customers, count, offset, limit })
} catch (error) {
throw error
}
}
@@ -0,0 +1,75 @@
import { Type } from "class-transformer"
import { IsNumber, IsOptional, IsString } from "class-validator"
import { Customer } from "../../../.."
import CustomerService from "../../../../services/customer"
import { FindConfig } from "../../../../types/common"
import { AdminListCustomerSelector } from "../../../../types/customers"
import { validator } from "../../../../utils/validator"
/**
* @oas [get] /customers
* operationId: "GetCustomers"
* summary: "List Customers"
* description: "Retrieves a list of Customers."
* x-authenticated: true
* tags:
* - Customer
* responses:
* 200:
* description: OK
* content:
* application/json:
* schema:
* properties:
* customer:
* $ref: "#/components/schemas/customer"
*/
export default async (req, res) => {
const validated = await validator(AdminGetCustomersParams, req.query)
const customerService: CustomerService = req.scope.resolve("customerService")
const selector: AdminListCustomerSelector = {}
if (validated.q) {
selector.q = validated.q
}
let expandFields: string[] = []
if (validated.expand) {
expandFields = validated.expand.split(",")
}
const listConfig: FindConfig<Customer> = {
relations: expandFields,
skip: validated.offset,
take: validated.limit,
}
const [customers, count] = await customerService.listAndCount(
selector,
listConfig
)
res.json({
customers,
count,
offset: validated.offset,
limit: validated.limit,
})
}
export class AdminGetCustomersParams extends AdminListCustomerSelector {
@IsNumber()
@IsOptional()
@Type(() => Number)
limit = 50
@IsNumber()
@IsOptional()
@Type(() => Number)
offset = 0
@IsString()
@IsOptional()
expand?: string
}
@@ -1,76 +0,0 @@
import { Validator, MedusaError } from "medusa-core-utils"
/**
* @oas [post] /customers/{id}
* operationId: "PostCustomersCustomer"
* summary: "Update a Customer"
* description: "Updates a Customer."
* parameters:
* - (path) id=* {string} The id of the Customer.
* requestBody:
* content:
* application/json:
* schema:
* properties:
* email:
* type: string
* description: The Customer's email. Only providable if user not registered.
* first_name:
* type: string
* description: The Customer's first name.
* last_name:
* type: string
* description: The Customer's last name.
* phone:
* description: The Customer's phone number.
* type: object
* tags:
* - Customer
* responses:
* 200:
* description: OK
* content:
* application/json:
* schema:
* properties:
* customer:
* $ref: "#/components/schemas/customer"
*/
export default async (req, res) => {
const { id } = req.params
const schema = Validator.object().keys({
email: Validator.string().optional(),
first_name: Validator.string().optional(),
last_name: Validator.string().optional(),
password: Validator.string().optional(),
phone: Validator.string().optional(),
})
const { value, error } = schema.validate(req.body)
if (error) {
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
}
try {
const customerService = req.scope.resolve("customerService")
let customer = await customerService.retrieve(id)
if (value.email && customer.has_account) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
"Email cannot be changed when the user has registered their account"
)
}
await customerService.update(id, value)
customer = await customerService.retrieve(id, {
relations: ["orders"],
})
res.status(200).json({ customer })
} catch (err) {
throw err
}
}
@@ -0,0 +1,97 @@
import { IsEmail, IsObject, IsOptional, IsString } from "class-validator"
import { MedusaError } from "medusa-core-utils"
import CustomerService from "../../../../services/customer"
import { validator } from "../../../../utils/validator"
/**
* @oas [post] /customers/{id}
* operationId: "PostCustomersCustomer"
* summary: "Update a Customer"
* description: "Updates a Customer."
* x-authenticated: true
* parameters:
* - (path) id=* {string} The id of the Customer.
* requestBody:
* content:
* application/json:
* schema:
* properties:
* email:
* type: string
* description: The Customer's email. Only providable if user not registered.
* first_name:
* type: string
* description: The Customer's first name.
* last_name:
* type: string
* description: The Customer's last name.
* phone:
* type: string
* description: The Customer's phone number.
* password:
* type: string
* description: The Customer's password.
* metadata:
* type: object
* description: Metadata for the customer.
* tags:
* - Customer
* responses:
* 200:
* description: OK
* content:
* application/json:
* schema:
* properties:
* customer:
* $ref: "#/components/schemas/customer"
*/
export default async (req, res) => {
const { id } = req.params
const validated = await validator(AdminPostCustomersCustomerReq, req.body)
const customerService: CustomerService = req.scope.resolve("customerService")
let customer = await customerService.retrieve(id)
if (validated.email && customer.has_account) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
"Email cannot be changed when the user has registered their account"
)
}
await customerService.update(id, validated)
customer = await customerService.retrieve(id, {
relations: ["orders"],
})
res.status(200).json({ customer })
}
export class AdminPostCustomersCustomerReq {
@IsEmail()
@IsOptional()
email?: string
@IsString()
@IsOptional()
first_name?: string
@IsString()
@IsOptional()
last_name?: string
@IsString()
@IsOptional()
password?: string
@IsString()
@IsOptional()
phone?: string
@IsObject()
@IsOptional()
metadata?: object
}
@@ -80,7 +80,7 @@ describe("POST /admin/discounts", () => {
})
it("returns error", () => {
expect(subject.body.message[0].message).toEqual(
expect(subject.body.message).toEqual(
`"valid_duration" must be a valid ISO 8601 duration`
)
})
@@ -160,7 +160,9 @@ describe("POST /admin/discounts", () => {
})
it("returns error", () => {
expect(subject.body.message[0].message).toEqual(`"rule.type" is required`)
expect(subject.body.message).toEqual(
`type should not be empty, type must be a string`
)
})
})
@@ -193,8 +195,8 @@ describe("POST /admin/discounts", () => {
})
it("returns error", () => {
expect(subject.body.message[0].message).toEqual(
`"ends_at" must be greater than "ref:starts_at"`
expect(subject.body.message).toEqual(
`"ends_at" must be greater than "starts_at"`
)
})
})
@@ -231,8 +233,8 @@ describe("POST /admin/discounts", () => {
it("returns error", () => {
expect(DiscountServiceMock.create).toHaveBeenCalledWith({
code: "TEST",
is_dynamic: true,
is_disabled: false,
is_dynamic: true,
is_disabled: false,
rule: {
description: "Test",
type: "fixed",
@@ -1,5 +1,5 @@
import { IdMap } from "medusa-test-utils"
import { defaultFields, defaultRelations } from ".."
import { defaultAdminDiscountsFields, defaultAdminDiscountsRelations } from ".."
import { request } from "../../../../../helpers/test-request"
import { DiscountServiceMock } from "../../../../../services/__mocks__/discount"
@@ -27,8 +27,8 @@ describe("GET /admin/discounts", () => {
expect(DiscountServiceMock.listAndCount).toHaveBeenCalledWith(
{},
{
select: defaultFields,
relations: defaultRelations,
select: defaultAdminDiscountsFields,
relations: defaultAdminDiscountsRelations,
skip: 0,
take: 20,
order: { created_at: "DESC" },
@@ -64,8 +64,8 @@ describe("GET /admin/discounts", () => {
expect(DiscountServiceMock.listAndCount).toHaveBeenCalledWith(
{ q: "OLI", is_dynamic: false, is_disabled: false },
{
select: defaultFields,
relations: defaultRelations,
select: defaultAdminDiscountsFields,
relations: defaultAdminDiscountsRelations,
skip: 20,
take: 40,
order: { created_at: "DESC" },
@@ -97,8 +97,8 @@ describe("GET /admin/discounts", () => {
expect(DiscountServiceMock.listAndCount).toHaveBeenCalledWith(
{ is_dynamic: true },
{
select: defaultFields,
relations: defaultRelations,
select: defaultAdminDiscountsFields,
relations: defaultAdminDiscountsRelations,
skip: 0,
take: 20,
order: { created_at: "DESC" },
@@ -130,8 +130,8 @@ describe("GET /admin/discounts", () => {
expect(DiscountServiceMock.listAndCount).toHaveBeenCalledWith(
{ is_disabled: true },
{
select: defaultFields,
relations: defaultRelations,
select: defaultAdminDiscountsFields,
relations: defaultAdminDiscountsRelations,
skip: 0,
take: 20,
order: { created_at: "DESC" },
@@ -46,7 +46,6 @@ describe("POST /admin/discounts", () => {
value: 10,
allocation: "total",
},
is_dynamic: false,
}
)
})
@@ -87,7 +86,7 @@ describe("POST /admin/discounts", () => {
})
it("returns error", () => {
expect(subject.body.message[0].message).toEqual(
expect(subject.body.message).toEqual(
`"valid_duration" must be a valid ISO 8601 duration`
)
})
@@ -181,8 +180,8 @@ describe("POST /admin/discounts", () => {
})
it("returns error", () => {
expect(subject.body.message[0].message).toEqual(
`"ends_at" must be greater than "ref:starts_at"`
expect(subject.body.message).toEqual(
`"ends_at" must be greater than "starts_at"`
)
})
})
@@ -1,10 +1,12 @@
import { defaultFields, defaultRelations } from "./"
import { defaultAdminDiscountsFields, defaultAdminDiscountsRelations } from "."
import { Discount } from "../../../.."
import DiscountService from "../../../../services/discount"
/**
* @oas [post] /discounts/{id}/regions/{region_id}
* operationId: "PostDiscountsDiscountRegionsRegion"
* summary: "Adds Region availability"
* description: "Adds a Region to the list of Regions that a Discount can be used in."
* x-authenticated: true
* parameters:
* - (path) id=* {string} The id of the Discount.
* - (path) region_id=* {string} The id of the Region.
@@ -22,18 +24,14 @@ import { defaultFields, defaultRelations } from "./"
*/
export default async (req, res) => {
const { discount_id, region_id } = req.params
try {
const discountService = req.scope.resolve("discountService")
await discountService.addRegion(discount_id, region_id)
const discountService: DiscountService = req.scope.resolve("discountService")
await discountService.addRegion(discount_id, region_id)
const discount = await discountService.retrieve(discount_id, {
select: defaultFields,
relations: defaultRelations,
})
const discount: Discount = await discountService.retrieve(discount_id, {
select: defaultAdminDiscountsFields,
relations: defaultAdminDiscountsRelations,
})
res.status(200).json({ discount })
} catch (err) {
throw err
}
res.status(200).json({ discount })
}
@@ -1,40 +0,0 @@
import { defaultFields, defaultRelations } from "./"
/**
* @oas [post] /discounts/{id}/products/{product_id}
* operationId: "PostDiscountsDiscountProductsProduct"
* summary: "Adds Product availability"
* description: "Adds a Product to the list of Products that a Discount can be used for."
* parameters:
* - (path) id=* {string} The id of the Discount.
* - (path) product_id=* {string} The id of the Product.
* tags:
* - Discount
* responses:
* 200:
* description: OK
* content:
* application/json:
* schema:
* properties:
* discount:
* $ref: "#/components/schemas/discount"
*/
export default async (req, res) => {
const { discount_id, variant_id } = req.params
try {
const discountService = req.scope.resolve("discountService")
await discountService.addValidProduct(discount_id, variant_id)
const discount = await discountService.retrieve(discount_id, {
select: defaultFields,
relations: defaultRelations,
})
res.status(200).json({ discount })
} catch (err) {
throw err
}
}
@@ -0,0 +1,37 @@
import { defaultAdminDiscountsFields, defaultAdminDiscountsRelations } from "."
import { Discount } from "../../../.."
import DiscountService from "../../../../services/discount"
/**
* @oas [post] /discounts/{id}/products/{variant_id}
* operationId: "PostDiscountsDiscountProductsProduct"
* summary: "Adds Product availability"
* description: "Adds a Product to the list of Products that a Discount can be used for."
* x-authenticated: true
* parameters:
* - (path) id=* {string} The id of the Discount.
* - (path) variant_id=* {string} The id of the Product.
* tags:
* - Discount
* responses:
* 200:
* description: OK
* content:
* application/json:
* schema:
* properties:
* discount:
* $ref: "#/components/schemas/discount"
*/
export default async (req, res) => {
const { discount_id, variant_id } = req.params
const discountService: DiscountService = req.scope.resolve("discountService")
await discountService.addValidProduct(discount_id, variant_id)
const discount: Discount = await discountService.retrieve(discount_id, {
select: defaultAdminDiscountsFields,
relations: defaultAdminDiscountsRelations,
})
res.status(200).json({ discount })
}
@@ -1,14 +1,33 @@
import { MedusaError, Validator } from "medusa-core-utils"
import { defaultRelations } from "."
import { Type } from "class-transformer"
import {
IsArray,
IsBoolean,
IsDate,
IsNotEmpty,
IsNumber,
IsObject,
IsOptional,
IsPositive,
IsString,
ValidateNested,
} from "class-validator"
import { defaultAdminDiscountsRelations } from "."
import DiscountService from "../../../../services/discount"
import { IsGreaterThan } from "../../../../utils/validators/greater-than"
import { validator } from "../../../../utils/validator"
import { IsISO8601Duration } from "../../../../utils/validators/iso8601-duration"
/**
* @oas [post] /discounts
* operationId: "PostDiscounts"
* summary: "Creates a Discount"
* x-authenticated: true
* description: "Creates a Discount with a given set of rules that define how the Discount behaves."
* requestBody:
* content:
* application/json:
* required:
* - code
* - rule
* schema:
* properties:
* code:
@@ -56,54 +75,84 @@ import { defaultRelations } from "."
* $ref: "#/components/schemas/discount"
*/
export default async (req, res) => {
const schema = Validator.object().keys({
code: Validator.string().required(),
is_dynamic: Validator.boolean().default(false),
rule: Validator.object()
.keys({
description: Validator.string().optional(),
type: Validator.string().required(),
value: Validator.number()
.positive()
.required(),
allocation: Validator.string().required(),
valid_for: Validator.array().items(Validator.string()),
})
.required(),
is_disabled: Validator.boolean().default(false),
starts_at: Validator.date().optional(),
ends_at: Validator.date()
.greater(Validator.ref("starts_at"))
.optional(),
valid_duration: Validator.string()
.isoDuration()
.allow(null)
.optional(),
usage_limit: Validator.number()
.positive()
.optional(),
regions: Validator.array()
.items(Validator.string())
.optional(),
metadata: Validator.object().optional(),
})
const validated = await validator(AdminPostDiscountsReq, req.body)
const { value, error } = schema.validate(req.body)
if (error) {
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
}
const discountService: DiscountService = req.scope.resolve("discountService")
const created = await discountService.create(validated)
const discount = await discountService.retrieve(
created.id,
defaultAdminDiscountsRelations
)
try {
const discountService = req.scope.resolve("discountService")
const created = await discountService.create(value)
const discount = await discountService.retrieve(
created.id,
defaultRelations
)
res.status(200).json({ discount })
} catch (err) {
throw err
}
res.status(200).json({ discount })
}
export class AdminPostDiscountsReq {
@IsString()
@IsNotEmpty()
code: string
@IsNotEmpty()
@ValidateNested()
@Type(() => AdminPostDiscountsDiscountRule)
rule: AdminPostDiscountsDiscountRule
@IsBoolean()
@IsOptional()
is_dynamic = false
@IsBoolean()
@IsOptional()
is_disabled = false
@IsDate()
@IsOptional()
@Type(() => Date)
starts_at?: Date
@IsDate()
@IsOptional()
@IsGreaterThan("starts_at")
@Type(() => Date)
ends_at?: Date
@IsISO8601Duration()
@IsOptional()
valid_duration?: string
@IsNumber()
@IsOptional()
@IsPositive()
usage_limit?: number
@IsArray()
@IsOptional()
@IsString({ each: true })
regions?: string[]
@IsObject()
@IsOptional()
metadata?: object
}
export class AdminPostDiscountsDiscountRule {
@IsString()
@IsOptional()
description?: string
@IsString()
@IsNotEmpty()
type: string
@IsNumber()
value: number
@IsString()
@IsNotEmpty()
allocation: string
@IsOptional()
@IsArray()
@IsString({ each: true })
valid_for?: string[]
}
@@ -1,10 +1,18 @@
import { MedusaError, Validator } from "medusa-core-utils"
import {
IsNotEmpty,
IsNumber,
IsObject,
IsOptional,
IsString,
} from "class-validator"
import DiscountService from "../../../../services/discount"
import { validator } from "../../../../utils/validator"
/**
* @oas [post] /discounts/{id}/dynamic-codes
* operationId: "PostDiscountsDiscountDynamicCodes"
* summary: "Create a dynamic Discount code"
* description: "Creates a unique code that can map to a parent Discount. This is useful if you want to automatically generate codes with the same behaviour."
* x-authenticated: true
* parameters:
* - (path) id=* {string} The id of the Discount to create the dynamic code from."
* - (body) code=* {string} The unique code that will be used to redeem the Discount.
@@ -24,27 +32,34 @@ import { MedusaError, Validator } from "medusa-core-utils"
export default async (req, res) => {
const { discount_id } = req.params
const schema = Validator.object().keys({
code: Validator.string().required(),
usage_limit: Validator.number().default(1),
metadata: Validator.object().optional(),
const validated = await validator(
AdminPostDiscountsDiscountDynamicCodesReq,
req.body
)
const discountService: DiscountService = req.scope.resolve("discountService")
const created = await discountService.createDynamicCode(
discount_id,
validated
)
const discount = await discountService.retrieve(created.id, {
relations: ["rule", "rule.valid_for", "regions"],
})
const { value, error } = schema.validate(req.body)
if (error) {
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
}
res.status(200).json({ discount })
}
try {
const discountService = req.scope.resolve("discountService")
const created = await discountService.createDynamicCode(discount_id, value)
export class AdminPostDiscountsDiscountDynamicCodesReq {
@IsString()
@IsNotEmpty()
code: string
const discount = await discountService.retrieve(created.id, {
relations: ["rule", "rule.valid_for", "regions"],
})
@IsNumber()
@IsOptional()
usage_limit = 1
res.status(200).json({ discount })
} catch (err) {
throw err
}
@IsObject()
@IsOptional()
metadata?: object
}
@@ -1,8 +1,11 @@
import DiscountService from "../../../../services/discount"
/**
* @oas [delete] /discounts/{id}
* operationId: "DeleteDiscountsDiscount"
* summary: "Delete a Discount"
* description: "Deletes a Discount."
* x-authenticated: true
* parameters:
* - (path) id=* {string} The id of the Discount
* tags:
@@ -26,16 +29,12 @@
export default async (req, res) => {
const { discount_id } = req.params
try {
const discountService = req.scope.resolve("discountService")
await discountService.delete(discount_id)
const discountService: DiscountService = req.scope.resolve("discountService")
await discountService.delete(discount_id)
res.json({
id: discount_id,
object: "discount",
deleted: true,
})
} catch (err) {
throw err
}
res.json({
id: discount_id,
object: "discount",
deleted: true,
})
}
@@ -1,8 +1,11 @@
import DiscountService from "../../../../services/discount"
/**
* @oas [delete] /discounts/{id}/dynamic-codes/{code}
* operationId: "DeleteDiscountsDiscountDynamicCodesCode"
* summary: "Delete a dynamic code"
* description: "Deletes a dynamic code from a Discount."
* x-authenticated: true
* parameters:
* - (path) id=* {string} The id of the Discount
* - (path) code=* {string} The id of the Discount
@@ -21,16 +24,12 @@
export default async (req, res) => {
const { discount_id, code } = req.params
try {
const discountService = req.scope.resolve("discountService")
await discountService.deleteDynamicCode(discount_id, code)
const discountService: DiscountService = req.scope.resolve("discountService")
await discountService.deleteDynamicCode(discount_id, code)
const discount = await discountService.retrieve(discount_id, {
relations: ["rule", "rule.valid_for", "regions"],
})
const discount = await discountService.retrieve(discount_id, {
relations: ["rule", "rule.valid_for", "regions"],
})
res.status(200).json({ discount })
} catch (err) {
throw err
}
res.status(200).json({ discount })
}
@@ -1,10 +1,11 @@
import { defaultFields, defaultRelations } from "./"
import { defaultAdminDiscountsRelations } from "."
import DiscountService from "../../../../services/discount"
/**
* @oas [get] /discounts/code/{code}
* operationId: "GetDiscountsDiscountCode"
* summary: "Retrieve a Discount by code"
* description: "Retrieves a Discount by its discount code"
* x-authenticated: true
* parameters:
* - (path) code=* {string} The code of the Discount
* tags:
@@ -21,15 +22,12 @@ import { defaultFields, defaultRelations } from "./"
*/
export default async (req, res) => {
const { code } = req.params
try {
const discountService = req.scope.resolve("discountService")
const discount = await discountService.retrieveByCode(
code,
defaultRelations
)
res.status(200).json({ discount })
} catch (err) {
throw err
}
const discountService: DiscountService = req.scope.resolve("discountService")
const discount = await discountService.retrieveByCode(
code,
defaultAdminDiscountsRelations
)
res.status(200).json({ discount })
}
@@ -1,10 +1,11 @@
import { defaultFields, defaultRelations } from "./"
import { defaultAdminDiscountsFields, defaultAdminDiscountsRelations } from "."
import DiscountService from "../../../../services/discount"
/**
* @oas [get] /discounts/{id}
* operationId: "GetDiscountsDiscount"
* summary: "Retrieve a Discount"
* description: "Retrieves a Discount"
* x-authenticated: true
* parameters:
* - (path) id=* {string} The id of the Discount
* tags:
@@ -21,15 +22,12 @@ import { defaultFields, defaultRelations } from "./"
*/
export default async (req, res) => {
const { discount_id } = req.params
try {
const discountService = req.scope.resolve("discountService")
const data = await discountService.retrieve(discount_id, {
select: defaultFields,
relations: defaultRelations,
})
res.status(200).json({ discount: data })
} catch (err) {
throw err
}
const discountService: DiscountService = req.scope.resolve("discountService")
const data = await discountService.retrieve(discount_id, {
select: defaultAdminDiscountsFields,
relations: defaultAdminDiscountsRelations,
})
res.status(200).json({ discount: data })
}
@@ -1,9 +1,12 @@
import { Router } from "express"
import { Discount } from "../../../.."
import middlewares from "../../../middlewares"
import "reflect-metadata"
import { DeleteResponse, PaginatedResponse } from "../../../../types/common"
const route = Router()
export default app => {
export default (app) => {
app.use("/discounts", route)
route.get("/", middlewares.wrap(require("./list-discounts").default))
@@ -59,7 +62,7 @@ export default app => {
return app
}
export const defaultFields = [
export const defaultAdminDiscountsFields = [
"id",
"code",
"is_dynamic",
@@ -77,9 +80,32 @@ export const defaultFields = [
"valid_duration",
]
export const defaultRelations = [
export const defaultAdminDiscountsRelations = [
"rule",
"parent_discount",
"regions",
"rule.valid_for",
]
export type AdminDiscountsRes = {
discount: Discount
}
export type AdminDiscountsDeleteRes = DeleteResponse
export type AdminDiscountsListRes = PaginatedResponse & {
discounts: Discount[]
}
export * from "./add-region"
export * from "./add-valid-product"
export * from "./create-discount"
export * from "./create-dynamic-code"
export * from "./delete-discount"
export * from "./delete-dynamic-code"
export * from "./get-discount"
export * from "./get-discount-by-code"
export * from "./list-discounts"
export * from "./remove-region"
export * from "./remove-valid-product"
export * from "./update-discount"
@@ -1,58 +0,0 @@
import { defaultFields, defaultRelations } from "./"
/**
* @oas [get] /discounts
* operationId: "GetDiscounts"
* summary: "List Discounts"
* description: "Retrieves a list of Discounts"
* tags:
* - Discount
* responses:
* 200:
* description: OK
* content:
* application/json:
* schema:
* properties:
* discount:
* $ref: "#/components/schemas/discount"
*/
export default async (req, res) => {
try {
const discountService = req.scope.resolve("discountService")
const limit = parseInt(req.query.limit) || 20
const offset = parseInt(req.query.offset) || 0
let selector = {}
if ("q" in req.query) {
selector.q = req.query.q
}
if ("is_dynamic" in req.query) {
selector.is_dynamic = req.query.is_dynamic === "true"
}
if ("is_disabled" in req.query) {
selector.is_disabled = req.query.is_disabled === "true"
}
const listConfig = {
select: defaultFields,
relations: defaultRelations,
skip: offset,
take: limit,
order: { created_at: "DESC" },
}
const [discounts, count] = await discountService.listAndCount(
selector,
listConfig
)
res.status(200).json({ discounts, count, offset, limit })
} catch (err) {
throw err
}
}
@@ -0,0 +1,93 @@
import { Type, Transform } from "class-transformer"
import { IsBoolean, IsInt, IsOptional, IsString } from "class-validator"
import { defaultAdminDiscountsFields, defaultAdminDiscountsRelations } from "."
import DiscountService from "../../../../services/discount"
import { ListSelector } from "../../../../types/discount"
import { validator } from "../../../../utils/validator"
/**
* @oas [get] /discounts
* operationId: "GetDiscounts"
* summary: "List Discounts"
* x-authenticated: true
* description: "Retrieves a list of Discounts"
* parameters:
* - (query) q {string} Search query applied on results.
* - (query) is_dynamic {boolean} Return only dynamic discounts.
* - (query) is_disabled {boolean} Return only disabled discounts.
* - (query) limit {number} The number of items in the response
* - (query) offset {number} The offset of items in response
* - (query) expand {string} Comma separated list of relations to include in the results.
* tags:
* - Discount
* responses:
* 200:
* description: OK
* content:
* application/json:
* schema:
* properties:
* discount:
* $ref: "#/components/schemas/discount"
*/
export default async (req, res) => {
const validated = await validator(AdminGetDiscountsParams, req.query)
const discountService: DiscountService = req.scope.resolve("discountService")
const selector: ListSelector = {}
if (validated.q) {
selector.q = validated.q
}
selector.is_disabled = validated.is_disabled
selector.is_dynamic = validated.is_dynamic
const listConfig = {
select: defaultAdminDiscountsFields,
relations: defaultAdminDiscountsRelations,
skip: validated.offset,
take: validated.limit,
order: { created_at: "DESC" },
}
const [discounts, count] = await discountService.listAndCount(
selector,
listConfig
)
res.status(200).json({
discounts,
count,
offset: validated.offset,
limit: validated.limit,
})
}
export class AdminGetDiscountsParams {
@IsString()
@IsOptional()
q?: string
@IsBoolean()
@IsOptional()
@Transform(({ value }) => value === "true")
is_dynamic?: boolean
@IsBoolean()
@IsOptional()
@Transform(({ value }) => value === "true")
is_disabled?: boolean
@IsInt()
@IsOptional()
@Type(() => Number)
limit = 20
@IsInt()
@IsOptional()
@Type(() => Number)
offset = 0
@IsString()
@IsOptional()
expand?: string
}
@@ -1,9 +1,10 @@
import { defaultFields, defaultRelations } from "./"
import DiscountService from "../../../../services/discount"
import { defaultAdminDiscountsFields, defaultAdminDiscountsRelations } from "."
/**
* @oas [delete] /discounts/{id}/regions/{region_id}
* operationId: "DeleteDiscountsDiscountRegionsRegion"
* summary: "Remove Region availability"
* x-authenticated: true
* description: "Removes a Region from the list of Regions that a Discount can be used in."
* parameters:
* - (path) id=* {string} The id of the Discount.
@@ -23,17 +24,13 @@ import { defaultFields, defaultRelations } from "./"
export default async (req, res) => {
const { discount_id, region_id } = req.params
try {
const discountService = req.scope.resolve("discountService")
const discountService: DiscountService = req.scope.resolve("discountService")
await discountService.removeRegion(discount_id, region_id)
await discountService.removeRegion(discount_id, region_id)
const discount = await discountService.retrieve(discount_id, {
select: defaultFields,
relations: defaultRelations,
})
const discount = await discountService.retrieve(discount_id, {
select: defaultAdminDiscountsFields,
relations: defaultAdminDiscountsRelations,
})
res.status(200).json({ discount })
} catch (err) {
throw err
}
res.status(200).json({ discount })
}
@@ -1,10 +1,11 @@
import { defaultFields, defaultRelations } from "./"
import DiscountService from "../../../../services/discount"
import { defaultAdminDiscountsFields, defaultAdminDiscountsRelations } from "."
/**
* @oas [post] /discounts/{id}/products/{product_id}
* operationId: "DeleteDiscountsDiscountProductsProduct"
* summary: "Remove Product availability"
* description: "Removes a Product from the list of Products that a Discount can be used for."
* x-authenticated: true
* parameters:
* - (path) id=* {string} The id of the Discount.
* - (path) product_id=* {string} The id of the Product.
@@ -23,18 +24,13 @@ import { defaultFields, defaultRelations } from "./"
export default async (req, res) => {
const { discount_id, variant_id } = req.params
try {
const discountService = req.scope.resolve("discountService")
const discountService: DiscountService = req.scope.resolve("discountService")
await discountService.removeValidProduct(discount_id, variant_id)
await discountService.removeValidProduct(discount_id, variant_id)
const discount = await discountService.retrieve(discount_id, {
select: defaultAdminDiscountsFields,
relations: defaultAdminDiscountsRelations,
})
const discount = await discountService.retrieve(discount_id, {
select: defaultFields,
relations: defaultRelations,
})
res.status(200).json({ discount })
} catch (err) {
throw err
}
res.status(200).json({ discount })
}
@@ -1,109 +0,0 @@
import { MedusaError, Validator } from "medusa-core-utils"
import { defaultFields, defaultRelations } from "./"
/**
* @oas [post] /discounts/{id}
* operationId: "PostDiscountsDiscount"
* summary: "Update a Discount"
* description: "Updates a Discount with a given set of rules that define how the Discount behaves."
* parameters:
* - (path) id=* {string} The id of the Discount.
* requestBody:
* content:
* application/json:
* schema:
* properties:
* code:
* type: string
* description: A unique code that will be used to redeem the Discount
* is_dynamic:
* type: string
* description: Whether the Discount should have multiple instances of itself, each with a different code. This can be useful for automatically generated codes that all have to follow a common set of rules.
* rule:
* description: The Discount Rule that defines how Discounts are calculated
* oneOf:
* - $ref: "#/components/schemas/discount_rule"
* is_disabled:
* type: boolean
* description: Whether the Discount code is disabled on creation. You will have to enable it later to make it available to Customers.
* starts_at:
* type: string
* format: date-time
* description: The time at which the Discount should be available.
* ends_at:
* type: string
* format: date-time
* description: The time at which the Discount should no longer be available.
* regions:
* description: A list of Region ids representing the Regions in which the Discount can be used.
* type: array
* items:
* type: string
* tags:
* - Discount
* responses:
* 200:
* description: OK
* content:
* application/json:
* schema:
* properties:
* discount:
* $ref: "#/components/schemas/discount"
*/
export default async (req, res) => {
const { discount_id } = req.params
const schema = Validator.object().keys({
code: Validator.string().optional(),
is_dynamic: Validator.boolean().default(false),
rule: Validator.object()
.keys({
id: Validator.string().required(),
description: Validator.string().optional(),
type: Validator.string().required(),
value: Validator.number().required(),
allocation: Validator.string().required(),
valid_for: Validator.array().items(Validator.string()),
})
.optional(),
is_disabled: Validator.boolean().optional(),
starts_at: Validator.date().optional(),
ends_at: Validator.when("starts_at", {
not: undefined,
then: Validator.date()
.greater(Validator.ref("starts_at"))
.optional(),
otherwise: Validator.date().optional(),
}),
valid_duration: Validator.string()
.isoDuration().allow(null)
.optional(),
usage_limit: Validator.number()
.positive()
.optional(),
regions: Validator.array()
.items(Validator.string())
.optional(),
})
const { value, error } = schema.validate(req.body)
if (error) {
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
}
try {
const discountService = req.scope.resolve("discountService")
await discountService.update(discount_id, value)
const discount = await discountService.retrieve(discount_id, {
select: defaultFields,
relations: defaultRelations,
})
res.status(200).json({ discount })
} catch (err) {
throw err
}
}
@@ -0,0 +1,158 @@
import { Type } from "class-transformer"
import {
IsArray,
IsBoolean,
IsDate,
IsNotEmpty,
IsNumber,
IsObject,
IsOptional,
IsPositive,
IsString,
ValidateNested,
} from "class-validator"
import { defaultAdminDiscountsFields, defaultAdminDiscountsRelations } from "."
import DiscountService from "../../../../services/discount"
import { IsGreaterThan } from "../../../../utils/validators/greater-than"
import { validator } from "../../../../utils/validator"
import { IsISO8601Duration } from "../../../../utils/validators/iso8601-duration"
/**
* @oas [post] /discounts/{id}
* operationId: "PostDiscountsDiscount"
* summary: "Update a Discount"
* description: "Updates a Discount with a given set of rules that define how the Discount behaves."
* x-authenticated: true
* parameters:
* - (path) id=* {string} The id of the Discount.
* requestBody:
* content:
* application/json:
* schema:
* properties:
* code:
* type: string
* description: A unique code that will be used to redeem the Discount
* is_dynamic:
* type: string
* description: Whether the Discount should have multiple instances of itself, each with a different code. This can be useful for automatically generated codes that all have to follow a common set of rules.
* rule:
* description: The Discount Rule that defines how Discounts are calculated
* oneOf:
* - $ref: "#/components/schemas/discount_rule"
* is_disabled:
* type: boolean
* description: Whether the Discount code is disabled on creation. You will have to enable it later to make it available to Customers.
* starts_at:
* type: Date
* description: The time at which the Discount should be available.
* ends_at:
* type: Date
* description: The time at which the Discount should no longer be available.
* regions:
* description: A list of Region ids representing the Regions in which the Discount can be used.
* type: array
* items:
* type: string
* metadata:
* description: An object containing metadata of the discount
* type: object
* tags:
* - Discount
* responses:
* 200:
* description: OK
* content:
* application/json:
* schema:
* properties:
* discount:
* $ref: "#/components/schemas/discount"
*/
export default async (req, res) => {
const { discount_id } = req.params
const validated = await validator(AdminPostDiscountsDiscountReq, req.body)
const discountService: DiscountService = req.scope.resolve("discountService")
await discountService.update(discount_id, validated)
const discount = await discountService.retrieve(discount_id, {
select: defaultAdminDiscountsFields,
relations: defaultAdminDiscountsRelations,
})
res.status(200).json({ discount })
}
export class AdminPostDiscountsDiscountReq {
@IsString()
@IsOptional()
code?: string
@IsOptional()
@ValidateNested()
@Type(() => AdminUpdateDiscountRule)
rule?: AdminUpdateDiscountRule
@IsBoolean()
@IsOptional()
is_dynamic?: boolean
@IsBoolean()
@IsOptional()
is_disabled?: boolean
@IsDate()
@IsOptional()
@Type(() => Date)
starts_at?: Date
@IsDate()
@IsOptional()
@IsGreaterThan("starts_at")
@Type(() => Date)
ends_at?: Date
@IsISO8601Duration()
@IsOptional()
valid_duration?: string
@IsNumber()
@IsOptional()
@IsPositive()
usage_limit?: number
@IsArray()
@IsOptional()
@IsString({ each: true })
regions?: string[]
@IsObject()
@IsOptional()
metadata?: object
}
export class AdminUpdateDiscountRule {
@IsString()
@IsNotEmpty()
id: string
@IsString()
@IsOptional()
description?: string
@IsString()
@IsNotEmpty()
type: string
@IsNumber()
value: string
@IsString()
@IsNotEmpty()
allocation: string
@IsArray()
@IsOptional()
@IsString({ each: true })
valid_for?: string
}
@@ -1,19 +1,40 @@
import { Type } from "class-transformer"
import {
MedusaError,
Validator,
transformIdableFields,
} from "medusa-core-utils"
import { defaultFields, defaultRelations } from "."
IsArray,
IsBoolean,
IsEmail,
IsEnum,
IsNotEmpty,
IsNumber,
IsObject,
IsOptional,
IsString,
ValidateNested,
} from "class-validator"
import { transformIdableFields } from "medusa-core-utils"
import {
defaultAdminDraftOrdersFields,
defaultAdminDraftOrdersRelations,
} from "."
import { DraftOrder } from "../../../.."
import { DraftOrderService } from "../../../../services"
import { AddressPayload } from "../../../../types/common"
import { validator } from "../../../../utils/validator"
/**
* @oas [post] /draft-orders
* operationId: "PostDraftOrders"
* summary: "Create a Draft Order"
* description: "Creates a Draft Order"
* x-authenticated: true
* requestBody:
* content:
* application/json:
* schema:
* required:
* - email
* - items
* - region_id
* - shipping_methods
* properties:
* status:
* description: "The status of the draft order"
@@ -97,68 +118,115 @@ import { defaultFields, defaultRelations } from "."
*/
export default async (req, res) => {
const schema = Validator.object().keys({
status: Validator.string()
.valid("open", "completed")
.optional(),
email: Validator.string()
.email()
.required(),
billing_address: Validator.address().optional(),
shipping_address: Validator.address().optional(),
items: Validator.array()
.items({
variant_id: Validator.string()
.optional()
.allow(""),
unit_price: Validator.number().optional(),
title: Validator.string()
.optional()
.allow(""),
quantity: Validator.number().required(),
metadata: Validator.object().default({}),
})
.required(),
region_id: Validator.string().required(),
discounts: Validator.array()
.items({
code: Validator.string().required(),
})
.optional(),
customer_id: Validator.string().optional(),
no_notification_order: Validator.boolean().optional(),
shipping_methods: Validator.array()
.items({
option_id: Validator.string().required(),
data: Validator.object().optional(),
price: Validator.number()
.integer()
.integer()
.allow(0)
.optional(),
})
.required(),
metadata: Validator.object().optional(),
const validated = await validator(AdminPostDraftOrdersReq, req.body)
const value = transformIdableFields(validated, [
"shipping_address",
"billing_address",
])
const draftOrderService: DraftOrderService =
req.scope.resolve("draftOrderService")
let draftOrder: DraftOrder = await draftOrderService.create(value)
draftOrder = await draftOrderService.retrieve(draftOrder.id, {
relations: defaultAdminDraftOrdersRelations,
select: defaultAdminDraftOrdersFields,
})
let { value, error } = schema.validate(req.body)
if (error) {
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
}
value = transformIdableFields(value, ["shipping_address", "billing_address"])
try {
const draftOrderService = req.scope.resolve("draftOrderService")
let draftOrder = await draftOrderService.create(value)
draftOrder = await draftOrderService.retrieve(draftOrder.id, {
relations: defaultRelations,
select: defaultFields,
})
res.status(200).json({ draft_order: draftOrder })
} catch (err) {
throw err
}
res.status(200).json({ draft_order: draftOrder })
}
enum Status {
open = "open",
completed = "completed",
}
export class AdminPostDraftOrdersReq {
@IsEnum(Status)
@IsOptional()
status?: string
@IsEmail()
email: string
@IsOptional()
@Type(() => AddressPayload)
billing_address?: AddressPayload
@IsOptional()
@Type(() => AddressPayload)
shipping_address?: AddressPayload
@IsArray()
@Type(() => Item)
@IsNotEmpty()
@ValidateNested({ each: true })
items: Item[]
@IsString()
region_id: string
@IsArray()
@IsOptional()
@Type(() => Discount)
@ValidateNested({ each: true })
discounts?: Discount[]
@IsString()
@IsOptional()
customer_id?: string
@IsBoolean()
@IsOptional()
no_notification_order?: boolean
@IsArray()
@Type(() => ShippingMethod)
@IsNotEmpty()
@ValidateNested({ each: true })
shipping_methods: ShippingMethod[]
@IsObject()
@IsOptional()
metadata?: object = {}
}
class ShippingMethod {
@IsString()
option_id: string
@IsObject()
@IsOptional()
data?: object = {}
@IsNumber()
@IsOptional()
price?: number
}
class Discount {
@IsString()
code: string
}
class Item {
@IsString()
@IsOptional()
title?: string
@IsNumber()
@IsOptional()
unit_price?: number
@IsString()
@IsOptional()
variant_id?: string
@IsNumber()
quantity: number
@IsObject()
@IsOptional()
metadata?: object = {}
}
@@ -1,111 +0,0 @@
import { MedusaError, Validator } from "medusa-core-utils"
import { defaultCartFields, defaultCartRelations, defaultFields } from "."
/**
* @oas [post] /draft-orders/{id}/line-items
* operationId: "PostDraftOrdersDraftOrderLineItems"
* summary: "Create a Line Item for Draft Order"
* description: "Creates a Line Item for the Draft Order"
* requestBody:
* content:
* application/json:
* schema:
* properties:
* variant_id:
* description: The id of the Product Variant to generate the Line Item from.
* type: string
* unit_price:
* description: The potential custom price of the item.
* type: integer
* title:
* description: The potential custom title of the item.
* type: string
* quantity:
* description: The quantity of the Line Item.
* type: integer
* metadata:
* description: The optional key-value map with additional details about the Line Item.
* type: object
* tags:
* - Draft Order
* responses:
* 200:
* description: OK
* content:
* application/json:
* schema:
* properties:
* draft_order:
* $ref: "#/components/schemas/draft-order"
*/
export default async (req, res) => {
const { id } = req.params
const schema = Validator.object().keys({
title: Validator.string().optional(),
unit_price: Validator.number().optional(),
variant_id: Validator.string().optional(),
quantity: Validator.number().required(),
metadata: Validator.object().optional(),
})
const { value, error } = schema.validate(req.body)
if (error) {
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
}
try {
const draftOrderService = req.scope.resolve("draftOrderService")
const cartService = req.scope.resolve("cartService")
const lineItemService = req.scope.resolve("lineItemService")
const entityManager = req.scope.resolve("manager")
await entityManager.transaction(async manager => {
const draftOrder = await draftOrderService
.withTransaction(manager)
.retrieve(id, { select: defaultFields, relations: ["cart"] })
if (draftOrder.status === "completed") {
throw new MedusaError(
MedusaError.Types.NOT_ALLOWED,
"You are only allowed to update open draft orders"
)
}
if (value.variant_id) {
const line = await lineItemService.generate(
value.variant_id,
draftOrder.cart.region_id,
value.quantity,
{ metadata: value.metadata, unit_price: value.unit_price }
)
await cartService
.withTransaction(manager)
.addLineItem(draftOrder.cart_id, line)
} else {
// custom line items can be added to a draft order
await lineItemService.withTransaction(manager).create({
cart_id: draftOrder.cart_id,
has_shipping: true,
title: value.title || "Custom item",
allow_discounts: false,
unit_price: value.unit_price || 0,
quantity: value.quantity,
})
}
draftOrder.cart = await cartService
.withTransaction(manager)
.retrieve(draftOrder.cart_id, {
relations: defaultCartRelations,
select: defaultCartFields,
})
res.status(200).json({ draft_order: draftOrder })
})
} catch (err) {
throw err
}
}
@@ -0,0 +1,138 @@
import { IsInt, IsObject, IsOptional, IsString } from "class-validator"
import { MedusaError } from "medusa-core-utils"
import { EntityManager } from "typeorm"
import {
defaultAdminDraftOrdersCartFields,
defaultAdminDraftOrdersCartRelations,
defaultAdminDraftOrdersFields,
} from "."
import {
CartService,
DraftOrderService,
LineItemService,
} from "../../../../services"
import { validator } from "../../../../utils/validator"
/**
* @oas [post] /draft-orders/{id}/line-items
* operationId: "PostDraftOrdersDraftOrderLineItems"
* summary: "Create a Line Item for Draft Order"
* description: "Creates a Line Item for the Draft Order"
* x-authenticated: true
* requestBody:
* content:
* application/json:
* required:
* - quantity
* schema:
* properties:
* variant_id:
* description: The id of the Product Variant to generate the Line Item from.
* type: string
* unit_price:
* description: The potential custom price of the item.
* type: integer
* title:
* description: The potential custom title of the item.
* type: string
* quantity:
* description: The quantity of the Line Item.
* type: integer
* metadata:
* description: The optional key-value map with additional details about the Line Item.
* type: object
* tags:
* - Draft Order
* responses:
* 200:
* description: OK
* content:
* application/json:
* schema:
* properties:
* draft_order:
* $ref: "#/components/schemas/draft-order"
*/
export default async (req, res) => {
const { id } = req.params
const validated = await validator(
AdminPostDraftOrdersDraftOrderLineItemsReq,
req.body
)
const draftOrderService: DraftOrderService =
req.scope.resolve("draftOrderService")
const cartService: CartService = req.scope.resolve("cartService")
const lineItemService: LineItemService = req.scope.resolve("lineItemService")
const entityManager: EntityManager = req.scope.resolve("manager")
await entityManager.transaction(async (manager) => {
const draftOrder = await draftOrderService
.withTransaction(manager)
.retrieve(id, {
select: defaultAdminDraftOrdersFields,
relations: ["cart"],
})
if (draftOrder.status === "completed") {
throw new MedusaError(
MedusaError.Types.NOT_ALLOWED,
"You are only allowed to update open draft orders"
)
}
if (validated.variant_id) {
const line = await lineItemService.generate(
validated.variant_id,
draftOrder.cart.region_id,
validated.quantity,
{ metadata: validated.metadata, unit_price: validated.unit_price }
)
await cartService
.withTransaction(manager)
.addLineItem(draftOrder.cart_id, line)
} else {
// custom line items can be added to a draft order
await lineItemService.withTransaction(manager).create({
cart_id: draftOrder.cart_id,
has_shipping: true,
title: validated.title,
allow_discounts: false,
unit_price: validated.unit_price || 0,
quantity: validated.quantity,
})
}
draftOrder.cart = await cartService
.withTransaction(manager)
.retrieve(draftOrder.cart_id, {
relations: defaultAdminDraftOrdersCartRelations,
select: defaultAdminDraftOrdersCartFields,
})
res.status(200).json({ draft_order: draftOrder })
})
}
export class AdminPostDraftOrdersDraftOrderLineItemsReq {
@IsString()
@IsOptional()
title?: string = "Custom item"
@IsInt()
@IsOptional()
unit_price?: number
@IsString()
@IsOptional()
variant_id?: string
@IsInt()
quantity: number
@IsObject()
@IsOptional()
metadata?: object = {}
}
@@ -1,8 +1,10 @@
import { DraftOrderService } from "../../../../services"
/**
* @oas [delete] /draft-orders/{id}
* operationId: DeleteDraftOrdersDraftOrder
* summary: Delete a Draft Order
* description: "Deletes a Draft Order"
* x-authenticated: true
* parameters:
* - (path) id=* {string} The id of the Draft Order.
* tags:
@@ -23,19 +25,16 @@
* deleted:
* type: boolean
*/
export default async (req, res) => {
const { id } = req.params
try {
const draftOrderService = req.scope.resolve("draftOrderService")
await draftOrderService.delete(id)
res.json({
id,
object: "draft-order",
deleted: true,
})
} catch (err) {
throw err
}
const draftOrderService: DraftOrderService =
req.scope.resolve("draftOrderService")
await draftOrderService.delete(id)
res.json({
id,
object: "draft-order",
deleted: true,
})
}
@@ -1,61 +0,0 @@
import { MedusaError, Validator } from "medusa-core-utils"
import { defaultCartFields, defaultCartRelations, defaultFields } from "."
/**
* @oas [delete] /draft-orders/{id}/line-items/{line_id}
* operationId: DeleteDraftOrdersDraftOrderLineItemsItem
* summary: Delete a Line Item
* description: "Removes a Line Item from a Draft Order."
* parameters:
* - (path) id=* {string} The id of the Draft Order.
* - (path) line_id=* {string} The id of the Draft Order.
* tags:
* - Draft Order
* responses:
* 200:
* description: OK
* content:
* application/json:
* schema:
* properties:
* draft_order:
* $ref: "#/components/schemas/draft-order"
*/
export default async (req, res) => {
const { id, line_id } = req.params
try {
const draftOrderService = req.scope.resolve("draftOrderService")
const cartService = req.scope.resolve("cartService")
const entityManager = req.scope.resolve("manager")
await entityManager.transaction(async manager => {
const draftOrder = await draftOrderService
.withTransaction(manager)
.retrieve(id, { select: defaultFields })
if (draftOrder.status === "completed") {
throw new MedusaError(
MedusaError.Types.NOT_ALLOWED,
"You are only allowed to update open draft orders"
)
}
await cartService
.withTransaction(manager)
.removeLineItem(draftOrder.cart_id, line_id)
draftOrder.cart = await cartService
.withTransaction(manager)
.retrieve(draftOrder.cart_id, {
relations: defaultCartRelations,
select: defaultCartFields,
})
res.status(200).json({ draft_order: draftOrder })
})
} catch (err) {
throw err
}
}
@@ -0,0 +1,65 @@
import { MedusaError } from "medusa-core-utils"
import { EntityManager } from "typeorm"
import {
defaultAdminDraftOrdersCartFields,
defaultAdminDraftOrdersCartRelations,
defaultAdminDraftOrdersFields,
} from "."
import { DraftOrder } from "../../../.."
import { CartService, DraftOrderService } from "../../../../services"
/**
* @oas [delete] /draft-orders/{id}/line-items/{line_id}
* operationId: DeleteDraftOrdersDraftOrderLineItemsItem
* summary: Delete a Line Item
* description: "Removes a Line Item from a Draft Order."
* x-authenticated: true
* parameters:
* - (path) id=* {string} The id of the Draft Order.
* - (path) line_id=* {string} The id of the Draft Order.
* tags:
* - Draft Order
* responses:
* 200:
* description: OK
* content:
* application/json:
* schema:
* properties:
* draft_order:
* $ref: "#/components/schemas/draft-order"
*/
export default async (req, res) => {
const { id, line_id } = req.params
const draftOrderService: DraftOrderService =
req.scope.resolve("draftOrderService")
const cartService: CartService = req.scope.resolve("cartService")
const entityManager: EntityManager = req.scope.resolve("manager")
await entityManager.transaction(async (manager) => {
const draftOrder: DraftOrder = await draftOrderService
.withTransaction(manager)
.retrieve(id, { select: defaultAdminDraftOrdersFields })
if (draftOrder.status === "completed") {
throw new MedusaError(
MedusaError.Types.NOT_ALLOWED,
"You are only allowed to update open draft orders"
)
}
await cartService
.withTransaction(manager)
.removeLineItem(draftOrder.cart_id, line_id)
draftOrder.cart = await cartService
.withTransaction(manager)
.retrieve(draftOrder.cart_id, {
relations: defaultAdminDraftOrdersCartRelations,
select: defaultAdminDraftOrdersCartFields,
})
res.status(200).json({ draft_order: draftOrder })
})
}
@@ -1,49 +0,0 @@
import {
defaultRelations,
defaultFields,
defaultCartRelations,
defaultCartFields,
} from "."
/**
* @oas [get] /draft-orders/{id}
* operationId: "GetDraftOrdersDraftOrder"
* summary: "Retrieve a Draft Order"
* description: "Retrieves a Draft Order."
* parameters:
* - (path) id=* {string} The id of the Draft Order.
* tags:
* - Draft Order
* responses:
* 200:
* description: OK
* content:
* application/json:
* schema:
* properties:
* draft_order:
* $ref: "#/components/schemas/draft-order"
*/
export default async (req, res) => {
const { id } = req.params
try {
const draftOrderService = req.scope.resolve("draftOrderService")
const cartService = req.scope.resolve("cartService")
const draftOrder = await draftOrderService.retrieve(id, {
select: defaultFields,
relations: defaultRelations,
})
draftOrder.cart = await cartService.retrieve(draftOrder.cart_id, {
relations: defaultCartRelations,
select: defaultCartFields,
})
res.json({ draft_order: draftOrder })
} catch (error) {
throw error
}
}
@@ -0,0 +1,49 @@
import {
defaultAdminDraftOrdersRelations,
defaultAdminDraftOrdersFields,
defaultAdminDraftOrdersCartRelations,
defaultAdminDraftOrdersCartFields,
} from "."
import { DraftOrder } from "../../../.."
import { CartService, DraftOrderService } from "../../../../services"
/**
* @oas [get] /draft-orders/{id}
* operationId: "GetDraftOrdersDraftOrder"
* summary: "Retrieve a Draft Order"
* description: "Retrieves a Draft Order."
* x-authenticated: true
* parameters:
* - (path) id=* {string} The id of the Draft Order.
* tags:
* - Draft Order
* responses:
* 200:
* description: OK
* content:
* application/json:
* schema:
* properties:
* draft_order:
* $ref: "#/components/schemas/draft-order"
*/
export default async (req, res) => {
const { id } = req.params
const draftOrderService: DraftOrderService =
req.scope.resolve("draftOrderService")
const cartService: CartService = req.scope.resolve("cartService")
const draftOrder: DraftOrder = await draftOrderService.retrieve(id, {
select: defaultAdminDraftOrdersFields,
relations: defaultAdminDraftOrdersRelations,
})
draftOrder.cart = await cartService.retrieve(draftOrder.cart_id, {
relations: defaultAdminDraftOrdersCartRelations,
select: defaultAdminDraftOrdersCartFields,
})
res.json({ draft_order: draftOrder })
}
@@ -1,9 +1,11 @@
import { Router } from "express"
import { DraftOrder, Order } from "../../../.."
import middlewares from "../../../middlewares"
import { DeleteResponse, PaginatedResponse } from "../../../../types/common"
const route = Router()
export default app => {
export default (app) => {
app.use("/draft-orders", route)
route.get("/", middlewares.wrap(require("./list-draft-orders").default))
@@ -44,9 +46,9 @@ export default app => {
return app
}
export const defaultRelations = ["order", "cart"]
export const defaultAdminDraftOrdersRelations = ["order", "cart"]
export const defaultCartRelations = [
export const defaultAdminDraftOrdersCartRelations = [
"region",
"items",
"payment",
@@ -60,7 +62,7 @@ export const defaultCartRelations = [
"discounts.rule",
]
export const defaultCartFields = [
export const defaultAdminDraftOrdersCartFields = [
"subtotal",
"tax_total",
"shipping_total",
@@ -69,7 +71,7 @@ export const defaultCartFields = [
"total",
]
export const defaultFields = [
export const defaultAdminDraftOrdersFields = [
"id",
"status",
"display_id",
@@ -82,7 +84,7 @@ export const defaultFields = [
"no_notification_order",
]
export const allowedFields = [
export const allowedAdminDraftOrdersFields = [
"id",
"status",
"display_id",
@@ -94,4 +96,28 @@ export const allowedFields = [
"no_notification_order",
]
export const allowedRelations = ["cart"]
export const allowedAdminDraftOrdersRelations = ["cart"]
export type AdminPostDraftOrdersDraftOrderRegisterPaymentRes = {
order: Order
}
export type AdminDraftOrdersRes = {
draft_order: DraftOrder
}
export type AdminDraftOrdersDeleteRes = DeleteResponse
export type AdminDraftOrdersListRes = PaginatedResponse & {
draft_orders: DraftOrder[]
}
export * from "./create-draft-order"
export * from "./create-line-item"
export * from "./delete-draft-order"
export * from "./delete-line-item"
export * from "./get-draft-order"
export * from "./list-draft-orders"
export * from "./register-payment"
export * from "./update-draft-order"
export * from "./update-line-item"
@@ -1,52 +0,0 @@
import _ from "lodash"
import { defaultFields, defaultRelations } from "./"
/**
* @oas [get] /draft-orders
* operationId: "GetDraftOrders"
* summary: "List Draft Orders"
* description: "Retrieves an list of Draft Orders"
* tags:
* - Draft Order
* responses:
* 200:
* description: OK
* content:
* application/json:
* schema:
* properties:
* draft_order:
* $ref: "#/components/schemas/draft-order"
*/
export default async (req, res) => {
try {
const draftOrderService = req.scope.resolve("draftOrderService")
const limit = parseInt(req.query.limit) || 50
const offset = parseInt(req.query.offset) || 0
let selector = {}
if ("q" in req.query) {
selector.q = req.query.q
}
const listConfig = {
select: defaultFields,
relations: defaultRelations,
skip: offset,
take: limit,
order: { created_at: "DESC" },
}
const [draftOrders, count] = await draftOrderService.listAndCount(
selector,
listConfig
)
res.json({ draft_orders: draftOrders, count, offset, limit })
} catch (error) {
throw error
}
}
@@ -0,0 +1,76 @@
import {
defaultAdminDraftOrdersFields,
defaultAdminDraftOrdersRelations,
} from "."
import { DraftOrderService } from "../../../../services"
import { IsNumber, IsOptional, IsString } from "class-validator"
import { validator } from "../../../../utils/validator"
import { Type } from "class-transformer"
import { DraftOrderListSelector } from "../../../../types/draft-orders"
/**
* @oas [get] /draft-orders
* operationId: "GetDraftOrders"
* summary: "List Draft Orders"
* description: "Retrieves an list of Draft Orders"
* x-authenticated: true
* tags:
* - Draft Order
* responses:
* 200:
* description: OK
* content:
* application/json:
* schema:
* properties:
* draft_order:
* $ref: "#/components/schemas/draft-order"
*/
export default async (req, res) => {
const draftOrderService: DraftOrderService =
req.scope.resolve("draftOrderService")
const validated = await validator(AdminGetDraftOrdersParams, req.query)
const selector: DraftOrderListSelector = {}
if (validated.q) {
selector.q = validated.q
}
const listConfig = {
select: defaultAdminDraftOrdersFields,
relations: defaultAdminDraftOrdersRelations,
skip: validated.offset,
take: validated.limit,
order: { created_at: "DESC" },
}
const [draftOrders, count] = await draftOrderService.listAndCount(
selector,
listConfig
)
res.json({
draft_orders: draftOrders,
count,
offset: validated.offset,
limit: validated.limit,
})
}
export class AdminGetDraftOrdersParams {
@IsString()
@IsOptional()
q?: string
@IsNumber()
@IsOptional()
@Type(() => Number)
limit?: number = 50
@IsNumber()
@IsOptional()
@Type(() => Number)
offset?: number = 0
}
@@ -1,84 +0,0 @@
import {
defaultFields as defaultOrderFields,
defaultRelations as defaultOrderRelations,
} from "../orders/index"
/**
* @oas [post] /draft-orders/{id}/register-payment
* summary: "Registers a payment for a Draft Order"
* operationId: "PostDraftOrdersDraftOrderRegisterPayment"
* description: "Registers a payment for a Draft Order."
* parameters:
* - (path) id=* {String} The Draft Order id.
* tags:
* - Draft Order
* responses:
* 200:
* description: OK
* content:
* application/json:
* schema:
* properties:
* draft_order:
* $ref: "#/components/schemas/draft-order"
*/
export default async (req, res) => {
const { id } = req.params
try {
const draftOrderService = req.scope.resolve("draftOrderService")
const paymentProviderService = req.scope.resolve("paymentProviderService")
const orderService = req.scope.resolve("orderService")
const cartService = req.scope.resolve("cartService")
const entityManager = req.scope.resolve("manager")
let result
await entityManager.transaction(async manager => {
const draftOrder = await draftOrderService
.withTransaction(manager)
.retrieve(id)
const cart = await cartService
.withTransaction(manager)
.retrieve(draftOrder.cart_id, {
select: ["total"],
relations: [
"discounts",
"discounts.rule",
"discounts.rule.valid_for",
"shipping_methods",
"region",
"items",
],
})
await paymentProviderService
.withTransaction(manager)
.createSession("system", cart)
await cartService
.withTransaction(manager)
.setPaymentSession(cart.id, "system")
await cartService.withTransaction(manager).authorizePayment(cart.id)
result = await orderService
.withTransaction(manager)
.createFromCart(cart.id)
await draftOrderService
.withTransaction(manager)
.registerCartCompletion(draftOrder.id, result.id)
})
const order = await orderService.retrieve(result.id, {
relations: defaultOrderRelations,
select: defaultOrderFields,
})
res.status(200).json({ order })
} catch (err) {
throw err
}
}
@@ -0,0 +1,89 @@
import { EntityManager } from "typeorm"
import {
CartService,
DraftOrderService,
OrderService,
PaymentProviderService,
} from "../../../../services"
import {
defaultAdminOrdersFields as defaultOrderFields,
defaultAdminOrdersRelations as defaultOrderRelations,
} from "../orders/index"
/**
* @oas [post] /draft-orders/{id}/register-payment
* summary: "Registers a payment for a Draft Order"
* operationId: "PostDraftOrdersDraftOrderRegisterPayment"
* description: "Registers a payment for a Draft Order."
* x-authenticated: true
* parameters:
* - (path) id=* {String} The Draft Order id.
* tags:
* - Draft Order
* responses:
* 200:
* description: OK
* content:
* application/json:
* schema:
* properties:
* draft_order:
* $ref: "#/components/schemas/draft-order"
*/
export default async (req, res) => {
const { id } = req.params
const draftOrderService: DraftOrderService =
req.scope.resolve("draftOrderService")
const paymentProviderService: PaymentProviderService = req.scope.resolve(
"paymentProviderService"
)
const orderService: OrderService = req.scope.resolve("orderService")
const cartService: CartService = req.scope.resolve("cartService")
const entityManager: EntityManager = req.scope.resolve("manager")
let result
await entityManager.transaction(async (manager) => {
const draftOrder = await draftOrderService
.withTransaction(manager)
.retrieve(id)
const cart = await cartService
.withTransaction(manager)
.retrieve(draftOrder.cart_id, {
select: ["total"],
relations: [
"discounts",
"discounts.rule",
"discounts.rule.valid_for",
"shipping_methods",
"region",
"items",
],
})
await paymentProviderService
.withTransaction(manager)
.createSession("system", cart)
await cartService
.withTransaction(manager)
.setPaymentSession(cart.id, "system")
await cartService.withTransaction(manager).authorizePayment(cart.id)
result = await orderService.withTransaction(manager).createFromCart(cart.id)
await draftOrderService
.withTransaction(manager)
.registerCartCompletion(draftOrder.id, result.id)
})
const order = await orderService.retrieve(result.id, {
relations: defaultOrderRelations,
select: defaultOrderFields,
})
res.status(200).json({ order })
}
@@ -1,113 +0,0 @@
import { MedusaError, Validator } from "medusa-core-utils"
import { defaultCartFields, defaultCartRelations, defaultFields } from "."
/**
* @oas [post] /admin/draft-orders/{id}
* operationId: PostDraftOrdersDraftOrder
* summary: Update a Draft Order"
* description: "Updates a Draft Order."
* parameters:
* - (path) id=* {string} The id of the Draft Order.
* requestBody:
* content:
* application/json:
* schema:
* properties:
* region_id:
* type: string
* description: The id of the Region to create the Draft Order in.
* email:
* type: string
* description: "An email to be used on the Draft Order."
* billing_address:
* description: "The Address to be used for billing purposes."
* anyOf:
* - $ref: "#/components/schemas/address"
* shipping_address:
* description: "The Address to be used for shipping."
* anyOf:
* - $ref: "#/components/schemas/address"
* discounts:
* description: "An array of Discount codes to add to the Draft Order."
* type: array
* items:
* properties:
* code:
* description: "The code that a Discount is identifed by."
* type: string
* no_notification_order:
* description: "An optional flag passed to the resulting order to determine use of notifications."
* type: boolean
* customer_id:
* description: "The id of the Customer to associate the Draft Order with."
* type: string
* tags:
* - Draft Order
* responses:
* 200:
* description: OK
* content:
* application/json:
* schema:
* properties:
* draft_order:
* $ref: "#/components/schemas/draft-order"
*/
export default async (req, res) => {
const { id } = req.params
const schema = Validator.object().keys({
region_id: Validator.string().optional(),
country_code: Validator.string().optional(),
email: Validator.string()
.email()
.optional(),
billing_address: Validator.object().optional(),
shipping_address: Validator.object().optional(),
discounts: Validator.array()
.items({
code: Validator.string(),
})
.optional(),
customer_id: Validator.string().optional(),
no_notification_order: Validator.boolean().optional(),
})
const { value, error } = schema.validate(req.body)
if (error) {
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
}
try {
const draftOrderService = req.scope.resolve("draftOrderService")
const cartService = req.scope.resolve("cartService")
const draftOrder = await draftOrderService.retrieve(id)
if (draftOrder.status === "completed") {
throw new MedusaError(
MedusaError.Types.NOT_ALLOWED,
"You are only allowed to update open draft orders"
)
}
if ("no_notification_order" in value) {
await draftOrderService.update(draftOrder.id, {
no_notification_order: value.no_notification_order,
})
delete value.no_notification_order
}
await cartService.update(draftOrder.cart_id, value)
draftOrder.cart = await cartService.retrieve(draftOrder.cart_id, {
relations: defaultCartRelations,
select: defaultCartFields,
})
res.status(200).json({ draft_order: draftOrder })
} catch (err) {
throw err
}
}
@@ -0,0 +1,146 @@
import { MedusaError } from "medusa-core-utils"
import {
defaultAdminDraftOrdersCartFields,
defaultAdminDraftOrdersCartRelations,
} from "."
import {
IsArray,
IsBoolean,
IsEmail,
IsOptional,
IsString,
ValidateNested,
} from "class-validator"
import { CartService, DraftOrderService } from "../../../../services"
import { Type } from "class-transformer"
import { AddressPayload } from "../../../../types/common"
import { validator } from "../../../../utils/validator"
/**
* @oas [post] /admin/draft-orders/{id}
* operationId: PostDraftOrdersDraftOrder
* summary: Update a Draft Order"
* description: "Updates a Draft Order."
* x-authenticated: true
* parameters:
* - (path) id=* {string} The id of the Draft Order.
* requestBody:
* content:
* application/json:
* schema:
* properties:
* region_id:
* type: string
* description: The id of the Region to create the Draft Order in.
* email:
* type: string
* description: "An email to be used on the Draft Order."
* billing_address:
* description: "The Address to be used for billing purposes."
* anyOf:
* - $ref: "#/components/schemas/address"
* shipping_address:
* description: "The Address to be used for shipping."
* anyOf:
* - $ref: "#/components/schemas/address"
* discounts:
* description: "An array of Discount codes to add to the Draft Order."
* type: array
* items:
* properties:
* code:
* description: "The code that a Discount is identifed by."
* type: string
* no_notification_order:
* description: "An optional flag passed to the resulting order to determine use of notifications."
* type: boolean
* customer_id:
* description: "The id of the Customer to associate the Draft Order with."
* type: string
* tags:
* - Draft Order
* responses:
* 200:
* description: OK
* content:
* application/json:
* schema:
* properties:
* draft_order:
* $ref: "#/components/schemas/draft-order"
*/
export default async (req, res) => {
const { id } = req.params
const validated = await validator(AdminPostDraftOrdersDraftOrderReq, req.body)
const draftOrderService: DraftOrderService =
req.scope.resolve("draftOrderService")
const cartService: CartService = req.scope.resolve("cartService")
const draftOrder = await draftOrderService.retrieve(id)
if (draftOrder.status === "completed") {
throw new MedusaError(
MedusaError.Types.NOT_ALLOWED,
"You are only allowed to update open draft orders"
)
}
if (validated.no_notification_order !== undefined) {
await draftOrderService.update(draftOrder.id, {
no_notification_order: validated.no_notification_order,
})
delete validated.no_notification_order
}
await cartService.update(draftOrder.cart_id, validated)
draftOrder.cart = await cartService.retrieve(draftOrder.cart_id, {
relations: defaultAdminDraftOrdersCartRelations,
select: defaultAdminDraftOrdersCartFields,
})
res.status(200).json({ draft_order: draftOrder })
}
export class AdminPostDraftOrdersDraftOrderReq {
@IsString()
@IsOptional()
region_id?: string
@IsString()
@IsOptional()
country_code?: string
@IsEmail()
@IsOptional()
email?: string
@IsOptional()
@Type(() => AddressPayload)
billing_address?: AddressPayload
@IsOptional()
@Type(() => AddressPayload)
shipping_address?: AddressPayload
@IsArray()
@IsOptional()
@Type(() => Discount)
@ValidateNested({ each: true })
discounts?: Discount[]
@IsString()
@IsOptional()
customer_id?: string
@IsBoolean()
@IsOptional()
no_notification_order?: boolean
}
class Discount {
@IsString()
code: string
}
@@ -1,114 +0,0 @@
import { MedusaError, Validator } from "medusa-core-utils"
import { defaultCartFields, defaultCartRelations, defaultFields } from "."
/**
* @oas [post] /draft-orders/{id}/line-items/{line_id}
* operationId: "PostDraftOrdersDraftOrderLineItemsItem"
* summary: "Update a Line Item for a Draft Order"
* description: "Updates a Line Item for a Draft Order"
* requestBody:
* content:
* application/json:
* schema:
* properties:
* unit_price:
* description: The potential custom price of the item.
* type: integer
* title:
* description: The potential custom title of the item.
* type: string
* quantity:
* description: The quantity of the Line Item.
* type: integer
* metadata:
* description: The optional key-value map with additional details about the Line Item.
* type: object
* tags:
* - Draft Order
* responses:
* 200:
* description: OK
* content:
* application/json:
* schema:
* properties:
* draft_order:
* $ref: "#/components/schemas/draft-order"
*/
export default async (req, res) => {
const { id, line_id } = req.params
const schema = Validator.object().keys({
title: Validator.string().optional(),
unit_price: Validator.number().optional(),
quantity: Validator.number().optional(),
metadata: Validator.object().optional(),
})
const { value, error } = schema.validate(req.body)
if (error) {
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
}
try {
const draftOrderService = req.scope.resolve("draftOrderService")
const cartService = req.scope.resolve("cartService")
const entityManager = req.scope.resolve("manager")
await entityManager.transaction(async manager => {
const draftOrder = await draftOrderService
.withTransaction(manager)
.retrieve(id, {
select: defaultFields,
relations: ["cart", "cart.items"],
})
if (draftOrder.status === "completed") {
throw new MedusaError(
MedusaError.Types.NOT_ALLOWED,
"You are only allowed to update open draft orders"
)
}
if (value.quantity === 0) {
await cartService
.withTransaction(manager)
.removeLineItem(draftOrder.cart.id, line_id)
} else {
const existing = draftOrder.cart.items.find(i => i.id === line_id)
if (!existing) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
"Could not find the line item"
)
}
const lineItemUpdate = {
...value,
region_id: draftOrder.cart.region_id,
}
if (existing.variant_id) {
lineItemUpdate.variant_id = existing.variant_id
}
await cartService
.withTransaction(manager)
.updateLineItem(draftOrder.cart_id, line_id, lineItemUpdate)
}
draftOrder.cart = await cartService
.withTransaction(manager)
.retrieve(draftOrder.cart_id, {
relations: defaultCartRelations,
select: defaultCartFields,
})
res.status(200).json({ draft_order: draftOrder })
})
} catch (err) {
throw err
}
}
@@ -0,0 +1,140 @@
import { IsInt, IsObject, IsOptional, IsString } from "class-validator"
import { MedusaError } from "medusa-core-utils"
import { EntityManager } from "typeorm"
import {
defaultAdminDraftOrdersCartFields,
defaultAdminDraftOrdersCartRelations,
defaultAdminDraftOrdersFields,
} from "."
import { DraftOrder } from "../../../.."
import { CartService, DraftOrderService } from "../../../../services"
import { validator } from "../../../../utils/validator"
/**
* @oas [post] /draft-orders/{id}/line-items/{line_id}
* operationId: "PostDraftOrdersDraftOrderLineItemsItem"
* summary: "Update a Line Item for a Draft Order"
* description: "Updates a Line Item for a Draft Order"
* x-authenticated: true
* requestBody:
* content:
* application/json:
* schema:
* properties:
* unit_price:
* description: The potential custom price of the item.
* type: integer
* title:
* description: The potential custom title of the item.
* type: string
* quantity:
* description: The quantity of the Line Item.
* type: integer
* metadata:
* description: The optional key-value map with additional details about the Line Item.
* type: object
* tags:
* - Draft Order
* responses:
* 200:
* description: OK
* content:
* application/json:
* schema:
* properties:
* draft_order:
* $ref: "#/components/schemas/draft-order"
*/
export default async (req, res) => {
const { id, line_id } = req.params
const validated = await validator(
AdminPostDraftOrdersDraftOrderLineItemsItemReq,
req.body
)
const draftOrderService: DraftOrderService =
req.scope.resolve("draftOrderService")
const cartService: CartService = req.scope.resolve("cartService")
const entityManager: EntityManager = req.scope.resolve("manager")
await entityManager.transaction(async (manager) => {
const draftOrder: DraftOrder = await draftOrderService
.withTransaction(manager)
.retrieve(id, {
select: defaultAdminDraftOrdersFields,
relations: ["cart", "cart.items"],
})
if (draftOrder.status === "completed") {
throw new MedusaError(
MedusaError.Types.NOT_ALLOWED,
"You are only allowed to update open draft orders"
)
}
if (validated.quantity === 0) {
await cartService
.withTransaction(manager)
.removeLineItem(draftOrder.cart.id, line_id)
} else {
const existing = draftOrder.cart.items.find((i) => i.id === line_id)
if (!existing) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
"Could not find the line item"
)
}
const lineItemUpdate: LineItemUpdate = {
...validated,
region_id: draftOrder.cart.region_id,
}
if (existing.variant_id) {
lineItemUpdate.variant_id = existing.variant_id
}
await cartService
.withTransaction(manager)
.updateLineItem(draftOrder.cart_id, line_id, lineItemUpdate)
}
draftOrder.cart = await cartService
.withTransaction(manager)
.retrieve(draftOrder.cart_id, {
relations: defaultAdminDraftOrdersCartRelations,
select: defaultAdminDraftOrdersCartFields,
})
res.status(200).json({ draft_order: draftOrder })
})
}
class LineItemUpdate {
title?: string
unit_price?: number
quantity?: number
metadata?: object = {}
region_id?: string
variant_id?: string
}
export class AdminPostDraftOrdersDraftOrderLineItemsItemReq {
@IsString()
@IsOptional()
title?: string
@IsInt()
@IsOptional()
unit_price?: number
@IsInt()
@IsOptional()
quantity?: number
@IsObject()
@IsOptional()
metadata?: object = {}
}
@@ -1,11 +1,15 @@
import { MedusaError, Validator } from "medusa-core-utils"
import { defaultFields, defaultRelations } from "./"
import { Type } from "class-transformer"
import { IsBoolean, IsDate, IsInt, IsOptional, IsString } from "class-validator"
import { defaultAdminGiftCardFields, defaultAdminGiftCardRelations } from "."
import { GiftCardService } from "../../../../services"
import { validator } from "../../../../utils/validator"
/**
* @oas [post] /gift-cards
* operationId: "PostGiftCards"
* summary: "Create a Gift Card"
* description: "Creates a Gift Card that can redeemed by its unique code. The Gift Card is only valid within 1 region."
* x-authenticated: true
* requestBody:
* content:
* application/json:
@@ -42,36 +46,41 @@ import { defaultFields, defaultRelations } from "./"
* $ref: "#/components/schemas/gift_card"
*/
export default async (req, res) => {
const schema = Validator.object().keys({
value: Validator.number()
.integer()
.optional(),
ends_at: Validator.date().optional(),
is_disabled: Validator.boolean().optional(),
region_id: Validator.string().optional(),
metadata: Validator.object().optional(),
const validated = await validator(AdminPostGiftCardsReq, req.body)
const giftCardService: GiftCardService = req.scope.resolve("giftCardService")
const newly = await giftCardService.create({
...validated,
balance: validated.value,
})
const { value, error } = schema.validate(req.body)
if (error) {
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
}
const giftCard = await giftCardService.retrieve(newly.id, {
select: defaultAdminGiftCardFields,
relations: defaultAdminGiftCardRelations,
})
try {
const giftCardService = req.scope.resolve("giftCardService")
res.status(200).json({ gift_card: giftCard })
}
const newly = await giftCardService.create({
...value,
balance: value.value,
})
export class AdminPostGiftCardsReq {
@IsOptional()
@IsInt()
value?: number
const giftCard = await giftCardService.retrieve(newly.id, {
select: defaultFields,
relations: defaultRelations,
})
@IsOptional()
@IsDate()
@Type(() => Date)
ends_at?: Date
res.status(200).json({ gift_card: giftCard })
} catch (err) {
throw err
}
@IsOptional()
@IsBoolean()
is_disabled?: boolean
@IsOptional()
@IsString()
region_id?: string
@IsOptional()
metadata?: object
}
@@ -3,6 +3,7 @@
* operationId: "DeleteGiftCardsGiftCard"
* summary: "Delete a Gift Card"
* description: "Deletes a Gift Card"
* x-authenticated: true
* parameters:
* - (path) id=* {string} The id of the Gift Card to delete.
* tags:
@@ -26,16 +27,12 @@
export default async (req, res) => {
const { id } = req.params
try {
const giftCardService = req.scope.resolve("giftCardService")
await giftCardService.delete(id)
const giftCardService = req.scope.resolve("giftCardService")
await giftCardService.delete(id)
res.json({
id,
object: "gift-card",
deleted: true,
})
} catch (err) {
throw err
}
res.json({
id,
object: "gift-card",
deleted: true,
})
}
@@ -1,10 +1,11 @@
import { defaultFields, defaultRelations } from "./"
import { defaultAdminGiftCardFields, defaultAdminGiftCardRelations } from "./"
/**
* @oas [get] /gift-cards/{id}
* operationId: "GetGiftCardsGiftCard"
* summary: "Retrieve a Gift Card"
* description: "Retrieves a Gift Card."
* x-authenticated: true
* parameters:
* - (path) id=* {string} The id of the Gift Card.
* tags:
@@ -22,15 +23,11 @@ import { defaultFields, defaultRelations } from "./"
export default async (req, res) => {
const { id } = req.params
try {
const giftCardService = req.scope.resolve("giftCardService")
const giftCard = await giftCardService.retrieve(id, {
select: defaultFields,
relations: defaultRelations,
})
const giftCardService = req.scope.resolve("giftCardService")
const giftCard = await giftCardService.retrieve(id, {
select: defaultAdminGiftCardFields,
relations: defaultAdminGiftCardRelations,
})
res.status(200).json({ gift_card: giftCard })
} catch (err) {
throw err
}
res.status(200).json({ gift_card: giftCard })
}
@@ -1,9 +1,12 @@
import { Router } from "express"
import "reflect-metadata"
import { GiftCard } from "../../../.."
import { DeleteResponse, PaginatedResponse } from "../../../../types/common"
import middlewares from "../../../middlewares"
const route = Router()
export default app => {
export default (app) => {
app.use("/gift-cards", route)
route.get("/", middlewares.wrap(require("./list-gift-cards").default))
@@ -19,7 +22,7 @@ export default app => {
return app
}
export const defaultFields = [
export const defaultAdminGiftCardFields = [
"id",
"code",
"value",
@@ -33,12 +36,9 @@ export const defaultFields = [
"metadata",
]
export const defaultRelations = [
"region",
"order",
]
export const defaultAdminGiftCardRelations = ["region", "order"]
export const allowedFields = [
export const allowedAdminGiftCardFields = [
"id",
"code",
"value",
@@ -52,4 +52,18 @@ export const allowedFields = [
"metadata",
]
export const allowedRelations = ["region"]
export const allowedAdminGiftCardRelations = ["region"]
export type AdminGiftCardsRes = {
gift_card: GiftCard
}
export type AdminGiftCardsDeleteRes = DeleteResponse
export type AdminGiftCardsListRes = PaginatedResponse & {
gift_cards: GiftCard[]
}
export * from "./create-gift-card"
export * from "./list-gift-cards"
export * from "./update-gift-card"
@@ -1,48 +0,0 @@
import { MedusaError, Validator } from "medusa-core-utils"
import { defaultFields, defaultRelations } from "./"
/**
* @oas [get] /gift-cards
* operationId: "GetGiftCards"
* summary: "List Gift Cards"
* description: "Retrieves a list of Gift Cards."
* tags:
* - Gift Card
* responses:
* 200:
* description: OK
* content:
* application/json:
* schema:
* properties:
* gift_cards:
* type: array
* items:
* $ref: "#/components/schemas/gift_card"
*/
export default async (req, res) => {
try {
const limit = parseInt(req.query.limit) || 50
const offset = parseInt(req.query.offset) || 0
const selector = {}
if ("q" in req.query) {
selector.q = req.query.q
}
const giftCardService = req.scope.resolve("giftCardService")
const giftCards = await giftCardService.list(selector, {
select: defaultFields,
relations: defaultRelations,
order: { created_at: "DESC" },
limit: limit,
skip: offset,
})
res.status(200).json({ gift_cards: giftCards })
} catch (err) {
throw err
}
}
@@ -0,0 +1,68 @@
import { Type } from "class-transformer"
import { IsInt, IsOptional, IsString } from "class-validator"
import { defaultAdminGiftCardFields, defaultAdminGiftCardRelations } from "."
import { GiftCardService } from "../../../../services"
import { validator } from "../../../../utils/validator"
/**
* @oas [get] /gift-cards
* operationId: "GetGiftCards"
* summary: "List Gift Cards"
* description: "Retrieves a list of Gift Cards."
* x-authenticated: true
* tags:
* - Gift Card
* responses:
* 200:
* description: OK
* content:
* application/json:
* schema:
* properties:
* gift_cards:
* type: array
* items:
* $ref: "#/components/schemas/gift_card"
*/
export default async (req, res) => {
const validated = await validator(AdminGetGiftCardsParams, req.query)
const selector = {}
if (validated.q && typeof validated.q !== "undefined") {
selector["q"] = validated.q
}
const giftCardService: GiftCardService = req.scope.resolve("giftCardService")
const giftCards = await giftCardService.list(selector, {
select: defaultAdminGiftCardFields,
relations: defaultAdminGiftCardRelations,
order: { created_at: "DESC" },
limit: validated.limit,
skip: validated.offset,
})
res.status(200).json({
gift_cards: giftCards,
count: giftCards.length,
offset: validated.offset,
limit: validated.limit,
})
}
export class AdminGetGiftCardsParams {
@IsOptional()
@IsInt()
@Type(() => Number)
limit = 50
@IsOptional()
@IsInt()
@Type(() => Number)
offset = 0
@IsOptional()
@IsString()
q?: string
}
@@ -1,11 +1,15 @@
import { MedusaError, Validator } from "medusa-core-utils"
import { defaultFields, defaultRelations } from "./"
import { Type } from "class-transformer"
import { IsBoolean, IsDate, IsInt, IsOptional, IsString } from "class-validator"
import { defaultAdminGiftCardFields, defaultAdminGiftCardRelations } from "."
import { GiftCardService } from "../../../../services"
import { validator } from "../../../../utils/validator"
/**
* @oas [post] /gift-cards/{id}
* operationId: "PostGiftCardsGiftCard"
* summary: "Create a Gift Card"
* description: "Creates a Gift Card that can redeemed by its unique code. The Gift Card is only valid within 1 region."
* x-authenticated: true
* parameters:
* - (path) id=* {string} The id of the Gift Card.
* requestBody:
@@ -46,33 +50,38 @@ import { defaultFields, defaultRelations } from "./"
export default async (req, res) => {
const { id } = req.params
const schema = Validator.object().keys({
balance: Validator.number()
.precision(0)
.optional(),
ends_at: Validator.date().optional(),
is_disabled: Validator.boolean().optional(),
region_id: Validator.string().optional(),
metadata: Validator.object().optional(),
const validated = await validator(AdminPostGiftCardsGiftCardReq, req.body)
const giftCardService: GiftCardService = req.scope.resolve("giftCardService")
await giftCardService.update(id, validated)
const giftCard = await giftCardService.retrieve(id, {
select: defaultAdminGiftCardFields,
relations: defaultAdminGiftCardRelations,
})
const { value, error } = schema.validate(req.body)
if (error) {
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
}
res.status(200).json({ gift_card: giftCard })
}
try {
const giftCardService = req.scope.resolve("giftCardService")
export class AdminPostGiftCardsGiftCardReq {
@IsOptional()
@IsInt()
balance?: number
await giftCardService.update(id, value)
@IsOptional()
@IsBoolean()
is_disabled?: boolean
const giftCard = await giftCardService.retrieve(id, {
select: defaultFields,
relations: defaultRelations,
})
@IsOptional()
@IsDate()
@Type(() => Date)
ends_at?: Date
res.status(200).json({ gift_card: giftCard })
} catch (err) {
throw err
}
@IsOptional()
@IsString()
region_id?: string
@IsOptional()
metadata?: object
}
@@ -1,10 +1,13 @@
import { MedusaError, Validator } from "medusa-core-utils"
import { IsNotEmpty, IsString } from "class-validator"
import NoteService from "../../../../services/note"
import { validator } from "../../../../utils/validator"
/**
* @oas [post] /notes
* operationId: "PostNotes"
* summary: "Creates a Note"
* description: "Creates a Note which can be associated with any resource as required."
* x-authenticated: true
* requestBody:
* content:
* application/json:
@@ -33,31 +36,32 @@ import { MedusaError, Validator } from "medusa-core-utils"
*
*/
export default async (req, res) => {
const schema = Validator.object().keys({
resource_id: Validator.string(),
resource_type: Validator.string(),
value: Validator.string(),
const validated = await validator(AdminPostNotesReq, req.body)
const userId: string = req.user.id || req.user.userId
const noteService: NoteService = req.scope.resolve("noteService")
const result = await noteService.create({
resource_id: validated.resource_id,
resource_type: validated.resource_type,
value: validated.value,
author_id: userId,
})
const userId = req.user.id || req.user.userId
res.status(200).json({ note: result })
}
const { value, error } = schema.validate(req.body)
if (error) {
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
}
export class AdminPostNotesReq {
@IsString()
@IsNotEmpty()
resource_id: string
try {
const noteService = req.scope.resolve("noteService")
@IsString()
@IsNotEmpty()
resource_type: string
const result = await noteService.create({
resource_id: value.resource_id,
resource_type: value.resource_type,
value: value.value,
author_id: userId,
})
res.status(200).json({ note: result })
} catch (err) {
throw err
}
@IsString()
@IsNotEmpty()
value: string
}
@@ -1,8 +1,11 @@
import NoteService from "../../../../services/note"
/**
* @oas [delete] /notes/{id}
* operationId: "DeleteNotesNote"
* summary: "Deletes a Note"
* description: "Deletes a Note."
* x-authenticated: true
* parameters:
* - (path) id=* {string} The id of the Note to delete.
* tags:
@@ -24,12 +27,8 @@
export default async (req, res) => {
const { id } = req.params
try {
const noteService = req.scope.resolve("noteService")
await noteService.delete(id)
const noteService: NoteService = req.scope.resolve("noteService")
await noteService.delete(id)
res.status(200).json({ id, deleted: true })
} catch (err) {
throw err
}
res.status(200).json({ id, object: "note", deleted: true })
}
@@ -1,8 +1,11 @@
import NoteService from "../../../../services/note"
/**
* @oas [get] /notes/{id}
* operationId: "GetNoteNote"
* operationId: "GetNotesNote"
* summary: "Get Note"
* description: "Retrieves a single note using its id"
* x-authenticated: true
* parameters:
* - (path) id=* {string} The id of the note to retrieve.
* tags:
@@ -20,12 +23,8 @@
export default async (req, res) => {
const { id } = req.params
try {
const noteService = req.scope.resolve("noteService")
const note = await noteService.retrieve(id, { relations: ["author"] })
const noteService: NoteService = req.scope.resolve("noteService")
const note = await noteService.retrieve(id, { relations: ["author"] })
res.status(200).json({ note })
} catch (err) {
throw err
}
res.status(200).json({ note })
}
@@ -1,9 +1,12 @@
import { Router } from "express"
import { Note } from "../../../.."
import { DeleteResponse, PaginatedResponse } from "../../../../types/common"
import middlewares from "../../../middlewares"
import "reflect-metadata"
const route = Router()
export default app => {
export default (app) => {
app.use("/notes", route)
route.get("/:id", middlewares.wrap(require("./get-note").default))
@@ -18,3 +21,18 @@ export default app => {
return app
}
export type AdminNotesRes = {
note: Note
}
export type AdminNotesDeleteRes = DeleteResponse
export type AdminNotesListRes = PaginatedResponse & {
notes: Note[]
}
export * from "./create-note"
export * from "./delete-note"
export * from "./get-note"
export * from "./list-notes"
export * from "./update-note"
@@ -1,42 +0,0 @@
/**
* @oas [get] /notes
* operationId: "GetNotes"
* summary: "List Notes"
* description: "Retrieves a list of notes"
* tags:
* - Note
* responses:
* 200:
* description: OK
* content:
* application/json:
* schema:
* properties:
* notes:
* type: array
* items:
* $ref: "#/components/schemas/note"
*/
export default async (req, res) => {
try {
const limit = parseInt(req.query.limit) || 50
const offset = parseInt(req.query.offset) || 0
const selector = {}
if ("resource_id" in req.query) {
selector.resource_id = req.query.resource_id
}
const noteService = req.scope.resolve("noteService")
const notes = await noteService.list(selector, {
take: limit,
skip: offset,
relations: ["author"],
})
res.status(200).json({ notes })
} catch (err) {
throw err
}
}
@@ -0,0 +1,68 @@
import { IsNumber, IsOptional, IsString } from "class-validator"
import NoteService from "../../../../services/note"
import { validator } from "../../../../utils/validator"
import { selector } from "../../../../types/note"
import { Type } from "class-transformer"
/**
* @oas [get] /notes
* operationId: "GetNotes"
* summary: "List Notes"
* x-authenticated: true
* description: "Retrieves a list of notes"
* * parameters:
* - (path) limit= {number} The number of notes to get
* - (path) offset= {number} The offset at which to get notes
* - (path) resource_id= {string} The id which the notes belongs to
* tags:
* - Note
* responses:
* 200:
* description: OK
* content:
* application/json:
* schema:
* properties:
* notes:
* type: array
* items:
* $ref: "#/components/schemas/note"
*/
export default async (req, res) => {
const validated = await validator(AdminGetNotesParams, req.query)
const selector: selector = {}
if (validated.resource_id) {
selector.resource_id = validated.resource_id
}
const noteService: NoteService = req.scope.resolve("noteService")
const notes = await noteService.list(selector, {
take: validated.limit,
skip: validated.offset,
relations: ["author"],
})
res.status(200).json({
notes,
count: notes.length,
offset: validated.offset,
limit: validated.limit,
})
}
export class AdminGetNotesParams {
@IsString()
@IsOptional()
resource_id?: string
@IsNumber()
@IsOptional()
@Type(() => Number)
limit = 50
@IsNumber()
@IsOptional()
@Type(() => Number)
offset = 0
}
@@ -1,9 +1,12 @@
import { MedusaError, Validator } from "medusa-core-utils"
import { IsString } from "class-validator"
import NoteService from "../../../../services/note"
import { validator } from "../../../../utils/validator"
/**
* @oas [post] /notes/{id}
* operationId: "PostNotesNote"
* summary: "Updates a Note"
* x-authenticated: true
* description: "Updates a Note associated with some resource"
* parameters:
* - (path) id=* {string} The id of the Note to update
@@ -11,6 +14,8 @@ import { MedusaError, Validator } from "medusa-core-utils"
* content:
* application/json:
* schema:
* required:
* - value
* properties:
* value:
* type: string
@@ -31,21 +36,15 @@ import { MedusaError, Validator } from "medusa-core-utils"
export default async (req, res) => {
const { id } = req.params
const schema = Validator.object().keys({
value: Validator.string(),
})
const validated = await validator(AdminPostNotesNoteReq, req.body)
const { value, error } = schema.validate(req.body)
if (error) {
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
}
const noteService: NoteService = req.scope.resolve("noteService")
const note = await noteService.update(id, validated.value)
try {
const noteService = req.scope.resolve("noteService")
const result = await noteService.update(id, value.value)
res.status(200).json({ note })
}
res.status(200).json({ note: result })
} catch (err) {
throw err
}
export class AdminPostNotesNoteReq {
@IsString()
value: string
}
@@ -1,9 +1,11 @@
import { Router } from "express"
import { Notification } from "./../../../../"
import { PaginatedResponse } from "./../../../../types/common"
import middlewares from "../../../middlewares"
const route = Router()
export default app => {
export default (app) => {
app.use("/notifications", route)
/**
@@ -22,10 +24,10 @@ export default app => {
return app
}
export const defaultRelations = ["resends"]
export const allowedRelations = ["resends"]
export const defaultAdminNotificationsRelations = ["resends"]
export const allowedAdminNotificationsRelations = ["resends"]
export const defaultFields = [
export const defaultAdminNotificationsFields = [
"id",
"resource_type",
"resource_id",
@@ -36,7 +38,7 @@ export const defaultFields = [
"updated_at",
]
export const allowedFields = [
export const allowedAdminNotificationsFields = [
"id",
"resource_type",
"resource_id",
@@ -46,3 +48,11 @@ export const allowedFields = [
"created_at",
"updated_at",
]
export type AdminNotificationsListRes = {
notifications: Notification[]
}
export type AdminNotificationsRes = PaginatedResponse & {
notification: Notification
}
@@ -1,83 +0,0 @@
import _ from "lodash"
import { defaultRelations, defaultFields } from "./"
/**
* @oas [get] /notifications
* operationId: "GetNotifications"
* summary: "List Notifications"
* description: "Retrieves a list of Notifications."
* tags:
* - Notification
* responses:
* 200:
* description: OK
* content:
* application/json:
* schema:
* properties:
* notifications:
* type: array
* items:
* $ref: "#/components/schemas/notification"
*/
export default async (req, res) => {
try {
const notificationService = req.scope.resolve("notificationService")
const limit = parseInt(req.query.limit) || 50
const offset = parseInt(req.query.offset) || 0
let selector = {}
let includeFields = []
if ("fields" in req.query) {
includeFields = req.query.fields.split(",")
}
let expandFields = []
if ("expand" in req.query) {
expandFields = req.query.expand.split(",")
}
if ("event_name" in req.query) {
const values = req.query.event_name.split(",")
selector.event_name = values.length > 1 ? values : values[0]
}
if ("resource_type" in req.query) {
const values = req.query.resource_type.split(",")
selector.resource_type = values.length > 1 ? values : values[0]
}
if ("resource_id" in req.query) {
const values = req.query.resource_id.split(",")
selector.resource_id = values.length > 1 ? values : values[0]
}
if ("to" in req.query) {
const values = req.query.to.split(",")
selector.to = values.length > 1 ? values : values[0]
}
if (!("include_resends" in req.query)) {
selector.parent_id = null
}
const listConfig = {
select: includeFields.length ? includeFields : defaultFields,
relations: expandFields.length ? expandFields : defaultRelations,
skip: offset,
take: limit,
order: { created_at: "DESC" },
}
const notifications = await notificationService.list(selector, listConfig)
const fields = [...listConfig.select, ...listConfig.relations]
const data = notifications.map(o => _.pick(o, fields))
res.json({ notifications: data, offset, limit })
} catch (error) {
throw error
}
}
@@ -0,0 +1,151 @@
import { Type } from "class-transformer"
import { IsBooleanString, IsInt, IsOptional, IsString } from "class-validator"
import { pick } from "lodash"
import { NotificationService } from "../../../../services"
import { validator } from "../../../../utils/validator"
import {
defaultAdminNotificationsFields,
defaultAdminNotificationsRelations,
} from "./"
/**
* @oas [get] /notifications
* operationId: "GetNotifications"
* summary: "List Notifications"
* description: "Retrieves a list of Notifications."
* x-authenticated: true
* parameters:
* - (query) offset=0 {integer} The number of notifications to skip before starting to collect the notifications set
* - (query) limit=50 {integer} The number of notifications to return
* - (query) fields {string} The fields to include in the result set
* - (query) expand {string} The fields to populate
* - (query) event_name {string}
* - (query) resource_type {string}
* - (query) resource_id {string}
* - (query) to {string}
* - (query) include_resends {boolean} Whether the result set should include resent notifications or not
* tags:
* - Notification
* responses:
* 200:
* description: OK
* content:
* application/json:
* schema:
* properties:
* notifications:
* type: array
* items:
* $ref: "#/components/schemas/notification"
*/
export default async (req, res) => {
const notificationService: NotificationService = req.scope.resolve(
"notificationService"
)
const {
limit,
offset,
fields,
expand,
event_name,
resource_id,
resource_type,
to,
include_resends,
} = await validator(AdminGetNotificationsParams, req.query)
const selector: any = {}
let includeFields: string[] = []
if (fields) {
includeFields = fields.split(",")
}
let expandFields: string[] = []
if (expand) {
expandFields = expand.split(",")
}
if (event_name) {
const values = event_name.split(",")
selector.event_name = values.length > 1 ? values : values[0]
}
if (resource_type) {
const values = resource_type.split(",")
selector.resource_type = values.length > 1 ? values : values[0]
}
if (resource_id) {
const values = resource_id.split(",")
selector.resource_id = values.length > 1 ? values : values[0]
}
if (to) {
const values = to.split(",")
selector.to = values.length > 1 ? values : values[0]
}
if (!include_resends || include_resends === "false") {
selector.parent_id = null
}
const listConfig = {
select: includeFields.length
? includeFields
: defaultAdminNotificationsFields,
relations: expandFields.length
? expandFields
: defaultAdminNotificationsRelations,
skip: offset,
take: limit,
order: { created_at: "DESC" },
}
const notifications = await notificationService.list(selector, listConfig)
const resultFields = [...listConfig.select, ...listConfig.relations]
const data = notifications.map((o) => pick(o, resultFields))
res.json({ notifications: data })
}
export class AdminGetNotificationsParams {
@IsOptional()
@IsInt()
@Type(() => Number)
limit?: number = 50
@IsOptional()
@IsInt()
@Type(() => Number)
offset?: number = 0
@IsOptional()
@IsString()
fields?: string
@IsOptional()
@IsString()
expand?: string
@IsOptional()
@IsString()
event_name?: string
@IsOptional()
@IsString()
resource_type?: string
@IsOptional()
@IsString()
resource_id?: string
@IsOptional()
@IsString()
to?: string
@IsOptional()
@IsBooleanString()
include_resends?: string
}
@@ -1,55 +0,0 @@
import { MedusaError, Validator } from "medusa-core-utils"
import { defaultFields, defaultRelations } from "./"
/**
* @oas [post] /notifications/{id}/resend
* operationId: "PostNotificationsNotificationResend"
* summary: "Resend Notification"
* description: "Resends a previously sent notifications, with the same data but optionally to a different address"
* parameters:
* - (path) id=* {string} The id of the Notification
* tags:
* - Notification
* responses:
* 200:
* description: OK
* content:
* application/json:
* schema:
* properties:
* notification:
* $ref: "#/components/schemas/notification"
*/
export default async (req, res) => {
const { id } = req.params
const schema = Validator.object().keys({
to: Validator.string().optional(),
})
const { value, error } = schema.validate(req.body)
if (error) {
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
}
try {
const notificationService = req.scope.resolve("notificationService")
const config = {}
if (value.to) {
config.to = value.to
}
await notificationService.resend(id, config)
const notification = await notificationService.retrieve(id, {
select: defaultFields,
relations: defaultRelations,
})
res.json({ notification })
} catch (error) {
throw error
}
}
@@ -0,0 +1,70 @@
import { IsString } from "class-validator"
import { IsOptional } from "class-validator"
import {
defaultAdminNotificationsFields,
defaultAdminNotificationsRelations,
} from "."
import { validator } from "../../../../utils/validator"
import { NotificationService } from "../../../../services"
/**
* @oas [post] /notifications/{id}/resend
* operationId: "PostNotificationsNotificationResend"
* summary: "Resend Notification"
* description: "Resends a previously sent notifications, with the same data but optionally to a different address"
* x-authenticated: true
* parameters:
* - (path) id=* {string} The id of the Notification
* requestBody:
* content:
* application/json:
* schema:
* properties:
* to:
* description: "The address or user identifier that the Notification was sent to"
* type: string
* tags:
* - Notification
* responses:
* 200:
* description: OK
* content:
* application/json:
* schema:
* properties:
* notification:
* $ref: "#/components/schemas/notification"
*/
export default async (req, res) => {
const { id } = req.params
const validatedBody = await validator(
AdminPostNotificationsNotificationResendReq,
req.body
)
const notificationService: NotificationService = req.scope.resolve(
"notificationService"
)
const config: any = {}
if (validatedBody.to) {
config.to = validatedBody.to
}
await notificationService.resend(id, config)
const notification = await notificationService.retrieve(id, {
select: defaultAdminNotificationsFields,
relations: defaultAdminNotificationsRelations,
})
res.json({ notification })
}
export class AdminPostNotificationsNotificationResendReq {
@IsOptional()
@IsString()
to?: string
}
@@ -1,12 +1,20 @@
import _ from "lodash"
import { Validator, MedusaError } from "medusa-core-utils"
import { defaultFields, defaultRelations } from "./"
import {
IsInt,
IsNotEmpty,
IsObject,
IsOptional,
IsString,
} from "class-validator"
import { defaultAdminOrdersFields, defaultAdminOrdersRelations } from "."
import { OrderService } from "../../../../services"
import { validator } from "../../../../utils/validator"
/**
* @oas [post] /orders/{id}/shipping-methods
* operationId: "PostOrdersOrderShippingMethods"
* summary: "Add a Shipping Method"
* description: "Adds a Shipping Method to an Order. If another Shipping Method exists with the same Shipping Profile, the previous Shipping Method will be replaced."
* x-authenticated: true
* parameters:
* - (path) id=* {string} The id of the Order.
* - (body) price=* {integer} The price (excluding VAT) that should be charged for the Shipping Method
@@ -27,37 +35,40 @@ import { defaultFields, defaultRelations } from "./"
export default async (req, res) => {
const { id } = req.params
const schema = Validator.object().keys({
price: Validator.number()
.integer()
.integer()
.allow(0)
.required(),
option_id: Validator.string().required(),
data: Validator.object()
.optional()
.default({}),
const validated = await validator(
AdminPostOrdersOrderShippingMethodsReq,
req.body
)
const orderService: OrderService = req.scope.resolve("orderService")
await orderService.addShippingMethod(
id,
validated.option_id,
validated.data,
{
price: validated.price,
}
)
const order = await orderService.retrieve(id, {
select: defaultAdminOrdersFields,
relations: defaultAdminOrdersRelations,
})
const { value, error } = schema.validate(req.body)
if (error) {
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
}
res.status(200).json({ order })
}
try {
const orderService = req.scope.resolve("orderService")
export class AdminPostOrdersOrderShippingMethodsReq {
@IsInt()
@IsNotEmpty()
price: number
await orderService.addShippingMethod(id, value.option_id, value.data, {
price: value.price,
})
@IsString()
@IsNotEmpty()
option_id: string
const order = await orderService.retrieve(id, {
select: defaultFields,
relations: defaultRelations,
})
res.status(200).json({ order })
} catch (err) {
throw err
}
@IsObject()
@IsOptional()
data?: object = {}
}
@@ -1,17 +0,0 @@
export default async (req, res) => {
const { id } = req.params
try {
const orderService = req.scope.resolve("orderService")
await orderService.archive(id)
const order = await orderService.retrieve(id, {
relations: ["region", "customer", "swaps"],
})
res.json({ order })
} catch (error) {
throw error
}
}
@@ -0,0 +1,35 @@
import { OrderService } from "../../../../services"
/**
* @oas [post] /orders/{id}/archive
* operationId: "PostOrdersOrderArchive"
* summary: "Archive order"
* description: "Archives the order with the given id."
* x-authenticated: true
* parameters:
* - (path) id=* {string} The id of the Order.
* tags:
* - Order
* responses:
* 200:
* description: OK
* content:
* application/json:
* schema:
* properties:
* order:
* $ref: "#/components/schemas/order"
*/
export default async (req, res) => {
const { id } = req.params
const orderService: OrderService = req.scope.resolve("orderService")
await orderService.archive(id)
const order = await orderService.retrieve(id, {
relations: ["region", "customer", "swaps"],
})
res.json({ order })
}
@@ -1,51 +0,0 @@
import { MedusaError } from "medusa-core-utils"
import { defaultRelations, defaultFields } from "."
/**
* @oas [post] /orders/{id}/claims/{claim_id}/cancel
* operationId: "PostOrdersClaimCancel"
* summary: "Cancels a Claim"
* description: "Cancels a Claim"
* parameters:
* - (path) id=* {string} The id of the Order.
* . (path) claim_id=* {string} The id of the Claim.
* tags:
* - Claim
* responses:
* 200:
* description: OK
* content:
* application/json:
* schema:
* properties:
* order:
* $ref: "#/components/schemas/claim"
*/
export default async (req, res) => {
const { id, claim_id } = req.params
try {
const claimService = req.scope.resolve("claimService")
const orderService = req.scope.resolve("orderService")
const claim = await claimService.retrieve(claim_id)
if (claim.order_id !== id) {
throw new MedusaError(
MedusaError.Types.NOT_FOUND,
`no claim was found with the id: ${claim_id} related to order: ${id}`
)
}
await claimService.cancel(claim_id)
const order = await orderService.retrieve(id, {
select: defaultFields,
relations: defaultRelations,
})
res.json({ order })
} catch (error) {
throw error
}
}
@@ -0,0 +1,49 @@
import { MedusaError } from "medusa-core-utils"
import { defaultAdminOrdersRelations, defaultAdminOrdersFields } from "."
import { ClaimService, OrderService } from "../../../../services"
/**
* @oas [post] /orders/{id}/claims/{claim_id}/cancel
* operationId: "PostOrdersClaimCancel"
* summary: "Cancels a Claim"
* description: "Cancels a Claim"
* x-authenticated: true
* parameters:
* - (path) id=* {string} The id of the Order.
* - (path) claim_id=* {string} The id of the Claim.
* tags:
* - Claim
* responses:
* 200:
* description: OK
* content:
* application/json:
* schema:
* properties:
* order:
* $ref: "#/components/schemas/claim_order"
*/
export default async (req, res) => {
const { id, claim_id } = req.params
const claimService: ClaimService = req.scope.resolve("claimService")
const orderService: OrderService = req.scope.resolve("orderService")
const claim = await claimService.retrieve(claim_id)
if (claim.order_id !== id) {
throw new MedusaError(
MedusaError.Types.NOT_FOUND,
`no claim was found with the id: ${claim_id} related to order: ${id}`
)
}
await claimService.cancel(claim_id)
const order = await orderService.retrieve(id, {
select: defaultAdminOrdersFields,
relations: defaultAdminOrdersRelations,
})
res.json({ order })
}
@@ -1,61 +0,0 @@
import { MedusaError } from "medusa-core-utils"
import { defaultRelations, defaultFields } from "."
/**
* @oas [post] orders//{id}/claims/{claim_id}/fulfillments/{fulfillment_id}/cancel
* operationId: "PostOrdersClaimFulfillmentsCancel"
* summary: "Cancels a fulfilmment related to a Claim"
* description: "Registers a Fulfillment as canceled."
* parameters:
* - (path) id=* {string} The id of the Order which the Claim relates to.
* - (path) claim_id=* {string} The id of the Claim which the Fulfillment relates to.
* - (path) fulfillment_id=* {string} The id of the Fulfillment.
* tags:
* - Fulfillment
* responses:
* 200:
* description: OK
* content:
* application/json:
* schema:
* properties:
* fulfillment:
* $ref: "#/components/schemas/fulfillment"
*/
export default async (req, res) => {
const { id, claim_id, fulfillment_id } = req.params
try {
const fulfillmentService = req.scope.resolve("fulfillmentService")
const claimService = req.scope.resolve("claimService")
const orderService = req.scope.resolve("orderService")
const fulfillment = await fulfillmentService.retrieve(fulfillment_id)
if (fulfillment.claim_order_id !== claim_id) {
throw new MedusaError(
MedusaError.Types.NOT_FOUND,
`no fulfillment was found with the id: ${fulfillment_id} related to claim: ${claim_id}`
)
}
const claim = await claimService.retrieve(claim_id)
if (claim.order_id !== id) {
throw new MedusaError(
MedusaError.Types.NOT_FOUND,
`no claim was found with the id: ${claim_id} related to order: ${id}`
)
}
await claimService.cancelFulfillment(fulfillment_id)
const order = await orderService.retrieve(id, {
select: defaultFields,
relations: defaultRelations,
})
res.json({ order })
} catch (error) {
throw error
}
}
@@ -0,0 +1,64 @@
import { MedusaError } from "medusa-core-utils"
import { defaultAdminOrdersRelations, defaultAdminOrdersFields } from "."
import {
ClaimService,
FulfillmentService,
OrderService,
} from "../../../../services"
/**
* @oas [post] /orders/{id}/claims/{claim_id}/fulfillments/{fulfillment_id}/cancel
* operationId: "PostOrdersClaimFulfillmentsCancel"
* summary: "Cancels a fulfilmment related to a Claim"
* description: "Registers a Fulfillment as canceled."
* x-authenticated: true
* parameters:
* - (path) id=* {string} The id of the Order which the Claim relates to.
* - (path) claim_id=* {string} The id of the Claim which the Fulfillment relates to.
* - (path) fulfillment_id=* {string} The id of the Fulfillment.
* tags:
* - Fulfillment
* responses:
* 200:
* description: OK
* content:
* application/json:
* schema:
* properties:
* fulfillment:
* $ref: "#/components/schemas/fulfillment"
*/
export default async (req, res) => {
const { id, claim_id, fulfillment_id } = req.params
const fulfillmentService: FulfillmentService =
req.scope.resolve("fulfillmentService")
const claimService: ClaimService = req.scope.resolve("claimService")
const orderService: OrderService = req.scope.resolve("orderService")
const fulfillment = await fulfillmentService.retrieve(fulfillment_id)
if (fulfillment.claim_order_id !== claim_id) {
throw new MedusaError(
MedusaError.Types.NOT_FOUND,
`no fulfillment was found with the id: ${fulfillment_id} related to claim: ${claim_id}`
)
}
const claim = await claimService.retrieve(claim_id)
if (claim.order_id !== id) {
throw new MedusaError(
MedusaError.Types.NOT_FOUND,
`no claim was found with the id: ${claim_id} related to order: ${id}`
)
}
await claimService.cancelFulfillment(fulfillment_id)
const order = await orderService.retrieve(id, {
select: defaultAdminOrdersFields,
relations: defaultAdminOrdersRelations,
})
res.json({ order })
}
@@ -1,62 +0,0 @@
import { MedusaError } from "medusa-core-utils"
import { defaultRelations, defaultFields } from "."
/**
* @oas [post] /orders/{id}/swaps/{swap_id}/fulfillments/{fulfillment_id}/cancel
* operationId: "PostOrdersSwapFulfillmentsCancel"
* summary: "Cancels a fulfilmment related to a Swap"
* description: "Registers a Fulfillment as canceled."
* parameters:
* - (path) id=* {string} The id of the Order which the Swap relates to.
* - (path) swap_id=* {string} The id of the Swap which the Fulfillment relates to.
* - (path) fulfillment_id=* {string} The id of the Fulfillment.
* tags:
* - Fulfillment
* responses:
* 200:
* description: OK
* content:
* application/json:
* schema:
* properties:
* fulfillment:
* $ref: "#/components/schemas/fulfillment"
*/
export default async (req, res) => {
const { id, swap_id, fulfillment_id } = req.params
try {
const fulfillmentService = req.scope.resolve("fulfillmentService")
const swapService = req.scope.resolve("swapService")
const orderService = req.scope.resolve("orderService")
const fulfillment = await fulfillmentService.retrieve(fulfillment_id)
if (fulfillment.swap_id !== swap_id) {
throw new MedusaError(
MedusaError.Types.NOT_FOUND,
`no fulfillment was found with the id: ${fulfillment_id} related to swap: ${id}`
)
}
const swap = await swapService.retrieve(swap_id)
if (swap.order_id !== id) {
throw new MedusaError(
MedusaError.Types.NOT_FOUND,
`no swap was found with the id: ${swap_id} related to order: ${id}`
)
}
await swapService.cancelFulfillment(fulfillment_id)
const order = await orderService.retrieve(id, {
select: defaultFields,
relations: defaultRelations,
})
res.json({ order })
} catch (error) {
throw error
}
}
@@ -0,0 +1,65 @@
import { MedusaError } from "medusa-core-utils"
import { defaultAdminOrdersRelations, defaultAdminOrdersFields } from "."
import {
FulfillmentService,
OrderService,
SwapService,
} from "../../../../services"
/**
* @oas [post] /orders/{id}/swaps/{swap_id}/fulfillments/{fulfillment_id}/cancel
* operationId: "PostOrdersSwapFulfillmentsCancel"
* summary: "Cancels a fulfilmment related to a Swap"
* description: "Registers a Fulfillment as canceled."
* x-authenticated: true
* parameters:
* - (path) id=* {string} The id of the Order which the Swap relates to.
* - (path) swap_id=* {string} The id of the Swap which the Fulfillment relates to.
* - (path) fulfillment_id=* {string} The id of the Fulfillment.
* tags:
* - Fulfillment
* responses:
* 200:
* description: OK
* content:
* application/json:
* schema:
* properties:
* fulfillment:
* $ref: "#/components/schemas/fulfillment"
*/
export default async (req, res) => {
const { id, swap_id, fulfillment_id } = req.params
const swapService: SwapService = req.scope.resolve("swapService")
const orderService: OrderService = req.scope.resolve("orderService")
const fulfillmentService: FulfillmentService =
req.scope.resolve("fulfillmentService")
const fulfillment = await fulfillmentService.retrieve(fulfillment_id)
if (fulfillment.swap_id !== swap_id) {
throw new MedusaError(
MedusaError.Types.NOT_FOUND,
`no fulfillment was found with the id: ${fulfillment_id} related to swap: ${id}`
)
}
const swap = await swapService.retrieve(swap_id)
if (swap.order_id !== id) {
throw new MedusaError(
MedusaError.Types.NOT_FOUND,
`no swap was found with the id: ${swap_id} related to order: ${id}`
)
}
await swapService.cancelFulfillment(fulfillment_id)
const order = await orderService.retrieve(id, {
select: defaultAdminOrdersFields,
relations: defaultAdminOrdersRelations,
})
res.json({ order })
}
@@ -1,51 +0,0 @@
import { MedusaError } from "medusa-core-utils"
import { defaultRelations, defaultFields } from "."
/**
* @oas [post] /orders/{id}/fulfillments/{fulfillment_id}/cancel
* operationId: "PostOrdersOrderFulfillmentsCancel"
* summary: "Cancels a fulfilmment"
* description: "Registers a Fulfillment as canceled."
* parameters:
* - (path) id=* {string} The id of the Order which the Fulfillment relates to.
* - (path) fulfillment_id=* {string} The id of the Fulfillment
* tags:
* - Fulfillment
* responses:
* 200:
* description: OK
* content:
* application/json:
* schema:
* properties:
* fulfillment:
* $ref: "#/components/schemas/fulfillment"
*/
export default async (req, res) => {
const { id, fulfillment_id } = req.params
try {
const fulfillmentService = req.scope.resolve("fulfillmentService")
const orderService = req.scope.resolve("orderService")
const fulfillment = await fulfillmentService.retrieve(fulfillment_id)
if (fulfillment.order_id !== id) {
throw new MedusaError(
MedusaError.Types.NOT_FOUND,
`no fulfillment was found with the id: ${fulfillment_id} related to order: ${id}`
)
}
await orderService.cancelFulfillment(fulfillment_id)
const order = await orderService.retrieve(id, {
select: defaultFields,
relations: defaultRelations,
})
res.json({ order })
} catch (error) {
throw error
}
}
Loaded 100 of 565 files, more files were not shown because too many files have changed in this diff. Show more