Adds admin endpoints / routes for user (#36)

Adds all endpoints / routes for user operations
Tweaks in UserService
Adds update in UserService
Fixes old tests
This commit is contained in:
Oliver Windall Juhl authored and GitHub committed 2020-04-17 08:13:24 +02:00
1 parent e3cdde3c9e
commit a53822d298
27 files changed
+718 -22

No files matched your search

-2
View File
@@ -1,14 +1,12 @@
import { Router } from "express"
import admin from "./routes/admin"
import store from "./routes/store"
import users from "./routes/users"
import errorHandler from "./middlewares/error-handler"
// guaranteed to get dependencies
export default () => {
const app = Router()
users(app)
admin(app)
store(app)
@@ -2,6 +2,7 @@ import { Router } from "express"
import middlewares from "../../middlewares"
import authRoutes from "./auth"
import productRoutes from "./products"
import userRoutes from "./users"
import productVariantRoutes from "./product-variants"
import regionRoutes from "./regions"
import shippingOptionRoutes from "./shipping-options"
@@ -20,6 +21,7 @@ export default app => {
route.use(middlewares.authenticate())
productRoutes(route)
userRoutes(route)
regionRoutes(route)
shippingOptionRoutes(route)
shippingProfileRoutes(route)
@@ -0,0 +1,71 @@
import { IdMap } from "medusa-test-utils"
import { request } from "../../../../../helpers/test-request"
import { UserServiceMock } from "../../../../../services/__mocks__/user"
describe("POST /admin/users", () => {
describe("successfully creates a user", () => {
let subject
beforeAll(async () => {
subject = await request("POST", "/admin/users", {
payload: {
email: "oliver@test.dk",
password: "123456789",
},
adminSession: {
jwt: {
userId: IdMap.getId("admin_user"),
},
},
})
})
afterAll(() => {
jest.clearAllMocks()
})
it("calls UserService create", () => {
expect(UserServiceMock.create).toHaveBeenCalledTimes(1)
expect(UserServiceMock.create).toHaveBeenCalledWith(
{
email: "oliver@test.dk",
},
"123456789"
)
})
it("returns 200", () => {
expect(subject.status).toEqual(200)
})
it("returns the user", () => {
expect(subject.body._id).toEqual(IdMap.getId("test-user"))
})
})
describe("handles failed create operation", () => {
let subject
beforeAll(async () => {
subject = await request("POST", "/admin/users", {
payload: {
email: "olivertest.dk",
password: "123456789",
},
adminSession: {
jwt: {
userId: IdMap.getId("admin_user"),
},
},
})
})
afterAll(() => {
jest.clearAllMocks()
})
it("returns 400 on invalid email", () => {
expect(subject.status).toEqual(400)
})
})
})
@@ -0,0 +1,46 @@
import { IdMap } from "medusa-test-utils"
import { request } from "../../../../../helpers/test-request"
import { UserServiceMock } from "../../../../../services/__mocks__/user"
describe("DELETE /admin/users/:id", () => {
describe("successfully deletes a user", () => {
let subject
beforeAll(async () => {
subject = await request(
"DELETE",
`/admin/users/${IdMap.getId("delete-user")}`,
{
adminSession: {
jwt: {
userId: IdMap.getId("admin_user"),
},
},
}
)
})
afterAll(() => {
jest.clearAllMocks()
})
it("calls UserService create", () => {
expect(UserServiceMock.delete).toHaveBeenCalledTimes(1)
expect(UserServiceMock.delete).toHaveBeenCalledWith(
IdMap.getId("delete-user")
)
})
it("returns 200", () => {
expect(subject.status).toEqual(200)
})
it("returns correct delete data", () => {
expect(subject.body).toEqual({
id: IdMap.getId("delete-user"),
object: "user",
deleted: true,
})
})
})
})
@@ -0,0 +1,42 @@
import { IdMap } from "medusa-test-utils"
import { request } from "../../../../../helpers/test-request"
import { UserServiceMock } from "../../../../../services/__mocks__/user"
describe("GET /admin/users/:id", () => {
describe("successfully gets a user", () => {
let subject
beforeAll(async () => {
subject = await request(
"GET",
`/admin/users/${IdMap.getId("test-user")}`,
{
adminSession: {
jwt: {
userId: IdMap.getId("admin_user"),
},
},
}
)
})
afterAll(() => {
jest.clearAllMocks()
})
it("calls UserService retrieve", () => {
expect(UserServiceMock.retrieve).toHaveBeenCalledTimes(1)
expect(UserServiceMock.retrieve).toHaveBeenCalledWith(
IdMap.getId("test-user")
)
})
it("returns 200", () => {
expect(subject.status).toEqual(200)
})
it("returns the user", () => {
expect(subject.body._id).toEqual(IdMap.getId("test-user"))
})
})
})
@@ -0,0 +1,28 @@
import { IdMap } from "medusa-test-utils"
import { request } from "../../../../../helpers/test-request"
import { UserServiceMock } from "../../../../../services/__mocks__/user"
describe("GET /admin/users", () => {
describe("successful retrieval", () => {
let subject
beforeAll(async () => {
subject = await request("GET", `/admin/users`, {
adminSession: {
jwt: {
userId: IdMap.getId("admin_user"),
},
},
})
})
it("returns 200", () => {
expect(subject.status).toEqual(200)
})
it("calls service retrieve", () => {
expect(UserServiceMock.list).toHaveBeenCalledTimes(1)
expect(UserServiceMock.list).toHaveBeenCalledWith({})
})
})
})
@@ -0,0 +1,40 @@
import { IdMap } from "medusa-test-utils"
import { request } from "../../../../../helpers/test-request"
import { UserServiceMock } from "../../../../../services/__mocks__/user"
describe("POST /admin/users/:id/reset-password-token", () => {
describe("successfully generates reset password token", () => {
let subject
beforeAll(async () => {
subject = await request(
"POST",
`/admin/users/${IdMap.getId("test-user")}/reset-password-token`,
{
adminSession: {
jwt: {
userId: IdMap.getId("admin_user"),
},
},
}
)
})
afterAll(() => {
jest.clearAllMocks()
})
it("calls UserService generateResetPasswordToken", () => {
expect(UserServiceMock.generateResetPasswordToken).toHaveBeenCalledTimes(
1
)
expect(UserServiceMock.generateResetPasswordToken).toHaveBeenCalledWith(
IdMap.getId("test-user")
)
})
it("returns 200", () => {
expect(subject.status).toEqual(200)
})
})
})
@@ -0,0 +1,56 @@
import { IdMap } from "medusa-test-utils"
import jwt from "jsonwebtoken"
import { request } from "../../../../../helpers/test-request"
import { UserServiceMock } from "../../../../../services/__mocks__/user"
describe("POST /admin/users/:id/reset-password", () => {
describe("successfully resets password", () => {
let subject
beforeEach(async () => {
jest.clearAllMocks()
})
it("calls UserService setPassword", async () => {
const exp = Math.floor(Date.now() / 1000) + 60 * 15
const token = jwt.sign(
{
userId: "test-user-id",
name: "Oliver Juhl",
email: "oliver@test.dk",
exp,
},
"123456789hash"
)
subject = await request(
"POST",
`/admin/users/test-user-id/reset-password`,
{
payload: {
token,
password: "new-password",
},
adminSession: {
jwt: {
userId: IdMap.getId("admin_user"),
},
},
}
)
expect(UserServiceMock.setPassword).toHaveBeenCalledTimes(1)
expect(UserServiceMock.setPassword).toHaveBeenCalledWith(
"test-user-id",
"new-password"
)
})
it("returns updated user", () => {
expect(subject.body._id).toEqual("test-user-id")
})
it("returns 200", () => {
expect(subject.status).toEqual(200)
})
})
})
@@ -0,0 +1,46 @@
import { IdMap } from "medusa-test-utils"
import { request } from "../../../../../helpers/test-request"
import { UserServiceMock } from "../../../../../services/__mocks__/user"
describe("POST /admin/users/:id/set-password", () => {
describe("successfully sets password", () => {
let subject
beforeAll(async () => {
subject = await request(
"POST",
`/admin/users/${IdMap.getId("test-user")}/set-password`,
{
payload: {
password: "987654321",
},
adminSession: {
jwt: {
userId: IdMap.getId("admin_user"),
},
},
}
)
})
afterAll(() => {
jest.clearAllMocks()
})
it("calls UserService setPassword", () => {
expect(UserServiceMock.setPassword).toHaveBeenCalledTimes(1)
expect(UserServiceMock.setPassword).toHaveBeenCalledWith(
IdMap.getId("test-user"),
"987654321"
)
})
it("returns the user", () => {
expect(subject.body._id).toEqual(IdMap.getId("test-user"))
})
it("returns 200", () => {
expect(subject.status).toEqual(200)
})
})
})
@@ -0,0 +1,40 @@
import { IdMap } from "medusa-test-utils"
import { request } from "../../../../../helpers/test-request"
import { UserServiceMock } from "../../../../../services/__mocks__/user"
describe("POST /admin/users/:id", () => {
describe("successful update", () => {
let subject
beforeAll(async () => {
subject = await request(
"POST",
`/admin/users/${IdMap.getId("test-user")}`,
{
payload: {
name: "Oliver Juhl",
},
adminSession: {
jwt: {
userId: IdMap.getId("admin_user"),
},
},
}
)
})
it("returns 200", () => {
expect(subject.status).toEqual(200)
})
it("calls service method", () => {
expect(UserServiceMock.update).toHaveBeenCalledTimes(1)
expect(UserServiceMock.update).toHaveBeenCalledWith(
IdMap.getId("test-user"),
{
name: "Oliver Juhl",
}
)
})
})
})
@@ -0,0 +1,28 @@
import { Validator, MedusaError } from "medusa-core-utils"
import _ from "lodash"
export default async (req, res) => {
const schema = Validator.object().keys({
email: Validator.string()
.email()
.required(),
name: Validator.string().optional(),
password: Validator.string().required(),
})
const { value, error } = schema.validate(req.body)
if (error) {
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
}
try {
const userService = req.scope.resolve("userService")
const data = _.pick(value, ["email", "name"])
const user = await userService.create(data, value.password)
res.status(200).json(user)
} catch (err) {
throw err
}
}
@@ -0,0 +1,12 @@
export default async (req, res) => {
const { user_id } = req.params
const userService = req.scope.resolve("userService")
await userService.delete(user_id)
res.status(200).send({
id: user_id,
object: "user",
deleted: true,
})
}
@@ -0,0 +1,12 @@
export default async (req, res) => {
const { user_id } = req.params
try {
const userService = req.scope.resolve("userService")
const user = await userService.retrieve(user_id)
res.json(user)
} catch (error) {
throw error
}
}
@@ -0,0 +1,31 @@
import { Router } from "express"
import middlewares from "../../../middlewares"
const route = Router()
export default app => {
app.use("/users", route)
route.get("/", middlewares.wrap(require("./list-users").default))
route.get("/:user_id", middlewares.wrap(require("./get-user").default))
route.post("/", middlewares.wrap(require("./create-user").default))
route.post("/:user_id", middlewares.wrap(require("./update-user").default))
route.post(
"/:user_id/set-password",
middlewares.wrap(require("./set-password").default)
)
route.post(
"/:user_id/reset-password-token",
middlewares.wrap(require("./reset-password-token").default)
)
route.post(
"/:user_id/reset-password",
middlewares.wrap(require("./reset-password").default)
)
route.delete("/:user_id", middlewares.wrap(require("./delete-user").default))
return app
}
@@ -0,0 +1,10 @@
export default async (req, res) => {
try {
const userService = req.scope.resolve("userService")
const users = await userService.list({})
res.status(200).json(users)
} catch (err) {
throw err
}
}
@@ -0,0 +1,14 @@
export default async (req, res) => {
const { user_id } = req.params
try {
const userService = req.scope.resolve("userService")
const token = await userService.generateResetPasswordToken(user_id)
if (!token) {
res.sendStatus(404)
return
}
res.json(token)
} catch (error) {
throw error
}
}
@@ -0,0 +1,33 @@
import { MedusaError, Validator } from "medusa-core-utils"
import jwt from "jsonwebtoken"
export default async (req, res) => {
const { user_id } = req.params
const schema = Validator.object().keys({
token: Validator.string().required(),
password: Validator.string().required(),
})
const { value, error } = schema.validate(req.body)
if (error) {
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
}
try {
const userService = req.scope.resolve("userService")
let user = await userService.retrieve(user_id)
const decodedToken = await jwt.verify(value.token, user.password_hash)
if (!decodedToken) {
res.status(401).send("Invalid or expired password reset token")
}
await userService.setPassword(user._id, value.password)
const updatedUser = await userService.retrieve(user._id)
res.status(200).json(updatedUser)
} catch (error) {
throw error
}
}
@@ -0,0 +1,24 @@
import { Validator, MedusaError } from "medusa-core-utils"
export default async (req, res) => {
const { user_id } = req.params
const schema = Validator.object().keys({
password: Validator.string().required(),
})
const { value, error } = schema.validate(req.body)
if (error) {
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
}
try {
const userService = req.scope.resolve("userService")
await userService.setPassword(user_id, value.password)
const newUser = await userService.retrieve(user_id)
res.json(newUser)
} catch (error) {
throw error
}
}
@@ -0,0 +1,26 @@
import { MedusaError, Validator } from "medusa-core-utils"
export default async (req, res) => {
const { user_id } = req.params
const schema = Validator.object().keys({
name: Validator.string().optional(),
api_token: Validator.string().optional(),
})
const { value, error } = schema.validate(req.body)
if (error) {
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
}
try {
const userService = req.scope.resolve("userService")
await userService.update(user_id, value)
const user = await userService.retrieve(user_id)
res.status(200).json(user)
} catch (err) {
console.log(err)
throw err
}
}
@@ -2,7 +2,6 @@ import { Router } from "express"
import productRoutes from "./products"
import cartRoutes from "./carts"
import shippingOptionRoutes from "./shipping-options"
import middlewares from "../../middlewares"
const route = Router()
@@ -1,8 +0,0 @@
import { Router } from "express"
import middlewares from "../../middlewares"
const route = Router()
export default app => {
return app
}