Adds Middleware API (#40)

* Adds test project

* Adds e2e for end-2-end tests

* Update gitignore

* Update loaders

* Creates test project

* v0.1.27

* Upgrade

* dependency fixes

* Load project plugins in the plugin loader

* Issue with instanceof

* Fixes versioning issues

* Adds medusa middleware api

* Adds documentation

* Fixes tests
This commit is contained in:
Sebastian Rindom authored and GitHub committed 2020-05-01 13:33:03 +02:00
1 parent 964e26e310
commit 516bc7675d
46 files changed
+10163 -977

No files matched your search

+9
View File
@@ -0,0 +1,9 @@
{
"plugins": ["prettier"],
"extends": ["prettier"],
"rules": {
"prettier/prettier": "error",
"semi": "error",
"no-unused-expressions": "true"
}
}
+7
View File
@@ -0,0 +1,7 @@
{
"endOfLine": "lf",
"semi": false,
"singleQuote": false,
"tabWidth": 2,
"trailingComma": "es5"
}
+4 -1
View File
@@ -19,11 +19,14 @@
"@babel/cli": "^7.7.5",
"@babel/core": "^7.7.5",
"@babel/plugin-proposal-class-properties": "^7.7.4",
"@babel/plugin-transform-classes": "^7.9.5",
"@babel/plugin-transform-runtime": "^7.7.6",
"@babel/preset-env": "^7.7.5",
"@babel/runtime": "^7.9.6",
"cross-env": "^5.2.1",
"eslint": "^6.8.0",
"jest": "^25.5.2",
"eslint": "^6.8.0"
"prettier": "^1.19.1"
},
"dependencies": {
"@hapi/joi": "^16.1.8",
@@ -0,0 +1,18 @@
import Module from "module"
import path from "path"
const fallback = filename => {
const mod = new Module(filename)
mod.filename = filename
mod.paths = Module._nodeModulePaths(path.dirname(filename))
mod._compile(`module.exports = require;`, filename)
return mod.exports
}
// Polyfill Node's `Module.createRequireFromPath` if not present (added in Node v10.12.0)
const createRequireFromPath =
Module.createRequire || Module.createRequireFromPath || fallback
export default createRequireFromPath
@@ -0,0 +1,23 @@
import path from "path"
/**
* Attempts to resolve the config file in a given root directory.
* @param {string} rootDir - the directory to find the config file in.
* @param {string} configName - the name of the config file.
* @return {object} an object containing the config module and its path.
*/
function getConfigFile(rootDir, configName) {
const configPath = path.join(rootDir, configName)
let configFilePath = ``
let configModule
try {
configFilePath = require.resolve(configPath)
configModule = require(configFilePath)
} catch (err) {
return {}
}
return { configModule, configFilePath }
}
export default getConfigFile
+2
View File
@@ -1,2 +1,4 @@
export { default as Validator } from "./validator"
export { default as MedusaError } from "./errors"
export { default as getConfigFile } from "./get-config-file"
export { default as createRequireFromPath } from "./create-require-from-path"
+29 -1
View File
@@ -559,6 +559,20 @@
"@babel/helper-split-export-declaration" "^7.8.3"
globals "^11.1.0"
"@babel/plugin-transform-classes@^7.9.5":
version "7.9.5"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.9.5.tgz#800597ddb8aefc2c293ed27459c1fcc935a26c2c"
integrity sha512-x2kZoIuLC//O5iA7PEvecB105o7TLzZo8ofBVhP79N+DO3jaX+KYfww9TQcfBEZD0nikNyYcGB1IKtRq36rdmg==
dependencies:
"@babel/helper-annotate-as-pure" "^7.8.3"
"@babel/helper-define-map" "^7.8.3"
"@babel/helper-function-name" "^7.9.5"
"@babel/helper-optimise-call-expression" "^7.8.3"
"@babel/helper-plugin-utils" "^7.8.3"
"@babel/helper-replace-supers" "^7.8.6"
"@babel/helper-split-export-declaration" "^7.8.3"
globals "^11.1.0"
"@babel/plugin-transform-computed-properties@^7.8.3":
version "7.8.3"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.8.3.tgz#96d0d28b7f7ce4eb5b120bb2e0e943343c86f81b"
@@ -880,7 +894,7 @@
globals "^11.1.0"
lodash "^4.17.13"
"@babel/types@^7.0.0", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.8.6", "@babel/types@^7.9.0", "@babel/types@^7.9.5", "@babel/types@^7.9.6":
"@babel/types@^7.0.0", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.9.0", "@babel/types@^7.9.6":
version "7.9.6"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.9.6.tgz#2c5502b427251e9de1bd2dff95add646d95cc9f7"
integrity sha512-qxXzvBO//jO9ZnoasKF1uJzHd2+M6Q2ZPIVfnFps8JJvXy0ZBbwbNOmE6SGIY5XOY6d1Bo5lb9d9RJ8nv3WSeA==
@@ -898,6 +912,15 @@
lodash "^4.17.13"
to-fast-properties "^2.0.0"
"@babel/types@^7.8.6", "@babel/types@^7.9.5":
version "7.9.5"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.9.5.tgz#89231f82915a8a566a703b3b20133f73da6b9444"
integrity sha512-XjnvNqenk818r5zMaba+sLQjnbda31UfUURv3ei0qPQw4u+j2jMyJ5b11y8ZHYTRSI3NnInQkkkRT4fLqqPdHg==
dependencies:
"@babel/helper-validator-identifier" "^7.9.5"
lodash "^4.17.13"
to-fast-properties "^2.0.0"
"@bcoe/v8-coverage@^0.2.3":
version "0.2.3"
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
@@ -3849,6 +3872,11 @@ prelude-ls@~1.1.2:
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=
prettier@^1.19.1:
version "1.19.1"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.19.1.tgz#f7d7f5ff8a9cd872a7be4ca142095956a60797cb"
integrity sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==
pretty-format@^25.5.0:
version "25.5.0"
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-25.5.0.tgz#7873c1d774f682c34b8d48b6743a2bf2ac55791a"
@@ -16,10 +16,11 @@
"@babel/plugin-proposal-class-properties": "^7.7.4",
"@babel/plugin-transform-runtime": "^7.7.6",
"@babel/preset-env": "^7.7.5",
"@babel/runtime": "^7.9.6",
"client-sessions": "^0.8.0",
"cross-env": "^5.2.1",
"jest": "^25.5.2",
"eslint": "^6.8.0"
"eslint": "^6.8.0",
"jest": "^25.5.2"
},
"scripts": {
"build": "babel src --out-dir . --ignore **/__tests__",
@@ -27,8 +28,11 @@
"watch": "babel -w src --out-dir . --ignore **/__tests__"
},
"dependencies": {
"@babel/plugin-transform-classes": "^7.9.5",
"@babel/plugin-transform-instanceof": "^7.8.3",
"@babel/runtime": "^7.7.6",
"express": "^4.17.1",
"medusa-core-utils": "^0.3.0",
"medusa-interfaces": "^0.1.27-alpha.0"
}
}
+4 -1
View File
@@ -1,5 +1,8 @@
{
"plugins": ["@babel/plugin-proposal-class-properties"],
"plugins": [
"@babel/plugin-proposal-class-properties",
"@babel/plugin-transform-instanceof"
],
"presets": ["@babel/preset-env"],
"env": {
"test": {
+4
View File
@@ -19,15 +19,19 @@
"devDependencies": {
"@babel/cli": "^7.7.5",
"@babel/core": "^7.7.5",
"@babel/plugin-transform-classes": "^7.9.5",
"@babel/plugin-proposal-class-properties": "^7.7.4",
"@babel/plugin-transform-instanceof": "^7.8.3",
"@babel/plugin-transform-runtime": "^7.7.6",
"@babel/preset-env": "^7.7.5",
"@babel/runtime": "^7.9.6",
"cross-env": "^5.2.1",
"eslint": "^6.8.0",
"jest": "^25.5.2",
"prettier": "^1.19.1"
},
"dependencies": {
"medusa-core-utils": "^0.3.0",
"mongoose": "^5.8.0"
},
"gitHead": "35e0930650d5f4aedf2610749cd131ae8b7e17cc"
+5 -2
View File
@@ -16,10 +16,11 @@
"@babel/plugin-proposal-class-properties": "^7.7.4",
"@babel/plugin-transform-runtime": "^7.7.6",
"@babel/preset-env": "^7.7.5",
"@babel/runtime": "^7.9.6",
"client-sessions": "^0.8.0",
"cross-env": "^5.2.1",
"jest": "^25.5.2",
"eslint": "^6.8.0"
"eslint": "^6.8.0",
"jest": "^25.5.2"
},
"scripts": {
"build": "babel src --out-dir . --ignore **/__tests__",
@@ -27,9 +28,11 @@
"watch": "babel -w src --out-dir . --ignore **/__tests__"
},
"dependencies": {
"@babel/plugin-transform-classes": "^7.9.5",
"@babel/plugin-transform-instanceof": "^7.8.3",
"@babel/runtime": "^7.7.6",
"express": "^4.17.1",
"medusa-core-utils": "^0.3.0",
"medusa-interfaces": "^0.3.0"
},
"gitHead": "35e0930650d5f4aedf2610749cd131ae8b7e17cc"
+5 -1
View File
@@ -1,5 +1,9 @@
{
"plugins": ["@babel/plugin-proposal-class-properties"],
"plugins": [
"@babel/plugin-proposal-class-properties",
"@babel/plugin-transform-classes",
"@babel/plugin-transform-instanceof"
],
"presets": ["@babel/preset-env"],
"env": {
"test": {
@@ -9,9 +9,9 @@
"directory": "packages/medusa-plugin-permissions"
},
"scripts": {
"build": "babel src --out-dir dist/ --ignore **/__tests__",
"build": "babel src --out-dir . --ignore **/__tests__",
"prepare": "cross-env NODE_ENV=production npm run build",
"watch": "babel -w src --out-dir dist/ --ignore **/__tests__",
"watch": "babel -w src --out-dir . --ignore **/__tests__",
"test": "jest"
},
"author": "Oliver Juhl",
@@ -20,6 +20,8 @@
"@babel/cli": "^7.7.5",
"@babel/core": "^7.7.5",
"@babel/plugin-proposal-class-properties": "^7.7.4",
"@babel/plugin-transform-classes": "^7.9.5",
"@babel/plugin-transform-instanceof": "^7.8.3",
"@babel/plugin-transform-runtime": "^7.7.6",
"@babel/preset-env": "^7.7.5",
"@babel/runtime": "^7.7.6",
@@ -27,9 +29,9 @@
"jest": "^25.5.2"
},
"dependencies": {
"medusa-core-utils": "^1.0.0",
"medusa-interfaces": "^1.0.0",
"medusa-test-utils": "^1.0.0",
"medusa-core-utils": "^0.3.0",
"medusa-interfaces": "^0.3.0",
"medusa-test-utils": "^0.3.0",
"mongoose": "^5.8.0"
}
}
@@ -0,0 +1,16 @@
// This middleware is injected to ensure authorization of requests
// Since this middleware uses the user object on the request, this should be
// injected after authentication in the core middleware, hence we name
// the middleware postAuth.
export default {
postAuthentication: () => {
return (err, req, res, next) => {
const permissionService = req.scope.resolve("permissionService")
if (permissionService.hasPermission(req.user, req.method, req.path)) {
next()
} else {
res.status(422)
}
}
}
}
@@ -1,14 +0,0 @@
// This middleware is injected to ensure authorization of requests
// Since this middleware uses the user object on the request, this should be
// injected after authentication in the core middleware, hence we name
// the middleware postAuth.
export default postAuth = () => {
return (err, req, res, next) => {
const permissionService = req.scope.resolve("permissionService")
if (permissionService.hasPermission(req.user, req.method, req.path)) {
next()
} else {
res.status(422)
}
}
}
@@ -55,6 +55,7 @@ class PermissionService extends BaseService {
}
async hasPermission(user, method, endpoint) {
if (!user) return false
for (let i = 0; i < user.metadata.roles.length; i++) {
const role = user.metadata.roles[i]
const permissions = await this.retrieveRole(role)
+5 -2
View File
@@ -21,11 +21,14 @@
"@babel/plugin-proposal-class-properties": "^7.7.4",
"@babel/plugin-transform-runtime": "^7.7.6",
"@babel/preset-env": "^7.7.5",
"@babel/runtime": "^7.9.6",
"cross-env": "^5.2.1",
"jest": "^25.5.2",
"eslint": "^6.8.0"
"eslint": "^6.8.0",
"jest": "^25.5.2"
},
"dependencies": {
"@babel/plugin-transform-classes": "^7.9.5",
"medusa-core-utils": "^0.3.0",
"mongoose": "^5.8.0"
},
"gitHead": "35e0930650d5f4aedf2610749cd131ae8b7e17cc"
File diff suppressed because it is too large. Load diff
+5 -1
View File
@@ -7,7 +7,11 @@ if (process.env.NODE_ENV !== `test`) {
}
module.exports = {
plugins: ["@babel/plugin-proposal-class-properties"],
plugins: [
"@babel/plugin-proposal-class-properties",
"@babel/plugin-transform-classes",
"@babel/plugin-transform-instanceof",
],
presets: ["@babel/preset-env"],
env: {
test: {
-3
View File
@@ -1,3 +0,0 @@
module.exports = {
plugins: [],
}
+3
View File
@@ -18,6 +18,7 @@
"@babel/core": "^7.7.5",
"@babel/node": "^7.7.4",
"@babel/plugin-proposal-class-properties": "^7.7.4",
"@babel/plugin-transform-instanceof": "^7.8.3",
"@babel/plugin-transform-runtime": "^7.7.6",
"@babel/preset-env": "^7.7.5",
"@babel/register": "^7.7.4",
@@ -31,12 +32,14 @@
},
"scripts": {
"start": "nodemon --watch plugins/ --watch src/ --exec babel-node src/app.js",
"watch": "babel -w src --out-dir dist --ignore **/__tests__",
"prepare": "cross-env NODE_ENV=production npm run build",
"build": "babel src -d dist",
"serve": "node dist/app.js",
"test": "jest"
},
"dependencies": {
"@babel/plugin-transform-classes": "^7.9.5",
"@hapi/joi": "^16.1.8",
"awilix": "^4.2.3",
"bcrypt": "^3.0.7",
+3 -3
View File
@@ -4,11 +4,11 @@ import store from "./routes/store"
import errorHandler from "./middlewares/error-handler"
// guaranteed to get dependencies
export default () => {
export default container => {
const app = Router()
admin(app)
store(app)
admin(app, container)
store(app, container)
app.use(errorHandler())
@@ -11,15 +11,23 @@ import discountRoutes from "./discounts"
const route = Router()
export default app => {
export default (app, container) => {
const middlewareService = container.resolve("middlewareService")
app.use("/admin", route)
// Unauthenticated routes
authRoutes(route)
// Calls all middleware that has been registered to run before authentication.
middlewareService.usePreAuthentication(app)
// Authenticated routes
route.use(middlewares.authenticate())
// Calls all middleware that has been registered to run after authentication.
middlewareService.usePostAuthentication(app)
productRoutes(route)
userRoutes(route)
regionRoutes(route)
+1 -1
View File
@@ -29,7 +29,7 @@ testApp.use((req, res, next) => {
next()
})
apiLoader({ app: testApp })
apiLoader({ container, app: testApp })
const supertestRequest = supertest(testApp)
+2 -13
View File
@@ -3,18 +3,7 @@ import routes from "../api"
import glob from "glob"
import path from "path"
export default async ({ app }) => {
const corePath = path.join(__dirname, "../api")
const appPath = path.resolve("src/api")
if (corePath !== appPath) {
const appRoutes = require(appPath).default
if (appRoutes) {
app.use("/", appRoutes())
}
}
app.use("/", routes())
export default async ({ app, container }) => {
app.use("/", routes(container))
return app
}
+2 -2
View File
@@ -36,7 +36,7 @@ export default async ({ expressApp }) => {
await servicesLoader({ container })
Logger.info("Services initialized")
await mongooseLoader()
await mongooseLoader({ container })
Logger.info("MongoDB Intialized")
await expressLoader({ app: expressApp })
@@ -54,7 +54,7 @@ export default async ({ expressApp }) => {
await pluginsLoader({ container, app: expressApp })
Logger.info("Plugins Intialized")
await apiLoader({ app: expressApp })
await apiLoader({ container, app: expressApp })
Logger.info("API initialized")
}
-22
View File
@@ -9,10 +9,7 @@ import { asFunction } from "awilix"
*/
export default ({ container }) => {
let corePath = "../models/*.js"
let appPath = "src/models/*.js"
const coreFull = path.join(__dirname, corePath)
const appFull = path.resolve(appPath)
const core = glob.sync(coreFull, { cwd: __dirname })
core.forEach(fn => {
@@ -22,25 +19,6 @@ export default ({ container }) => {
[name]: asFunction(cradle => new loaded(cradle)).singleton(),
})
})
if (coreFull !== appFull) {
const files = glob.sync(appFull)
files.forEach(fn => {
const loaded = require(fn).default
if (!(loaded.prototype instanceof BaseModel)) {
const logger = container.resolve("logger")
const message = `Models must inherit from BaseModel, please check ${fn}`
logger.error(message)
throw new Error(message)
}
const name = formatRegistrationName(fn)
container.register({
[name]: asFunction(cradle => new loaded(cradle)).singleton(),
})
})
}
}
function formatRegistrationName(fn) {
+14 -6
View File
@@ -1,11 +1,19 @@
import mongoose from "mongoose"
import config from "../config"
export default async () => {
const connection = await mongoose.connect(config.databaseURL, {
useNewUrlParser: true,
useCreateIndex: true,
useUnifiedTopology: true,
export default async ({ container }) => {
const logger = container.resolve("logger")
await mongoose
.connect(config.databaseURL, {
useNewUrlParser: true,
useCreateIndex: true,
useUnifiedTopology: true,
})
.catch(err => {
logger.error(err)
})
mongoose.connection.on("error", err => {
logger.error(err)
})
return connection.connection.db
}
+70 -7
View File
@@ -5,6 +5,7 @@ import {
PaymentService,
FulfillmentService,
} from "medusa-interfaces"
import { getConfigFile, createRequireFromPath } from "medusa-core-utils"
import _ from "lodash"
import path from "path"
import fs from "fs"
@@ -15,8 +16,16 @@ import { sync as existsSync } from "fs-exists-cached"
* Registers all services in the services directory
*/
export default ({ container, app }) => {
const configPath = path.resolve("./medusa-config")
const { plugins } = require(configPath)
const { configModule, configFilePath } = getConfigFile(
process.cwd(),
`medusa-config`
)
if (!configModule) {
return
}
const { plugins } = configModule
const resolved = plugins.map(plugin => {
if (_.isString(plugin)) {
@@ -29,13 +38,53 @@ export default ({ container, app }) => {
return details
})
resolved.push({
resolve: process.cwd(),
name: `project-plugin`,
id: createPluginId(`project-plugin`),
options: {},
version: createFileContentHash(process.cwd(), `**`),
})
resolved.forEach(pluginDetails => {
registerServices(pluginDetails, container)
registerModels(pluginDetails, container)
registerServices(pluginDetails, container)
registerMedusaApi(pluginDetails, container)
registerApi(pluginDetails, app)
})
}
function registerMedusaApi(pluginDetails, container) {
registerMedusaMiddleware(pluginDetails, container)
}
function registerMedusaMiddleware(pluginDetails, container) {
let module
try {
module = require(`${pluginDetails.resolve}/api/medusa-middleware`).default
} catch (err) {
return
}
const middlewareService = container.resolve("middlewareService")
if (module.postAuthentication) {
middlewareService.addPostAuthentication(
module.postAuthentication,
pluginDetails.options
)
}
if (module.preAuthentication) {
middlewareService.addPreAuthentication(
module.preAuthentication,
pluginDetails.options
)
}
}
/**
* Registers the plugin's api routes.
*/
function registerApi(pluginDetails, app) {
try {
const routes = require(`${pluginDetails.resolve}/api`).default
@@ -59,7 +108,7 @@ function registerApi(pluginDetails, app) {
* @return {void}
*/
function registerServices(pluginDetails, container) {
const files = glob.sync(`${pluginDetails.resolve}/services/*`, {})
const files = glob.sync(`${pluginDetails.resolve}/services/[!__]*`, {})
files.forEach(fn => {
const loaded = require(fn).default
@@ -119,9 +168,10 @@ function registerServices(pluginDetails, container) {
* @return {void}
*/
function registerModels(pluginDetails, container) {
const files = glob.sync(`${pluginDetails.resolve}/models/*`, {})
const files = glob.sync(`${pluginDetails.resolve}/models/*.js`, {})
files.forEach(fn => {
const loaded = require(fn).default
if (!(loaded.prototype instanceof BaseModel)) {
const logger = container.resolve("logger")
const message = `Models must inherit from BaseModel, please check ${fn}`
@@ -131,7 +181,9 @@ function registerModels(pluginDetails, container) {
const name = formatRegistrationName(fn)
container.register({
[name]: asFunction(cradle => new loaded(cradle, pluginDetails.options)),
[name]: asFunction(
cradle => new loaded(cradle, pluginDetails.options)
).singleton(),
})
})
}
@@ -201,15 +253,22 @@ function resolvePlugin(pluginName) {
}
}
const rootDir = path.resolve(".")
/**
* Here we have an absolute path to an internal plugin, or a name of a module
* which should be located in node_modules.
*/
try {
const requireSource =
rootDir !== null
? createRequireFromPath(`${rootDir}/:internal:`)
: require
// If the path is absolute, resolve the directory of the internal plugin,
// otherwise resolve the directory containing the package.json
const resolvedPath = path.dirname(
require.resolve(`${pluginName}/package.json`)
requireSource.resolve(`${pluginName}/package.json`)
)
const packageJSON = JSON.parse(
@@ -229,3 +288,7 @@ function resolvePlugin(pluginName) {
)
}
}
function createFileContentHash(path, files) {
return path + files
}
+4 -44
View File
@@ -1,65 +1,25 @@
import { BaseService, PaymentService } from "medusa-interfaces"
import glob from "glob"
import path from "path"
import { Lifetime } from "awilix"
import { asFunction } from "awilix"
import { Lifetime, asFunction } from "awilix"
/**
* Registers all services in the services directory
*/
export default ({ container }) => {
let corePath = "../services/*.js"
let appPath = "src/services/*.js"
if (process.env.NODE_ENV === "test") {
corePath = "../services/__mocks__/*.js"
appPath = "src/services/__mocks__/*.js"
}
const isTest = process.env.NODE_ENV === "test"
const corePath = isTest ? "../services/__mocks__/*.js" : "../services/*.js"
const coreFull = path.join(__dirname, corePath)
const appFull = path.resolve(appPath)
const core = glob.sync(coreFull, { cwd: __dirname })
core.forEach(fn => {
const loaded = require(fn).default
const name = formatRegistrationName(fn)
container.register({
[name]: asFunction(cradle => new loaded(cradle)),
[name]: asFunction(cradle => new loaded(cradle)).singleton(),
})
})
if (coreFull !== appFull) {
const files = glob.sync(appFull)
files.forEach(fn => {
const loaded = require(fn).default
if (!(loaded.prototype instanceof BaseService)) {
const logger = container.resolve("logger")
const message = `Models must inherit from BaseModel, please check ${fn}`
logger.error(message)
throw new Error(message)
}
if (loaded.prototype instanceof PaymentService) {
// Register our payment providers to paymentProviders
container.registerAdd(
"paymentProviders",
asFunction(cradle => new loaded(cradle))
)
// Add the service directly to the container in order to make simple
// resolution if we already know which payment provider we need to use
container.register({
[`pp_${loaded.identifier}`]: asFunction(cradle => new loaded(cradle)),
})
} else {
const name = formatRegistrationName(fn)
container.register({
[name]: asFunction(cradle => new loaded(cradle)),
})
}
})
}
}
function formatRegistrationName(fn) {
+1
View File
@@ -1,3 +1,4 @@
import mongoose from "mongoose"
import { BaseModel } from "medusa-interfaces"
import DiscountRule from "./schemas/discount-rule"
@@ -0,0 +1,10 @@
export const MiddlewareServiceMock = {
usePostAuthentication: jest.fn(),
usePreAuthentication: jest.fn(),
}
const mock = jest.fn().mockImplementation(() => {
return MiddlewareServiceMock
})
export default mock
@@ -0,0 +1,81 @@
import mongoose from "mongoose"
import { IdMap } from "medusa-test-utils"
import MiddlewareService from "../middleware"
describe("MiddlewareService", () => {
beforeEach(() => {
jest.clearAllMocks()
})
describe("addPostAuthentication", () => {
const middlewareService = new MiddlewareService()
it("adds middleware function", () => {
middlewareService.addPostAuthentication(() => "post", {})
expect(middlewareService.postAuthentication_.length).toEqual(1)
})
it("fails when no function", () => {
expect(() => middlewareService.addPostAuthentication("post", {})).toThrow(
"Middleware must be a function"
)
})
})
describe("addPreAuthentication", () => {
const middlewareService = new MiddlewareService()
it("adds middleware function", () => {
middlewareService.addPreAuthentication(() => "pre", {})
expect(middlewareService.preAuthentication_.length).toEqual(1)
})
it("fails when no function", () => {
expect(() => middlewareService.addPreAuthentication("pre", {})).toThrow(
"Middleware must be a function"
)
})
})
describe("usePostAuthentication", () => {
const middlewareService = new MiddlewareService()
it("calls middleware", () => {
// This doesn't reflect how middleware works but does suffice in our
// testing situation
const mid = args => args
middlewareService.addPostAuthentication(mid, { data: "yes" })
const app = {
use: jest.fn(),
}
middlewareService.usePostAuthentication(app)
expect(app.use).toHaveBeenCalledTimes(1)
expect(app.use).toHaveBeenCalledWith({ data: "yes" })
})
})
describe("usePreAuthentication", () => {
const middlewareService = new MiddlewareService()
it("calls middleware", () => {
// This doesn't reflect how middleware works but does suffice in our
// testing situation
const mid = args => args
middlewareService.addPreAuthentication(mid, { data: "yes" })
const app = {
use: jest.fn(),
}
middlewareService.usePreAuthentication(app)
expect(app.use).toHaveBeenCalledTimes(1)
expect(app.use).toHaveBeenCalledWith({ data: "yes" })
})
})
})
@@ -0,0 +1,80 @@
import { MedusaError } from "medusa-core-utils"
/**
* Orchestrates dynamic middleware registered through the Medusa Middleware API
*/
class MiddlewareService {
constructor(container) {
this.postAuthentication_ = []
this.preAuthentication_ = []
}
/**
* Validates a middleware function, throws if fn is not of type function.
* @param {function} fn - the middleware function to validate.
*/
validateMiddleware_(fn) {
if (typeof fn !== "function") {
throw new MedusaError(
MedusaError.Types.NOT_ALLOWED,
"Middleware must be a function"
)
}
}
/**
* Adds a middleware function to be called after authentication is completed.
* @param {function} middleware - the middleware function. Should return a
* middleware function.
* @param {object} options - the arguments that will be passed to the
* middleware
* @return {void}
*/
addPostAuthentication(middleware, options) {
this.validateMiddleware_(middleware)
this.postAuthentication_.push({
middleware,
options: options || {},
})
}
/**
* Adds a middleware function to be called before authentication is completed.
* @param {function} middleware - the middleware function. Should return a
* middleware function.
* @param {object} options - the arguments that will be passed to the
* middleware
* @return {void}
*/
addPreAuthentication(middleware, options) {
this.validateMiddleware_(middleware)
this.preAuthentication_.push({
middleware,
options: options || {},
})
}
/**
* Adds post authentication middleware to an express app.
* @param {ExpressApp} app - the express app to add the middleware to
* @return {void}
*/
usePostAuthentication(app) {
for (const object of this.postAuthentication_) {
app.use(object.middleware(object.options))
}
}
/**
* Adds pre authentication middleware to an express app.
* @param {ExpressApp} app - the express app to add the middleware to
* @return {void}
*/
usePreAuthentication(app) {
for (const object of this.preAuthentication_) {
app.use(object.middleware(object.options))
}
}
}
export default MiddlewareService
+1331 -810
View File
File diff suppressed because it is too large. Load diff