From 5a09c25b1c91700adeccf04dd1a01b89c6db1f65 Mon Sep 17 00:00:00 2001 From: Sujith Date: Wed, 19 Oct 2022 23:03:11 +0530 Subject: [PATCH 1/7] feat: fulfillment type pickup and method store Signed-off-by: Sujith --- .../.gitignore | 61 ++++++ .../LICENSE | 201 ++++++++++++++++++ .../README.md | 70 ++++++ .../babel.config.cjs | 1 + .../index.js | 3 + .../jest.config.cjs | 1 + .../package.json | 44 ++++ ...FulfillmentMethodsWithQuotesPickupStore.js | 77 +++++++ ...llmentMethodsWithQuotesPickupStore.test.js | 128 +++++++++++ .../src/index.js | 36 ++++ .../src/preStartup.js | 27 +++ .../src/schemas/index.js | 5 + .../src/schemas/schema.graphql | 14 ++ .../src/simpleSchemas.js | 45 ++++ .../src/startup.js | 22 ++ ...eckAndCreateFulfillmentMethod.test.js.snap | 3 + .../util/checkAndCreateFulfillmentMethod.js | 31 +++ .../checkAndCreateFulfillmentMethod.test.js | 89 ++++++++ .../src/util/collectStoreDetails.js | 48 +++++ .../src/util/validateOrderMethodsstore.js | 22 ++ .../.gitignore | 61 ++++++ .../LICENSE | 201 ++++++++++++++++++ .../README.md | 72 +++++++ .../babel.config.cjs | 1 + .../index.js | 3 + .../jest.config.cjs | 1 + .../package.json | 42 ++++ .../src/checkAndCreateFulfillmentType.js | 26 +++ .../src/checkAndCreateFulfillmentType.test.js | 61 ++++++ .../src/index.js | 26 +++ .../src/schemas/index.js | 5 + .../src/schemas/schema.graphql | 5 + .../src/startup.js | 22 ++ 33 files changed, 1454 insertions(+) create mode 100644 packages/api-plugin-fulfillment-method-pickup-store/.gitignore create mode 100644 packages/api-plugin-fulfillment-method-pickup-store/LICENSE create mode 100644 packages/api-plugin-fulfillment-method-pickup-store/README.md create mode 100644 packages/api-plugin-fulfillment-method-pickup-store/babel.config.cjs create mode 100644 packages/api-plugin-fulfillment-method-pickup-store/index.js create mode 100644 packages/api-plugin-fulfillment-method-pickup-store/jest.config.cjs create mode 100644 packages/api-plugin-fulfillment-method-pickup-store/package.json create mode 100644 packages/api-plugin-fulfillment-method-pickup-store/src/getFulfillmentMethodsWithQuotesPickupStore.js create mode 100644 packages/api-plugin-fulfillment-method-pickup-store/src/getFulfillmentMethodsWithQuotesPickupStore.test.js create mode 100644 packages/api-plugin-fulfillment-method-pickup-store/src/index.js create mode 100644 packages/api-plugin-fulfillment-method-pickup-store/src/preStartup.js create mode 100644 packages/api-plugin-fulfillment-method-pickup-store/src/schemas/index.js create mode 100644 packages/api-plugin-fulfillment-method-pickup-store/src/schemas/schema.graphql create mode 100644 packages/api-plugin-fulfillment-method-pickup-store/src/simpleSchemas.js create mode 100644 packages/api-plugin-fulfillment-method-pickup-store/src/startup.js create mode 100644 packages/api-plugin-fulfillment-method-pickup-store/src/util/__snapshots__/checkAndCreateFulfillmentMethod.test.js.snap create mode 100644 packages/api-plugin-fulfillment-method-pickup-store/src/util/checkAndCreateFulfillmentMethod.js create mode 100644 packages/api-plugin-fulfillment-method-pickup-store/src/util/checkAndCreateFulfillmentMethod.test.js create mode 100644 packages/api-plugin-fulfillment-method-pickup-store/src/util/collectStoreDetails.js create mode 100644 packages/api-plugin-fulfillment-method-pickup-store/src/util/validateOrderMethodsstore.js create mode 100644 packages/api-plugin-fulfillment-type-pickup/.gitignore create mode 100644 packages/api-plugin-fulfillment-type-pickup/LICENSE create mode 100644 packages/api-plugin-fulfillment-type-pickup/README.md create mode 100644 packages/api-plugin-fulfillment-type-pickup/babel.config.cjs create mode 100644 packages/api-plugin-fulfillment-type-pickup/index.js create mode 100644 packages/api-plugin-fulfillment-type-pickup/jest.config.cjs create mode 100644 packages/api-plugin-fulfillment-type-pickup/package.json create mode 100644 packages/api-plugin-fulfillment-type-pickup/src/checkAndCreateFulfillmentType.js create mode 100644 packages/api-plugin-fulfillment-type-pickup/src/checkAndCreateFulfillmentType.test.js create mode 100644 packages/api-plugin-fulfillment-type-pickup/src/index.js create mode 100644 packages/api-plugin-fulfillment-type-pickup/src/schemas/index.js create mode 100644 packages/api-plugin-fulfillment-type-pickup/src/schemas/schema.graphql create mode 100644 packages/api-plugin-fulfillment-type-pickup/src/startup.js diff --git a/packages/api-plugin-fulfillment-method-pickup-store/.gitignore b/packages/api-plugin-fulfillment-method-pickup-store/.gitignore new file mode 100644 index 00000000000..ad46b30886f --- /dev/null +++ b/packages/api-plugin-fulfillment-method-pickup-store/.gitignore @@ -0,0 +1,61 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# TypeScript v1 declaration files +typings/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env + +# next.js build output +.next diff --git a/packages/api-plugin-fulfillment-method-pickup-store/LICENSE b/packages/api-plugin-fulfillment-method-pickup-store/LICENSE new file mode 100644 index 00000000000..261eeb9e9f8 --- /dev/null +++ b/packages/api-plugin-fulfillment-method-pickup-store/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/packages/api-plugin-fulfillment-method-pickup-store/README.md b/packages/api-plugin-fulfillment-method-pickup-store/README.md new file mode 100644 index 00000000000..447b4df70a0 --- /dev/null +++ b/packages/api-plugin-fulfillment-method-pickup-store/README.md @@ -0,0 +1,70 @@ +# api-plugin-fulfillment-method-pickup-store + +[![npm (scoped)](https://img.shields.io/npm/v/@reactioncommerce/api-plugin-fulfillment-method-pickup-store.svg)](https://www.npmjs.com/package/@reactioncommerce/api-plugin-fulfillment-method-pickup-store) +[![CircleCI](https://circleci.com/gh/reactioncommerce/api-plugin-fulfillment-method-pickup-store.svg?style=svg)](https://circleci.com/gh/reactioncommerce/api-plugin-fulfillment-method-pickup-store) + + +## Summary + +This plugin Implements Store pickup as a fulfillment method under the type pickup. + +## Included in this fulfillment-method-pickup-store plugin +ß +### `src/` + +The `src` folder contains all the plugin files. + +### `.gitignore` + +A basic `gitignore` file + +### `babel.config.cjs` + +If your plugin includes linting and tests, this file is required to allow esmodules to run correctly. + +### `jest.config.cjs` + +If your plugin includes tests, this file is required to allow esmodules to run correctly. You'll need to update the `transformIgnorePatterns` and `moduleNameMapper` sections to include any esmodule `npm` packages used in your plugin. + +### `License.md` + +If your plugin uses `Apache 2` licensing, you can leave this file as-is. If another type of licensing is used, you need to update this file, and the README, accordingly. + +### `package.json` + +The provided `package.json` is set up to install all needed packages and config for linting, testing, and semantic-release. You'll need to update the `name`, `description`, and add any new dependencies your plugin files use. + +### `index.js` + +The entrypoint file for your npm package, will most likely just export your plugin registration from the `src` folder. + +## Developer Certificate of Origin +We use the [Developer Certificate of Origin (DCO)](https://developercertificate.org/) in lieu of a Contributor License Agreement for all contributions to Reaction Commerce open source projects. We request that contributors agree to the terms of the DCO and indicate that agreement by signing all commits made to Reaction Commerce projects by adding a line with your name and email address to every Git commit message contributed: +``` +Signed-off-by: Jane Doe +``` + +You can sign your commit automatically with Git by using `git commit -s` if you have your `user.name` and `user.email` set as part of your Git configuration. + +We ask that you use your real name (please no anonymous contributions or pseudonyms). By signing your commit you are certifying that you have the right have the right to submit it under the open source license used by that particular Reaction Commerce project. You must use your real name (no pseudonyms or anonymous contributions are allowed.) + +We use the [Probot DCO GitHub app](https://github.com/apps/dco) to check for DCO signoffs of every commit. + +If you forget to sign your commits, the DCO bot will remind you and give you detailed instructions for how to amend your commits to add a signature. + +## License + + Copyright 2020 Reaction Commerce + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/packages/api-plugin-fulfillment-method-pickup-store/babel.config.cjs b/packages/api-plugin-fulfillment-method-pickup-store/babel.config.cjs new file mode 100644 index 00000000000..5fa924c0809 --- /dev/null +++ b/packages/api-plugin-fulfillment-method-pickup-store/babel.config.cjs @@ -0,0 +1 @@ +module.exports = require("@reactioncommerce/api-utils/lib/configs/babel.config.cjs"); diff --git a/packages/api-plugin-fulfillment-method-pickup-store/index.js b/packages/api-plugin-fulfillment-method-pickup-store/index.js new file mode 100644 index 00000000000..d7ea8b28c59 --- /dev/null +++ b/packages/api-plugin-fulfillment-method-pickup-store/index.js @@ -0,0 +1,3 @@ +import register from "./src/index.js"; + +export default register; diff --git a/packages/api-plugin-fulfillment-method-pickup-store/jest.config.cjs b/packages/api-plugin-fulfillment-method-pickup-store/jest.config.cjs new file mode 100644 index 00000000000..2bdefefceb9 --- /dev/null +++ b/packages/api-plugin-fulfillment-method-pickup-store/jest.config.cjs @@ -0,0 +1 @@ +module.exports = require("@reactioncommerce/api-utils/lib/configs/jest.config.cjs"); diff --git a/packages/api-plugin-fulfillment-method-pickup-store/package.json b/packages/api-plugin-fulfillment-method-pickup-store/package.json new file mode 100644 index 00000000000..22d15f80d78 --- /dev/null +++ b/packages/api-plugin-fulfillment-method-pickup-store/package.json @@ -0,0 +1,44 @@ +{ + "name": "@reactioncommerce/api-plugin-fulfillment-method-pickup-store", + "description": "Implements Store-pickup as a fulfillment method under the type pickup", + "label": "Fulfillment Method Store Pickup", + "version": "1.0.0", + "main": "index.js", + "type": "module", + "engines": { + "node": ">=14.18.1", + "npm": ">=7" + }, + "homepage": "https://github.com/reactioncommerce/reaction", + "url": "https://github.com/reactioncommerce/reaction", + "email": "hello-open-commerce@mailchimp.com", + "repository": { + "type": "git", + "url": "git+https://github.com/reactioncommerce/reaction.git", + "directory": "packages/api-plugin-fulfillment-method-pickup-store" + }, + "author": { + "name": "Mailchimp Open Commerce", + "email": "hello-open-commerce@mailchimp.com", + "url": "https://mailchimp.com/developer/open-commerce/" + }, + "license": "Apache-2.0", + "bugs": { + "url": "https://github.com/reactioncommerce/reaction/issues" + }, + "sideEffects": false, + "dependencies": { + "simpl-schema": "^1.12.2", + "@reactioncommerce/logger": "^1.1.4", + "@reactioncommerce/reaction-error": "^1.0.1", + "@reactioncommerce/api-utils": "^1.16.9" + }, + "devDependencies": {}, + "scripts": { + "lint": "npm run lint:eslint", + "lint:eslint": "eslint .", + "test": "jest", + "test:watch": "jest --watch", + "test:file": "jest --no-cache --watch --coverage=false" + } +} diff --git a/packages/api-plugin-fulfillment-method-pickup-store/src/getFulfillmentMethodsWithQuotesPickupStore.js b/packages/api-plugin-fulfillment-method-pickup-store/src/getFulfillmentMethodsWithQuotesPickupStore.js new file mode 100644 index 00000000000..e3be3ee3658 --- /dev/null +++ b/packages/api-plugin-fulfillment-method-pickup-store/src/getFulfillmentMethodsWithQuotesPickupStore.js @@ -0,0 +1,77 @@ +import Logger from "@reactioncommerce/logger"; +import collectStoreDetails from "./util/collectStoreDetails.js"; + +const packageName = "fulfillment-method-pickup-store"; +const fulfillmentTypeName = "pickup"; +const fulfillmentMethodName = "store"; +const logCtx = { name: "fulfillment-method-pickup-store", file: "getFulfillmentMethodsWithQuotesPickupStore" }; + +/** + * @summary Returns a list of fulfillment method quotes based on the items in a fulfillment group. + * @param {Object} context - Context + * @param {Object} commonOrder - details about the purchase a user wants to make. + * @param {Array} [previousQueryResults] - an array of shipping rates and + * info about failed calls to the APIs of some shipping methods providers + * @returns {Array} - an array that contains two arrays: the first array will + * be an updated list of shipping rates, and the second will contain info for + * retrying this specific package if any errors occurred while retrieving the + * shipping rates. + * @private + */ +export default async function getFulfillmentMethodsWithQuotesPickupStore(context, commonOrder, previousQueryResults = []) { + const { collections } = context; + const { Fulfillment } = collections; + const [rates = [], retrialTargets = []] = previousQueryResults; + const currentMethodInfo = { packageName }; + + if (retrialTargets.length > 0) { + const isNotAmongFailedRequests = retrialTargets.every((target) => target.packageName !== packageName); + if (isNotAmongFailedRequests) { + return previousQueryResults; + } + } + + const pickupDocs = await Fulfillment.find({ + "shopId": commonOrder.shopId, + "fulfillmentType": fulfillmentTypeName, + "provider.enabled": true + }).toArray(); + if (!pickupDocs || !pickupDocs.length) { + return [rates, retrialTargets]; + } + + const initialNumOfRates = rates.length; + + const awaitedPickupDocs = pickupDocs.map(async (doc) => { + const carrier = doc.provider.label; + const currentPluginMethods = doc.methods.filter((method) => ((method.name === fulfillmentMethodName) && (method.enabled))); + + for (const method of currentPluginMethods) { + const updatedMethod = collectStoreDetails(method, commonOrder); + + rates.push({ + carrier, + handlingPrice: updatedMethod.handling, + method: updatedMethod, + rate: updatedMethod.rate, + shippingPrice: updatedMethod.rate + updatedMethod.handling, + shopId: doc.shopId + }); + } + }); + await Promise.all(awaitedPickupDocs); + + if (rates.length === initialNumOfRates) { + const errorDetails = { + requestStatus: "error", + shippingProvider: packageName, + message: "Pickup Store did not return any pickup methods." + }; + rates.push(errorDetails); + retrialTargets.push(currentMethodInfo); + return [rates, retrialTargets]; + } + + Logger.debug({ ...logCtx, rates }, "Store getFulfillmentMethodsWithQuotesPickupStore"); + return [rates, retrialTargets]; +} diff --git a/packages/api-plugin-fulfillment-method-pickup-store/src/getFulfillmentMethodsWithQuotesPickupStore.test.js b/packages/api-plugin-fulfillment-method-pickup-store/src/getFulfillmentMethodsWithQuotesPickupStore.test.js new file mode 100644 index 00000000000..a9c378d8bfb --- /dev/null +++ b/packages/api-plugin-fulfillment-method-pickup-store/src/getFulfillmentMethodsWithQuotesPickupStore.test.js @@ -0,0 +1,128 @@ +import mockContext from "@reactioncommerce/api-utils/tests/mockContext.js"; +import getFulfillmentMethodsWithQuotesPickupStore from "./getFulfillmentMethodsWithQuotesPickupStore.js"; + +test("should return previousResults if pickup is not among FailedRequests", async () => { + const commonOrder = { + _id: "order123" + }; + const previousResults = [ + [], [ + { + packageName: "some-other-fulfillment-method" + } + ] + ]; + const result = await getFulfillmentMethodsWithQuotesPickupStore(mockContext, commonOrder, previousResults); + expect(result).toEqual(previousResults); +}); + + +test("should return previousResults if not fulfillment records enabled", async () => { + const previousResults = [ + [ + { + carrier: "carrier123", + handlingPrice: 99, + rate: 99, + shippingPrice: 198, + shopId: "SHOP_ID" + } + ], [] + ]; + const commonOrder = { + _id: "order123" + }; + + mockContext.collections.Fulfillment = { + find: jest.fn(() => ({ toArray: () => [] })) + }; + const result = await getFulfillmentMethodsWithQuotesPickupStore(mockContext, commonOrder, previousResults); + expect(result).toEqual(previousResults); +}); + + +test("should return rates witout error", async () => { + const previousResults = [ + [ + { + carrier: "Pickup", + handlingPrice: 10, + rate: 5, + shippingPrice: 15, + shopId: "SHOP_ID" + } + ], [] + ]; + const commonOrder = { + _id: "order123" + }; + const pickupDoc = { + _id: "fulfillment123", + name: "Default Pickup Provider", + shopId: "SHOP_ID", + provider: { + enabled: true, + label: "Pickup", + name: "pickup" + }, + fulfillmentType: "pickup", + methods: [{ + shopId: "SHOP_ID", + cost: 99, + handling: 99, + rate: 99, + fulfillmentTypes: ["pickup"], + group: "Ground", + enabled: true, + label: "Store", + name: "store", + fulfillmentMethod: "store", + displayMessageMethod: "Sample display message" + }] + }; + const expectedNewRate = { + carrier: "Pickup", + handlingPrice: 10, + method: { + shopId: "SHOP_ID", + cost: 99, + handling: 10, + rate: 5, + fulfillmentTypes: [ + "pickup" + ], + group: "Ground", + enabled: true, + label: "Store", + name: "store", + fulfillmentMethod: "store", + displayMessageMethod: "Sample display message", + carrier: "Store", + methodAdditionalData: { + gqlType: "storeData", + storeData: [ + { + storeId: "Store-1", + storeAddress: "123, 5th Main, Some place", + storeTiming: "7am to 9pm" + }, + { + storeId: "Store-2", + storeAddress: "456, 50th Main, Some other place", + storeTiming: "7am to 9pm" + } + ] + } + }, + rate: 5, + shippingPrice: 15, + shopId: "SHOP_ID" + }; + const expectedResult = [[...previousResults[0], expectedNewRate], []]; + + mockContext.collections.Fulfillment = { + find: jest.fn(() => ({ toArray: () => [pickupDoc] })) + }; + const result = await getFulfillmentMethodsWithQuotesPickupStore(mockContext, commonOrder, previousResults); + expect(result).toEqual(expectedResult); +}); diff --git a/packages/api-plugin-fulfillment-method-pickup-store/src/index.js b/packages/api-plugin-fulfillment-method-pickup-store/src/index.js new file mode 100644 index 00000000000..d43775c514b --- /dev/null +++ b/packages/api-plugin-fulfillment-method-pickup-store/src/index.js @@ -0,0 +1,36 @@ +import { createRequire } from "module"; +import fulfillmentMethodPickupStorePreStartup from "./preStartup.js"; +import fulfillmentMethodPickupStoreStartup from "./startup.js"; +import { MethodStoreData } from "./simpleSchemas.js"; +import schemas from "./schemas/index.js"; +import getFulfillmentMethodsWithQuotesPickupStore from "./getFulfillmentMethodsWithQuotesPickupStore.js"; +import validateOrderMethodsstore from "./util/validateOrderMethodsstore.js"; + +const require = createRequire(import.meta.url); +const pkg = require("../package.json"); + +/** + * @summary Import and call this function to add this plugin to your API. + * @param {Object} app The ReactionAPI instance + * @returns {undefined} + */ +export default async function register(app) { + await app.registerPlugin({ + label: "Fulfillment Method Pickup Store", + name: "fulfillment-method-pickup-store", + version: pkg.version, + graphQL: { + schemas + }, + simpleSchemas: { + MethodStoreData + }, + functionsByType: { + preStartup: [fulfillmentMethodPickupStorePreStartup], + startup: [fulfillmentMethodPickupStoreStartup], + validateOrderMethods: [validateOrderMethodsstore], + getFulfillmentMethodsWithQuotes: [getFulfillmentMethodsWithQuotesPickupStore], + getFulfillmentMethodsWithQuotesPickup: [getFulfillmentMethodsWithQuotesPickupStore] + } + }); +} diff --git a/packages/api-plugin-fulfillment-method-pickup-store/src/preStartup.js b/packages/api-plugin-fulfillment-method-pickup-store/src/preStartup.js new file mode 100644 index 00000000000..a0773a8d02a --- /dev/null +++ b/packages/api-plugin-fulfillment-method-pickup-store/src/preStartup.js @@ -0,0 +1,27 @@ +import { MethodStoreData } from "./simpleSchemas.js"; +/** + * @summary Called on preStartup + * @param {Object} context Startup context + * @returns {undefined} + */ +export default async function fulfillmentMethodPickupStorePreStartup(context) { + const { simpleSchemas: { ShippingMethod, SelectedFulfillmentOption } } = context; + + ShippingMethod.extend({ + methodAdditionalData: { + type: ShippingMethod.getDefinition( + "methodAdditionalData", + ["type"] + ).type[0].type.extend(MethodStoreData) + } + }); + + SelectedFulfillmentOption.extend({ + methodAdditionalData: { + type: SelectedFulfillmentOption.getDefinition( + "methodAdditionalData", + ["type"] + ).type[0].type.extend(MethodStoreData) + } + }); +} diff --git a/packages/api-plugin-fulfillment-method-pickup-store/src/schemas/index.js b/packages/api-plugin-fulfillment-method-pickup-store/src/schemas/index.js new file mode 100644 index 00000000000..30096f92e54 --- /dev/null +++ b/packages/api-plugin-fulfillment-method-pickup-store/src/schemas/index.js @@ -0,0 +1,5 @@ +import importAsString from "@reactioncommerce/api-utils/importAsString.js"; + +const schema = importAsString("./schema.graphql"); + +export default [schema]; diff --git a/packages/api-plugin-fulfillment-method-pickup-store/src/schemas/schema.graphql b/packages/api-plugin-fulfillment-method-pickup-store/src/schemas/schema.graphql new file mode 100644 index 00000000000..fccb208a807 --- /dev/null +++ b/packages/api-plugin-fulfillment-method-pickup-store/src/schemas/schema.graphql @@ -0,0 +1,14 @@ +"Additional data fields from Store" +type storeFields { + storeId: String + storeAddress: String + storeTiming: String +} + +"Additional Store data - Pickup" +type storeData { + gqlType: String + storeData: [storeFields] +} + +extend union AdditionalData = storeData diff --git a/packages/api-plugin-fulfillment-method-pickup-store/src/simpleSchemas.js b/packages/api-plugin-fulfillment-method-pickup-store/src/simpleSchemas.js new file mode 100644 index 00000000000..c7140eda9af --- /dev/null +++ b/packages/api-plugin-fulfillment-method-pickup-store/src/simpleSchemas.js @@ -0,0 +1,45 @@ +import SimpleSchema from "simpl-schema"; + +/** + * @name StoreFields + * @memberof Schemas + * @type {SimpleSchema} + * @summary Specific fields returned by the Store Pickup fulfillment method. + * @property {String} storeId Store ID + * @property {String} storeAddress Store Address + * @property {String} storeTiming Store Timing + */ +const StoreFields = new SimpleSchema({ + storeId: { + type: String, + optional: true + }, + storeAddress: { + type: String, + optional: true + }, + storeTiming: { + type: String, + optional: true + } +}); + +/** + * @name MethodStoreData + * @memberof Schemas + * @type {SimpleSchema} + * @summary Defines an array of Store fields + * @property {String} gqlType Defines the method type + * @property {StoreFields[]} storeData Store Data fields + */ +export const MethodStoreData = new SimpleSchema({ + "gqlType": String, + "storeData": { + type: Array, + optional: true + }, + "storeData.$": { + type: StoreFields, + optional: true + } +}); diff --git a/packages/api-plugin-fulfillment-method-pickup-store/src/startup.js b/packages/api-plugin-fulfillment-method-pickup-store/src/startup.js new file mode 100644 index 00000000000..1f93a05501c --- /dev/null +++ b/packages/api-plugin-fulfillment-method-pickup-store/src/startup.js @@ -0,0 +1,22 @@ +import ReactionError from "@reactioncommerce/reaction-error"; +import checkAndCreateFulfillmentMethod from "./util/checkAndCreateFulfillmentMethod"; +/** + * @summary Called on startup to create the root entry of this fulfillment type in Fulfillment collection + * @param {Object} context Startup context + * @param {Object} context.collections Map of MongoDB collections + * @returns {undefined} + */ +export default async function fulfillmentMethodPickupStoreStartup(context) { + context.appEvents.on("afterShopCreate", async (payload) => { + const { shop } = payload; + const shopId = shop._id; + + // We do not have validatePermissions in context during this startup stage, hence commenting below + // await context.validatePermissions("reaction:legacy:fulfillmentTypes", "read", { shopId }); + + const insertedMethod = await checkAndCreateFulfillmentMethod(context, shopId); + if (!insertedMethod) { + throw new ReactionError("server-error", "Error in creating fulfillment method"); + } + }); +} diff --git a/packages/api-plugin-fulfillment-method-pickup-store/src/util/__snapshots__/checkAndCreateFulfillmentMethod.test.js.snap b/packages/api-plugin-fulfillment-method-pickup-store/src/util/__snapshots__/checkAndCreateFulfillmentMethod.test.js.snap new file mode 100644 index 00000000000..e08c70088fd --- /dev/null +++ b/packages/api-plugin-fulfillment-method-pickup-store/src/util/__snapshots__/checkAndCreateFulfillmentMethod.test.js.snap @@ -0,0 +1,3 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should throw error and NOT call createFulfillmentMethod mutation 1`] = `"Unable to create fulfillment method Pickup-Store without defined type"`; diff --git a/packages/api-plugin-fulfillment-method-pickup-store/src/util/checkAndCreateFulfillmentMethod.js b/packages/api-plugin-fulfillment-method-pickup-store/src/util/checkAndCreateFulfillmentMethod.js new file mode 100644 index 00000000000..df5df2b78cd --- /dev/null +++ b/packages/api-plugin-fulfillment-method-pickup-store/src/util/checkAndCreateFulfillmentMethod.js @@ -0,0 +1,31 @@ +import ReactionError from "@reactioncommerce/reaction-error"; +/** + * @summary Called on startup to create the root entry of this fulfillment type in Fulfillment collection + * @param {Object} context Startup context + * @param {String} shopId Shop ID + * @returns {Boolean} true if entry exist or insert success else false + */ +export default async function checkAndCreateFulfillmentMethod(context, shopId) { + const { collections } = context; + const { Fulfillment } = collections; + + const pickupRecord = await Fulfillment.findOne({ fulfillmentType: "pickup", shopId }); + if (!pickupRecord) throw new ReactionError("server-error", "Unable to create fulfillment method Pickup-Store without defined type"); + + const fulfillmentTypeId = pickupRecord._id; + const method = { + name: "store", + label: "Pickup from Store", + fulfillmentTypes: ["pickup"], + group: "Ground", + cost: 0, + handling: 0, + rate: 0, + enabled: true, + fulfillmentMethod: "store", + displayMessageMethod: "Placeholder for display message" + }; + + await context.mutations.createFulfillmentMethod(context.getInternalContext(), { shopId, fulfillmentTypeId, method }); + return { method }; +} diff --git a/packages/api-plugin-fulfillment-method-pickup-store/src/util/checkAndCreateFulfillmentMethod.test.js b/packages/api-plugin-fulfillment-method-pickup-store/src/util/checkAndCreateFulfillmentMethod.test.js new file mode 100644 index 00000000000..9bb9869eff8 --- /dev/null +++ b/packages/api-plugin-fulfillment-method-pickup-store/src/util/checkAndCreateFulfillmentMethod.test.js @@ -0,0 +1,89 @@ +import mockContext from "@reactioncommerce/api-utils/tests/mockContext.js"; +import checkAndCreateFulfillmentMethod from "./checkAndCreateFulfillmentMethod.js"; + +test("should call createFulfillmentMethod mutation", async () => { + const shopId = "SHOP_ID"; + const fulfillment = { + _id: "fulfillment123" + }; + const fulfillmentTypeId = fulfillment._id; + const groupInfo = { + name: "Pickup Provider", + shopId: "SHOP_ID", + provider: { + enabled: true, + label: "Pickup", + name: "pickup" + }, + fulfillmentType: "pickup" + }; + const method = { + name: "store", + label: "Pickup from Store", + fulfillmentTypes: ["pickup"], + group: "Ground", + cost: 0, + handling: 0, + rate: 0, + enabled: true, + fulfillmentMethod: "store", + displayMessageMethod: "Placeholder for display message" + }; + + mockContext.getInternalContext = () => ({ + ...mockContext, + account: null, + accountId: null, + isInternalCall: true, + user: null, + userHasPermission: async () => true, + userId: null, + validatePermissions: async () => undefined + }); + + mockContext.collections.Fulfillment = { + findOne: jest.fn().mockReturnValueOnce(Promise.resolve(groupInfo)) + }; + mockContext.mutations.createFulfillmentMethod = jest.fn().mockName("createFulfillmentMethod").mockReturnValueOnce(Promise.resolve(method)); + + await checkAndCreateFulfillmentMethod(mockContext, { shopId, fulfillmentTypeId, method }); + expect(mockContext.mutations.createFulfillmentMethod).toHaveBeenCalled(); +}); + +test("should throw error and NOT call createFulfillmentMethod mutation", async () => { + const fulfillment = { _id: "fulfillment123" }; + const shopId = "SHOP_ID"; + const fulfillmentTypeId = fulfillment._id; + const method = { + name: "store", + label: "Pickup from Store", + fulfillmentTypes: ["pickup"], + group: "Ground", + cost: 0, + handling: 0, + rate: 0, + enabled: true, + fulfillmentMethod: "store", + displayMessageMethod: "Placeholder for display message" + }; + + mockContext.getInternalContext = () => ({ + ...mockContext, + account: null, + accountId: null, + isInternalCall: true, + user: null, + userHasPermission: async () => true, + userId: null, + validatePermissions: async () => undefined + }); + + mockContext.collections.Fulfillment = { + findOne: jest.fn().mockReturnValueOnce(undefined) + }; + mockContext.mutations.createFulfillmentMethod = jest.fn().mockName("createFulfillmentMethod").mockReturnValueOnce(Promise.resolve(method)); + + await expect(checkAndCreateFulfillmentMethod(mockContext, { shopId, fulfillmentTypeId, method })).rejects.toThrowErrorMatchingSnapshot(); + // await checkAndCreateFulfillmentMethod(mockContext, { shopId, fulfillmentTypeId, method }); + // expect(mockContext.mutations.createFulfillmentMethod).toHaveBeenCalled(); +}); diff --git a/packages/api-plugin-fulfillment-method-pickup-store/src/util/collectStoreDetails.js b/packages/api-plugin-fulfillment-method-pickup-store/src/util/collectStoreDetails.js new file mode 100644 index 00000000000..9b28440b4f9 --- /dev/null +++ b/packages/api-plugin-fulfillment-method-pickup-store/src/util/collectStoreDetails.js @@ -0,0 +1,48 @@ +/** + * @summary Sample dummy function returning store details + * @param {Object} customerAddress - Address of the customer to find nearest store + * @param {Object} [items] - List of items to verify the stock availability + * @returns {Object} [storesArray] - list of available stores + */ +function getStoresArray() { + // Custom logic to find out the list of stores that has the mentioned items in stock + // customerAddress could be used in here and used to figure out the nearest store for listing the results + // items details could be used to verify the availability of the particular item in a store + + return { + gqlType: "storeData", + storeData: [ + { + storeId: "Store-1", + storeAddress: "123, 5th Main, Some place", + storeTiming: "7am to 9pm" + }, + { + storeId: "Store-2", + storeAddress: "456, 50th Main, Some other place", + storeTiming: "7am to 9pm" + } + ] + }; +} + +/** + * @summary Sample dummy function to simulate custom logic to retrieve the rates of the selected method + * @param {Object} method - current method for which rates are to be retrieved + * @param {Object} currentOrder - Current order which provide required details to perform rate calculation + * @returns {Object} updatedMethod - with the rate details populated + */ +export default function collectStoreDetails(method, currentOrder) { + // Calculation of the returned rate could be dependent on currentOrder or + const { items, shippingAddress } = currentOrder; + + // Make call to the Fulfillment method API and collect the rates + // Below we are just hardcoding with some dummy values + const updatedMethod = method; + updatedMethod.rate = 5; + updatedMethod.handling = 10; + updatedMethod.carrier = "Store"; + updatedMethod.methodAdditionalData = getStoresArray(shippingAddress, items); + + return updatedMethod; +} diff --git a/packages/api-plugin-fulfillment-method-pickup-store/src/util/validateOrderMethodsstore.js b/packages/api-plugin-fulfillment-method-pickup-store/src/util/validateOrderMethodsstore.js new file mode 100644 index 00000000000..cf6b6c4aa0b --- /dev/null +++ b/packages/api-plugin-fulfillment-method-pickup-store/src/util/validateOrderMethodsstore.js @@ -0,0 +1,22 @@ +/** + * @summary Sample dummy function validating the data requirements for this metod + * @param {Object} context - Context object + * @param {Object} commonOrder - Current order which provide available details to perform validation + * @param {Object[]} validationResults - Validation results collected till now + * @returns {Object[]} validationResults - with the validation details populated + */ +export default function validateOrderMethodsstore(context, commonOrder, validationResults = []) { +// const { items, shippingAddress } = commonOrder; + + const validationResult = { + errorName: "invalid", + errorType: "ReactionError", + errorField: "Store pickup - some field", + fieldValue: "field-value", + errorMessage: "Customer address not available to find nearest store" + }; + + validationResults.push(validationResult); + + return validationResults; +} diff --git a/packages/api-plugin-fulfillment-type-pickup/.gitignore b/packages/api-plugin-fulfillment-type-pickup/.gitignore new file mode 100644 index 00000000000..ad46b30886f --- /dev/null +++ b/packages/api-plugin-fulfillment-type-pickup/.gitignore @@ -0,0 +1,61 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# TypeScript v1 declaration files +typings/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env + +# next.js build output +.next diff --git a/packages/api-plugin-fulfillment-type-pickup/LICENSE b/packages/api-plugin-fulfillment-type-pickup/LICENSE new file mode 100644 index 00000000000..261eeb9e9f8 --- /dev/null +++ b/packages/api-plugin-fulfillment-type-pickup/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/packages/api-plugin-fulfillment-type-pickup/README.md b/packages/api-plugin-fulfillment-type-pickup/README.md new file mode 100644 index 00000000000..a3c2587c38a --- /dev/null +++ b/packages/api-plugin-fulfillment-type-pickup/README.md @@ -0,0 +1,72 @@ +# api-plugin-fulfillment-type-pickup + +[![npm (scoped)](https://img.shields.io/npm/v/@reactioncommerce/api-plugin-fulfillment-type-pickup.svg)](https://www.npmjs.com/package/@reactioncommerce/api-plugin-fulfillment-type-pickup) +[![CircleCI](https://circleci.com/gh/reactioncommerce/api-plugin-fulfillment-type-pickup.svg?style=svg)](https://circleci.com/gh/reactioncommerce/api-plugin-fulfillment-type-pickup) + + +## Summary + +This plugin implements fulfillment type pickup plugin for [Reaction API](https://github.com/reactioncommerce/reaction). A sample Fulfillment method under 'pickup' is implemented via plugin api-plugin-fulfillment-method-pickup-store. + +The `Developer Certificate of Origin` and `License` sections can stay as they are, assuming `Apache 2` license is used (our preferred license). All other sections of this README should be updated to reflect your plugin. + +## Included in this fulfillment-type-pickup plugin + +### `src/` + +The `src` folder is where you'll put all the plugin files. + +### `.gitignore` + +A basic `gitignore` file + +### `babel.config.cjs` + +If your plugin includes linting and tests, this file is required to allow esmodules to run correctly. + +### `jest.config.cjs` + +If your plugin includes tests, this file is required to allow esmodules to run correctly. You'll need to update the `transformIgnorePatterns` and `moduleNameMapper` sections to include any esmodule `npm` packages used in your plugin. + +### `License.md` + +If your plugin uses `Apache 2` licensing, you can leave this file as-is. If another type of licensing is used, you need to update this file, and the README, accordingly. + +### `package.json` + +The provided `package.json` is set up to install all needed packages and config for linting, testing, and semantic-release. You'll need to update the `name`, `description`, and add any new dependencies your plugin files use. + +### `index.js` + +The entrypoint file for your npm package, will most likely just export your plugin registration from the `src` folder. + +## Developer Certificate of Origin +We use the [Developer Certificate of Origin (DCO)](https://developercertificate.org/) in lieu of a Contributor License Agreement for all contributions to Reaction Commerce open source projects. We request that contributors agree to the terms of the DCO and indicate that agreement by signing all commits made to Reaction Commerce projects by adding a line with your name and email address to every Git commit message contributed: +``` +Signed-off-by: Jane Doe +``` + +You can sign your commit automatically with Git by using `git commit -s` if you have your `user.name` and `user.email` set as part of your Git configuration. + +We ask that you use your real name (please no anonymous contributions or pseudonyms). By signing your commit you are certifying that you have the right have the right to submit it under the open source license used by that particular Reaction Commerce project. You must use your real name (no pseudonyms or anonymous contributions are allowed.) + +We use the [Probot DCO GitHub app](https://github.com/apps/dco) to check for DCO signoffs of every commit. + +If you forget to sign your commits, the DCO bot will remind you and give you detailed instructions for how to amend your commits to add a signature. + +## License + + Copyright 2020 Reaction Commerce + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/packages/api-plugin-fulfillment-type-pickup/babel.config.cjs b/packages/api-plugin-fulfillment-type-pickup/babel.config.cjs new file mode 100644 index 00000000000..5fa924c0809 --- /dev/null +++ b/packages/api-plugin-fulfillment-type-pickup/babel.config.cjs @@ -0,0 +1 @@ +module.exports = require("@reactioncommerce/api-utils/lib/configs/babel.config.cjs"); diff --git a/packages/api-plugin-fulfillment-type-pickup/index.js b/packages/api-plugin-fulfillment-type-pickup/index.js new file mode 100644 index 00000000000..d7ea8b28c59 --- /dev/null +++ b/packages/api-plugin-fulfillment-type-pickup/index.js @@ -0,0 +1,3 @@ +import register from "./src/index.js"; + +export default register; diff --git a/packages/api-plugin-fulfillment-type-pickup/jest.config.cjs b/packages/api-plugin-fulfillment-type-pickup/jest.config.cjs new file mode 100644 index 00000000000..2bdefefceb9 --- /dev/null +++ b/packages/api-plugin-fulfillment-type-pickup/jest.config.cjs @@ -0,0 +1 @@ +module.exports = require("@reactioncommerce/api-utils/lib/configs/jest.config.cjs"); diff --git a/packages/api-plugin-fulfillment-type-pickup/package.json b/packages/api-plugin-fulfillment-type-pickup/package.json new file mode 100644 index 00000000000..818316cc1e2 --- /dev/null +++ b/packages/api-plugin-fulfillment-type-pickup/package.json @@ -0,0 +1,42 @@ +{ + "name": "@reactioncommerce/api-plugin-fulfillment-type-pickup", + "description": "Plugin which implements Pickup as one of the Fulfillment type", + "label": "Fulfillment type Pickup Plugin", + "version": "1.0.0", + "main": "index.js", + "type": "module", + "engines": { + "node": ">=14.18.1", + "npm": ">=7" + }, + "homepage": "https://github.com/reactioncommerce/reaction", + "url": "https://github.com/reactioncommerce/reaction", + "email": "hello-open-commerce@mailchimp.com", + "repository": { + "type": "git", + "url": "git+https://github.com/reactioncommerce/reaction.git", + "directory": "packages/api-plugin-fulfillment-type-pickup" + }, + "author": { + "name": "Mailchimp Open Commerce", + "email": "hello-open-commerce@mailchimp.com", + "url": "https://mailchimp.com/developer/open-commerce/" + }, + "license": "Apache-2.0", + "bugs": { + "url": "https://github.com/reactioncommerce/reaction/issues" + }, + "sideEffects": false, + "dependencies": { + "@reactioncommerce/api-utils": "^1.16.9", + "@reactioncommerce/random": "~1.0.2" + }, + "devDependencies": {}, + "scripts": { + "lint": "npm run lint:eslint", + "lint:eslint": "eslint .", + "test": "jest", + "test:watch": "jest --watch", + "test:file": "jest --no-cache --watch --coverage=false" + } +} diff --git a/packages/api-plugin-fulfillment-type-pickup/src/checkAndCreateFulfillmentType.js b/packages/api-plugin-fulfillment-type-pickup/src/checkAndCreateFulfillmentType.js new file mode 100644 index 00000000000..a6fdc156f81 --- /dev/null +++ b/packages/api-plugin-fulfillment-type-pickup/src/checkAndCreateFulfillmentType.js @@ -0,0 +1,26 @@ +/** + * @summary Called on startup to create the root entry of this fulfillment type in Fulfillment collection + * @param {Object} context Startup context + * @param {String} shopId Shop ID + * @returns {Boolean} true if entry exist or insert success else false + */ +export default async function checkAndCreateFulfillmentType(context, shopId) { + const { collections } = context; + const { Fulfillment } = collections; + + const pickupRecord = await Fulfillment.findOne({ fulfillmentType: "pickup", shopId }); + if (!pickupRecord) { + const groupInfo = { + name: "Pickup Provider", + shopId, + provider: { + enabled: true, + label: "Pickup", + name: "pickup" + }, + fulfillmentType: "pickup" + }; + await context.mutations.createFulfillmentType(context.getInternalContext(), groupInfo); + } + return true; +} diff --git a/packages/api-plugin-fulfillment-type-pickup/src/checkAndCreateFulfillmentType.test.js b/packages/api-plugin-fulfillment-type-pickup/src/checkAndCreateFulfillmentType.test.js new file mode 100644 index 00000000000..b64314ff0ef --- /dev/null +++ b/packages/api-plugin-fulfillment-type-pickup/src/checkAndCreateFulfillmentType.test.js @@ -0,0 +1,61 @@ +import mockContext from "@reactioncommerce/api-utils/tests/mockContext.js"; +import checkAndCreateFulfillmentType from "./checkAndCreateFulfillmentType.js"; + +test("should NOT call createFulfillmentType mutation", async () => { + const shopId = "SHOP_ID"; + const fulfillment = { + _id: "fulfillment123" + }; + const groupInfo = { + name: "Pickup Provider", + shopId: "SHOP_ID", + provider: { + enabled: true, + label: "Pickup", + name: "pickup" + }, + fulfillmentType: "pickup" + }; + mockContext.getInternalContext = () => ({ + ...mockContext, + account: null, + accountId: null, + isInternalCall: true, + user: null, + userHasPermission: async () => true, + userId: null, + validatePermissions: async () => undefined + }); + + mockContext.collections.Fulfillment = { + findOne: jest.fn().mockReturnValueOnce(Promise.resolve(groupInfo)) + }; + mockContext.mutations.createFulfillmentType = jest.fn().mockName("createFulfillmentType").mockReturnValueOnce(Promise.resolve(fulfillment)); + + await checkAndCreateFulfillmentType(mockContext, shopId); + expect(mockContext.mutations.createFulfillmentType).not.toHaveBeenCalled(); +}); + +test("should call createFulfillmentType mutation", async () => { + const fulfillment = { _id: "fulfillment123" }; + const shopId = "SHOP_ID"; + + mockContext.getInternalContext = () => ({ + ...mockContext, + account: null, + accountId: null, + isInternalCall: true, + user: null, + userHasPermission: async () => true, + userId: null, + validatePermissions: async () => undefined + }); + + mockContext.collections.Fulfillment = { + findOne: jest.fn().mockReturnValueOnce(undefined) + }; + mockContext.mutations.createFulfillmentType = jest.fn().mockName("createFulfillmentType").mockReturnValueOnce(Promise.resolve(fulfillment)); + + await checkAndCreateFulfillmentType(mockContext, shopId); + expect(mockContext.mutations.createFulfillmentType).toHaveBeenCalled(); +}); diff --git a/packages/api-plugin-fulfillment-type-pickup/src/index.js b/packages/api-plugin-fulfillment-type-pickup/src/index.js new file mode 100644 index 00000000000..40f27edacdf --- /dev/null +++ b/packages/api-plugin-fulfillment-type-pickup/src/index.js @@ -0,0 +1,26 @@ +import { createRequire } from "module"; +import schemas from "./schemas/index.js"; +import startup from "./startup.js"; + +const require = createRequire(import.meta.url); +const pkg = require("../package.json"); + +/** + * @summary Import and call this function to add this plugin to your API. + * @param {Object} app The ReactionAPI instance + * @returns {undefined} + */ +export default async function register(app) { + await app.registerPlugin({ + label: "Fulfillment Type Pickup", + name: "fulfillment-type-pickup", + version: pkg.version, + graphQL: { + schemas + }, + functionsByType: { + startup: [startup] + }, + registeredFulfillmentTypes: ["pickup"] + }); +} diff --git a/packages/api-plugin-fulfillment-type-pickup/src/schemas/index.js b/packages/api-plugin-fulfillment-type-pickup/src/schemas/index.js new file mode 100644 index 00000000000..30096f92e54 --- /dev/null +++ b/packages/api-plugin-fulfillment-type-pickup/src/schemas/index.js @@ -0,0 +1,5 @@ +import importAsString from "@reactioncommerce/api-utils/importAsString.js"; + +const schema = importAsString("./schema.graphql"); + +export default [schema]; diff --git a/packages/api-plugin-fulfillment-type-pickup/src/schemas/schema.graphql b/packages/api-plugin-fulfillment-type-pickup/src/schemas/schema.graphql new file mode 100644 index 00000000000..e9386d14d4c --- /dev/null +++ b/packages/api-plugin-fulfillment-type-pickup/src/schemas/schema.graphql @@ -0,0 +1,5 @@ +"Allowed fulfillment types, extended by fulfillment-type plugins" +enum FulfillmentType { + "An order will be fulfilled by the customer picking it up" + pickup +} diff --git a/packages/api-plugin-fulfillment-type-pickup/src/startup.js b/packages/api-plugin-fulfillment-type-pickup/src/startup.js new file mode 100644 index 00000000000..401c8f4d7ad --- /dev/null +++ b/packages/api-plugin-fulfillment-type-pickup/src/startup.js @@ -0,0 +1,22 @@ +import ReactionError from "@reactioncommerce/reaction-error"; +import checkAndCreateFulfillmentType from "./checkAndCreateFulfillmentType.js"; +/** + * @summary Called on startup to create the root entry of this fulfillment type in Fulfillment collection + * @param {Object} context Startup context + * @param {Object} context.collections Map of MongoDB collections + * @returns {undefined} + */ +export default async function fulfillmentTypePickupStartup(context) { + context.appEvents.on("afterShopCreate", async (payload) => { + const { shop } = payload; + const shopId = shop._id; + + // We do not have validatePermissions in context during this startup stage, hence commenting below + // await context.validatePermissions("reaction:legacy:fulfillmentTypes", "read", { shopId }); + + const insertSuccess = await checkAndCreateFulfillmentType(context, shopId); + if (!insertSuccess) { + throw new ReactionError("server-error", "Error in creating fulfillment type"); + } + }); +} From 5dbcb91cb20b5ccfa320c03122c3b8091bac2036 Mon Sep 17 00:00:00 2001 From: Sujith Date: Fri, 28 Oct 2022 00:37:54 +0530 Subject: [PATCH 2/7] fix: review comments fixes Signed-off-by: Sujith --- .../.gitignore | 61 ------------------- .../README.md | 46 +++----------- ...FulfillmentMethodsWithQuotesPickupStore.js | 6 +- .../src/index.js | 7 +-- .../src/startup.js | 1 - .../util/checkAndCreateFulfillmentMethod.js | 3 +- .../src/util/validateOrderMethodsstore.js | 19 +++--- .../.gitignore | 61 ------------------- .../README.md | 42 ++----------- .../src/checkAndCreateFulfillmentType.js | 3 +- 10 files changed, 31 insertions(+), 218 deletions(-) delete mode 100644 packages/api-plugin-fulfillment-method-pickup-store/.gitignore delete mode 100644 packages/api-plugin-fulfillment-type-pickup/.gitignore diff --git a/packages/api-plugin-fulfillment-method-pickup-store/.gitignore b/packages/api-plugin-fulfillment-method-pickup-store/.gitignore deleted file mode 100644 index ad46b30886f..00000000000 --- a/packages/api-plugin-fulfillment-method-pickup-store/.gitignore +++ /dev/null @@ -1,61 +0,0 @@ -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* - -# Runtime data -pids -*.pid -*.seed -*.pid.lock - -# Directory for instrumented libs generated by jscoverage/JSCover -lib-cov - -# Coverage directory used by tools like istanbul -coverage - -# nyc test coverage -.nyc_output - -# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) -.grunt - -# Bower dependency directory (https://bower.io/) -bower_components - -# node-waf configuration -.lock-wscript - -# Compiled binary addons (https://nodejs.org/api/addons.html) -build/Release - -# Dependency directories -node_modules/ -jspm_packages/ - -# TypeScript v1 declaration files -typings/ - -# Optional npm cache directory -.npm - -# Optional eslint cache -.eslintcache - -# Optional REPL history -.node_repl_history - -# Output of 'npm pack' -*.tgz - -# Yarn Integrity file -.yarn-integrity - -# dotenv environment variables file -.env - -# next.js build output -.next diff --git a/packages/api-plugin-fulfillment-method-pickup-store/README.md b/packages/api-plugin-fulfillment-method-pickup-store/README.md index 447b4df70a0..3fa11ee62fa 100644 --- a/packages/api-plugin-fulfillment-method-pickup-store/README.md +++ b/packages/api-plugin-fulfillment-method-pickup-store/README.md @@ -1,42 +1,14 @@ # api-plugin-fulfillment-method-pickup-store -[![npm (scoped)](https://img.shields.io/npm/v/@reactioncommerce/api-plugin-fulfillment-method-pickup-store.svg)](https://www.npmjs.com/package/@reactioncommerce/api-plugin-fulfillment-method-pickup-store) -[![CircleCI](https://circleci.com/gh/reactioncommerce/api-plugin-fulfillment-method-pickup-store.svg?style=svg)](https://circleci.com/gh/reactioncommerce/api-plugin-fulfillment-method-pickup-store) - - -## Summary - -This plugin Implements Store pickup as a fulfillment method under the type pickup. - -## Included in this fulfillment-method-pickup-store plugin -ß -### `src/` - -The `src` folder contains all the plugin files. - -### `.gitignore` - -A basic `gitignore` file - -### `babel.config.cjs` - -If your plugin includes linting and tests, this file is required to allow esmodules to run correctly. - -### `jest.config.cjs` - -If your plugin includes tests, this file is required to allow esmodules to run correctly. You'll need to update the `transformIgnorePatterns` and `moduleNameMapper` sections to include any esmodule `npm` packages used in your plugin. - -### `License.md` - -If your plugin uses `Apache 2` licensing, you can leave this file as-is. If another type of licensing is used, you need to update this file, and the README, accordingly. - -### `package.json` - -The provided `package.json` is set up to install all needed packages and config for linting, testing, and semantic-release. You'll need to update the `name`, `description`, and add any new dependencies your plugin files use. - -### `index.js` - -The entrypoint file for your npm package, will most likely just export your plugin registration from the `src` folder. +This is a fulfillment-method plugin which which works along with the base `api-plugin-fulfillment` and the ff-type plugin `api-plugin-fulfillment-type-pickup`. This plugin actually implements all the functionality associated with this fulfillment-method. It can work along-side other fulfillment-methods under the same ff-type. + +This main features/functionalities of this plugin includes the following: +* getFulfillmentMethodsWithQuotesPickupStore - returns the quote or equivalent details for the method when called from base ff plugin +* preStartup - extends the union of "methodAdditionalData" with data structure specific to Store +* startup - Inserts the required ff-method entry into Fulfillment collection +* util/checkAndCreateFulfillmentMethod - confirms existing ff-type entry and adds a new ff-method under it. +* util/collectStoreDetails - dummy function to simulate api providing store specific info while returning quotes. +* util/validateOrderMethodsPickupStore - dummy function to simulate store specific validations. Called by prepareOrder.js ## Developer Certificate of Origin We use the [Developer Certificate of Origin (DCO)](https://developercertificate.org/) in lieu of a Contributor License Agreement for all contributions to Reaction Commerce open source projects. We request that contributors agree to the terms of the DCO and indicate that agreement by signing all commits made to Reaction Commerce projects by adding a line with your name and email address to every Git commit message contributed: diff --git a/packages/api-plugin-fulfillment-method-pickup-store/src/getFulfillmentMethodsWithQuotesPickupStore.js b/packages/api-plugin-fulfillment-method-pickup-store/src/getFulfillmentMethodsWithQuotesPickupStore.js index e3be3ee3658..a43f8693704 100644 --- a/packages/api-plugin-fulfillment-method-pickup-store/src/getFulfillmentMethodsWithQuotesPickupStore.js +++ b/packages/api-plugin-fulfillment-method-pickup-store/src/getFulfillmentMethodsWithQuotesPickupStore.js @@ -19,8 +19,7 @@ const logCtx = { name: "fulfillment-method-pickup-store", file: "getFulfillmentM * @private */ export default async function getFulfillmentMethodsWithQuotesPickupStore(context, commonOrder, previousQueryResults = []) { - const { collections } = context; - const { Fulfillment } = collections; + const { collections: { Fulfillment } } = context; const [rates = [], retrialTargets = []] = previousQueryResults; const currentMethodInfo = { packageName }; @@ -42,7 +41,7 @@ export default async function getFulfillmentMethodsWithQuotesPickupStore(context const initialNumOfRates = rates.length; - const awaitedPickupDocs = pickupDocs.map(async (doc) => { + pickupDocs.map(async (doc) => { const carrier = doc.provider.label; const currentPluginMethods = doc.methods.filter((method) => ((method.name === fulfillmentMethodName) && (method.enabled))); @@ -59,7 +58,6 @@ export default async function getFulfillmentMethodsWithQuotesPickupStore(context }); } }); - await Promise.all(awaitedPickupDocs); if (rates.length === initialNumOfRates) { const errorDetails = { diff --git a/packages/api-plugin-fulfillment-method-pickup-store/src/index.js b/packages/api-plugin-fulfillment-method-pickup-store/src/index.js index d43775c514b..f26c434e73f 100644 --- a/packages/api-plugin-fulfillment-method-pickup-store/src/index.js +++ b/packages/api-plugin-fulfillment-method-pickup-store/src/index.js @@ -4,7 +4,7 @@ import fulfillmentMethodPickupStoreStartup from "./startup.js"; import { MethodStoreData } from "./simpleSchemas.js"; import schemas from "./schemas/index.js"; import getFulfillmentMethodsWithQuotesPickupStore from "./getFulfillmentMethodsWithQuotesPickupStore.js"; -import validateOrderMethodsstore from "./util/validateOrderMethodsstore.js"; +import validateOrderMethodsStore from "./util/validateOrderMethodsStore.js"; const require = createRequire(import.meta.url); const pkg = require("../package.json"); @@ -28,9 +28,8 @@ export default async function register(app) { functionsByType: { preStartup: [fulfillmentMethodPickupStorePreStartup], startup: [fulfillmentMethodPickupStoreStartup], - validateOrderMethods: [validateOrderMethodsstore], - getFulfillmentMethodsWithQuotes: [getFulfillmentMethodsWithQuotesPickupStore], - getFulfillmentMethodsWithQuotesPickup: [getFulfillmentMethodsWithQuotesPickupStore] + validateOrderMethods: [{ key: "store", handler: validateOrderMethodsStore }], + getFulfillmentMethodsWithQuotes: [{ key: "pickup", handler: getFulfillmentMethodsWithQuotesPickupStore }] } }); } diff --git a/packages/api-plugin-fulfillment-method-pickup-store/src/startup.js b/packages/api-plugin-fulfillment-method-pickup-store/src/startup.js index 1f93a05501c..6403f481e6f 100644 --- a/packages/api-plugin-fulfillment-method-pickup-store/src/startup.js +++ b/packages/api-plugin-fulfillment-method-pickup-store/src/startup.js @@ -3,7 +3,6 @@ import checkAndCreateFulfillmentMethod from "./util/checkAndCreateFulfillmentMet /** * @summary Called on startup to create the root entry of this fulfillment type in Fulfillment collection * @param {Object} context Startup context - * @param {Object} context.collections Map of MongoDB collections * @returns {undefined} */ export default async function fulfillmentMethodPickupStoreStartup(context) { diff --git a/packages/api-plugin-fulfillment-method-pickup-store/src/util/checkAndCreateFulfillmentMethod.js b/packages/api-plugin-fulfillment-method-pickup-store/src/util/checkAndCreateFulfillmentMethod.js index df5df2b78cd..23b956a4b65 100644 --- a/packages/api-plugin-fulfillment-method-pickup-store/src/util/checkAndCreateFulfillmentMethod.js +++ b/packages/api-plugin-fulfillment-method-pickup-store/src/util/checkAndCreateFulfillmentMethod.js @@ -6,8 +6,7 @@ import ReactionError from "@reactioncommerce/reaction-error"; * @returns {Boolean} true if entry exist or insert success else false */ export default async function checkAndCreateFulfillmentMethod(context, shopId) { - const { collections } = context; - const { Fulfillment } = collections; + const { collections: { Fulfillment } } = context; const pickupRecord = await Fulfillment.findOne({ fulfillmentType: "pickup", shopId }); if (!pickupRecord) throw new ReactionError("server-error", "Unable to create fulfillment method Pickup-Store without defined type"); diff --git a/packages/api-plugin-fulfillment-method-pickup-store/src/util/validateOrderMethodsstore.js b/packages/api-plugin-fulfillment-method-pickup-store/src/util/validateOrderMethodsstore.js index cf6b6c4aa0b..91e985d2716 100644 --- a/packages/api-plugin-fulfillment-method-pickup-store/src/util/validateOrderMethodsstore.js +++ b/packages/api-plugin-fulfillment-method-pickup-store/src/util/validateOrderMethodsstore.js @@ -6,17 +6,18 @@ * @returns {Object[]} validationResults - with the validation details populated */ export default function validateOrderMethodsstore(context, commonOrder, validationResults = []) { -// const { items, shippingAddress } = commonOrder; +// This is a dummy code to demo how validation results could be returned. +// Commenting out since the placeOrder will fail if the error record is pushed. - const validationResult = { - errorName: "invalid", - errorType: "ReactionError", - errorField: "Store pickup - some field", - fieldValue: "field-value", - errorMessage: "Customer address not available to find nearest store" - }; + // const validationResult = { + // errorName: "invalid", + // errorType: "ReactionError", + // errorField: "Store pickup - some field", + // fieldValue: "field-value", + // errorMessage: "Customer address not available to find nearest store" + // }; - validationResults.push(validationResult); + // validationResults.push(validationResult); return validationResults; } diff --git a/packages/api-plugin-fulfillment-type-pickup/.gitignore b/packages/api-plugin-fulfillment-type-pickup/.gitignore deleted file mode 100644 index ad46b30886f..00000000000 --- a/packages/api-plugin-fulfillment-type-pickup/.gitignore +++ /dev/null @@ -1,61 +0,0 @@ -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* - -# Runtime data -pids -*.pid -*.seed -*.pid.lock - -# Directory for instrumented libs generated by jscoverage/JSCover -lib-cov - -# Coverage directory used by tools like istanbul -coverage - -# nyc test coverage -.nyc_output - -# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) -.grunt - -# Bower dependency directory (https://bower.io/) -bower_components - -# node-waf configuration -.lock-wscript - -# Compiled binary addons (https://nodejs.org/api/addons.html) -build/Release - -# Dependency directories -node_modules/ -jspm_packages/ - -# TypeScript v1 declaration files -typings/ - -# Optional npm cache directory -.npm - -# Optional eslint cache -.eslintcache - -# Optional REPL history -.node_repl_history - -# Output of 'npm pack' -*.tgz - -# Yarn Integrity file -.yarn-integrity - -# dotenv environment variables file -.env - -# next.js build output -.next diff --git a/packages/api-plugin-fulfillment-type-pickup/README.md b/packages/api-plugin-fulfillment-type-pickup/README.md index a3c2587c38a..0e6590ad696 100644 --- a/packages/api-plugin-fulfillment-type-pickup/README.md +++ b/packages/api-plugin-fulfillment-type-pickup/README.md @@ -1,44 +1,12 @@ # api-plugin-fulfillment-type-pickup -[![npm (scoped)](https://img.shields.io/npm/v/@reactioncommerce/api-plugin-fulfillment-type-pickup.svg)](https://www.npmjs.com/package/@reactioncommerce/api-plugin-fulfillment-type-pickup) -[![CircleCI](https://circleci.com/gh/reactioncommerce/api-plugin-fulfillment-type-pickup.svg?style=svg)](https://circleci.com/gh/reactioncommerce/api-plugin-fulfillment-type-pickup) +This is a fulfillment-type plugin which which works along with the base `api-plugin-fulfillment` and other fulfillment-method plugins (like pickup-store). This plugin provides the basic updates needed to establish the fulfillment-type. +Each of the newly introduced fulfillment-method plugins under this fulfillment-type (pickup) would need to bee implemented as separate plugins. -## Summary - -This plugin implements fulfillment type pickup plugin for [Reaction API](https://github.com/reactioncommerce/reaction). A sample Fulfillment method under 'pickup' is implemented via plugin api-plugin-fulfillment-method-pickup-store. - -The `Developer Certificate of Origin` and `License` sections can stay as they are, assuming `Apache 2` license is used (our preferred license). All other sections of this README should be updated to reflect your plugin. - -## Included in this fulfillment-type-pickup plugin - -### `src/` - -The `src` folder is where you'll put all the plugin files. - -### `.gitignore` - -A basic `gitignore` file - -### `babel.config.cjs` - -If your plugin includes linting and tests, this file is required to allow esmodules to run correctly. - -### `jest.config.cjs` - -If your plugin includes tests, this file is required to allow esmodules to run correctly. You'll need to update the `transformIgnorePatterns` and `moduleNameMapper` sections to include any esmodule `npm` packages used in your plugin. - -### `License.md` - -If your plugin uses `Apache 2` licensing, you can leave this file as-is. If another type of licensing is used, you need to update this file, and the README, accordingly. - -### `package.json` - -The provided `package.json` is set up to install all needed packages and config for linting, testing, and semantic-release. You'll need to update the `name`, `description`, and add any new dependencies your plugin files use. - -### `index.js` - -The entrypoint file for your npm package, will most likely just export your plugin registration from the `src` folder. +This plugins does the following: +* Registers the 'pickup' as a ff-type via registeredFulfillmentTypes: ["pickup"] +* Inserts the default entry for pickup ff-type in Fulfillment collection ## Developer Certificate of Origin We use the [Developer Certificate of Origin (DCO)](https://developercertificate.org/) in lieu of a Contributor License Agreement for all contributions to Reaction Commerce open source projects. We request that contributors agree to the terms of the DCO and indicate that agreement by signing all commits made to Reaction Commerce projects by adding a line with your name and email address to every Git commit message contributed: diff --git a/packages/api-plugin-fulfillment-type-pickup/src/checkAndCreateFulfillmentType.js b/packages/api-plugin-fulfillment-type-pickup/src/checkAndCreateFulfillmentType.js index a6fdc156f81..945cc4f98e7 100644 --- a/packages/api-plugin-fulfillment-type-pickup/src/checkAndCreateFulfillmentType.js +++ b/packages/api-plugin-fulfillment-type-pickup/src/checkAndCreateFulfillmentType.js @@ -5,8 +5,7 @@ * @returns {Boolean} true if entry exist or insert success else false */ export default async function checkAndCreateFulfillmentType(context, shopId) { - const { collections } = context; - const { Fulfillment } = collections; + const { collections: { Fulfillment } } = context; const pickupRecord = await Fulfillment.findOne({ fulfillmentType: "pickup", shopId }); if (!pickupRecord) { From ef301ba56844d35053f0bb78a0d1a487c1bd5861 Mon Sep 17 00:00:00 2001 From: Sujith Date: Sat, 29 Oct 2022 11:33:26 +0530 Subject: [PATCH 3/7] fix: import fix Signed-off-by: Sujith --- .../api-plugin-fulfillment-method-pickup-store/src/startup.js | 2 +- packages/api-plugin-fulfillment-type-pickup/package.json | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/api-plugin-fulfillment-method-pickup-store/src/startup.js b/packages/api-plugin-fulfillment-method-pickup-store/src/startup.js index 6403f481e6f..10547ae3ee0 100644 --- a/packages/api-plugin-fulfillment-method-pickup-store/src/startup.js +++ b/packages/api-plugin-fulfillment-method-pickup-store/src/startup.js @@ -1,5 +1,5 @@ import ReactionError from "@reactioncommerce/reaction-error"; -import checkAndCreateFulfillmentMethod from "./util/checkAndCreateFulfillmentMethod"; +import checkAndCreateFulfillmentMethod from "./util/checkAndCreateFulfillmentMethod.js"; /** * @summary Called on startup to create the root entry of this fulfillment type in Fulfillment collection * @param {Object} context Startup context diff --git a/packages/api-plugin-fulfillment-type-pickup/package.json b/packages/api-plugin-fulfillment-type-pickup/package.json index 818316cc1e2..0695628b175 100644 --- a/packages/api-plugin-fulfillment-type-pickup/package.json +++ b/packages/api-plugin-fulfillment-type-pickup/package.json @@ -29,7 +29,8 @@ "sideEffects": false, "dependencies": { "@reactioncommerce/api-utils": "^1.16.9", - "@reactioncommerce/random": "~1.0.2" + "@reactioncommerce/random": "~1.0.2", + "@reactioncommerce/reaction-error": "1.0.1" }, "devDependencies": {}, "scripts": { From d202e97f6a8ac09c4f8ae8da93b5a89928bcb677 Mon Sep 17 00:00:00 2001 From: Sujith Date: Thu, 3 Nov 2022 15:15:03 +0530 Subject: [PATCH 4/7] fix: pnpm-lock update without snyk Signed-off-by: Sujith --- pnpm-lock.yaml | 26 ++++---------------------- 1 file changed, 4 insertions(+), 22 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a54914d1c58..b0d87ef02a8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -694,45 +694,27 @@ importers: '@reactioncommerce/api-plugin-carts': link:../api-plugin-carts '@reactioncommerce/data-factory': 1.0.1 - packages/api-plugin-fulfillment-method-shipping-flat-rate: + packages/api-plugin-fulfillment-method-pickup-store: specifiers: '@reactioncommerce/api-utils': ^1.16.9 - '@reactioncommerce/logger': ^1.1.3 - '@reactioncommerce/random': ~1.0.2 + '@reactioncommerce/logger': ^1.1.4 '@reactioncommerce/reaction-error': ^1.0.1 simpl-schema: ^1.12.2 dependencies: '@reactioncommerce/api-utils': link:../api-utils '@reactioncommerce/logger': link:../logger - '@reactioncommerce/random': link:../random - '@reactioncommerce/reaction-error': link:../reaction-error - simpl-schema: 1.12.3 - - packages/api-plugin-fulfillment-method-shipping-ups: - specifiers: - '@reactioncommerce/api-utils': ^1.16.9 - '@reactioncommerce/reaction-error': ^1.0.1 - simpl-schema: ^1.12.2 - dependencies: - '@reactioncommerce/api-utils': link:../api-utils '@reactioncommerce/reaction-error': link:../reaction-error simpl-schema: 1.12.3 - packages/api-plugin-fulfillment-type-shipping: + packages/api-plugin-fulfillment-type-pickup: specifiers: '@reactioncommerce/api-utils': ^1.16.9 - '@reactioncommerce/logger': ^1.1.3 '@reactioncommerce/random': ~1.0.2 - '@reactioncommerce/reaction-error': ^1.0.1 - lodash: ^4.17.21 - simpl-schema: ^1.12.2 + '@reactioncommerce/reaction-error': 1.0.1 dependencies: '@reactioncommerce/api-utils': link:../api-utils - '@reactioncommerce/logger': link:../logger '@reactioncommerce/random': link:../random '@reactioncommerce/reaction-error': link:../reaction-error - lodash: 4.17.21 - simpl-schema: 1.12.3 packages/api-plugin-i18n: specifiers: From 6e1c711441442a7e4abee005a7d048936de3d035 Mon Sep 17 00:00:00 2001 From: Sujith Date: Thu, 17 Nov 2022 13:17:24 +0530 Subject: [PATCH 5/7] fix: review comment fixes Signed-off-by: Sujith --- .../api-plugin-fulfillment-method-pickup-store/README.md | 2 +- ...tore.js => fulfillmentMethodsWithQuotesPickupStore.js} | 6 +++--- ...js => fulfillmentMethodsWithQuotesPickupStore.test.js} | 8 ++++---- .../src/index.js | 4 ++-- .../src/schemas/schema.graphql | 8 ++++---- .../checkAndCreateFulfillmentMethod.test.js.snap | 3 --- .../src/util/checkAndCreateFulfillmentMethod.test.js | 5 ++--- .../src/util/validateOrderMethodsstore.js | 2 +- 8 files changed, 17 insertions(+), 21 deletions(-) rename packages/api-plugin-fulfillment-method-pickup-store/src/{getFulfillmentMethodsWithQuotesPickupStore.js => fulfillmentMethodsWithQuotesPickupStore.js} (90%) rename packages/api-plugin-fulfillment-method-pickup-store/src/{getFulfillmentMethodsWithQuotesPickupStore.test.js => fulfillmentMethodsWithQuotesPickupStore.test.js} (86%) delete mode 100644 packages/api-plugin-fulfillment-method-pickup-store/src/util/__snapshots__/checkAndCreateFulfillmentMethod.test.js.snap diff --git a/packages/api-plugin-fulfillment-method-pickup-store/README.md b/packages/api-plugin-fulfillment-method-pickup-store/README.md index 3fa11ee62fa..9ccd75f9b54 100644 --- a/packages/api-plugin-fulfillment-method-pickup-store/README.md +++ b/packages/api-plugin-fulfillment-method-pickup-store/README.md @@ -3,7 +3,7 @@ This is a fulfillment-method plugin which which works along with the base `api-plugin-fulfillment` and the ff-type plugin `api-plugin-fulfillment-type-pickup`. This plugin actually implements all the functionality associated with this fulfillment-method. It can work along-side other fulfillment-methods under the same ff-type. This main features/functionalities of this plugin includes the following: -* getFulfillmentMethodsWithQuotesPickupStore - returns the quote or equivalent details for the method when called from base ff plugin +* fulfillmentMethodsWithQuotesPickupStore - returns the quote or equivalent details for the method when called from base ff plugin * preStartup - extends the union of "methodAdditionalData" with data structure specific to Store * startup - Inserts the required ff-method entry into Fulfillment collection * util/checkAndCreateFulfillmentMethod - confirms existing ff-type entry and adds a new ff-method under it. diff --git a/packages/api-plugin-fulfillment-method-pickup-store/src/getFulfillmentMethodsWithQuotesPickupStore.js b/packages/api-plugin-fulfillment-method-pickup-store/src/fulfillmentMethodsWithQuotesPickupStore.js similarity index 90% rename from packages/api-plugin-fulfillment-method-pickup-store/src/getFulfillmentMethodsWithQuotesPickupStore.js rename to packages/api-plugin-fulfillment-method-pickup-store/src/fulfillmentMethodsWithQuotesPickupStore.js index a43f8693704..08ebe85cfc9 100644 --- a/packages/api-plugin-fulfillment-method-pickup-store/src/getFulfillmentMethodsWithQuotesPickupStore.js +++ b/packages/api-plugin-fulfillment-method-pickup-store/src/fulfillmentMethodsWithQuotesPickupStore.js @@ -4,7 +4,7 @@ import collectStoreDetails from "./util/collectStoreDetails.js"; const packageName = "fulfillment-method-pickup-store"; const fulfillmentTypeName = "pickup"; const fulfillmentMethodName = "store"; -const logCtx = { name: "fulfillment-method-pickup-store", file: "getFulfillmentMethodsWithQuotesPickupStore" }; +const logCtx = { name: "fulfillment-method-pickup-store", file: "fulfillmentMethodsWithQuotesPickupStore" }; /** * @summary Returns a list of fulfillment method quotes based on the items in a fulfillment group. @@ -18,7 +18,7 @@ const logCtx = { name: "fulfillment-method-pickup-store", file: "getFulfillmentM * shipping rates. * @private */ -export default async function getFulfillmentMethodsWithQuotesPickupStore(context, commonOrder, previousQueryResults = []) { +export default async function fulfillmentMethodsWithQuotesPickupStore(context, commonOrder, previousQueryResults = []) { const { collections: { Fulfillment } } = context; const [rates = [], retrialTargets = []] = previousQueryResults; const currentMethodInfo = { packageName }; @@ -70,6 +70,6 @@ export default async function getFulfillmentMethodsWithQuotesPickupStore(context return [rates, retrialTargets]; } - Logger.debug({ ...logCtx, rates }, "Store getFulfillmentMethodsWithQuotesPickupStore"); + Logger.debug({ ...logCtx, rates }, "Store fulfillmentMethodsWithQuotesPickupStore"); return [rates, retrialTargets]; } diff --git a/packages/api-plugin-fulfillment-method-pickup-store/src/getFulfillmentMethodsWithQuotesPickupStore.test.js b/packages/api-plugin-fulfillment-method-pickup-store/src/fulfillmentMethodsWithQuotesPickupStore.test.js similarity index 86% rename from packages/api-plugin-fulfillment-method-pickup-store/src/getFulfillmentMethodsWithQuotesPickupStore.test.js rename to packages/api-plugin-fulfillment-method-pickup-store/src/fulfillmentMethodsWithQuotesPickupStore.test.js index a9c378d8bfb..ac9a23d8a16 100644 --- a/packages/api-plugin-fulfillment-method-pickup-store/src/getFulfillmentMethodsWithQuotesPickupStore.test.js +++ b/packages/api-plugin-fulfillment-method-pickup-store/src/fulfillmentMethodsWithQuotesPickupStore.test.js @@ -1,5 +1,5 @@ import mockContext from "@reactioncommerce/api-utils/tests/mockContext.js"; -import getFulfillmentMethodsWithQuotesPickupStore from "./getFulfillmentMethodsWithQuotesPickupStore.js"; +import fulfillmentMethodsWithQuotesPickupStore from "./fulfillmentMethodsWithQuotesPickupStore.js"; test("should return previousResults if pickup is not among FailedRequests", async () => { const commonOrder = { @@ -12,7 +12,7 @@ test("should return previousResults if pickup is not among FailedRequests", asyn } ] ]; - const result = await getFulfillmentMethodsWithQuotesPickupStore(mockContext, commonOrder, previousResults); + const result = await fulfillmentMethodsWithQuotesPickupStore(mockContext, commonOrder, previousResults); expect(result).toEqual(previousResults); }); @@ -36,7 +36,7 @@ test("should return previousResults if not fulfillment records enabled", async ( mockContext.collections.Fulfillment = { find: jest.fn(() => ({ toArray: () => [] })) }; - const result = await getFulfillmentMethodsWithQuotesPickupStore(mockContext, commonOrder, previousResults); + const result = await fulfillmentMethodsWithQuotesPickupStore(mockContext, commonOrder, previousResults); expect(result).toEqual(previousResults); }); @@ -123,6 +123,6 @@ test("should return rates witout error", async () => { mockContext.collections.Fulfillment = { find: jest.fn(() => ({ toArray: () => [pickupDoc] })) }; - const result = await getFulfillmentMethodsWithQuotesPickupStore(mockContext, commonOrder, previousResults); + const result = await fulfillmentMethodsWithQuotesPickupStore(mockContext, commonOrder, previousResults); expect(result).toEqual(expectedResult); }); diff --git a/packages/api-plugin-fulfillment-method-pickup-store/src/index.js b/packages/api-plugin-fulfillment-method-pickup-store/src/index.js index f26c434e73f..05bb2603785 100644 --- a/packages/api-plugin-fulfillment-method-pickup-store/src/index.js +++ b/packages/api-plugin-fulfillment-method-pickup-store/src/index.js @@ -3,7 +3,7 @@ import fulfillmentMethodPickupStorePreStartup from "./preStartup.js"; import fulfillmentMethodPickupStoreStartup from "./startup.js"; import { MethodStoreData } from "./simpleSchemas.js"; import schemas from "./schemas/index.js"; -import getFulfillmentMethodsWithQuotesPickupStore from "./getFulfillmentMethodsWithQuotesPickupStore.js"; +import fulfillmentMethodsWithQuotesPickupStore from "./fulfillmentMethodsWithQuotesPickupStore.js"; import validateOrderMethodsStore from "./util/validateOrderMethodsStore.js"; const require = createRequire(import.meta.url); @@ -29,7 +29,7 @@ export default async function register(app) { preStartup: [fulfillmentMethodPickupStorePreStartup], startup: [fulfillmentMethodPickupStoreStartup], validateOrderMethods: [{ key: "store", handler: validateOrderMethodsStore }], - getFulfillmentMethodsWithQuotes: [{ key: "pickup", handler: getFulfillmentMethodsWithQuotesPickupStore }] + fulfillmentMethodsWithQuotes: [{ key: "pickup", handler: fulfillmentMethodsWithQuotesPickupStore }] } }); } diff --git a/packages/api-plugin-fulfillment-method-pickup-store/src/schemas/schema.graphql b/packages/api-plugin-fulfillment-method-pickup-store/src/schemas/schema.graphql index fccb208a807..7eea8eac63b 100644 --- a/packages/api-plugin-fulfillment-method-pickup-store/src/schemas/schema.graphql +++ b/packages/api-plugin-fulfillment-method-pickup-store/src/schemas/schema.graphql @@ -1,14 +1,14 @@ "Additional data fields from Store" -type storeFields { +type StoreFields { storeId: String storeAddress: String storeTiming: String } "Additional Store data - Pickup" -type storeData { +type StoreData { gqlType: String - storeData: [storeFields] + storeData: [StoreFields] } -extend union AdditionalData = storeData +extend union AdditionalData = StoreData diff --git a/packages/api-plugin-fulfillment-method-pickup-store/src/util/__snapshots__/checkAndCreateFulfillmentMethod.test.js.snap b/packages/api-plugin-fulfillment-method-pickup-store/src/util/__snapshots__/checkAndCreateFulfillmentMethod.test.js.snap deleted file mode 100644 index e08c70088fd..00000000000 --- a/packages/api-plugin-fulfillment-method-pickup-store/src/util/__snapshots__/checkAndCreateFulfillmentMethod.test.js.snap +++ /dev/null @@ -1,3 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should throw error and NOT call createFulfillmentMethod mutation 1`] = `"Unable to create fulfillment method Pickup-Store without defined type"`; diff --git a/packages/api-plugin-fulfillment-method-pickup-store/src/util/checkAndCreateFulfillmentMethod.test.js b/packages/api-plugin-fulfillment-method-pickup-store/src/util/checkAndCreateFulfillmentMethod.test.js index 9bb9869eff8..7b817c13be4 100644 --- a/packages/api-plugin-fulfillment-method-pickup-store/src/util/checkAndCreateFulfillmentMethod.test.js +++ b/packages/api-plugin-fulfillment-method-pickup-store/src/util/checkAndCreateFulfillmentMethod.test.js @@ -83,7 +83,6 @@ test("should throw error and NOT call createFulfillmentMethod mutation", async ( }; mockContext.mutations.createFulfillmentMethod = jest.fn().mockName("createFulfillmentMethod").mockReturnValueOnce(Promise.resolve(method)); - await expect(checkAndCreateFulfillmentMethod(mockContext, { shopId, fulfillmentTypeId, method })).rejects.toThrowErrorMatchingSnapshot(); - // await checkAndCreateFulfillmentMethod(mockContext, { shopId, fulfillmentTypeId, method }); - // expect(mockContext.mutations.createFulfillmentMethod).toHaveBeenCalled(); + const expectedError = "Unable to create fulfillment method Pickup-Store without defined type"; + await expect(checkAndCreateFulfillmentMethod(mockContext, { shopId, fulfillmentTypeId, method })).rejects.toThrowError(expectedError); }); diff --git a/packages/api-plugin-fulfillment-method-pickup-store/src/util/validateOrderMethodsstore.js b/packages/api-plugin-fulfillment-method-pickup-store/src/util/validateOrderMethodsstore.js index 91e985d2716..9f7b08c7f3b 100644 --- a/packages/api-plugin-fulfillment-method-pickup-store/src/util/validateOrderMethodsstore.js +++ b/packages/api-plugin-fulfillment-method-pickup-store/src/util/validateOrderMethodsstore.js @@ -5,7 +5,7 @@ * @param {Object[]} validationResults - Validation results collected till now * @returns {Object[]} validationResults - with the validation details populated */ -export default function validateOrderMethodsstore(context, commonOrder, validationResults = []) { +export default function validateOrderMethodsStore(context, commonOrder, validationResults = []) { // This is a dummy code to demo how validation results could be returned. // Commenting out since the placeOrder will fail if the error record is pushed. From 9f16175e537d4f126141e2216171b9389f07d7d0 Mon Sep 17 00:00:00 2001 From: Sujith Date: Fri, 10 Mar 2023 21:50:16 +0530 Subject: [PATCH 6/7] fix: review comment updates Signed-off-by: Sujith --- ...fulfillmentMethodsWithQuotesPickupStore.js | 35 +++++++++---------- .../src/startup.js | 3 -- .../src/checkAndCreateFulfillmentType.js | 27 +++++++------- .../src/startup.js | 3 -- 4 files changed, 30 insertions(+), 38 deletions(-) diff --git a/packages/api-plugin-fulfillment-method-pickup-store/src/fulfillmentMethodsWithQuotesPickupStore.js b/packages/api-plugin-fulfillment-method-pickup-store/src/fulfillmentMethodsWithQuotesPickupStore.js index 08ebe85cfc9..80ef4240cb7 100644 --- a/packages/api-plugin-fulfillment-method-pickup-store/src/fulfillmentMethodsWithQuotesPickupStore.js +++ b/packages/api-plugin-fulfillment-method-pickup-store/src/fulfillmentMethodsWithQuotesPickupStore.js @@ -30,34 +30,33 @@ export default async function fulfillmentMethodsWithQuotesPickupStore(context, c } } - const pickupDocs = await Fulfillment.find({ + const pickupFulfillmentType = await Fulfillment.findOne({ "shopId": commonOrder.shopId, "fulfillmentType": fulfillmentTypeName, "provider.enabled": true - }).toArray(); - if (!pickupDocs || !pickupDocs.length) { + }); + if (!pickupFulfillmentType) { return [rates, retrialTargets]; } const initialNumOfRates = rates.length; - pickupDocs.map(async (doc) => { - const carrier = doc.provider.label; - const currentPluginMethods = doc.methods.filter((method) => ((method.name === fulfillmentMethodName) && (method.enabled))); - for (const method of currentPluginMethods) { - const updatedMethod = collectStoreDetails(method, commonOrder); + const carrier = pickupFulfillmentType.provider?.label || ""; + const currentPluginMethods = pickupFulfillmentType.methods ? + pickupFulfillmentType.methods.filter((method) => ((method.name === fulfillmentMethodName) && (method.enabled))) : []; - rates.push({ - carrier, - handlingPrice: updatedMethod.handling, - method: updatedMethod, - rate: updatedMethod.rate, - shippingPrice: updatedMethod.rate + updatedMethod.handling, - shopId: doc.shopId - }); - } - }); + for (const method of currentPluginMethods) { + const updatedMethod = collectStoreDetails(method, commonOrder); + rates.push({ + carrier, + handlingPrice: updatedMethod.handling, + method: updatedMethod, + rate: updatedMethod.rate, + shippingPrice: updatedMethod.rate + updatedMethod.handling, + shopId: pickupFulfillmentType.shopId + }); + } if (rates.length === initialNumOfRates) { const errorDetails = { diff --git a/packages/api-plugin-fulfillment-method-pickup-store/src/startup.js b/packages/api-plugin-fulfillment-method-pickup-store/src/startup.js index 10547ae3ee0..b01c43f3053 100644 --- a/packages/api-plugin-fulfillment-method-pickup-store/src/startup.js +++ b/packages/api-plugin-fulfillment-method-pickup-store/src/startup.js @@ -10,9 +10,6 @@ export default async function fulfillmentMethodPickupStoreStartup(context) { const { shop } = payload; const shopId = shop._id; - // We do not have validatePermissions in context during this startup stage, hence commenting below - // await context.validatePermissions("reaction:legacy:fulfillmentTypes", "read", { shopId }); - const insertedMethod = await checkAndCreateFulfillmentMethod(context, shopId); if (!insertedMethod) { throw new ReactionError("server-error", "Error in creating fulfillment method"); diff --git a/packages/api-plugin-fulfillment-type-pickup/src/checkAndCreateFulfillmentType.js b/packages/api-plugin-fulfillment-type-pickup/src/checkAndCreateFulfillmentType.js index 945cc4f98e7..1ae1516f9d6 100644 --- a/packages/api-plugin-fulfillment-type-pickup/src/checkAndCreateFulfillmentType.js +++ b/packages/api-plugin-fulfillment-type-pickup/src/checkAndCreateFulfillmentType.js @@ -7,19 +7,18 @@ export default async function checkAndCreateFulfillmentType(context, shopId) { const { collections: { Fulfillment } } = context; - const pickupRecord = await Fulfillment.findOne({ fulfillmentType: "pickup", shopId }); - if (!pickupRecord) { - const groupInfo = { - name: "Pickup Provider", - shopId, - provider: { - enabled: true, - label: "Pickup", - name: "pickup" - }, - fulfillmentType: "pickup" - }; - await context.mutations.createFulfillmentType(context.getInternalContext(), groupInfo); - } + const pickupFulfillmentType = await Fulfillment.findOne({ fulfillmentType: "pickup", shopId }); + if (pickupFulfillmentType) return true; + const newPickupFulfillmentType = { + name: "Pickup Provider", + shopId, + provider: { + enabled: true, + label: "Pickup", + name: "pickup" + }, + fulfillmentType: "pickup" + }; + await context.mutations.createFulfillmentType(context.getInternalContext(), newPickupFulfillmentType); return true; } diff --git a/packages/api-plugin-fulfillment-type-pickup/src/startup.js b/packages/api-plugin-fulfillment-type-pickup/src/startup.js index 401c8f4d7ad..37f937ff585 100644 --- a/packages/api-plugin-fulfillment-type-pickup/src/startup.js +++ b/packages/api-plugin-fulfillment-type-pickup/src/startup.js @@ -11,9 +11,6 @@ export default async function fulfillmentTypePickupStartup(context) { const { shop } = payload; const shopId = shop._id; - // We do not have validatePermissions in context during this startup stage, hence commenting below - // await context.validatePermissions("reaction:legacy:fulfillmentTypes", "read", { shopId }); - const insertSuccess = await checkAndCreateFulfillmentType(context, shopId); if (!insertSuccess) { throw new ReactionError("server-error", "Error in creating fulfillment type"); From 22b0c79f5e13f55a90d8378de3f10b21d4f639d1 Mon Sep 17 00:00:00 2001 From: Sujith Date: Fri, 10 Mar 2023 22:27:56 +0530 Subject: [PATCH 7/7] fix: unit test fix Signed-off-by: Sujith --- .../src/fulfillmentMethodsWithQuotesPickupStore.test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/api-plugin-fulfillment-method-pickup-store/src/fulfillmentMethodsWithQuotesPickupStore.test.js b/packages/api-plugin-fulfillment-method-pickup-store/src/fulfillmentMethodsWithQuotesPickupStore.test.js index ac9a23d8a16..1a50f81c2db 100644 --- a/packages/api-plugin-fulfillment-method-pickup-store/src/fulfillmentMethodsWithQuotesPickupStore.test.js +++ b/packages/api-plugin-fulfillment-method-pickup-store/src/fulfillmentMethodsWithQuotesPickupStore.test.js @@ -34,7 +34,7 @@ test("should return previousResults if not fulfillment records enabled", async ( }; mockContext.collections.Fulfillment = { - find: jest.fn(() => ({ toArray: () => [] })) + findOne: jest.fn(() => ({})) }; const result = await fulfillmentMethodsWithQuotesPickupStore(mockContext, commonOrder, previousResults); expect(result).toEqual(previousResults); @@ -121,7 +121,7 @@ test("should return rates witout error", async () => { const expectedResult = [[...previousResults[0], expectedNewRate], []]; mockContext.collections.Fulfillment = { - find: jest.fn(() => ({ toArray: () => [pickupDoc] })) + findOne: jest.fn(() => (pickupDoc)) }; const result = await fulfillmentMethodsWithQuotesPickupStore(mockContext, commonOrder, previousResults); expect(result).toEqual(expectedResult);