feat(medusa, types, core-flows): Add invite endpoints for api-v2 (#6395)
**What** - Add invite endpoints for simple operations on invites
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
import { initDb, useDb } from "../../../environment-helpers/use-db"
|
||||
|
||||
import path from "path"
|
||||
import { startBootstrapApp } from "../../../environment-helpers/bootstrap-app"
|
||||
import { useApi } from "../../../environment-helpers/use-api"
|
||||
import adminSeeder from "../../../helpers/admin-seeder"
|
||||
import { AxiosInstance } from "axios"
|
||||
|
||||
jest.setTimeout(50000)
|
||||
|
||||
const env = { MEDUSA_FF_MEDUSA_V2: true }
|
||||
const adminHeaders = {
|
||||
headers: { "x-medusa-access-token": "test_token" },
|
||||
}
|
||||
|
||||
describe("POST /admin/invites", () => {
|
||||
let dbConnection
|
||||
let shutdownServer
|
||||
|
||||
beforeAll(async () => {
|
||||
const cwd = path.resolve(path.join(__dirname, "..", ".."))
|
||||
dbConnection = await initDb({ cwd, env } as any)
|
||||
shutdownServer = await startBootstrapApp({ cwd, env })
|
||||
})
|
||||
|
||||
beforeEach(async () => {
|
||||
await adminSeeder(dbConnection)
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
const db = useDb()
|
||||
await db.shutdown()
|
||||
await shutdownServer()
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
const db = useDb()
|
||||
await db.teardown()
|
||||
})
|
||||
|
||||
it("create an invite", async () => {
|
||||
const api = useApi()! as AxiosInstance
|
||||
|
||||
const body = {
|
||||
email: "test_member@test.com",
|
||||
}
|
||||
|
||||
const response = await api.post(`/admin/invites`, body, adminHeaders)
|
||||
|
||||
expect(response.status).toEqual(200)
|
||||
expect(response.data).toEqual({
|
||||
invite: expect.objectContaining(body),
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,76 @@
|
||||
import { initDb, useDb } from "../../../environment-helpers/use-db"
|
||||
|
||||
import { IUserModuleService } from "@medusajs/types"
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import { getContainer } from "../../../environment-helpers/use-container"
|
||||
import path from "path"
|
||||
import { startBootstrapApp } from "../../../environment-helpers/bootstrap-app"
|
||||
import { useApi } from "../../../environment-helpers/use-api"
|
||||
import adminSeeder from "../../../helpers/admin-seeder"
|
||||
import { AxiosInstance } from "axios"
|
||||
|
||||
jest.setTimeout(50000)
|
||||
|
||||
const env = { MEDUSA_FF_MEDUSA_V2: true }
|
||||
const adminHeaders = {
|
||||
headers: { "x-medusa-access-token": "test_token" },
|
||||
}
|
||||
|
||||
describe("DELETE /admin/invites/:id", () => {
|
||||
let dbConnection
|
||||
let appContainer
|
||||
let shutdownServer
|
||||
let userModuleService: IUserModuleService
|
||||
|
||||
beforeAll(async () => {
|
||||
const cwd = path.resolve(path.join(__dirname, "..", ".."))
|
||||
dbConnection = await initDb({ cwd, env } as any)
|
||||
shutdownServer = await startBootstrapApp({ cwd, env })
|
||||
appContainer = getContainer()
|
||||
userModuleService = appContainer.resolve(ModuleRegistrationName.USER)
|
||||
})
|
||||
|
||||
beforeEach(async () => {
|
||||
await adminSeeder(dbConnection)
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
const db = useDb()
|
||||
await db.shutdown()
|
||||
await shutdownServer()
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
const db = useDb()
|
||||
await db.teardown()
|
||||
})
|
||||
|
||||
it("should delete a single invite", async () => {
|
||||
const invite = await userModuleService.createInvites({
|
||||
email: "potential_member@test.com",
|
||||
token: "test",
|
||||
expires_at: new Date(),
|
||||
})
|
||||
|
||||
const api = useApi()! as AxiosInstance
|
||||
|
||||
const response = await api.delete(
|
||||
`/admin/invites/${invite.id}`,
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
expect(response.status).toEqual(200)
|
||||
expect(response.data).toEqual({
|
||||
id: invite.id,
|
||||
object: "invite",
|
||||
deleted: true,
|
||||
})
|
||||
|
||||
const { response: deletedResponse } = await api
|
||||
.get(`/admin/invites/${invite.id}`, adminHeaders)
|
||||
.catch((e) => e)
|
||||
|
||||
expect(deletedResponse.status).toEqual(404)
|
||||
expect(deletedResponse.data.type).toEqual("not_found")
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,69 @@
|
||||
import { initDb, useDb } from "../../../environment-helpers/use-db"
|
||||
|
||||
import { IUserModuleService } from "@medusajs/types"
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import { getContainer } from "../../../environment-helpers/use-container"
|
||||
import path from "path"
|
||||
import { startBootstrapApp } from "../../../environment-helpers/bootstrap-app"
|
||||
import { useApi } from "../../../environment-helpers/use-api"
|
||||
import adminSeeder from "../../../helpers/admin-seeder"
|
||||
import { AxiosInstance } from "axios"
|
||||
|
||||
jest.setTimeout(50000)
|
||||
|
||||
const env = { MEDUSA_FF_MEDUSA_V2: true }
|
||||
const adminHeaders = {
|
||||
headers: { "x-medusa-access-token": "test_token" },
|
||||
}
|
||||
|
||||
describe("GET /admin/invites", () => {
|
||||
let dbConnection
|
||||
let appContainer
|
||||
let shutdownServer
|
||||
let userModuleService: IUserModuleService
|
||||
|
||||
beforeAll(async () => {
|
||||
const cwd = path.resolve(path.join(__dirname, "..", ".."))
|
||||
dbConnection = await initDb({ cwd, env } as any)
|
||||
shutdownServer = await startBootstrapApp({ cwd, env })
|
||||
appContainer = getContainer()
|
||||
userModuleService = appContainer.resolve(ModuleRegistrationName.USER)
|
||||
})
|
||||
|
||||
beforeEach(async () => {
|
||||
await adminSeeder(dbConnection)
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
const db = useDb()
|
||||
await db.shutdown()
|
||||
await shutdownServer()
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
const db = useDb()
|
||||
await db.teardown()
|
||||
})
|
||||
|
||||
it("should list invites", async () => {
|
||||
await userModuleService.createInvites({
|
||||
email: "potential_member@test.com",
|
||||
token: "test",
|
||||
expires_at: new Date(),
|
||||
})
|
||||
|
||||
const api = useApi()! as AxiosInstance
|
||||
|
||||
const response = await api.get(`/admin/invites`, adminHeaders)
|
||||
|
||||
expect(response.status).toEqual(200)
|
||||
expect(response.data).toEqual({
|
||||
invites: [
|
||||
expect.objectContaining({ email: "potential_member@test.com" }),
|
||||
],
|
||||
count: 1,
|
||||
offset: 0,
|
||||
limit: 50,
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,64 @@
|
||||
import { initDb, useDb } from "../../../environment-helpers/use-db"
|
||||
|
||||
import { IUserModuleService } from "@medusajs/types"
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import { getContainer } from "../../../environment-helpers/use-container"
|
||||
import path from "path"
|
||||
import { startBootstrapApp } from "../../../environment-helpers/bootstrap-app"
|
||||
import { useApi } from "../../../environment-helpers/use-api"
|
||||
import adminSeeder from "../../../helpers/admin-seeder"
|
||||
import { AxiosInstance } from "axios"
|
||||
|
||||
jest.setTimeout(50000)
|
||||
|
||||
const env = { MEDUSA_FF_MEDUSA_V2: true }
|
||||
const adminHeaders = {
|
||||
headers: { "x-medusa-access-token": "test_token" },
|
||||
}
|
||||
|
||||
describe("GET /admin/invites/:id", () => {
|
||||
let dbConnection
|
||||
let appContainer
|
||||
let shutdownServer
|
||||
let userModuleService: IUserModuleService
|
||||
|
||||
beforeAll(async () => {
|
||||
const cwd = path.resolve(path.join(__dirname, "..", ".."))
|
||||
dbConnection = await initDb({ cwd, env } as any)
|
||||
shutdownServer = await startBootstrapApp({ cwd, env })
|
||||
appContainer = getContainer()
|
||||
userModuleService = appContainer.resolve(ModuleRegistrationName.USER)
|
||||
})
|
||||
|
||||
beforeEach(async () => {
|
||||
await adminSeeder(dbConnection)
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
const db = useDb()
|
||||
await db.shutdown()
|
||||
await shutdownServer()
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
const db = useDb()
|
||||
await db.teardown()
|
||||
})
|
||||
|
||||
it("should retrieve a single invite", async () => {
|
||||
const invite = await userModuleService.createInvites({
|
||||
email: "potential_member@test.com",
|
||||
token: "test",
|
||||
expires_at: new Date(),
|
||||
})
|
||||
|
||||
const api = useApi()! as AxiosInstance
|
||||
|
||||
const response = await api.get(`/admin/invites/${invite.id}`, adminHeaders)
|
||||
|
||||
expect(response.status).toEqual(200)
|
||||
expect(response.data.invite).toEqual(
|
||||
expect.objectContaining({ email: "potential_member@test.com" })
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -5,3 +5,4 @@ export * from "./promotion"
|
||||
export * from "./customer"
|
||||
export * from "./customer-group"
|
||||
export * from "./user"
|
||||
export * from "./invite"
|
||||
|
||||
2
packages/core-flows/src/invite/index.ts
Normal file
2
packages/core-flows/src/invite/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from "./steps"
|
||||
export * from "./workflows"
|
||||
31
packages/core-flows/src/invite/steps/create-invites.ts
Normal file
31
packages/core-flows/src/invite/steps/create-invites.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import { CreateInviteDTO, IUserModuleService, InviteDTO } from "@medusajs/types"
|
||||
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||
|
||||
export const createInviteStepId = "create-invite-step"
|
||||
export const createInviteStep = createStep(
|
||||
createInviteStepId,
|
||||
async (input: CreateInviteDTO[], { container }) => {
|
||||
const service: IUserModuleService = container.resolve(
|
||||
ModuleRegistrationName.USER
|
||||
)
|
||||
|
||||
const createdInvites = await service.createInvites(input)
|
||||
|
||||
return new StepResponse(
|
||||
createdInvites,
|
||||
createdInvites.map((inv) => inv.id)
|
||||
)
|
||||
},
|
||||
async (createdInvitesIds, { container }) => {
|
||||
if (!createdInvitesIds?.length) {
|
||||
return
|
||||
}
|
||||
|
||||
const service: IUserModuleService = container.resolve(
|
||||
ModuleRegistrationName.USER
|
||||
)
|
||||
|
||||
await service.deleteInvites(createdInvitesIds)
|
||||
}
|
||||
)
|
||||
28
packages/core-flows/src/invite/steps/delete-invites.ts
Normal file
28
packages/core-flows/src/invite/steps/delete-invites.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import { IUserModuleService } from "@medusajs/types"
|
||||
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||
|
||||
export const deleteInvitesStepId = "delete-invites-step"
|
||||
export const deleteInvitesStep = createStep(
|
||||
deleteInvitesStepId,
|
||||
async (input: string[], { container }) => {
|
||||
const service: IUserModuleService = container.resolve(
|
||||
ModuleRegistrationName.USER
|
||||
)
|
||||
|
||||
await service.softDeleteInvites(input)
|
||||
|
||||
return new StepResponse(void 0, input)
|
||||
},
|
||||
async (deletedInviteIds, { container }) => {
|
||||
if (!deletedInviteIds?.length) {
|
||||
return
|
||||
}
|
||||
|
||||
const service: IUserModuleService = container.resolve(
|
||||
ModuleRegistrationName.USER
|
||||
)
|
||||
|
||||
await service.restoreInvites(deletedInviteIds)
|
||||
}
|
||||
)
|
||||
2
packages/core-flows/src/invite/steps/index.ts
Normal file
2
packages/core-flows/src/invite/steps/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from "./create-invites"
|
||||
export * from "./delete-invites"
|
||||
13
packages/core-flows/src/invite/workflows/create-invites.ts
Normal file
13
packages/core-flows/src/invite/workflows/create-invites.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { WorkflowData, createWorkflow } from "@medusajs/workflows-sdk"
|
||||
import { createInviteStep } from "../steps"
|
||||
import { InviteDTO, InviteWorkflow } from "@medusajs/types"
|
||||
|
||||
export const createInvitesWorkflowId = "create-invite-step"
|
||||
export const createInvitesWorkflow = createWorkflow(
|
||||
createInvitesWorkflowId,
|
||||
(
|
||||
input: WorkflowData<InviteWorkflow.CreateInvitesWorkflowInputDTO>
|
||||
): WorkflowData<InviteDTO[]> => {
|
||||
return createInviteStep(input.invites)
|
||||
}
|
||||
)
|
||||
13
packages/core-flows/src/invite/workflows/delete-invites.ts
Normal file
13
packages/core-flows/src/invite/workflows/delete-invites.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { WorkflowData, createWorkflow } from "@medusajs/workflows-sdk"
|
||||
import { deleteInvitesStep } from "../steps"
|
||||
import { InviteWorkflow, UserWorkflow } from "@medusajs/types"
|
||||
|
||||
export const deleteInvitesWorkflowId = "delete-invites-workflow"
|
||||
export const deleteInvitesWorkflow = createWorkflow(
|
||||
deleteInvitesWorkflowId,
|
||||
(
|
||||
input: WorkflowData<InviteWorkflow.DeleteInvitesWorkflowInput>
|
||||
): WorkflowData<void> => {
|
||||
return deleteInvitesStep(input.ids)
|
||||
}
|
||||
)
|
||||
2
packages/core-flows/src/invite/workflows/index.ts
Normal file
2
packages/core-flows/src/invite/workflows/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from "./create-invites"
|
||||
export * from "./delete-invites"
|
||||
55
packages/medusa/src/api-v2/admin/invites/[id]/route.ts
Normal file
55
packages/medusa/src/api-v2/admin/invites/[id]/route.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import {
|
||||
ContainerRegistrationKeys,
|
||||
MedusaError,
|
||||
remoteQueryObjectFromString,
|
||||
} from "@medusajs/utils"
|
||||
import { MedusaRequest, MedusaResponse } from "../../../../types/routing"
|
||||
import { deleteInvitesWorkflow } from "@medusajs/core-flows"
|
||||
import { IUserModuleService, UpdateUserDTO } from "@medusajs/types"
|
||||
import { ModuleRegistrationName } from "../../../../../../modules-sdk/dist"
|
||||
|
||||
// Get invite
|
||||
export const GET = async (req: MedusaRequest, res: MedusaResponse) => {
|
||||
const { id } = req.params
|
||||
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
|
||||
|
||||
const query = remoteQueryObjectFromString({
|
||||
entryPoint: "invite",
|
||||
variables: {
|
||||
id,
|
||||
},
|
||||
fields: req.retrieveConfig.select as string[],
|
||||
})
|
||||
|
||||
const [invite] = await remoteQuery(query)
|
||||
|
||||
if (!invite) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_FOUND,
|
||||
`Invite with id: ${id} was not found`
|
||||
)
|
||||
}
|
||||
|
||||
res.status(200).json({ invite })
|
||||
}
|
||||
|
||||
// delete invite
|
||||
export const DELETE = async (req: MedusaRequest, res: MedusaResponse) => {
|
||||
const { id } = req.params
|
||||
const workflow = deleteInvitesWorkflow(req.scope)
|
||||
|
||||
const { errors } = await workflow.run({
|
||||
input: { ids: [id] },
|
||||
throwOnError: false,
|
||||
})
|
||||
|
||||
if (Array.isArray(errors) && errors[0]) {
|
||||
throw errors[0].error
|
||||
}
|
||||
|
||||
res.status(200).json({
|
||||
id,
|
||||
object: "invite",
|
||||
deleted: true,
|
||||
})
|
||||
}
|
||||
36
packages/medusa/src/api-v2/admin/invites/middlewares.ts
Normal file
36
packages/medusa/src/api-v2/admin/invites/middlewares.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import { transformBody, transformQuery } from "../../../api/middlewares"
|
||||
import {
|
||||
AdminCreateInviteRequest,
|
||||
AdminGetInvitesParams,
|
||||
AdminGetInvitesInviteParams,
|
||||
} from "./validators"
|
||||
import * as QueryConfig from "./query-config"
|
||||
import { MiddlewareRoute } from "../../../types/middlewares"
|
||||
|
||||
export const adminInviteRoutesMiddlewares: MiddlewareRoute[] = [
|
||||
{
|
||||
method: ["GET"],
|
||||
matcher: "/admin/invites",
|
||||
middlewares: [
|
||||
transformQuery(
|
||||
AdminGetInvitesParams,
|
||||
QueryConfig.listTransformQueryConfig
|
||||
),
|
||||
],
|
||||
},
|
||||
{
|
||||
method: ["POST"],
|
||||
matcher: "/admin/invites",
|
||||
middlewares: [transformBody(AdminCreateInviteRequest)],
|
||||
},
|
||||
{
|
||||
method: ["GET"],
|
||||
matcher: "/admin/invites/:id",
|
||||
middlewares: [
|
||||
transformQuery(
|
||||
AdminGetInvitesInviteParams,
|
||||
QueryConfig.retrieveTransformQueryConfig
|
||||
),
|
||||
],
|
||||
},
|
||||
]
|
||||
25
packages/medusa/src/api-v2/admin/invites/query-config.ts
Normal file
25
packages/medusa/src/api-v2/admin/invites/query-config.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
export const defaultAdminInviteRelations = []
|
||||
export const allowedAdminInviteRelations = []
|
||||
export const defaultAdminInviteFields = [
|
||||
"id",
|
||||
"email",
|
||||
"accepted",
|
||||
"token",
|
||||
"expires_at",
|
||||
"metadata",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"deleted_at",
|
||||
]
|
||||
|
||||
export const retrieveTransformQueryConfig = {
|
||||
defaultFields: defaultAdminInviteFields,
|
||||
defaultRelations: defaultAdminInviteRelations,
|
||||
allowedRelations: allowedAdminInviteRelations,
|
||||
isList: false,
|
||||
}
|
||||
|
||||
export const listTransformQueryConfig = {
|
||||
...retrieveTransformQueryConfig,
|
||||
isList: true,
|
||||
}
|
||||
50
packages/medusa/src/api-v2/admin/invites/route.ts
Normal file
50
packages/medusa/src/api-v2/admin/invites/route.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
import {
|
||||
ContainerRegistrationKeys,
|
||||
remoteQueryObjectFromString,
|
||||
} from "@medusajs/utils"
|
||||
import { MedusaRequest, MedusaResponse } from "../../../types/routing"
|
||||
import { createInvitesWorkflow } from "@medusajs/core-flows"
|
||||
import { CreateInviteDTO, CreateUserDTO } from "@medusajs/types"
|
||||
|
||||
// List invites
|
||||
export const GET = async (req: MedusaRequest, res: MedusaResponse) => {
|
||||
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
|
||||
|
||||
const query = remoteQueryObjectFromString({
|
||||
entryPoint: "invite",
|
||||
variables: {
|
||||
filters: req.filterableFields,
|
||||
order: req.listConfig.order,
|
||||
skip: req.listConfig.skip,
|
||||
take: req.listConfig.take,
|
||||
},
|
||||
fields: req.listConfig.select as string[],
|
||||
})
|
||||
|
||||
const { rows: invites, metadata } = await remoteQuery({
|
||||
...query,
|
||||
})
|
||||
|
||||
res.status(200).json({
|
||||
invites,
|
||||
count: metadata.count,
|
||||
offset: metadata.skip,
|
||||
limit: metadata.take,
|
||||
})
|
||||
}
|
||||
|
||||
// Create invite
|
||||
export const POST = async (req: MedusaRequest, res: MedusaResponse) => {
|
||||
const workflow = createInvitesWorkflow(req.scope)
|
||||
|
||||
const input = {
|
||||
input: {
|
||||
invites: [req.validatedBody as CreateInviteDTO],
|
||||
},
|
||||
}
|
||||
|
||||
const { result } = await workflow.run(input)
|
||||
|
||||
const [invite] = result
|
||||
res.status(200).json({ invite })
|
||||
}
|
||||
72
packages/medusa/src/api-v2/admin/invites/validators.ts
Normal file
72
packages/medusa/src/api-v2/admin/invites/validators.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
import { Type } from "class-transformer"
|
||||
import { IsEmail, IsOptional, IsString, ValidateNested } from "class-validator"
|
||||
import {
|
||||
DateComparisonOperator,
|
||||
FindParams,
|
||||
extendedFindParamsMixin,
|
||||
} from "../../../types/common"
|
||||
import { IsType } from "../../../utils"
|
||||
|
||||
export class AdminGetInvitesInviteParams extends FindParams {}
|
||||
|
||||
export class AdminGetInvitesParams extends extendedFindParamsMixin({
|
||||
limit: 50,
|
||||
offset: 0,
|
||||
}) {
|
||||
/**
|
||||
* IDs to filter invites by.
|
||||
*/
|
||||
@IsOptional()
|
||||
@IsType([String, [String]])
|
||||
id?: string | 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 invites' `update_at` date.
|
||||
*/
|
||||
@IsOptional()
|
||||
@ValidateNested()
|
||||
@Type(() => DateComparisonOperator)
|
||||
updated_at?: DateComparisonOperator
|
||||
|
||||
/**
|
||||
* Date filters to apply on the customer invites' `created_at` date.
|
||||
*/
|
||||
@IsOptional()
|
||||
@ValidateNested()
|
||||
@Type(() => DateComparisonOperator)
|
||||
created_at?: DateComparisonOperator
|
||||
|
||||
/**
|
||||
* Date filters to apply on the invites' `deleted_at` date.
|
||||
*/
|
||||
@IsOptional()
|
||||
@ValidateNested()
|
||||
@Type(() => DateComparisonOperator)
|
||||
deleted_at?: DateComparisonOperator
|
||||
|
||||
/**
|
||||
* Filter to apply on the invites' `email` field.
|
||||
*/
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
email?: string
|
||||
|
||||
/**
|
||||
* Comma-separated fields that should be included in the returned invites.
|
||||
*/
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
fields?: string
|
||||
}
|
||||
|
||||
export class AdminCreateInviteRequest {
|
||||
@IsEmail()
|
||||
email: string
|
||||
}
|
||||
@@ -10,6 +10,7 @@ import { storeCartRoutesMiddlewares } from "./store/carts/middlewares"
|
||||
import { storeCustomerRoutesMiddlewares } from "./store/customers/middlewares"
|
||||
import { adminUserRoutesMiddlewares } from "./admin/users/middlewares"
|
||||
import { storeRegionRoutesMiddlewares } from "./store/regions/middlewares"
|
||||
import { adminInviteRoutesMiddlewares } from "./admin/invites/middlewares"
|
||||
|
||||
export const config: MiddlewaresConfig = {
|
||||
routes: [
|
||||
@@ -25,5 +26,6 @@ export const config: MiddlewaresConfig = {
|
||||
...storeRegionRoutesMiddlewares,
|
||||
...adminRegionRoutesMiddlewares,
|
||||
...adminUserRoutesMiddlewares,
|
||||
...adminInviteRoutesMiddlewares,
|
||||
],
|
||||
}
|
||||
|
||||
@@ -12,8 +12,6 @@ export interface UpdateUserDTO extends Partial<Omit<CreateUserDTO, "email">> {
|
||||
export interface CreateInviteDTO {
|
||||
email: string
|
||||
accepted?: boolean
|
||||
token: string
|
||||
expires_at: Date
|
||||
metadata?: Record<string, unknown> | null
|
||||
}
|
||||
|
||||
|
||||
@@ -4,3 +4,4 @@ export * as ProductWorkflow from "./product"
|
||||
export * as InventoryWorkflow from "./inventory"
|
||||
export * as PriceListWorkflow from "./price-list"
|
||||
export * as UserWorkflow from "./user"
|
||||
export * as InviteWorkflow from "./invite"
|
||||
|
||||
5
packages/types/src/workflow/invite/create-invite.ts
Normal file
5
packages/types/src/workflow/invite/create-invite.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import { CreateInviteDTO } from "../../user"
|
||||
|
||||
export interface CreateInvitesWorkflowInputDTO {
|
||||
invites: CreateInviteDTO[]
|
||||
}
|
||||
3
packages/types/src/workflow/invite/delete-invite.ts
Normal file
3
packages/types/src/workflow/invite/delete-invite.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export interface DeleteInvitesWorkflowInput {
|
||||
ids: string[]
|
||||
}
|
||||
3
packages/types/src/workflow/invite/index.ts
Normal file
3
packages/types/src/workflow/invite/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export * from "./create-invite"
|
||||
export * from "./update-invite"
|
||||
export * from "./delete-invite"
|
||||
5
packages/types/src/workflow/invite/update-invite.ts
Normal file
5
packages/types/src/workflow/invite/update-invite.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import { UpdateInviteDTO } from "../../user"
|
||||
|
||||
export interface UpdateInvitesWorkflowInputDTO {
|
||||
updates: UpdateInviteDTO[]
|
||||
}
|
||||
@@ -1,10 +1,11 @@
|
||||
import { User } from "@models"
|
||||
import { Invite, User } from "@models"
|
||||
import { MapToConfig } from "@medusajs/utils"
|
||||
import { ModuleJoinerConfig } from "@medusajs/types"
|
||||
import { Modules } from "@medusajs/modules-sdk"
|
||||
|
||||
export const LinkableKeys = {
|
||||
user_id: User.name,
|
||||
invite_id: Invite.name,
|
||||
}
|
||||
|
||||
const entityLinkableKeysMap: MapToConfig = {}
|
||||
@@ -22,10 +23,19 @@ export const joinerConfig: ModuleJoinerConfig = {
|
||||
serviceName: Modules.USER,
|
||||
primaryKeys: ["id"],
|
||||
linkableKeys: LinkableKeys,
|
||||
alias: {
|
||||
name: ["user", "users"],
|
||||
args: {
|
||||
entity: User.name,
|
||||
alias: [
|
||||
{
|
||||
name: ["user", "users"],
|
||||
args: {
|
||||
entity: User.name,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: ["invite", "invites"],
|
||||
args: {
|
||||
entity: Invite.name,
|
||||
methodSuffix: "Invites",
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
@@ -130,7 +130,7 @@ export default class UserModuleService<
|
||||
): Promise<UserTypes.InviteDTO | UserTypes.InviteDTO[]> {
|
||||
const input = Array.isArray(data) ? data : [data]
|
||||
|
||||
const invites = await this.inviteService_.create(input, sharedContext)
|
||||
const invites = await this.createInvites_(input, sharedContext)
|
||||
|
||||
const serializedInvites = await this.baseRepository_.serialize<
|
||||
UserTypes.InviteDTO[] | UserTypes.InviteDTO
|
||||
@@ -141,6 +141,25 @@ export default class UserModuleService<
|
||||
return Array.isArray(data) ? serializedInvites : serializedInvites[0]
|
||||
}
|
||||
|
||||
@InjectTransactionManager("baseRepository_")
|
||||
private async createInvites_(
|
||||
data: UserTypes.CreateInviteDTO[],
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
): Promise<TInvite[]> {
|
||||
// expiration date in 10 days
|
||||
const expirationDate = new Date().setDate(new Date().getDate() + 10)
|
||||
|
||||
const toCreate = data.map((invite) => {
|
||||
return {
|
||||
...invite,
|
||||
expires_at: new Date(expirationDate),
|
||||
token: "placeholder", // TODO: generate token
|
||||
}
|
||||
})
|
||||
|
||||
return await this.inviteService_.create(toCreate)
|
||||
}
|
||||
|
||||
updateInvites(
|
||||
data: UserTypes.UpdateInviteDTO[],
|
||||
sharedContext?: Context
|
||||
|
||||
Reference in New Issue
Block a user