Creates test request helper for API endpoints
Separates common utils into the medusa-core-utils package. Sets up a testing environment where mocked models/services/etc. can be placed in __mocks__ folder within its corresponding directory. The mocks will automatically be registered in a awilix container only used for testing.
This commit is contained in:
@@ -1,10 +1,3 @@
|
||||
module.exports = {
|
||||
plugins: [
|
||||
{
|
||||
resolve: `mrbl-payment-stripe`,
|
||||
options: {
|
||||
stripeApiKey: "12345",
|
||||
},
|
||||
},
|
||||
],
|
||||
plugins: [],
|
||||
}
|
||||
|
||||
@@ -19,10 +19,12 @@
|
||||
"@babel/preset-env": "^7.7.5",
|
||||
"@babel/register": "^7.7.4",
|
||||
"@babel/runtime": "^7.7.6",
|
||||
"client-sessions": "^0.8.0",
|
||||
"eslint": "^6.7.2",
|
||||
"jest": "^24.9.0",
|
||||
"nodemon": "^2.0.1",
|
||||
"prettier": "^1.19.1"
|
||||
"prettier": "^1.19.1",
|
||||
"supertest": "^4.0.2"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "nodemon --watch plugins/ --watch src/ --exec babel-node src/app.js",
|
||||
@@ -42,13 +44,15 @@
|
||||
"dotenv": "^8.2.0",
|
||||
"express": "^4.17.1",
|
||||
"express-session": "^1.17.0",
|
||||
"joi-objectid": "^3.0.1",
|
||||
"fs-exists-cached": "^1.0.0",
|
||||
"glob": "^7.1.6",
|
||||
"joi-objectid": "^3.0.1",
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"medusa-core-utils": "^1.0.0",
|
||||
"mongoose": "^5.8.0",
|
||||
"morgan": "^1.9.1",
|
||||
"passport": "^0.4.0",
|
||||
"passport-http-bearer": "^1.0.1",
|
||||
"passport-jwt": "^4.0.0",
|
||||
"passport-local": "^1.0.0",
|
||||
"regenerator-runtime": "^0.13.3",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import passport from "passport"
|
||||
|
||||
export default () => {
|
||||
return passport.authenticate("jwt", { session: false })
|
||||
return passport.authenticate(["jwt", "bearer"], { session: false })
|
||||
}
|
||||
|
||||
30
packages/medusa/src/api/routes/admin/auth/create-session.js
Normal file
30
packages/medusa/src/api/routes/admin/auth/create-session.js
Normal file
@@ -0,0 +1,30 @@
|
||||
import jwt from "jsonwebtoken"
|
||||
import { Validator } from "medusa-core-utils"
|
||||
import config from "../../../../config"
|
||||
|
||||
export default async (req, res) => {
|
||||
const { body } = req
|
||||
const schema = Validator.object().keys({
|
||||
email: Validator.string().required(),
|
||||
password: Validator.string().required(),
|
||||
})
|
||||
const { value, error } = schema.validate(body)
|
||||
|
||||
if (error) {
|
||||
throw error
|
||||
}
|
||||
|
||||
const authService = req.scope.resolve("authService")
|
||||
const result = await authService.authenticate(value.email, value.password)
|
||||
if (!result.success) {
|
||||
res.sendStatus(401)
|
||||
return
|
||||
}
|
||||
|
||||
// Add JWT to cookie
|
||||
req.session.jwt = jwt.sign({ userId: result.user._id }, config.jwtSecret, {
|
||||
expiresIn: "24h",
|
||||
})
|
||||
|
||||
res.json(result.user)
|
||||
}
|
||||
12
packages/medusa/src/api/routes/admin/auth/index.js
Normal file
12
packages/medusa/src/api/routes/admin/auth/index.js
Normal file
@@ -0,0 +1,12 @@
|
||||
import { Router } from "express"
|
||||
import middlewares from "../../../middlewares"
|
||||
|
||||
const route = Router()
|
||||
|
||||
export default app => {
|
||||
app.use("/auth", route)
|
||||
|
||||
route.post("/", middlewares.wrap(require("./create-session").default))
|
||||
|
||||
return app
|
||||
}
|
||||
@@ -1,5 +1,8 @@
|
||||
import { Router } from "express"
|
||||
import middlewares from "../../middlewares"
|
||||
import authRoutes from "./auth"
|
||||
import productRoutes from "./products"
|
||||
import productVariantRoutes from "./product-variants"
|
||||
|
||||
const route = Router()
|
||||
|
||||
@@ -7,12 +10,13 @@ export default app => {
|
||||
app.use("/admin", route)
|
||||
|
||||
// Unauthenticated routes
|
||||
// route.use("/auth", require("./auth").default)
|
||||
authRoutes(route)
|
||||
|
||||
// Authenticated routes
|
||||
route.use(middlewares.authenticate())
|
||||
route.use("/products", require("./products").default)
|
||||
route.use("/product-variants", require("./product-variants").default)
|
||||
|
||||
productRoutes(route)
|
||||
// productVariantRoutes(route)
|
||||
|
||||
return app
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import IdMap from "../../../../../helpers/id-map"
|
||||
import { request } from "../../../../../helpers/test-request"
|
||||
|
||||
describe("POST /admin/products", () => {
|
||||
describe("successful creation", () => {
|
||||
it("calls mock function", async () => {
|
||||
const res = await request("POST", "/admin/products", {
|
||||
adminSession: {
|
||||
jwt: {
|
||||
userId: IdMap.getId("admin_user"),
|
||||
},
|
||||
},
|
||||
})
|
||||
expect(res.status).toEqual(200)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,11 @@
|
||||
import { Validator } from "medusa-core-utils"
|
||||
|
||||
export default async (req, res) => {
|
||||
try {
|
||||
const variantService = req.scope.resolve("productVariantService")
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
}
|
||||
|
||||
res.sendStatus(200)
|
||||
}
|
||||
@@ -4,5 +4,11 @@ import middlewares from "../../../middlewares"
|
||||
const route = Router()
|
||||
|
||||
export default app => {
|
||||
app.use("/products", route)
|
||||
|
||||
route.post("/", middlewares.wrap(require("./create-product").default))
|
||||
|
||||
// route.get("/:productId", middlewares.wrap(require("./get-product").default))
|
||||
|
||||
return app
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import validator from "../../../../utils/validator"
|
||||
import { Validator } from "medusa-core-utils"
|
||||
|
||||
export default async (req, res) => {
|
||||
const { productId } = req.params
|
||||
|
||||
const schema = validator.objectId()
|
||||
const schema = Validator.objectId()
|
||||
const { value, error } = schema.validate(productId)
|
||||
|
||||
if (error) {
|
||||
|
||||
@@ -5,6 +5,8 @@ const route = Router()
|
||||
|
||||
export default app => {
|
||||
app.use("/products", route)
|
||||
|
||||
route.get("/:productId", middlewares.wrap(require("./get-product").default))
|
||||
|
||||
return app
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import "core-js/stable"
|
||||
import "regenerator-runtime/runtime"
|
||||
import express from "express"
|
||||
import { MedusaError } from "medusa-core-utils"
|
||||
import loaders from "./loaders"
|
||||
import Logger from "./loaders/logger"
|
||||
import { MedusaErrorTypes } from "./utils/errors"
|
||||
|
||||
const PORT = process.env.PORT || 80
|
||||
|
||||
@@ -21,10 +21,10 @@ const startServer = async () => {
|
||||
case "ValidationError":
|
||||
statusCode = 400
|
||||
break
|
||||
case MedusaErrorTypes.INVALID_DATA:
|
||||
case MedusaError.Types.INVALID_DATA:
|
||||
statusCode = 400
|
||||
break
|
||||
case MedusaErrorTypes.DB_ERROR:
|
||||
case MedusaError.Types.DB_ERROR:
|
||||
statusCode = 500
|
||||
break
|
||||
default:
|
||||
|
||||
@@ -9,7 +9,7 @@ if (!envFound) {
|
||||
throw new Error("⚠️ Couldn't find .env file ⚠️")
|
||||
}
|
||||
|
||||
export default {
|
||||
const config = {
|
||||
/**
|
||||
* Your favorite port
|
||||
*/
|
||||
@@ -21,9 +21,10 @@ export default {
|
||||
/**
|
||||
* Your secret sauce
|
||||
*/
|
||||
jwtSecret: process.env.JWT_SECRET,
|
||||
jwtSecret: process.env.NODE_ENV === "test" ? "test" : process.env.JWT_SECRET,
|
||||
|
||||
cookieSecret: process.env.COOKIE_SECRET,
|
||||
cookieSecret:
|
||||
process.env.NODE_ENV === "test" ? "test" : process.env.COOKIE_SECRET,
|
||||
|
||||
/**
|
||||
* Used by winston logger
|
||||
@@ -39,3 +40,5 @@ export default {
|
||||
prefix: "/api",
|
||||
},
|
||||
}
|
||||
|
||||
export default config
|
||||
|
||||
116
packages/medusa/src/helpers/test-request.js
Normal file
116
packages/medusa/src/helpers/test-request.js
Normal file
@@ -0,0 +1,116 @@
|
||||
import { createContainer } from "awilix"
|
||||
import express from "express"
|
||||
import supertest from "supertest"
|
||||
import jwt from "jsonwebtoken"
|
||||
import sessions from "client-sessions"
|
||||
import cookie from "cookie"
|
||||
import servicesLoader from "../loaders/services"
|
||||
import expressLoader from "../loaders/express"
|
||||
import apiLoader from "../loaders/api"
|
||||
import passportLoader from "../loaders/passport"
|
||||
import config from "../config"
|
||||
|
||||
const testApp = express()
|
||||
|
||||
const container = createContainer()
|
||||
|
||||
servicesLoader({ container })
|
||||
expressLoader({ app: testApp })
|
||||
passportLoader({ app: testApp, container })
|
||||
|
||||
// Add the registered services to the request scope
|
||||
testApp.use((req, res, next) => {
|
||||
req.scope = container.createScope()
|
||||
next()
|
||||
})
|
||||
|
||||
apiLoader({ app: testApp })
|
||||
|
||||
const supertestRequest = supertest(testApp)
|
||||
|
||||
let adminSessionOpts = {
|
||||
cookieName: "adminSession",
|
||||
secret: "test",
|
||||
}
|
||||
export { adminSessionOpts }
|
||||
|
||||
let clientSessionOpts = {
|
||||
cookieName: "clientSession",
|
||||
secret: "test",
|
||||
}
|
||||
export { clientSessionOpts }
|
||||
|
||||
export async function request(method, url, opts = {}) {
|
||||
let { payload, headers } = opts
|
||||
|
||||
headers = headers || {}
|
||||
headers.Cookie = headers.Cookie || ""
|
||||
if (opts.adminSession) {
|
||||
if (opts.adminSession.jwt) {
|
||||
opts.adminSession.jwt = jwt.sign(
|
||||
opts.adminSession.jwt,
|
||||
config.jwtSecret,
|
||||
{
|
||||
expiresIn: "30m",
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
headers.Cookie +=
|
||||
adminSessionOpts.cookieName +
|
||||
"=" +
|
||||
sessions.util.encode(adminSessionOpts, opts.adminSession) +
|
||||
"; "
|
||||
// console.log(sessions.util.decode(adminSessionOpts, opts.headers.Cookie))
|
||||
}
|
||||
if (opts.clientSession) {
|
||||
headers.Cookie +=
|
||||
clientSessionOpts.cookieName +
|
||||
"=" +
|
||||
sessions.util.encode(clientSessionOpts, opts.clientSession) +
|
||||
"; "
|
||||
// console.log(sessions.util.decode(adminSessionOpts, opts.headers.Cookie))
|
||||
}
|
||||
|
||||
let req = supertestRequest[method.toLowerCase()](url)
|
||||
|
||||
for (let name in headers) {
|
||||
req.set(name, headers[name])
|
||||
}
|
||||
|
||||
if (payload && !req.get("content-type")) {
|
||||
req.set("Content-Type", "application/json")
|
||||
}
|
||||
|
||||
if (!req.get("accept")) {
|
||||
req.set("Accept", "application/json")
|
||||
}
|
||||
|
||||
req.set("Host", "localhost")
|
||||
|
||||
let res
|
||||
try {
|
||||
res = await req.send(JSON.stringify(payload))
|
||||
} catch (e) {
|
||||
if (e.response) {
|
||||
res = e.response
|
||||
} else {
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
//let c =
|
||||
// res.headers["set-cookie"] && cookie.parse(res.headers["set-cookie"][0])
|
||||
//res.adminSession =
|
||||
// c &&
|
||||
// c[adminSessionOpts.cookieName] &&
|
||||
// sessions.util.decode(adminSessionOpts, c[adminSessionOpts.cookieName])
|
||||
// .content
|
||||
//res.clientSession =
|
||||
// c &&
|
||||
// c[clientSessionOpts.cookieName] &&
|
||||
// sessions.util.decode(clientSessionOpts, c[clientSessionOpts.cookieName])
|
||||
// .content
|
||||
|
||||
return res
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import express from "express"
|
||||
import bodyParser from "body-parser"
|
||||
import session from "express-session"
|
||||
import session from "client-sessions"
|
||||
import cookieParser from "cookie-parser"
|
||||
import cors from "cors"
|
||||
import morgan from "morgan"
|
||||
@@ -11,15 +11,23 @@ export default async ({ app }) => {
|
||||
app.enable("trust proxy")
|
||||
|
||||
app.use(cors())
|
||||
app.use(morgan("combined"))
|
||||
app.use(
|
||||
morgan("combined", {
|
||||
skip: () => process.env.NODE_ENV === "test",
|
||||
})
|
||||
)
|
||||
app.use(cookieParser())
|
||||
app.use(bodyParser.json())
|
||||
app.use(
|
||||
session({
|
||||
cookieName: "session",
|
||||
secret: config.cookieSecret,
|
||||
resave: false,
|
||||
saveUninitialized: true,
|
||||
cookie: { secure: true },
|
||||
duration: 24 * 60 * 60 * 1000,
|
||||
activeDuration: 1000 * 60 * 5,
|
||||
cookie: {
|
||||
httpOnly: true,
|
||||
secure: false,
|
||||
},
|
||||
})
|
||||
)
|
||||
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import passport from "passport"
|
||||
import { Strategy as LocalStrategy } from "passport-local"
|
||||
import { Strategy as BearerStrategy } from "passport-http-bearer"
|
||||
import { Strategy as JWTStrategy } from "passport-jwt"
|
||||
import config from "../config"
|
||||
|
||||
export default async ({ app, container }) => {
|
||||
const authService = container.cradle.authService
|
||||
const authService = container.resolve("authService")
|
||||
|
||||
// For good old email password authentication
|
||||
passport.use(
|
||||
new LocalStrategy(
|
||||
{
|
||||
@@ -27,19 +29,32 @@ export default async ({ app, container }) => {
|
||||
)
|
||||
)
|
||||
|
||||
// After a user has authenticated a JWT will be placed on a cookie, all
|
||||
// calls will be authenticated based on the JWT
|
||||
passport.use(
|
||||
new JWTStrategy(
|
||||
{
|
||||
jwtFromRequest: req => req.cookies.jwt,
|
||||
jwtFromRequest: req => req.session.jwt,
|
||||
secretOrKey: config.jwtSecret,
|
||||
},
|
||||
(jwtPayload, done) => {
|
||||
if (Date.now() > jwtPayload.expires) {
|
||||
return done("jwt expired")
|
||||
}
|
||||
|
||||
return done(null, jwtPayload)
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
// Alternatively use bearer token to authenticate to the admin api
|
||||
passport.use(
|
||||
new BearerStrategy((token, done) => {
|
||||
const auth = authService.authenticateAPIToken(token)
|
||||
if (auth.success) {
|
||||
done(null, auth.user)
|
||||
} else {
|
||||
done(auth.error)
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
app.use(passport.initialize())
|
||||
app.use(passport.session())
|
||||
}
|
||||
|
||||
@@ -4,8 +4,14 @@ import { Lifetime } from "awilix"
|
||||
* Registers all services in the services directory
|
||||
*/
|
||||
export default ({ container }) => {
|
||||
let loadPath = "src/services/*.js"
|
||||
|
||||
if (process.env.NODE_ENV === "test") {
|
||||
loadPath = "src/services/__mocks__/*.js"
|
||||
}
|
||||
|
||||
// service/auth.js -> authService
|
||||
container.loadModules(["src/services/*.js"], {
|
||||
container.loadModules([loadPath], {
|
||||
resolverOptions: {
|
||||
lifetime: Lifetime.SINGLETON,
|
||||
},
|
||||
@@ -19,7 +25,13 @@ export default ({ container }) => {
|
||||
const name = parts.join("")
|
||||
|
||||
const splat = descriptor.path.split("/")
|
||||
const namespace = splat[splat.length - 2]
|
||||
|
||||
let offset = 2
|
||||
if (process.env.NODE_ENV === "test") {
|
||||
offset = 3
|
||||
}
|
||||
|
||||
const namespace = splat[splat.length - offset]
|
||||
const upperNamespace =
|
||||
namespace.charAt(0).toUpperCase() + namespace.slice(1, -1)
|
||||
return name + upperNamespace
|
||||
|
||||
26
packages/medusa/src/services/__mocks__/auth.js
Normal file
26
packages/medusa/src/services/__mocks__/auth.js
Normal file
@@ -0,0 +1,26 @@
|
||||
import IdMap from "../../helpers/id-map"
|
||||
|
||||
const adminUser = {
|
||||
_id: IdMap.getId("admin_user"),
|
||||
password: "1235",
|
||||
name: "hi",
|
||||
}
|
||||
|
||||
const mock = jest.fn().mockImplementation(() => {
|
||||
return {
|
||||
authenticate: jest.fn().mockImplementation((email, password) => {
|
||||
return Promise.resolve({
|
||||
success: true,
|
||||
user: adminUser,
|
||||
})
|
||||
}),
|
||||
authenticateAPIToken: jest.fn().mockImplementation(token => {
|
||||
return Promise.resolve({
|
||||
success: true,
|
||||
user: adminUser,
|
||||
})
|
||||
}),
|
||||
}
|
||||
})
|
||||
|
||||
export default mock
|
||||
128
packages/medusa/src/services/__mocks__/product-variant.js
Normal file
128
packages/medusa/src/services/__mocks__/product-variant.js
Normal file
@@ -0,0 +1,128 @@
|
||||
import IdMap from "../../helpers/id-map"
|
||||
|
||||
const variant1 = {
|
||||
_id: "1",
|
||||
title: "variant1",
|
||||
options: [
|
||||
{
|
||||
option_id: IdMap.getId("color_id"),
|
||||
value: "blue",
|
||||
},
|
||||
{
|
||||
option_id: IdMap.getId("size_id"),
|
||||
value: "160",
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
const variant2 = {
|
||||
_id: "2",
|
||||
title: "variant2",
|
||||
options: [
|
||||
{
|
||||
option_id: IdMap.getId("color_id"),
|
||||
value: "black",
|
||||
},
|
||||
{
|
||||
option_id: IdMap.getId("size_id"),
|
||||
value: "160",
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
const variant3 = {
|
||||
_id: "3",
|
||||
title: "variant3",
|
||||
options: [
|
||||
{
|
||||
option_id: IdMap.getId("color_id"),
|
||||
value: "blue",
|
||||
},
|
||||
{
|
||||
option_id: IdMap.getId("size_id"),
|
||||
value: "150",
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
const variant4 = {
|
||||
_id: "4",
|
||||
title: "variant4",
|
||||
options: [
|
||||
{
|
||||
option_id: IdMap.getId("color_id"),
|
||||
value: "blue",
|
||||
},
|
||||
{
|
||||
option_id: IdMap.getId("size_id"),
|
||||
value: "50",
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
const invalidVariant = {
|
||||
_id: "invalid_option",
|
||||
title: "variant3",
|
||||
options: [
|
||||
{
|
||||
option_id: "invalid_id",
|
||||
value: "blue",
|
||||
},
|
||||
{
|
||||
option_id: IdMap.getId("size_id"),
|
||||
value: "150",
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
const emptyVariant = {
|
||||
_id: "empty_option",
|
||||
title: "variant3",
|
||||
options: [],
|
||||
}
|
||||
|
||||
export const variants = {
|
||||
one: variant1,
|
||||
two: variant2,
|
||||
three: variant3,
|
||||
four: variant4,
|
||||
invalid_variant: invalidVariant,
|
||||
empty_variant: emptyVariant,
|
||||
}
|
||||
|
||||
const mock = jest.fn().mockImplementation(() => {
|
||||
return {
|
||||
retrieve: jest.fn().mockImplementation(variantId => {
|
||||
if (variantId === "1") {
|
||||
return Promise.resolve(variant1)
|
||||
}
|
||||
if (variantId === "2") {
|
||||
return Promise.resolve(variant2)
|
||||
}
|
||||
if (variantId === "3") {
|
||||
return Promise.resolve(variant3)
|
||||
}
|
||||
if (variantId === "4") {
|
||||
return Promise.resolve(variant4)
|
||||
}
|
||||
if (variantId === "invalid_option") {
|
||||
return Promise.resolve(invalidVariant)
|
||||
}
|
||||
if (variantId === "empty_option") {
|
||||
return Promise.resolve(emptyVariant)
|
||||
}
|
||||
return Promise.resolve(undefined)
|
||||
}),
|
||||
delete: jest.fn().mockReturnValue(Promise.resolve()),
|
||||
addOptionValue: jest
|
||||
.fn()
|
||||
.mockImplementation((variantId, optionId, value) => {
|
||||
return Promise.resolve({})
|
||||
}),
|
||||
deleteOptionValue: jest.fn().mockImplementation((variantId, optionId) => {
|
||||
return Promise.resolve({})
|
||||
}),
|
||||
}
|
||||
})
|
||||
|
||||
export default mock
|
||||
@@ -13,6 +13,33 @@ class AuthService extends BaseService {
|
||||
this.userModel_ = userModel
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticates a given user with an API token
|
||||
* @param {string} token - the api_token of the user to authenticate
|
||||
* @return {{
|
||||
* success: (bool),
|
||||
* user: (object | undefined),
|
||||
* error: (string | undefined)
|
||||
* }}
|
||||
* success: whether authentication succeeded
|
||||
* user: the user document if authentication succeded
|
||||
* error: a string with the error message
|
||||
*/
|
||||
async authenticateAPIToken(token) {
|
||||
const user = await this.userModel_.findOne({ api_token: token })
|
||||
|
||||
if (user) {
|
||||
return {
|
||||
success: true,
|
||||
user,
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
success: false,
|
||||
error: "Invalid API Token",
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Authenticates a given user based on an email, password combination. Uses
|
||||
* bcrypt to match password with hashed value.
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import mongoose from "mongoose"
|
||||
import _ from "lodash"
|
||||
import { Validator, MedusaError } from "medusa-core-utils"
|
||||
import { BaseService } from "../interfaces"
|
||||
import MedusaError, { MedusaErrorTypes } from "../utils/errors"
|
||||
import validator from "../utils/validator"
|
||||
|
||||
/**
|
||||
* Provides layer to manipulate products.
|
||||
@@ -29,11 +28,11 @@ class ProductService extends BaseService {
|
||||
* @return {string} the validated id
|
||||
*/
|
||||
validateId_(rawId) {
|
||||
const schema = validator.objectId()
|
||||
const schema = Validator.objectId()
|
||||
const { value, error } = schema.validate(rawId)
|
||||
if (error) {
|
||||
throw new MedusaError(
|
||||
MedusaErrorTypes.INVALID_ARGUMENT,
|
||||
MedusaError.Types.INVALID_ARGUMENT,
|
||||
"The productId could not be casted to an ObjectId"
|
||||
)
|
||||
}
|
||||
@@ -57,7 +56,7 @@ class ProductService extends BaseService {
|
||||
retrieve(productId) {
|
||||
const validatedId = this.validateId_(productId)
|
||||
return this.productModel_.findOne({ _id: validatedId }).catch(err => {
|
||||
throw new MedusaError(MedusaErrorTypes.DB_ERROR, err.message)
|
||||
throw new MedusaError(MedusaError.Types.DB_ERROR, err.message)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -73,7 +72,7 @@ class ProductService extends BaseService {
|
||||
published: false,
|
||||
})
|
||||
.catch(err => {
|
||||
throw new MedusaError(MedusaErrorTypes.DB_ERROR, err.message)
|
||||
throw new MedusaError(MedusaError.Types.DB_ERROR, err.message)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -86,7 +85,7 @@ class ProductService extends BaseService {
|
||||
return this.productModel_
|
||||
.updateOne({ _id: productId }, { $set: { published: true } })
|
||||
.catch(err => {
|
||||
throw new MedusaError(MedusaErrorTypes.DB_ERROR, err.message)
|
||||
throw new MedusaError(MedusaError.Types.DB_ERROR, err.message)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -104,14 +103,14 @@ class ProductService extends BaseService {
|
||||
|
||||
if (update.metadata) {
|
||||
throw new MedusaError(
|
||||
MedusaErrorTypes.INVALID_DATA,
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
"Use setMetadata to update metadata fields"
|
||||
)
|
||||
}
|
||||
|
||||
if (update.variants) {
|
||||
throw new MedusaError(
|
||||
MedusaErrorTypes.INVALID_DATA,
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
"Use addVariant, reorderVariants, removeVariant to update Product Variants"
|
||||
)
|
||||
}
|
||||
@@ -123,7 +122,7 @@ class ProductService extends BaseService {
|
||||
{ runValidators: true }
|
||||
)
|
||||
.catch(err => {
|
||||
throw new MedusaError(MedusaErrorTypes.DB_ERROR, err.message)
|
||||
throw new MedusaError(MedusaError.Types.DB_ERROR, err.message)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -148,7 +147,7 @@ class ProductService extends BaseService {
|
||||
})
|
||||
|
||||
return this.productModel_.deleteOne({ _id: product._id }).catch(err => {
|
||||
throw new MedusaError(MedusaErrorTypes.DB_ERROR, err.message)
|
||||
throw new MedusaError(MedusaError.Types.DB_ERROR, err.message)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -163,7 +162,7 @@ class ProductService extends BaseService {
|
||||
const product = await this.retrieve(productId)
|
||||
if (!product) {
|
||||
throw new MedusaError(
|
||||
MedusaErrorTypes.NOT_FOUND,
|
||||
MedusaError.Types.NOT_FOUND,
|
||||
`Product with ${product._id} was not found`
|
||||
)
|
||||
}
|
||||
@@ -171,14 +170,14 @@ class ProductService extends BaseService {
|
||||
const variant = await this.productVariantService_.retrieve(variantId)
|
||||
if (!variant) {
|
||||
throw new MedusaError(
|
||||
MedusaErrorTypes.NOT_FOUND,
|
||||
MedusaError.Types.NOT_FOUND,
|
||||
`Variant with ${variantId} was not found`
|
||||
)
|
||||
}
|
||||
|
||||
if (product.options.length !== variant.options.length) {
|
||||
throw new MedusaError(
|
||||
MedusaErrorTypes.INVALID_DATA,
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Product options length does not match variant options length. Product has ${product.options.length} and variant has ${variant.options.length}.`
|
||||
)
|
||||
}
|
||||
@@ -186,7 +185,7 @@ class ProductService extends BaseService {
|
||||
product.options.forEach(option => {
|
||||
if (!variant.options.find(vo => vo.option_id === option._id)) {
|
||||
throw new MedusaError(
|
||||
MedusaErrorTypes.INVALID_DATA,
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Variant options do not contain value for ${option.title}`
|
||||
)
|
||||
}
|
||||
@@ -207,7 +206,7 @@ class ProductService extends BaseService {
|
||||
|
||||
if (combinationExists) {
|
||||
throw new MedusaError(
|
||||
MedusaErrorTypes.INVALID_DATA,
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Variant with provided options already exists`
|
||||
)
|
||||
}
|
||||
@@ -230,7 +229,7 @@ class ProductService extends BaseService {
|
||||
const product = await this.retrieve(productId)
|
||||
if (!product) {
|
||||
throw new MedusaError(
|
||||
MedusaErrorTypes.NOT_FOUND,
|
||||
MedusaError.Types.NOT_FOUND,
|
||||
`Product with ${product._id} was not found`
|
||||
)
|
||||
}
|
||||
@@ -238,7 +237,7 @@ class ProductService extends BaseService {
|
||||
// Make sure that option doesn't already exist
|
||||
if (product.options.find(o => o.title === optionTitle)) {
|
||||
throw new MedusaError(
|
||||
MedusaErrorTypes.INVALID_DATA,
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`An option with the title: ${optionTitle} already exists`
|
||||
)
|
||||
}
|
||||
@@ -297,14 +296,14 @@ class ProductService extends BaseService {
|
||||
const product = await this.retrieve(productId)
|
||||
if (!product) {
|
||||
throw new MedusaError(
|
||||
MedusaErrorTypes.NOT_FOUND,
|
||||
MedusaError.Types.NOT_FOUND,
|
||||
`Product with ${product._id} was not found`
|
||||
)
|
||||
}
|
||||
|
||||
if (product.variants.length !== variantOrder.length) {
|
||||
throw new MedusaError(
|
||||
MedusaErrorTypes.INVALID_DATA,
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Product variants and new variant order differ in length. To delete or add variants use removeVariant or addVariant`
|
||||
)
|
||||
}
|
||||
@@ -313,7 +312,7 @@ class ProductService extends BaseService {
|
||||
const variant = product.variants.find(id => id === vId)
|
||||
if (!variant) {
|
||||
throw new MedusaError(
|
||||
MedusaErrorTypes.INVALID_DATA,
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Product has no variant with id: ${vId}`
|
||||
)
|
||||
}
|
||||
@@ -344,14 +343,14 @@ class ProductService extends BaseService {
|
||||
const product = await this.retrieve(productId)
|
||||
if (!product) {
|
||||
throw new MedusaError(
|
||||
MedusaErrorTypes.NOT_FOUND,
|
||||
MedusaError.Types.NOT_FOUND,
|
||||
`Product with ${product._id} was not found`
|
||||
)
|
||||
}
|
||||
|
||||
if (product.options.length !== optionOrder.length) {
|
||||
throw new MedusaError(
|
||||
MedusaErrorTypes.INVALID_DATA,
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Product options and new options order differ in length. To delete or add options use removeOption or addOption`
|
||||
)
|
||||
}
|
||||
@@ -360,7 +359,7 @@ class ProductService extends BaseService {
|
||||
const option = product.options.find(o => o._id === oId)
|
||||
if (!option) {
|
||||
throw new MedusaError(
|
||||
MedusaErrorTypes.INVALID_DATA,
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Product has no option with id: ${oId}`
|
||||
)
|
||||
}
|
||||
@@ -390,7 +389,7 @@ class ProductService extends BaseService {
|
||||
const product = await this.retrieve(productId)
|
||||
if (!product) {
|
||||
throw new MedusaError(
|
||||
MedusaErrorTypes.NOT_FOUND,
|
||||
MedusaError.Types.NOT_FOUND,
|
||||
`Product with ${product._id} was not found`
|
||||
)
|
||||
}
|
||||
@@ -398,7 +397,7 @@ class ProductService extends BaseService {
|
||||
const option = product.options.find(o => o._id === optionId)
|
||||
if (!option) {
|
||||
throw new MedusaError(
|
||||
MedusaErrorTypes.NOT_FOUND,
|
||||
MedusaError.Types.NOT_FOUND,
|
||||
`Product has no option with id: ${optionId}`
|
||||
)
|
||||
}
|
||||
@@ -410,7 +409,7 @@ class ProductService extends BaseService {
|
||||
|
||||
if (titleExists) {
|
||||
throw new MedusaError(
|
||||
MedusaErrorTypes.NOT_FOUND,
|
||||
MedusaError.Types.NOT_FOUND,
|
||||
`An option with title ${title} already exists`
|
||||
)
|
||||
}
|
||||
@@ -439,7 +438,7 @@ class ProductService extends BaseService {
|
||||
const product = await this.retrieve(productId)
|
||||
if (!product) {
|
||||
throw new MedusaError(
|
||||
MedusaErrorTypes.NOT_FOUND,
|
||||
MedusaError.Types.NOT_FOUND,
|
||||
`Product with ${product._id} was not found`
|
||||
)
|
||||
}
|
||||
@@ -474,7 +473,7 @@ class ProductService extends BaseService {
|
||||
|
||||
if (!equalsFirst.every(v => v)) {
|
||||
throw new MedusaError(
|
||||
MedusaErrorTypes.INVALID_DATA,
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`To delete an option, first delete all variants, such that when option is deleted, no duplicate variants will exist. For more info check MEDUSA.com`
|
||||
)
|
||||
}
|
||||
@@ -508,7 +507,7 @@ class ProductService extends BaseService {
|
||||
const product = await this.retrieve(productId)
|
||||
if (!product) {
|
||||
throw new MedusaError(
|
||||
MedusaErrorTypes.NOT_FOUND,
|
||||
MedusaError.Types.NOT_FOUND,
|
||||
`Product with ${product._id} was not found`
|
||||
)
|
||||
}
|
||||
@@ -555,7 +554,7 @@ class ProductService extends BaseService {
|
||||
|
||||
if (typeof key !== "string") {
|
||||
throw new MedusaError(
|
||||
MedusaErrorTypes.INVALID_ARGUMENT,
|
||||
MedusaError.Types.INVALID_ARGUMENT,
|
||||
"Key type is invalid. Metadata keys must be strings"
|
||||
)
|
||||
}
|
||||
@@ -564,7 +563,7 @@ class ProductService extends BaseService {
|
||||
return this.productModel_
|
||||
.updateOne({ _id: validatedId }, { $set: { [keyPath]: value } })
|
||||
.catch(err => {
|
||||
throw new MedusaError(MedusaErrorTypes.DB_ERROR, err.message)
|
||||
throw new MedusaError(MedusaError.Types.DB_ERROR, err.message)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
/**
|
||||
* @typedef MedusaErrorType
|
||||
*
|
||||
*/
|
||||
export const MedusaErrorTypes = {
|
||||
/** Errors stemming from the database */
|
||||
DB_ERROR: "database_error",
|
||||
INVALID_ARGUMENT: "invalid_argument",
|
||||
INVALID_DATA: "invalid_data",
|
||||
NOT_FOUND: "not_found"
|
||||
}
|
||||
|
||||
/**
|
||||
* Standardized error to be used across Medusa project.
|
||||
* @extends Error
|
||||
*/
|
||||
class MedusaError extends Error {
|
||||
/**
|
||||
* Creates a standardized error to be used across Medusa project.
|
||||
* @param type {MedusaErrorType} - the type of error.
|
||||
* @param params {Array} - Error params.
|
||||
*/
|
||||
constructor(name, message, ...params) {
|
||||
super(...params)
|
||||
|
||||
if (Error.captureStackTrace) {
|
||||
Error.captureStackTrace(this, MedusaError)
|
||||
}
|
||||
|
||||
this.name = name
|
||||
this.message = message
|
||||
this.date = new Date()
|
||||
}
|
||||
}
|
||||
|
||||
export default MedusaError
|
||||
@@ -1,4 +0,0 @@
|
||||
import Joi from "@hapi/joi"
|
||||
Joi.objectId = require("joi-objectid")(Joi)
|
||||
|
||||
export default Joi
|
||||
6486
packages/medusa/yarn-error.log
Normal file
6486
packages/medusa/yarn-error.log
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1605,6 +1605,13 @@ cli-width@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639"
|
||||
integrity sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=
|
||||
|
||||
client-sessions@^0.8.0:
|
||||
version "0.8.0"
|
||||
resolved "https://registry.yarnpkg.com/client-sessions/-/client-sessions-0.8.0.tgz#a7d8c5558ad5d56f2a199f3533eb654b5df893fd"
|
||||
integrity sha1-p9jFVYrV1W8qGZ81M+tlS134k/0=
|
||||
dependencies:
|
||||
cookies "^0.7.0"
|
||||
|
||||
cliui@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5"
|
||||
@@ -1710,7 +1717,7 @@ commondir@^1.0.1:
|
||||
resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
|
||||
integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=
|
||||
|
||||
component-emitter@^1.2.1:
|
||||
component-emitter@^1.2.0, component-emitter@^1.2.1:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0"
|
||||
integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==
|
||||
@@ -1779,6 +1786,19 @@ cookie@0.4.0:
|
||||
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba"
|
||||
integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==
|
||||
|
||||
cookiejar@^2.1.0:
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.2.tgz#dd8a235530752f988f9a0844f3fc589e3111125c"
|
||||
integrity sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA==
|
||||
|
||||
cookies@^0.7.0:
|
||||
version "0.7.3"
|
||||
resolved "https://registry.yarnpkg.com/cookies/-/cookies-0.7.3.tgz#7912ce21fbf2e8c2da70cf1c3f351aecf59dadfa"
|
||||
integrity sha512-+gixgxYSgQLTaTIilDHAdlNPZDENDQernEMiIcZpYYP14zgHsCt4Ce1FEjFtcp6GefhozebB6orvhAAWx/IS0A==
|
||||
dependencies:
|
||||
depd "~1.1.2"
|
||||
keygrip "~1.0.3"
|
||||
|
||||
copy-descriptor@^0.1.0:
|
||||
version "0.1.1"
|
||||
resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d"
|
||||
@@ -1892,7 +1912,7 @@ debug@3.1.0:
|
||||
dependencies:
|
||||
ms "2.0.0"
|
||||
|
||||
debug@^3.2.6:
|
||||
debug@^3.1.0, debug@^3.2.6:
|
||||
version "3.2.6"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b"
|
||||
integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==
|
||||
@@ -2412,7 +2432,7 @@ extend-shallow@^3.0.0, extend-shallow@^3.0.2:
|
||||
assign-symbols "^1.0.0"
|
||||
is-extendable "^1.0.1"
|
||||
|
||||
extend@~3.0.2:
|
||||
extend@^3.0.0, extend@~3.0.2:
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa"
|
||||
integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==
|
||||
@@ -2566,6 +2586,15 @@ forever-agent@~0.6.1:
|
||||
resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
|
||||
integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=
|
||||
|
||||
form-data@^2.3.1:
|
||||
version "2.5.1"
|
||||
resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.5.1.tgz#f2cbec57b5e59e23716e128fe44d4e5dd23895f4"
|
||||
integrity sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==
|
||||
dependencies:
|
||||
asynckit "^0.4.0"
|
||||
combined-stream "^1.0.6"
|
||||
mime-types "^2.1.12"
|
||||
|
||||
form-data@~2.3.2:
|
||||
version "2.3.3"
|
||||
resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6"
|
||||
@@ -2575,6 +2604,11 @@ form-data@~2.3.2:
|
||||
combined-stream "^1.0.6"
|
||||
mime-types "^2.1.12"
|
||||
|
||||
formidable@^1.2.0:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/formidable/-/formidable-1.2.1.tgz#70fb7ca0290ee6ff961090415f4b3df3d2082659"
|
||||
integrity sha512-Fs9VRguL0gqGHkXS5GQiMCr1VhZBxz0JnJs4JmMp/2jL18Fmbzvv7vOFRU+U8TBkHEE/CX1qDXzJplVULgsLeg==
|
||||
|
||||
forwarded@~0.1.2:
|
||||
version "0.1.2"
|
||||
resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84"
|
||||
@@ -3834,6 +3868,11 @@ kareem@2.3.1:
|
||||
resolved "https://registry.yarnpkg.com/kareem/-/kareem-2.3.1.tgz#def12d9c941017fabfb00f873af95e9c99e1be87"
|
||||
integrity sha512-l3hLhffs9zqoDe8zjmb/mAN4B8VT3L56EUvKNqLFVs9YlFA+zx7ke1DO8STAdDyYNkeSo1nKmjuvQeI12So8Xw==
|
||||
|
||||
keygrip@~1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/keygrip/-/keygrip-1.0.3.tgz#399d709f0aed2bab0a059e0cdd3a5023a053e1dc"
|
||||
integrity sha512-/PpesirAIfaklxUzp4Yb7xBper9MwP6hNRA6BGGUFCgbJ+BM5CKBtsoxinNXkLHAr+GXS1/lSlF2rP7cv5Fl+g==
|
||||
|
||||
kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0:
|
||||
version "3.2.2"
|
||||
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64"
|
||||
@@ -4058,7 +4097,7 @@ merge-stream@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60"
|
||||
integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==
|
||||
|
||||
methods@~1.1.2:
|
||||
methods@^1.1.1, methods@^1.1.2, methods@~1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
|
||||
integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=
|
||||
@@ -4094,7 +4133,7 @@ mime-types@^2.1.12, mime-types@~2.1.19, mime-types@~2.1.24:
|
||||
dependencies:
|
||||
mime-db "1.42.0"
|
||||
|
||||
mime@1.6.0:
|
||||
mime@1.6.0, mime@^1.4.1:
|
||||
version "1.6.0"
|
||||
resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
|
||||
integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
|
||||
@@ -4685,6 +4724,13 @@ pascalcase@^0.1.1:
|
||||
resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14"
|
||||
integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=
|
||||
|
||||
passport-http-bearer@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/passport-http-bearer/-/passport-http-bearer-1.0.1.tgz#147469ea3669e2a84c6167ef99dbb77e1f0098a8"
|
||||
integrity sha1-FHRp6jZp4qhMYWfvmdu3fh8AmKg=
|
||||
dependencies:
|
||||
passport-strategy "1.x.x"
|
||||
|
||||
passport-jwt@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/passport-jwt/-/passport-jwt-4.0.0.tgz#7f0be7ba942e28b9f5d22c2ebbb8ce96ef7cf065"
|
||||
@@ -4907,6 +4953,11 @@ qs@6.7.0:
|
||||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc"
|
||||
integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==
|
||||
|
||||
qs@^6.5.1:
|
||||
version "6.9.1"
|
||||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.1.tgz#20082c65cb78223635ab1a9eaca8875a29bf8ec9"
|
||||
integrity sha512-Cxm7/SS/y/Z3MHWSxXb8lIFqgqBowP5JMlTUFyJN88y0SGQhVmZnqFK/PeuMX9LzUyWsqqhNxIyg0jlzq946yA==
|
||||
|
||||
qs@~6.5.2:
|
||||
version "6.5.2"
|
||||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
|
||||
@@ -4977,6 +5028,19 @@ readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.3.6:
|
||||
string_decoder "~1.1.1"
|
||||
util-deprecate "~1.0.1"
|
||||
|
||||
readable-stream@^2.3.5:
|
||||
version "2.3.7"
|
||||
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57"
|
||||
integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==
|
||||
dependencies:
|
||||
core-util-is "~1.0.0"
|
||||
inherits "~2.0.3"
|
||||
isarray "~1.0.0"
|
||||
process-nextick-args "~2.0.0"
|
||||
safe-buffer "~5.1.1"
|
||||
string_decoder "~1.1.1"
|
||||
util-deprecate "~1.0.1"
|
||||
|
||||
readable-stream@^3.1.1:
|
||||
version "3.4.0"
|
||||
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.4.0.tgz#a51c26754658e0a3c21dbf59163bd45ba6f447fc"
|
||||
@@ -5718,6 +5782,30 @@ strip-json-comments@~2.0.1:
|
||||
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
|
||||
integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo=
|
||||
|
||||
superagent@^3.8.3:
|
||||
version "3.8.3"
|
||||
resolved "https://registry.yarnpkg.com/superagent/-/superagent-3.8.3.tgz#460ea0dbdb7d5b11bc4f78deba565f86a178e128"
|
||||
integrity sha512-GLQtLMCoEIK4eDv6OGtkOoSMt3D+oq0y3dsxMuYuDvaNUvuT8eFBuLmfR0iYYzHC1e8hpzC6ZsxbuP6DIalMFA==
|
||||
dependencies:
|
||||
component-emitter "^1.2.0"
|
||||
cookiejar "^2.1.0"
|
||||
debug "^3.1.0"
|
||||
extend "^3.0.0"
|
||||
form-data "^2.3.1"
|
||||
formidable "^1.2.0"
|
||||
methods "^1.1.1"
|
||||
mime "^1.4.1"
|
||||
qs "^6.5.1"
|
||||
readable-stream "^2.3.5"
|
||||
|
||||
supertest@^4.0.2:
|
||||
version "4.0.2"
|
||||
resolved "https://registry.yarnpkg.com/supertest/-/supertest-4.0.2.tgz#c2234dbdd6dc79b6f15b99c8d6577b90e4ce3f36"
|
||||
integrity sha512-1BAbvrOZsGA3YTCWqbmh14L0YEq0EGICX/nBnfkfVJn7SrxQV1I3pMYjSzG9y/7ZU2V9dWqyqk2POwxlb09duQ==
|
||||
dependencies:
|
||||
methods "^1.1.2"
|
||||
superagent "^3.8.3"
|
||||
|
||||
supports-color@^5.3.0, supports-color@^5.5.0:
|
||||
version "5.5.0"
|
||||
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
|
||||
|
||||
Reference in New Issue
Block a user