fix(medusa): Implement listAndCount for UserService and update list endpoint (#6190)
This commit is contained in:
@@ -0,0 +1,9 @@
|
|||||||
|
---
|
||||||
|
"@medusajs/medusa": patch
|
||||||
|
"@medusajs/medusa-js": patch
|
||||||
|
"medusa-react": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
fix(medusa): Implements `listAndCount` method for UserService, and updates list endpoint to accept the expected params.
|
||||||
|
fix(medusa-js): Update `admin.users.list` to accept query params.
|
||||||
|
fix(medusa-react): Update `useAdminUsers` hook to accept query params.
|
||||||
@@ -1,90 +0,0 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
||||||
|
|
||||||
exports[`/admin/users GET /admin/users lists users 1`] = `
|
|
||||||
Array [
|
|
||||||
Object {
|
|
||||||
"api_token": "test_token",
|
|
||||||
"created_at": Any<String>,
|
|
||||||
"deleted_at": null,
|
|
||||||
"email": "admin@medusa.js",
|
|
||||||
"first_name": null,
|
|
||||||
"id": "admin_user",
|
|
||||||
"last_name": null,
|
|
||||||
"metadata": null,
|
|
||||||
"role": "admin",
|
|
||||||
"updated_at": Any<String>,
|
|
||||||
},
|
|
||||||
Object {
|
|
||||||
"api_token": null,
|
|
||||||
"created_at": Any<String>,
|
|
||||||
"deleted_at": null,
|
|
||||||
"email": "member@test.com",
|
|
||||||
"first_name": "member",
|
|
||||||
"id": "member-user",
|
|
||||||
"last_name": "user",
|
|
||||||
"metadata": null,
|
|
||||||
"role": "member",
|
|
||||||
"updated_at": Any<String>,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`/admin/users GET /admin/users returns user by id 1`] = `
|
|
||||||
Object {
|
|
||||||
"api_token": "test_token",
|
|
||||||
"created_at": Any<String>,
|
|
||||||
"deleted_at": null,
|
|
||||||
"email": "admin@medusa.js",
|
|
||||||
"first_name": null,
|
|
||||||
"id": "admin_user",
|
|
||||||
"last_name": null,
|
|
||||||
"metadata": null,
|
|
||||||
"role": "admin",
|
|
||||||
"updated_at": Any<String>,
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`/admin/users POST /admin/users creates a user 1`] = `
|
|
||||||
Object {
|
|
||||||
"api_token": null,
|
|
||||||
"created_at": Any<String>,
|
|
||||||
"deleted_at": null,
|
|
||||||
"email": "test@test123.com",
|
|
||||||
"first_name": null,
|
|
||||||
"id": StringMatching /\\^usr_\\*/,
|
|
||||||
"last_name": null,
|
|
||||||
"metadata": null,
|
|
||||||
"role": "member",
|
|
||||||
"updated_at": Any<String>,
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`/admin/users POST /admin/users updates a user 1`] = `
|
|
||||||
Object {
|
|
||||||
"api_token": null,
|
|
||||||
"created_at": Any<String>,
|
|
||||||
"deleted_at": null,
|
|
||||||
"email": "member@test.com",
|
|
||||||
"first_name": "karl",
|
|
||||||
"id": "member-user",
|
|
||||||
"last_name": "user",
|
|
||||||
"metadata": null,
|
|
||||||
"password_hash": null,
|
|
||||||
"role": "member",
|
|
||||||
"updated_at": Any<String>,
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`[MEDUSA_FF_ANALYTICS] /admin/analytics-config DELETE /admin/users Deletes a user and their analytics config 1`] = `
|
|
||||||
Array [
|
|
||||||
Object {
|
|
||||||
"anonymize": false,
|
|
||||||
"created_at": Any<Date>,
|
|
||||||
"deleted_at": Any<Date>,
|
|
||||||
"id": Any<String>,
|
|
||||||
"opt_out": false,
|
|
||||||
"updated_at": Any<Date>,
|
|
||||||
"user_id": "member-user",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
@@ -54,14 +54,16 @@ describe("/admin/users", () => {
|
|||||||
const response = await api.get("/admin/users/admin_user", adminReqConfig)
|
const response = await api.get("/admin/users/admin_user", adminReqConfig)
|
||||||
|
|
||||||
expect(response.status).toEqual(200)
|
expect(response.status).toEqual(200)
|
||||||
expect(response.data.user).toMatchSnapshot({
|
expect(response.data.user).toEqual(
|
||||||
id: "admin_user",
|
expect.objectContaining({
|
||||||
email: "admin@medusa.js",
|
id: "admin_user",
|
||||||
api_token: "test_token",
|
email: "admin@medusa.js",
|
||||||
role: "admin",
|
api_token: "test_token",
|
||||||
created_at: expect.any(String),
|
role: "admin",
|
||||||
updated_at: expect.any(String),
|
created_at: expect.any(String),
|
||||||
})
|
updated_at: expect.any(String),
|
||||||
|
})
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("lists users", async () => {
|
it("lists users", async () => {
|
||||||
@@ -75,25 +77,72 @@ describe("/admin/users", () => {
|
|||||||
|
|
||||||
expect(response.status).toEqual(200)
|
expect(response.status).toEqual(200)
|
||||||
|
|
||||||
expect(response.data.users).toMatchSnapshot([
|
expect(response.data.users).toEqual(
|
||||||
{
|
expect.arrayContaining([
|
||||||
id: "admin_user",
|
expect.objectContaining({
|
||||||
email: "admin@medusa.js",
|
id: "admin_user",
|
||||||
api_token: "test_token",
|
email: "admin@medusa.js",
|
||||||
role: "admin",
|
api_token: "test_token",
|
||||||
created_at: expect.any(String),
|
role: "admin",
|
||||||
updated_at: expect.any(String),
|
created_at: expect.any(String),
|
||||||
},
|
updated_at: expect.any(String),
|
||||||
{
|
}),
|
||||||
id: "member-user",
|
expect.objectContaining({
|
||||||
role: "member",
|
id: "member-user",
|
||||||
email: "member@test.com",
|
role: "member",
|
||||||
first_name: "member",
|
email: "member@test.com",
|
||||||
last_name: "user",
|
first_name: "member",
|
||||||
created_at: expect.any(String),
|
last_name: "user",
|
||||||
updated_at: expect.any(String),
|
created_at: expect.any(String),
|
||||||
},
|
updated_at: expect.any(String),
|
||||||
])
|
}),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("lists users that match the free text search", async () => {
|
||||||
|
const api = useApi()
|
||||||
|
|
||||||
|
const response = await api.get("/admin/users?q=member", adminReqConfig)
|
||||||
|
|
||||||
|
expect(response.status).toEqual(200)
|
||||||
|
|
||||||
|
expect(response.data.users.length).toEqual(1)
|
||||||
|
expect(response.data.users).toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
id: "member-user",
|
||||||
|
role: "member",
|
||||||
|
email: "member@test.com",
|
||||||
|
first_name: "member",
|
||||||
|
last_name: "user",
|
||||||
|
created_at: expect.any(String),
|
||||||
|
updated_at: expect.any(String),
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("orders users by created_at", async () => {
|
||||||
|
const api = useApi()
|
||||||
|
|
||||||
|
const response = await api.get(
|
||||||
|
"/admin/users?order=created_at",
|
||||||
|
adminReqConfig
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(response.status).toEqual(200)
|
||||||
|
expect(response.data.users.length).toBeGreaterThan(0)
|
||||||
|
|
||||||
|
for (let i = 0; i < response.data.users.length - 1; i++) {
|
||||||
|
const user1 = response.data.users[i]
|
||||||
|
const user2 = response.data.users[i + 1]
|
||||||
|
|
||||||
|
const date1 = new Date(user1.created_at)
|
||||||
|
const date2 = new Date(user2.created_at)
|
||||||
|
|
||||||
|
expect(date1.getTime()).toBeLessThanOrEqual(date2.getTime())
|
||||||
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -138,13 +187,15 @@ describe("/admin/users", () => {
|
|||||||
.catch((err) => console.log(err))
|
.catch((err) => console.log(err))
|
||||||
|
|
||||||
expect(response.status).toEqual(200)
|
expect(response.status).toEqual(200)
|
||||||
expect(response.data.user).toMatchSnapshot({
|
expect(response.data.user).toEqual(
|
||||||
id: expect.stringMatching(/^usr_*/),
|
expect.objectContaining({
|
||||||
created_at: expect.any(String),
|
id: expect.stringMatching(/^usr_*/),
|
||||||
updated_at: expect.any(String),
|
created_at: expect.any(String),
|
||||||
role: "member",
|
updated_at: expect.any(String),
|
||||||
email: "test@test123.com",
|
role: "member",
|
||||||
})
|
email: "test@test123.com",
|
||||||
|
})
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("updates a user", async () => {
|
it("updates a user", async () => {
|
||||||
@@ -159,15 +210,17 @@ describe("/admin/users", () => {
|
|||||||
.catch((err) => console.log(err.response.data.message))
|
.catch((err) => console.log(err.response.data.message))
|
||||||
|
|
||||||
expect(updateResponse.status).toEqual(200)
|
expect(updateResponse.status).toEqual(200)
|
||||||
expect(updateResponse.data.user).toMatchSnapshot({
|
expect(updateResponse.data.user).toEqual(
|
||||||
id: "member-user",
|
expect.objectContaining({
|
||||||
created_at: expect.any(String),
|
id: "member-user",
|
||||||
updated_at: expect.any(String),
|
created_at: expect.any(String),
|
||||||
role: "member",
|
updated_at: expect.any(String),
|
||||||
email: "member@test.com",
|
role: "member",
|
||||||
first_name: "karl",
|
email: "member@test.com",
|
||||||
last_name: "user",
|
first_name: "karl",
|
||||||
})
|
last_name: "user",
|
||||||
|
})
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("Password reset", () => {
|
describe("Password reset", () => {
|
||||||
@@ -417,17 +470,19 @@ describe("[MEDUSA_FF_ANALYTICS] /admin/analytics-config", () => {
|
|||||||
`SELECT * FROM public.analytics_config WHERE user_id = '${userId}'`
|
`SELECT * FROM public.analytics_config WHERE user_id = '${userId}'`
|
||||||
)
|
)
|
||||||
|
|
||||||
expect(configs).toMatchSnapshot([
|
expect(configs).toEqual(
|
||||||
{
|
expect.arrayContaining([
|
||||||
created_at: expect.any(Date),
|
expect.objectContaining({
|
||||||
updated_at: expect.any(Date),
|
created_at: expect.any(Date),
|
||||||
deleted_at: expect.any(Date),
|
updated_at: expect.any(Date),
|
||||||
id: expect.any(String),
|
deleted_at: expect.any(Date),
|
||||||
user_id: userId,
|
id: expect.any(String),
|
||||||
opt_out: false,
|
user_id: userId,
|
||||||
anonymize: false,
|
opt_out: false,
|
||||||
},
|
anonymize: false,
|
||||||
])
|
}),
|
||||||
|
])
|
||||||
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -0,0 +1,110 @@
|
|||||||
|
/* istanbul ignore file */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
import { SetRelation, Merge } from "../core/ModelUtils"
|
||||||
|
|
||||||
|
export interface AdminGetUsersParams {
|
||||||
|
/**
|
||||||
|
* Filter by a user ID.
|
||||||
|
*/
|
||||||
|
id?: string
|
||||||
|
/**
|
||||||
|
* Filter by email.
|
||||||
|
*/
|
||||||
|
email?: string
|
||||||
|
/**
|
||||||
|
* Filter by first name.
|
||||||
|
*/
|
||||||
|
first_name?: string
|
||||||
|
/**
|
||||||
|
* Filter by last name.
|
||||||
|
*/
|
||||||
|
last_name?: string
|
||||||
|
/**
|
||||||
|
* term used to search users' first name, last name, and email.
|
||||||
|
*/
|
||||||
|
q?: string
|
||||||
|
/**
|
||||||
|
* A user field to sort-order the retrieved users by.
|
||||||
|
*/
|
||||||
|
order?: string
|
||||||
|
/**
|
||||||
|
* Filter by a creation date range.
|
||||||
|
*/
|
||||||
|
created_at?: {
|
||||||
|
/**
|
||||||
|
* filter by dates less than this date
|
||||||
|
*/
|
||||||
|
lt?: string
|
||||||
|
/**
|
||||||
|
* filter by dates greater than this date
|
||||||
|
*/
|
||||||
|
gt?: string
|
||||||
|
/**
|
||||||
|
* filter by dates less than or equal to this date
|
||||||
|
*/
|
||||||
|
lte?: string
|
||||||
|
/**
|
||||||
|
* filter by dates greater than or equal to this date
|
||||||
|
*/
|
||||||
|
gte?: string
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Filter by an update date range.
|
||||||
|
*/
|
||||||
|
updated_at?: {
|
||||||
|
/**
|
||||||
|
* filter by dates less than this date
|
||||||
|
*/
|
||||||
|
lt?: string
|
||||||
|
/**
|
||||||
|
* filter by dates greater than this date
|
||||||
|
*/
|
||||||
|
gt?: string
|
||||||
|
/**
|
||||||
|
* filter by dates less than or equal to this date
|
||||||
|
*/
|
||||||
|
lte?: string
|
||||||
|
/**
|
||||||
|
* filter by dates greater than or equal to this date
|
||||||
|
*/
|
||||||
|
gte?: string
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Filter by a deletion date range.
|
||||||
|
*/
|
||||||
|
deleted_at?: {
|
||||||
|
/**
|
||||||
|
* filter by dates less than this date
|
||||||
|
*/
|
||||||
|
lt?: string
|
||||||
|
/**
|
||||||
|
* filter by dates greater than this date
|
||||||
|
*/
|
||||||
|
gt?: string
|
||||||
|
/**
|
||||||
|
* filter by dates less than or equal to this date
|
||||||
|
*/
|
||||||
|
lte?: string
|
||||||
|
/**
|
||||||
|
* filter by dates greater than or equal to this date
|
||||||
|
*/
|
||||||
|
gte?: string
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* The number of users to skip when retrieving the users.
|
||||||
|
*/
|
||||||
|
offset?: number
|
||||||
|
/**
|
||||||
|
* Limit the number of users returned.
|
||||||
|
*/
|
||||||
|
limit?: number
|
||||||
|
/**
|
||||||
|
* Comma-separated relations that should be expanded in the returned users.
|
||||||
|
*/
|
||||||
|
expand?: string
|
||||||
|
/**
|
||||||
|
* Comma-separated fields that should be included in the returned users.
|
||||||
|
*/
|
||||||
|
fields?: string
|
||||||
|
}
|
||||||
@@ -92,6 +92,7 @@ export type { AdminGetStockLocationsParams } from "./AdminGetStockLocationsParam
|
|||||||
export type { AdminGetSwapsParams } from "./AdminGetSwapsParams"
|
export type { AdminGetSwapsParams } from "./AdminGetSwapsParams"
|
||||||
export type { AdminGetTaxRatesParams } from "./AdminGetTaxRatesParams"
|
export type { AdminGetTaxRatesParams } from "./AdminGetTaxRatesParams"
|
||||||
export type { AdminGetTaxRatesTaxRateParams } from "./AdminGetTaxRatesTaxRateParams"
|
export type { AdminGetTaxRatesTaxRateParams } from "./AdminGetTaxRatesTaxRateParams"
|
||||||
|
export type { AdminGetUsersParams } from "./AdminGetUsersParams"
|
||||||
export type { AdminGetVariantParams } from "./AdminGetVariantParams"
|
export type { AdminGetVariantParams } from "./AdminGetVariantParams"
|
||||||
export type { AdminGetVariantsParams } from "./AdminGetVariantsParams"
|
export type { AdminGetVariantsParams } from "./AdminGetVariantsParams"
|
||||||
export type { AdminGetVariantsVariantInventoryRes } from "./AdminGetVariantsVariantInventoryRes"
|
export type { AdminGetVariantsVariantInventoryRes } from "./AdminGetVariantsVariantInventoryRes"
|
||||||
|
|||||||
@@ -1,37 +1,38 @@
|
|||||||
import {
|
import {
|
||||||
AdminDeleteUserRes,
|
AdminDeleteUserRes,
|
||||||
|
AdminGetUsersParams,
|
||||||
AdminResetPasswordRequest,
|
AdminResetPasswordRequest,
|
||||||
AdminResetPasswordTokenRequest,
|
AdminResetPasswordTokenRequest,
|
||||||
AdminUserRes,
|
AdminUserRes,
|
||||||
AdminUsersListRes,
|
AdminUsersListRes,
|
||||||
} from "@medusajs/medusa"
|
} from "@medusajs/medusa"
|
||||||
|
import qs from "qs"
|
||||||
import {
|
import {
|
||||||
ResponsePromise,
|
|
||||||
AdminCreateUserPayload,
|
AdminCreateUserPayload,
|
||||||
AdminUpdateUserPayload,
|
AdminUpdateUserPayload,
|
||||||
|
ResponsePromise,
|
||||||
} from "../.."
|
} from "../.."
|
||||||
import BaseResource from "../base"
|
import BaseResource from "../base"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class is used to send requests to [Admin User API Routes](https://docs.medusajs.com/api/admin#users). All its method
|
* This class is used to send requests to [Admin User API Routes](https://docs.medusajs.com/api/admin#users). All its method
|
||||||
* are available in the JS Client under the `medusa.admin.users` property.
|
* are available in the JS Client under the `medusa.admin.users` property.
|
||||||
*
|
*
|
||||||
* All methods in this class require {@link AdminAuthResource.createSession | user authentication}.
|
* All methods in this class require {@link AdminAuthResource.createSession | user authentication}.
|
||||||
*
|
*
|
||||||
* A store can have more than one user, each having the same privileges. Admins can manage users, their passwords, and more.
|
* A store can have more than one user, each having the same privileges. Admins can manage users, their passwords, and more.
|
||||||
*
|
*
|
||||||
* Related Guide: [How to manage users](https://docs.medusajs.com/modules/users/admin/manage-users).
|
* Related Guide: [How to manage users](https://docs.medusajs.com/modules/users/admin/manage-users).
|
||||||
*/
|
*/
|
||||||
class AdminUsersResource extends BaseResource {
|
class AdminUsersResource extends BaseResource {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate a password token for an admin user with a given email. This also triggers the `user.password_reset` event. So, if you have a Notification Service installed
|
* Generate a password token for an admin user with a given email. This also triggers the `user.password_reset` event. So, if you have a Notification Service installed
|
||||||
* that can handle this event, a notification, such as an email, will be sent to the user. The token is triggered as part of the `user.password_reset` event's payload.
|
* that can handle this event, a notification, such as an email, will be sent to the user. The token is triggered as part of the `user.password_reset` event's payload.
|
||||||
* That token must be used later to reset the password using the {@link resetPassword} method.
|
* That token must be used later to reset the password using the {@link resetPassword} method.
|
||||||
* @param {AdminResetPasswordTokenRequest} payload - The user's reset details.
|
* @param {AdminResetPasswordTokenRequest} payload - The user's reset details.
|
||||||
* @param {Record<string, any>} customHeaders - Custom headers to attach to the request.
|
* @param {Record<string, any>} customHeaders - Custom headers to attach to the request.
|
||||||
* @returns {ResponsePromise<void>} Resolves when the token is generated successfully.
|
* @returns {ResponsePromise<void>} Resolves when the token is generated successfully.
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* import Medusa from "@medusajs/medusa-js"
|
* import Medusa from "@medusajs/medusa-js"
|
||||||
* const medusa = new Medusa({ baseUrl: MEDUSA_BACKEND_URL, maxRetries: 3 })
|
* const medusa = new Medusa({ baseUrl: MEDUSA_BACKEND_URL, maxRetries: 3 })
|
||||||
@@ -60,7 +61,7 @@ class AdminUsersResource extends BaseResource {
|
|||||||
* @param {AdminResetPasswordRequest} payload - The reset details.
|
* @param {AdminResetPasswordRequest} payload - The reset details.
|
||||||
* @param {Record<string, any>} customHeaders - Custom headers to attach to the request.
|
* @param {Record<string, any>} customHeaders - Custom headers to attach to the request.
|
||||||
* @returns {ResponsePromise<AdminUserRes>} Resolves to the user's details.
|
* @returns {ResponsePromise<AdminUserRes>} Resolves to the user's details.
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* import Medusa from "@medusajs/medusa-js"
|
* import Medusa from "@medusajs/medusa-js"
|
||||||
* const medusa = new Medusa({ baseUrl: MEDUSA_BACKEND_URL, maxRetries: 3 })
|
* const medusa = new Medusa({ baseUrl: MEDUSA_BACKEND_URL, maxRetries: 3 })
|
||||||
@@ -86,7 +87,7 @@ class AdminUsersResource extends BaseResource {
|
|||||||
* @param {string} id - The user's ID.
|
* @param {string} id - The user's ID.
|
||||||
* @param {Record<string, any>} customHeaders - Custom headers to attach to the request.
|
* @param {Record<string, any>} customHeaders - Custom headers to attach to the request.
|
||||||
* @returns {ResponsePromise<AdminUserRes>} Resolves to the user's details.
|
* @returns {ResponsePromise<AdminUserRes>} Resolves to the user's details.
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* import Medusa from "@medusajs/medusa-js"
|
* import Medusa from "@medusajs/medusa-js"
|
||||||
* const medusa = new Medusa({ baseUrl: MEDUSA_BACKEND_URL, maxRetries: 3 })
|
* const medusa = new Medusa({ baseUrl: MEDUSA_BACKEND_URL, maxRetries: 3 })
|
||||||
@@ -109,7 +110,7 @@ class AdminUsersResource extends BaseResource {
|
|||||||
* @param {AdminCreateUserPayload} payload - The user to create.
|
* @param {AdminCreateUserPayload} payload - The user to create.
|
||||||
* @param {Record<string, any>} customHeaders - Custom headers to attach to the request.
|
* @param {Record<string, any>} customHeaders - Custom headers to attach to the request.
|
||||||
* @returns {ResponsePromise<AdminUserRes>} Resolves to the user's details.
|
* @returns {ResponsePromise<AdminUserRes>} Resolves to the user's details.
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* import Medusa from "@medusajs/medusa-js"
|
* import Medusa from "@medusajs/medusa-js"
|
||||||
* const medusa = new Medusa({ baseUrl: MEDUSA_BACKEND_URL, maxRetries: 3 })
|
* const medusa = new Medusa({ baseUrl: MEDUSA_BACKEND_URL, maxRetries: 3 })
|
||||||
@@ -136,7 +137,7 @@ class AdminUsersResource extends BaseResource {
|
|||||||
* @param {AdminUpdateUserPayload} payload - The attributes to update in the user.
|
* @param {AdminUpdateUserPayload} payload - The attributes to update in the user.
|
||||||
* @param {Record<string, any>} customHeaders - Custom headers to attach to the request.
|
* @param {Record<string, any>} customHeaders - Custom headers to attach to the request.
|
||||||
* @returns {ResponsePromise<AdminUserRes>} Resolves to the user's details.
|
* @returns {ResponsePromise<AdminUserRes>} Resolves to the user's details.
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* import Medusa from "@medusajs/medusa-js"
|
* import Medusa from "@medusajs/medusa-js"
|
||||||
* const medusa = new Medusa({ baseUrl: MEDUSA_BACKEND_URL, maxRetries: 3 })
|
* const medusa = new Medusa({ baseUrl: MEDUSA_BACKEND_URL, maxRetries: 3 })
|
||||||
@@ -162,7 +163,7 @@ class AdminUsersResource extends BaseResource {
|
|||||||
* @param {string} id - The user's ID.
|
* @param {string} id - The user's ID.
|
||||||
* @param {Record<string, any>} customHeaders - Custom headers to attach to the request.
|
* @param {Record<string, any>} customHeaders - Custom headers to attach to the request.
|
||||||
* @returns {ResponsePromise<AdminDeleteUserRes>} Resolves to the deletion operation's details.
|
* @returns {ResponsePromise<AdminDeleteUserRes>} Resolves to the deletion operation's details.
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* import Medusa from "@medusajs/medusa-js"
|
* import Medusa from "@medusajs/medusa-js"
|
||||||
* const medusa = new Medusa({ baseUrl: MEDUSA_BACKEND_URL, maxRetries: 3 })
|
* const medusa = new Medusa({ baseUrl: MEDUSA_BACKEND_URL, maxRetries: 3 })
|
||||||
@@ -184,8 +185,10 @@ class AdminUsersResource extends BaseResource {
|
|||||||
* Retrieve all admin users.
|
* Retrieve all admin users.
|
||||||
* @param {Record<string, any>} customHeaders - Custom headers to attach to the request.
|
* @param {Record<string, any>} customHeaders - Custom headers to attach to the request.
|
||||||
* @returns {ResponsePromise<AdminUsersListRes>} Resolves to the list of users.
|
* @returns {ResponsePromise<AdminUsersListRes>} Resolves to the list of users.
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
|
* To list users:
|
||||||
|
*
|
||||||
* import Medusa from "@medusajs/medusa-js"
|
* import Medusa from "@medusajs/medusa-js"
|
||||||
* const medusa = new Medusa({ baseUrl: MEDUSA_BACKEND_URL, maxRetries: 3 })
|
* const medusa = new Medusa({ baseUrl: MEDUSA_BACKEND_URL, maxRetries: 3 })
|
||||||
* // must be previously logged in or use api token
|
* // must be previously logged in or use api token
|
||||||
@@ -193,11 +196,33 @@ class AdminUsersResource extends BaseResource {
|
|||||||
* .then(({ users }) => {
|
* .then(({ users }) => {
|
||||||
* console.log(users.length);
|
* console.log(users.length);
|
||||||
* })
|
* })
|
||||||
|
*
|
||||||
|
* By default, only the first `20` users are returned. You can control pagination by specifying the `limit` and `offset` properties:
|
||||||
|
*
|
||||||
|
* ```ts
|
||||||
|
* import Medusa from "@medusajs/medusa-js"
|
||||||
|
* const medusa = new Medusa({ baseUrl: MEDUSA_BACKEND_URL, maxRetries: 3 })
|
||||||
|
* // must be previously logged in or use api token
|
||||||
|
* medusa.admin.users.list({
|
||||||
|
* limit,
|
||||||
|
* offset
|
||||||
|
* })
|
||||||
|
* .then(({ users, limit, offset, count }) => {
|
||||||
|
* console.log(users.length);
|
||||||
|
* })
|
||||||
|
* ```
|
||||||
*/
|
*/
|
||||||
list(
|
list(
|
||||||
|
query?: AdminGetUsersParams,
|
||||||
customHeaders: Record<string, any> = {}
|
customHeaders: Record<string, any> = {}
|
||||||
): ResponsePromise<AdminUsersListRes> {
|
): ResponsePromise<AdminUsersListRes> {
|
||||||
const path = `/admin/users`
|
let path = `/admin/users`
|
||||||
|
|
||||||
|
if (query) {
|
||||||
|
const queryString = qs.stringify(query)
|
||||||
|
path += `?${queryString}`
|
||||||
|
}
|
||||||
|
|
||||||
return this.client.request("GET", path, undefined, {}, customHeaders)
|
return this.client.request("GET", path, undefined, {}, customHeaders)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
import { AdminUserRes, AdminUsersListRes } from "@medusajs/medusa"
|
import {
|
||||||
|
AdminGetUsersParams,
|
||||||
|
AdminUserRes,
|
||||||
|
AdminUsersListRes,
|
||||||
|
} from "@medusajs/medusa"
|
||||||
import { Response } from "@medusajs/medusa-js"
|
import { Response } from "@medusajs/medusa-js"
|
||||||
import { useQuery } from "@tanstack/react-query"
|
import { useQuery } from "@tanstack/react-query"
|
||||||
import { useMedusa } from "../../../contexts"
|
import { useMedusa } from "../../../contexts"
|
||||||
@@ -13,14 +17,17 @@ type UserQueryKeys = typeof adminUserKeys
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* This hook retrieves all admin users.
|
* This hook retrieves all admin users.
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
|
* To list users:
|
||||||
|
*
|
||||||
|
* ```tsx
|
||||||
* import React from "react"
|
* import React from "react"
|
||||||
* import { useAdminUsers } from "medusa-react"
|
* import { useAdminUsers } from "medusa-react"
|
||||||
*
|
*
|
||||||
* const Users = () => {
|
* const Users = () => {
|
||||||
* const { users, isLoading } = useAdminUsers()
|
* const { users, isLoading } = useAdminUsers()
|
||||||
*
|
*
|
||||||
* return (
|
* return (
|
||||||
* <div>
|
* <div>
|
||||||
* {isLoading && <span>Loading...</span>}
|
* {isLoading && <span>Loading...</span>}
|
||||||
@@ -35,23 +42,60 @@ type UserQueryKeys = typeof adminUserKeys
|
|||||||
* </div>
|
* </div>
|
||||||
* )
|
* )
|
||||||
* }
|
* }
|
||||||
*
|
*
|
||||||
* export default Users
|
* export default Users
|
||||||
*
|
* ```
|
||||||
|
*
|
||||||
|
* By default, only the first `20` records are retrieved. You can control pagination by specifying the `limit` and `offset` properties:
|
||||||
|
*
|
||||||
|
* ```tsx
|
||||||
|
* import React from "react"
|
||||||
|
* import { useAdminUsers } from "medusa-react"
|
||||||
|
*
|
||||||
|
* const Users = () => {
|
||||||
|
* const {
|
||||||
|
* users,
|
||||||
|
* limit,
|
||||||
|
* offset,
|
||||||
|
* isLoading
|
||||||
|
* } = useAdminUsers({
|
||||||
|
* limit: 20,
|
||||||
|
* offset: 0
|
||||||
|
* })
|
||||||
|
*
|
||||||
|
* return (
|
||||||
|
* <div>
|
||||||
|
* {isLoading && <span>Loading...</span>}
|
||||||
|
* {users && !users.length && <span>No Users</span>}
|
||||||
|
* {users && users.length > 0 && (
|
||||||
|
* <ul>
|
||||||
|
* {users.map((user) => (
|
||||||
|
* <li key={user.id}>{user.email}</li>
|
||||||
|
* ))}
|
||||||
|
* </ul>
|
||||||
|
* )}
|
||||||
|
* </div>
|
||||||
|
* )
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* export default Users
|
||||||
|
* ```
|
||||||
|
*
|
||||||
* @customNamespace Hooks.Admin.Users
|
* @customNamespace Hooks.Admin.Users
|
||||||
* @category Queries
|
* @category Queries
|
||||||
*/
|
*/
|
||||||
export const useAdminUsers = (
|
export const useAdminUsers = (
|
||||||
|
query?: AdminGetUsersParams,
|
||||||
options?: UseQueryOptionsWrapper<
|
options?: UseQueryOptionsWrapper<
|
||||||
Response<AdminUsersListRes>,
|
Response<AdminUsersListRes>,
|
||||||
Error,
|
Error,
|
||||||
ReturnType<UserQueryKeys["lists"]>
|
ReturnType<UserQueryKeys["list"]>
|
||||||
>
|
>
|
||||||
) => {
|
) => {
|
||||||
const { client } = useMedusa()
|
const { client } = useMedusa()
|
||||||
const { data, ...rest } = useQuery(
|
const { data, ...rest } = useQuery(
|
||||||
adminUserKeys.lists(),
|
adminUserKeys.list(query),
|
||||||
() => client.admin.users.list(),
|
() => client.admin.users.list(query),
|
||||||
options
|
options
|
||||||
)
|
)
|
||||||
return { ...data, ...rest } as const
|
return { ...data, ...rest } as const
|
||||||
@@ -59,20 +103,20 @@ export const useAdminUsers = (
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* This hook retrieves an admin user's details.
|
* This hook retrieves an admin user's details.
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* import React from "react"
|
* import React from "react"
|
||||||
* import { useAdminUser } from "medusa-react"
|
* import { useAdminUser } from "medusa-react"
|
||||||
*
|
*
|
||||||
* type Props = {
|
* type Props = {
|
||||||
* userId: string
|
* userId: string
|
||||||
* }
|
* }
|
||||||
*
|
*
|
||||||
* const User = ({ userId }: Props) => {
|
* const User = ({ userId }: Props) => {
|
||||||
* const { user, isLoading } = useAdminUser(
|
* const { user, isLoading } = useAdminUser(
|
||||||
* userId
|
* userId
|
||||||
* )
|
* )
|
||||||
*
|
*
|
||||||
* return (
|
* return (
|
||||||
* <div>
|
* <div>
|
||||||
* {isLoading && <span>Loading...</span>}
|
* {isLoading && <span>Loading...</span>}
|
||||||
@@ -80,9 +124,9 @@ export const useAdminUsers = (
|
|||||||
* </div>
|
* </div>
|
||||||
* )
|
* )
|
||||||
* }
|
* }
|
||||||
*
|
*
|
||||||
* export default User
|
* export default User
|
||||||
*
|
*
|
||||||
* @customNamespace Hooks.Admin.Users
|
* @customNamespace Hooks.Admin.Users
|
||||||
* @category Queries
|
* @category Queries
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -21,8 +21,15 @@ describe("GET /admin/users", () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it("calls service retrieve", () => {
|
it("calls service retrieve", () => {
|
||||||
expect(UserServiceMock.list).toHaveBeenCalledTimes(1)
|
expect(UserServiceMock.listAndCount).toHaveBeenCalledTimes(1)
|
||||||
expect(UserServiceMock.list).toHaveBeenCalledWith({})
|
expect(UserServiceMock.listAndCount).toHaveBeenCalledWith(
|
||||||
|
{},
|
||||||
|
expect.objectContaining({
|
||||||
|
order: { created_at: "DESC" },
|
||||||
|
skip: 0,
|
||||||
|
take: 20,
|
||||||
|
})
|
||||||
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import { Router } from "express"
|
import { Router } from "express"
|
||||||
import { User } from "../../../.."
|
import { User } from "../../../../models/user"
|
||||||
import { DeleteResponse } from "../../../../types/common"
|
import { DeleteResponse } from "../../../../types/common"
|
||||||
import middlewares from "../../../middlewares"
|
import middlewares, { transformQuery } from "../../../middlewares"
|
||||||
|
import { AdminGetUsersParams } from "./list-users"
|
||||||
|
|
||||||
export const unauthenticatedUserRoutes = (app) => {
|
export const unauthenticatedUserRoutes = (app) => {
|
||||||
const route = Router()
|
const route = Router()
|
||||||
@@ -30,11 +31,31 @@ export default (app) => {
|
|||||||
|
|
||||||
route.delete("/:user_id", middlewares.wrap(require("./delete-user").default))
|
route.delete("/:user_id", middlewares.wrap(require("./delete-user").default))
|
||||||
|
|
||||||
route.get("/", middlewares.wrap(require("./list-users").default))
|
route.get(
|
||||||
|
"/",
|
||||||
|
transformQuery(AdminGetUsersParams, {
|
||||||
|
defaultFields: defaultAdminUserFields,
|
||||||
|
isList: true,
|
||||||
|
}),
|
||||||
|
middlewares.wrap(require("./list-users").default)
|
||||||
|
)
|
||||||
|
|
||||||
return app
|
return app
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const defaultAdminUserFields: (keyof User)[] = [
|
||||||
|
"id",
|
||||||
|
"email",
|
||||||
|
"first_name",
|
||||||
|
"last_name",
|
||||||
|
"role",
|
||||||
|
"api_token",
|
||||||
|
"created_at",
|
||||||
|
"updated_at",
|
||||||
|
"deleted_at",
|
||||||
|
"metadata",
|
||||||
|
]
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @schema AdminUserRes
|
* @schema AdminUserRes
|
||||||
* type: object
|
* type: object
|
||||||
|
|||||||
@@ -1,13 +1,100 @@
|
|||||||
|
import { Type } from "class-transformer"
|
||||||
|
import { IsEnum, IsOptional, IsString, ValidateNested } from "class-validator"
|
||||||
|
import { Request, Response } from "express"
|
||||||
import UserService from "../../../../services/user"
|
import UserService from "../../../../services/user"
|
||||||
|
import {
|
||||||
|
DateComparisonOperator,
|
||||||
|
extendedFindParamsMixin,
|
||||||
|
} from "../../../../types/common"
|
||||||
|
import { UserRole } from "../../../../types/user"
|
||||||
|
import { IsType } from "../../../../utils"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @oas [get] /admin/users
|
* @oas [get] /admin/users
|
||||||
* operationId: "GetUsers"
|
* operationId: "GetUsers"
|
||||||
* summary: "List Users"
|
* summary: "List Users"
|
||||||
* description: "Retrieve all admin users."
|
* description: "Retrieves a list of users. The users can be filtered by fields such as `q` or `email`. The users can also be sorted or paginated."
|
||||||
* x-authenticated: true
|
* x-authenticated: true
|
||||||
|
* parameters:
|
||||||
|
* - (query) id {string} Filter by a user ID.
|
||||||
|
* - (query) email {string} Filter by email.
|
||||||
|
* - (query) first_name {string} Filter by first name.
|
||||||
|
* - (query) last_name {string} Filter by last name.
|
||||||
|
* - (query) q {string} term used to search users' first name, last name, and email.
|
||||||
|
* - (query) order {string} A user field to sort-order the retrieved users by.
|
||||||
|
* - in: query
|
||||||
|
* name: created_at
|
||||||
|
* description: Filter by a creation date range.
|
||||||
|
* schema:
|
||||||
|
* type: object
|
||||||
|
* properties:
|
||||||
|
* lt:
|
||||||
|
* type: string
|
||||||
|
* description: filter by dates less than this date
|
||||||
|
* format: date
|
||||||
|
* gt:
|
||||||
|
* type: string
|
||||||
|
* description: filter by dates greater than this date
|
||||||
|
* format: date
|
||||||
|
* lte:
|
||||||
|
* type: string
|
||||||
|
* description: filter by dates less than or equal to this date
|
||||||
|
* format: date
|
||||||
|
* gte:
|
||||||
|
* type: string
|
||||||
|
* description: filter by dates greater than or equal to this date
|
||||||
|
* format: date
|
||||||
|
* - in: query
|
||||||
|
* name: updated_at
|
||||||
|
* description: Filter by an update date range.
|
||||||
|
* schema:
|
||||||
|
* type: object
|
||||||
|
* properties:
|
||||||
|
* lt:
|
||||||
|
* type: string
|
||||||
|
* description: filter by dates less than this date
|
||||||
|
* format: date
|
||||||
|
* gt:
|
||||||
|
* type: string
|
||||||
|
* description: filter by dates greater than this date
|
||||||
|
* format: date
|
||||||
|
* lte:
|
||||||
|
* type: string
|
||||||
|
* description: filter by dates less than or equal to this date
|
||||||
|
* format: date
|
||||||
|
* gte:
|
||||||
|
* type: string
|
||||||
|
* description: filter by dates greater than or equal to this date
|
||||||
|
* format: date
|
||||||
|
* - in: query
|
||||||
|
* name: deleted_at
|
||||||
|
* description: Filter by a deletion date range.
|
||||||
|
* schema:
|
||||||
|
* type: object
|
||||||
|
* properties:
|
||||||
|
* lt:
|
||||||
|
* type: string
|
||||||
|
* description: filter by dates less than this date
|
||||||
|
* format: date
|
||||||
|
* gt:
|
||||||
|
* type: string
|
||||||
|
* description: filter by dates greater than this date
|
||||||
|
* format: date
|
||||||
|
* lte:
|
||||||
|
* type: string
|
||||||
|
* description: filter by dates less than or equal to this date
|
||||||
|
* format: date
|
||||||
|
* gte:
|
||||||
|
* type: string
|
||||||
|
* description: filter by dates greater than or equal to this date
|
||||||
|
* format: date
|
||||||
|
* - (query) offset=0 {integer} The number of users to skip when retrieving the users.
|
||||||
|
* - (query) limit=20 {integer} Limit the number of users returned.
|
||||||
|
* - (query) expand {string} Comma-separated relations that should be expanded in the returned users.
|
||||||
|
* - (query) fields {string} Comma-separated fields that should be included in the returned users.
|
||||||
* x-codegen:
|
* x-codegen:
|
||||||
* method: list
|
* method: list
|
||||||
|
* queryParams: AdminGetUsersParams
|
||||||
* x-codeSamples:
|
* x-codeSamples:
|
||||||
* - lang: JavaScript
|
* - lang: JavaScript
|
||||||
* label: JS Client
|
* label: JS Client
|
||||||
@@ -16,7 +103,7 @@ import UserService from "../../../../services/user"
|
|||||||
* const medusa = new Medusa({ baseUrl: MEDUSA_BACKEND_URL, maxRetries: 3 })
|
* const medusa = new Medusa({ baseUrl: MEDUSA_BACKEND_URL, maxRetries: 3 })
|
||||||
* // must be previously logged in or use api token
|
* // must be previously logged in or use api token
|
||||||
* medusa.admin.users.list()
|
* medusa.admin.users.list()
|
||||||
* .then(({ users }) => {
|
* .then(({ users, limit, offset, count }) => {
|
||||||
* console.log(users.length);
|
* console.log(users.length);
|
||||||
* })
|
* })
|
||||||
* - lang: tsx
|
* - lang: tsx
|
||||||
@@ -75,9 +162,96 @@ import UserService from "../../../../services/user"
|
|||||||
* "500":
|
* "500":
|
||||||
* $ref: "#/components/responses/500_error"
|
* $ref: "#/components/responses/500_error"
|
||||||
*/
|
*/
|
||||||
export default async (req, res) => {
|
export default async (req: Request, res: Response) => {
|
||||||
const userService: UserService = req.scope.resolve("userService")
|
const userService: UserService = req.scope.resolve("userService")
|
||||||
const users = await userService.list({})
|
|
||||||
|
|
||||||
res.status(200).json({ users })
|
const listConfig = req.listConfig
|
||||||
|
const filterableFields = req.filterableFields
|
||||||
|
|
||||||
|
const [users, count] = await userService.listAndCount(
|
||||||
|
filterableFields,
|
||||||
|
listConfig
|
||||||
|
)
|
||||||
|
|
||||||
|
res
|
||||||
|
.status(200)
|
||||||
|
.json({ users, count, offset: listConfig.skip, limit: listConfig.take })
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parameters used to filter and configure the pagination of the retrieved users.
|
||||||
|
*/
|
||||||
|
export class AdminGetUsersParams extends extendedFindParamsMixin() {
|
||||||
|
/**
|
||||||
|
* IDs to filter users by.
|
||||||
|
*/
|
||||||
|
@IsOptional()
|
||||||
|
@IsType([String, [String]])
|
||||||
|
id?: string | string[]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search terms to search users' first name, last name, and email.
|
||||||
|
*/
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
q?: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The field to sort the data by. By default, the sort order is ascending. To change the order to descending, prefix the field name with `-`.
|
||||||
|
*/
|
||||||
|
@IsString()
|
||||||
|
@IsOptional()
|
||||||
|
order?: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Date filters to apply on the users' `update_at` date.
|
||||||
|
*/
|
||||||
|
@IsOptional()
|
||||||
|
@ValidateNested()
|
||||||
|
@Type(() => DateComparisonOperator)
|
||||||
|
updated_at?: DateComparisonOperator
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Date filters to apply on the customer users' `created_at` date.
|
||||||
|
*/
|
||||||
|
@IsOptional()
|
||||||
|
@ValidateNested()
|
||||||
|
@Type(() => DateComparisonOperator)
|
||||||
|
created_at?: DateComparisonOperator
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Date filters to apply on the users' `deleted_at` date.
|
||||||
|
*/
|
||||||
|
@IsOptional()
|
||||||
|
@ValidateNested()
|
||||||
|
@Type(() => DateComparisonOperator)
|
||||||
|
deleted_at?: DateComparisonOperator
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter to apply on the users' `email` field.
|
||||||
|
*/
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
email?: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter to apply on the users' `first_name` field.
|
||||||
|
*/
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
first_name?: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter to apply on the users' `last_name` field.
|
||||||
|
*/
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
last_name?: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter to apply on the users' `role` field.
|
||||||
|
*/
|
||||||
|
@IsOptional()
|
||||||
|
@IsEnum(UserRole, { each: true })
|
||||||
|
role?: UserRole
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import Scrypt from "scrypt-kdf"
|
|
||||||
import { IdMap } from "medusa-test-utils"
|
import { IdMap } from "medusa-test-utils"
|
||||||
import _ from "lodash"
|
import Scrypt from "scrypt-kdf"
|
||||||
|
|
||||||
export const users = {
|
export const users = {
|
||||||
testUser: {
|
testUser: {
|
||||||
@@ -29,7 +28,7 @@ export const UserServiceMock = {
|
|||||||
withTransaction: function () {
|
withTransaction: function () {
|
||||||
return this
|
return this
|
||||||
},
|
},
|
||||||
create: jest.fn().mockImplementation(data => {
|
create: jest.fn().mockImplementation((data) => {
|
||||||
if (data.email === "oliver@test.dk") {
|
if (data.email === "oliver@test.dk") {
|
||||||
return Promise.resolve(users.testUser)
|
return Promise.resolve(users.testUser)
|
||||||
}
|
}
|
||||||
@@ -37,7 +36,8 @@ export const UserServiceMock = {
|
|||||||
}),
|
}),
|
||||||
update: jest.fn().mockReturnValue(Promise.resolve()),
|
update: jest.fn().mockReturnValue(Promise.resolve()),
|
||||||
list: jest.fn().mockReturnValue(Promise.resolve([])),
|
list: jest.fn().mockReturnValue(Promise.resolve([])),
|
||||||
delete: jest.fn().mockImplementation(data => {
|
listAndCount: jest.fn().mockReturnValue(Promise.resolve([[], 0])),
|
||||||
|
delete: jest.fn().mockImplementation((data) => {
|
||||||
if (data === IdMap.getId("delete-user")) {
|
if (data === IdMap.getId("delete-user")) {
|
||||||
return Promise.resolve({
|
return Promise.resolve({
|
||||||
id: IdMap.getId("delete-user"),
|
id: IdMap.getId("delete-user"),
|
||||||
@@ -47,7 +47,7 @@ export const UserServiceMock = {
|
|||||||
}
|
}
|
||||||
return Promise.resolve(undefined)
|
return Promise.resolve(undefined)
|
||||||
}),
|
}),
|
||||||
retrieve: jest.fn().mockImplementation(userId => {
|
retrieve: jest.fn().mockImplementation((userId) => {
|
||||||
if (userId === IdMap.getId("test-user")) {
|
if (userId === IdMap.getId("test-user")) {
|
||||||
return Promise.resolve(users.testUser)
|
return Promise.resolve(users.testUser)
|
||||||
}
|
}
|
||||||
@@ -60,7 +60,7 @@ export const UserServiceMock = {
|
|||||||
}
|
}
|
||||||
return Promise.resolve(undefined)
|
return Promise.resolve(undefined)
|
||||||
}),
|
}),
|
||||||
setPassword_: jest.fn().mockImplementation(userId => {
|
setPassword_: jest.fn().mockImplementation((userId) => {
|
||||||
if (userId === IdMap.getId("test-user")) {
|
if (userId === IdMap.getId("test-user")) {
|
||||||
return Promise.resolve(users.testUser)
|
return Promise.resolve(users.testUser)
|
||||||
}
|
}
|
||||||
@@ -80,13 +80,13 @@ export const UserServiceMock = {
|
|||||||
generateResetPasswordToken: jest
|
generateResetPasswordToken: jest
|
||||||
.fn()
|
.fn()
|
||||||
.mockReturnValue(Promise.resolve("JSONWEBTOKEN")),
|
.mockReturnValue(Promise.resolve("JSONWEBTOKEN")),
|
||||||
retrieveByApiToken: jest.fn().mockImplementation(token => {
|
retrieveByApiToken: jest.fn().mockImplementation((token) => {
|
||||||
if (token === "123456789") {
|
if (token === "123456789") {
|
||||||
return Promise.resolve(users.user1)
|
return Promise.resolve(users.user1)
|
||||||
}
|
}
|
||||||
return Promise.resolve(undefined)
|
return Promise.resolve(undefined)
|
||||||
}),
|
}),
|
||||||
retrieveByEmail: jest.fn().mockImplementation(email => {
|
retrieveByEmail: jest.fn().mockImplementation((email) => {
|
||||||
if (email === "vandijk@test.dk") {
|
if (email === "vandijk@test.dk") {
|
||||||
return Promise.resolve({
|
return Promise.resolve({
|
||||||
id: IdMap.getId("vandijk"),
|
id: IdMap.getId("vandijk"),
|
||||||
@@ -95,7 +95,7 @@ export const UserServiceMock = {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
if (email === "oliver@test.dk") {
|
if (email === "oliver@test.dk") {
|
||||||
return Scrypt.kdf("123456789", { logN: 1, r: 1, p: 1 }).then(hash => ({
|
return Scrypt.kdf("123456789", { logN: 1, r: 1, p: 1 }).then((hash) => ({
|
||||||
email,
|
email,
|
||||||
password_hash: hash.toString("base64"),
|
password_hash: hash.toString("base64"),
|
||||||
}))
|
}))
|
||||||
|
|||||||
@@ -1,17 +1,18 @@
|
|||||||
|
import { Selector } from "@medusajs/types"
|
||||||
import { FlagRouter } from "@medusajs/utils"
|
import { FlagRouter } from "@medusajs/utils"
|
||||||
import jwt from "jsonwebtoken"
|
import jwt from "jsonwebtoken"
|
||||||
import { isDefined, MedusaError } from "medusa-core-utils"
|
import { isDefined, MedusaError } from "medusa-core-utils"
|
||||||
import Scrypt from "scrypt-kdf"
|
import Scrypt from "scrypt-kdf"
|
||||||
import { EntityManager } from "typeorm"
|
import { EntityManager, FindOptionsWhere, ILike } from "typeorm"
|
||||||
import { TransactionBaseService } from "../interfaces"
|
import { TransactionBaseService } from "../interfaces"
|
||||||
import AnalyticsFeatureFlag from "../loaders/feature-flags/analytics"
|
import AnalyticsFeatureFlag from "../loaders/feature-flags/analytics"
|
||||||
import { User } from "../models"
|
import { User } from "../models"
|
||||||
import { UserRepository } from "../repositories/user"
|
import { UserRepository } from "../repositories/user"
|
||||||
import { FindConfig } from "../types/common"
|
import { FindConfig } from "../types/common"
|
||||||
import {
|
import {
|
||||||
CreateUserInput,
|
CreateUserInput,
|
||||||
FilterableUserProps,
|
FilterableUserProps,
|
||||||
UpdateUserInput,
|
UpdateUserInput,
|
||||||
} from "../types/user"
|
} from "../types/user"
|
||||||
import { buildQuery, setMetadata } from "../utils"
|
import { buildQuery, setMetadata } from "../utils"
|
||||||
import { validateEmail } from "../utils/is-email"
|
import { validateEmail } from "../utils/is-email"
|
||||||
@@ -62,9 +63,86 @@ class UserService extends TransactionBaseService {
|
|||||||
* @param {Object} config - the configuration object for the query
|
* @param {Object} config - the configuration object for the query
|
||||||
* @return {Promise} the result of the find operation
|
* @return {Promise} the result of the find operation
|
||||||
*/
|
*/
|
||||||
async list(selector: FilterableUserProps, config = {}): Promise<User[]> {
|
async list(
|
||||||
|
selector: Selector<FilterableUserProps> & { q?: string } = {},
|
||||||
|
config: FindConfig<FilterableUserProps> = { skip: 0, take: 20 }
|
||||||
|
): Promise<User[]> {
|
||||||
const userRepo = this.activeManager_.withRepository(this.userRepository_)
|
const userRepo = this.activeManager_.withRepository(this.userRepository_)
|
||||||
return await userRepo.find(buildQuery(selector, config))
|
|
||||||
|
let q: string | undefined
|
||||||
|
|
||||||
|
if (selector.q) {
|
||||||
|
q = selector.q
|
||||||
|
delete selector.q
|
||||||
|
}
|
||||||
|
|
||||||
|
const query = buildQuery(selector, config)
|
||||||
|
|
||||||
|
if (q) {
|
||||||
|
const where = query.where as FindOptionsWhere<FilterableUserProps>
|
||||||
|
|
||||||
|
delete where.email
|
||||||
|
delete where.first_name
|
||||||
|
delete where.last_name
|
||||||
|
|
||||||
|
query.where = [
|
||||||
|
{
|
||||||
|
...where,
|
||||||
|
email: ILike(`%${q}%`),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
...where,
|
||||||
|
first_name: ILike(`%${q}%`),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
...where,
|
||||||
|
last_name: ILike(`%${q}%`),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
return await userRepo.find(query)
|
||||||
|
}
|
||||||
|
|
||||||
|
async listAndCount(
|
||||||
|
selector: Selector<FilterableUserProps> & { q?: string } = {},
|
||||||
|
config: FindConfig<FilterableUserProps> = { skip: 0, take: 20 }
|
||||||
|
) {
|
||||||
|
const userRepo = this.activeManager_.withRepository(this.userRepository_)
|
||||||
|
|
||||||
|
let q: string | undefined
|
||||||
|
|
||||||
|
if (selector.q) {
|
||||||
|
q = selector.q
|
||||||
|
delete selector.q
|
||||||
|
}
|
||||||
|
|
||||||
|
const query = buildQuery(selector, config)
|
||||||
|
|
||||||
|
if (q) {
|
||||||
|
const where = query.where as FindOptionsWhere<FilterableUserProps>
|
||||||
|
|
||||||
|
delete where.email
|
||||||
|
delete where.first_name
|
||||||
|
delete where.last_name
|
||||||
|
|
||||||
|
query.where = [
|
||||||
|
{
|
||||||
|
...where,
|
||||||
|
email: ILike(`%${q}%`),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
...where,
|
||||||
|
first_name: ILike(`%${q}%`),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
...where,
|
||||||
|
last_name: ILike(`%${q}%`),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
return await userRepo.findAndCount(query)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ export type FilterableUserProps = PartialPick<
|
|||||||
| "email"
|
| "email"
|
||||||
| "first_name"
|
| "first_name"
|
||||||
| "last_name"
|
| "last_name"
|
||||||
|
| "role"
|
||||||
| "created_at"
|
| "created_at"
|
||||||
| "updated_at"
|
| "updated_at"
|
||||||
| "deleted_at"
|
| "deleted_at"
|
||||||
|
|||||||
Reference in New Issue
Block a user