chore: Move most of the remaining endpoints to zod (#7096)
This commit is contained in:
@@ -1,25 +1,22 @@
|
||||
import { revokeApiKeysWorkflow } from "@medusajs/core-flows"
|
||||
import { RevokeApiKeyDTO } from "@medusajs/types"
|
||||
import {
|
||||
ContainerRegistrationKeys,
|
||||
remoteQueryObjectFromString,
|
||||
} from "@medusajs/utils"
|
||||
import {
|
||||
AuthenticatedMedusaRequest,
|
||||
MedusaResponse,
|
||||
} from "../../../../../types/routing"
|
||||
import { AdminRevokeApiKeyType } from "../../validators"
|
||||
import { refetchApiKey } from "../../helpers"
|
||||
|
||||
export const POST = async (
|
||||
req: AuthenticatedMedusaRequest,
|
||||
req: AuthenticatedMedusaRequest<AdminRevokeApiKeyType>,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
const { errors } = await revokeApiKeysWorkflow(req.scope).run({
|
||||
input: {
|
||||
selector: { id: req.params.id },
|
||||
revoke: {
|
||||
...(req.validatedBody as Omit<RevokeApiKeyDTO, "revoked_by">),
|
||||
...req.validatedBody,
|
||||
revoked_by: req.auth.actor_id,
|
||||
} as RevokeApiKeyDTO,
|
||||
},
|
||||
},
|
||||
throwOnError: false,
|
||||
})
|
||||
@@ -28,17 +25,11 @@ export const POST = async (
|
||||
throw errors[0].error
|
||||
}
|
||||
|
||||
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
|
||||
|
||||
const queryObject = remoteQueryObjectFromString({
|
||||
entryPoint: "api_key",
|
||||
variables: {
|
||||
id: req.params.id,
|
||||
},
|
||||
fields: req.remoteQueryConfig.fields,
|
||||
})
|
||||
|
||||
const [apiKey] = await remoteQuery(queryObject)
|
||||
const apiKey = await refetchApiKey(
|
||||
req.params.id,
|
||||
req.scope,
|
||||
req.remoteQueryConfig.fields
|
||||
)
|
||||
|
||||
res.status(200).json({ api_key: apiKey })
|
||||
}
|
||||
|
||||
@@ -7,40 +7,34 @@ import {
|
||||
MedusaResponse,
|
||||
} from "../../../../types/routing"
|
||||
|
||||
import { UpdateApiKeyDTO } from "@medusajs/types"
|
||||
import {
|
||||
ContainerRegistrationKeys,
|
||||
remoteQueryObjectFromString,
|
||||
} from "@medusajs/utils"
|
||||
import { defaultAdminApiKeyFields } from "../query-config"
|
||||
import { refetchApiKey } from "../helpers"
|
||||
import { AdminUpdateApiKeyType } from "../validators"
|
||||
|
||||
export const GET = async (
|
||||
req: AuthenticatedMedusaRequest,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
|
||||
|
||||
const variables = { id: req.params.id }
|
||||
|
||||
const queryObject = remoteQueryObjectFromString({
|
||||
entryPoint: "api_key",
|
||||
variables,
|
||||
fields: defaultAdminApiKeyFields,
|
||||
})
|
||||
|
||||
const [apiKey] = await remoteQuery(queryObject)
|
||||
const apiKey = await refetchApiKey(
|
||||
req.params.id,
|
||||
req.scope,
|
||||
req.remoteQueryConfig.fields
|
||||
)
|
||||
|
||||
res.status(200).json({ api_key: apiKey })
|
||||
}
|
||||
|
||||
export const POST = async (
|
||||
req: AuthenticatedMedusaRequest<Omit<UpdateApiKeyDTO, "id">>,
|
||||
req: AuthenticatedMedusaRequest<AdminUpdateApiKeyType>,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
const { result, errors } = await updateApiKeysWorkflow(req.scope).run({
|
||||
input: {
|
||||
selector: { id: req.params.id },
|
||||
update: req.validatedBody as UpdateApiKeyDTO,
|
||||
update: req.validatedBody,
|
||||
},
|
||||
throwOnError: false,
|
||||
})
|
||||
@@ -49,17 +43,11 @@ export const POST = async (
|
||||
throw errors[0].error
|
||||
}
|
||||
|
||||
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
|
||||
|
||||
const queryObject = remoteQueryObjectFromString({
|
||||
entryPoint: "api_key",
|
||||
variables: {
|
||||
id: req.params.id,
|
||||
},
|
||||
fields: req.remoteQueryConfig.fields,
|
||||
})
|
||||
|
||||
const [apiKey] = await remoteQuery(queryObject)
|
||||
const apiKey = await refetchApiKey(
|
||||
req.params.id,
|
||||
req.scope,
|
||||
req.remoteQueryConfig.fields
|
||||
)
|
||||
|
||||
res.status(200).json({ api_key: apiKey })
|
||||
}
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
import { addSalesChannelsToApiKeyWorkflow } from "@medusajs/core-flows"
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import {
|
||||
ContainerRegistrationKeys,
|
||||
MedusaError,
|
||||
remoteQueryObjectFromString,
|
||||
} from "@medusajs/utils"
|
||||
import {
|
||||
AuthenticatedMedusaRequest,
|
||||
MedusaResponse,
|
||||
} from "../../../../../../../types/routing"
|
||||
import { AdminPostApiKeysApiKeySalesChannelsBatchAddReq } from "../../../../validators"
|
||||
|
||||
export const POST = async (
|
||||
req: AuthenticatedMedusaRequest,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
const body =
|
||||
req.validatedBody as AdminPostApiKeysApiKeySalesChannelsBatchAddReq
|
||||
|
||||
const apiKeyModule = req.scope.resolve(ModuleRegistrationName.API_KEY)
|
||||
|
||||
const apiKey = await apiKeyModule.retrieve(req.params.id)
|
||||
|
||||
if (apiKey.type !== "publishable") {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
"Sales channels can only be associated with publishable API keys"
|
||||
)
|
||||
}
|
||||
|
||||
const workflowInput = {
|
||||
data: [
|
||||
{
|
||||
api_key_id: req.params.id,
|
||||
sales_channel_ids: body.sales_channel_ids,
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
const { errors } = await addSalesChannelsToApiKeyWorkflow(req.scope).run({
|
||||
input: workflowInput,
|
||||
throwOnError: false,
|
||||
})
|
||||
|
||||
if (Array.isArray(errors) && errors[0]) {
|
||||
throw errors[0].error
|
||||
}
|
||||
|
||||
const query = remoteQueryObjectFromString({
|
||||
entryPoint: "api_key",
|
||||
fields: req.remoteQueryConfig.fields,
|
||||
variables: {
|
||||
id: req.params.id,
|
||||
},
|
||||
})
|
||||
|
||||
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
|
||||
|
||||
const [result] = await remoteQuery(query)
|
||||
|
||||
res.status(200).json({ api_key: result })
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
import { removeSalesChannelsFromApiKeyWorkflow } from "@medusajs/core-flows"
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import {
|
||||
ContainerRegistrationKeys,
|
||||
MedusaError,
|
||||
remoteQueryObjectFromString,
|
||||
} from "@medusajs/utils"
|
||||
import {
|
||||
AuthenticatedMedusaRequest,
|
||||
MedusaResponse,
|
||||
} from "../../../../../../../types/routing"
|
||||
import { AdminPostApiKeysApiKeySalesChannelsBatchRemoveReq } from "../../../../validators"
|
||||
|
||||
export const POST = async (
|
||||
req: AuthenticatedMedusaRequest,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
const body =
|
||||
req.validatedBody as AdminPostApiKeysApiKeySalesChannelsBatchRemoveReq
|
||||
|
||||
const apiKeyModule = req.scope.resolve(ModuleRegistrationName.API_KEY)
|
||||
|
||||
const apiKey = await apiKeyModule.retrieve(req.params.id)
|
||||
|
||||
if (apiKey.type !== "publishable") {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
"Sales channels can only be associated with publishable API keys"
|
||||
)
|
||||
}
|
||||
|
||||
const workflowInput = {
|
||||
data: [
|
||||
{
|
||||
api_key_id: req.params.id,
|
||||
sales_channel_ids: body.sales_channel_ids,
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
const { errors } = await removeSalesChannelsFromApiKeyWorkflow(req.scope).run(
|
||||
{
|
||||
input: workflowInput,
|
||||
throwOnError: false,
|
||||
}
|
||||
)
|
||||
|
||||
if (Array.isArray(errors) && errors[0]) {
|
||||
throw errors[0].error
|
||||
}
|
||||
|
||||
const query = remoteQueryObjectFromString({
|
||||
entryPoint: "api_key",
|
||||
fields: req.remoteQueryConfig.fields,
|
||||
variables: {
|
||||
id: req.params.id,
|
||||
},
|
||||
})
|
||||
|
||||
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
|
||||
|
||||
const [result] = await remoteQuery(query)
|
||||
|
||||
res.status(200).json({ api_key: result })
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
import {
|
||||
addSalesChannelsToApiKeyWorkflow,
|
||||
removeSalesChannelsFromApiKeyWorkflow,
|
||||
} from "@medusajs/core-flows"
|
||||
import { ApiKeyType, MedusaError } from "@medusajs/utils"
|
||||
import {
|
||||
AuthenticatedMedusaRequest,
|
||||
MedusaResponse,
|
||||
} from "../../../../../../types/routing"
|
||||
import { AdminApiKeySalesChannelType } from "../../../validators"
|
||||
import { BatchMethodRequest } from "@medusajs/types"
|
||||
import { refetchApiKey } from "../../../helpers"
|
||||
|
||||
export const POST = async (
|
||||
req: AuthenticatedMedusaRequest<
|
||||
BatchMethodRequest<AdminApiKeySalesChannelType, AdminApiKeySalesChannelType>
|
||||
>,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
const { create, delete: toDelete } = req.validatedBody
|
||||
const apiKey = await refetchApiKey(
|
||||
req.params.id,
|
||||
req.scope,
|
||||
req.remoteQueryConfig.fields
|
||||
)
|
||||
|
||||
if (apiKey.type !== ApiKeyType.PUBLISHABLE) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
"Sales channels can only be associated with publishable API keys"
|
||||
)
|
||||
}
|
||||
|
||||
if (create && create.length) {
|
||||
const workflowInput = {
|
||||
data: [
|
||||
{
|
||||
api_key_id: req.params.id,
|
||||
sales_channel_ids: create ?? [],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
const { errors } = await addSalesChannelsToApiKeyWorkflow(req.scope).run({
|
||||
input: workflowInput,
|
||||
throwOnError: false,
|
||||
})
|
||||
|
||||
if (Array.isArray(errors) && errors[0]) {
|
||||
throw errors[0].error
|
||||
}
|
||||
}
|
||||
|
||||
if (toDelete && toDelete.length) {
|
||||
const workflowInput = {
|
||||
data: [
|
||||
{
|
||||
api_key_id: req.params.id,
|
||||
sales_channel_ids: toDelete,
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
const { errors } = await removeSalesChannelsFromApiKeyWorkflow(
|
||||
req.scope
|
||||
).run({
|
||||
input: workflowInput,
|
||||
throwOnError: false,
|
||||
})
|
||||
|
||||
if (Array.isArray(errors) && errors[0]) {
|
||||
throw errors[0].error
|
||||
}
|
||||
}
|
||||
|
||||
const newApiKey = await refetchApiKey(
|
||||
req.params.id,
|
||||
req.scope,
|
||||
req.remoteQueryConfig.fields
|
||||
)
|
||||
|
||||
res.status(200).json({ api_key: newApiKey })
|
||||
}
|
||||
23
packages/medusa/src/api-v2/admin/api-keys/helpers.ts
Normal file
23
packages/medusa/src/api-v2/admin/api-keys/helpers.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { MedusaContainer } from "@medusajs/types"
|
||||
import {
|
||||
ContainerRegistrationKeys,
|
||||
remoteQueryObjectFromString,
|
||||
} from "@medusajs/utils"
|
||||
|
||||
export const refetchApiKey = async (
|
||||
apiKeyId: string,
|
||||
scope: MedusaContainer,
|
||||
fields: string[]
|
||||
) => {
|
||||
const remoteQuery = scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
|
||||
const queryObject = remoteQueryObjectFromString({
|
||||
entryPoint: "api_key",
|
||||
variables: {
|
||||
filters: { id: apiKeyId },
|
||||
},
|
||||
fields: fields,
|
||||
})
|
||||
|
||||
const apiKeys = await remoteQuery(queryObject)
|
||||
return apiKeys[0]
|
||||
}
|
||||
@@ -1,18 +1,18 @@
|
||||
import * as QueryConfig from "./query-config"
|
||||
|
||||
import { transformBody, transformQuery } from "../../../api/middlewares"
|
||||
import {
|
||||
AdminGetApiKeysApiKeyParams,
|
||||
AdminGetApiKeysParams,
|
||||
AdminPostApiKeysApiKeyReq,
|
||||
AdminPostApiKeysApiKeySalesChannelsBatchAddReq,
|
||||
AdminPostApiKeysApiKeySalesChannelsBatchRemoveReq,
|
||||
AdminPostApiKeysReq,
|
||||
AdminRevokeApiKeysApiKeyReq,
|
||||
} from "./validators"
|
||||
|
||||
import { MiddlewareRoute } from "../../../loaders/helpers/routing/types"
|
||||
import { authenticate } from "../../../utils/authenticate-middleware"
|
||||
import { validateAndTransformQuery } from "../../utils/validate-query"
|
||||
import {
|
||||
AdminApiKeySalesChannel,
|
||||
AdminCreateApiKey,
|
||||
AdminGetApiKeyParams,
|
||||
AdminGetApiKeysParams,
|
||||
AdminRevokeApiKey,
|
||||
AdminUpdateApiKey,
|
||||
} from "./validators"
|
||||
import { validateAndTransformBody } from "../../utils/validate-body"
|
||||
import { createBatchBody } from "../../utils/validators"
|
||||
|
||||
export const adminApiKeyRoutesMiddlewares: MiddlewareRoute[] = [
|
||||
{
|
||||
@@ -23,7 +23,7 @@ export const adminApiKeyRoutesMiddlewares: MiddlewareRoute[] = [
|
||||
method: ["GET"],
|
||||
matcher: "/admin/api-keys",
|
||||
middlewares: [
|
||||
transformQuery(
|
||||
validateAndTransformQuery(
|
||||
AdminGetApiKeysParams,
|
||||
QueryConfig.listTransformQueryConfig
|
||||
),
|
||||
@@ -33,8 +33,8 @@ export const adminApiKeyRoutesMiddlewares: MiddlewareRoute[] = [
|
||||
method: ["GET"],
|
||||
matcher: "/admin/api-keys/:id",
|
||||
middlewares: [
|
||||
transformQuery(
|
||||
AdminGetApiKeysApiKeyParams,
|
||||
validateAndTransformQuery(
|
||||
AdminGetApiKeyParams,
|
||||
QueryConfig.retrieveTransformQueryConfig
|
||||
),
|
||||
],
|
||||
@@ -43,22 +43,22 @@ export const adminApiKeyRoutesMiddlewares: MiddlewareRoute[] = [
|
||||
method: ["POST"],
|
||||
matcher: "/admin/api-keys",
|
||||
middlewares: [
|
||||
transformQuery(
|
||||
AdminGetApiKeysApiKeyParams,
|
||||
validateAndTransformBody(AdminCreateApiKey),
|
||||
validateAndTransformQuery(
|
||||
AdminGetApiKeyParams,
|
||||
QueryConfig.retrieveTransformQueryConfig
|
||||
),
|
||||
transformBody(AdminPostApiKeysReq),
|
||||
],
|
||||
},
|
||||
{
|
||||
method: ["POST"],
|
||||
matcher: "/admin/api-keys/:id",
|
||||
middlewares: [
|
||||
transformQuery(
|
||||
AdminGetApiKeysApiKeyParams,
|
||||
validateAndTransformBody(AdminUpdateApiKey),
|
||||
validateAndTransformQuery(
|
||||
AdminGetApiKeyParams,
|
||||
QueryConfig.retrieveTransformQueryConfig
|
||||
),
|
||||
transformBody(AdminPostApiKeysApiKeyReq),
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -70,33 +70,24 @@ export const adminApiKeyRoutesMiddlewares: MiddlewareRoute[] = [
|
||||
method: ["POST"],
|
||||
matcher: "/admin/api-keys/:id/revoke",
|
||||
middlewares: [
|
||||
transformQuery(
|
||||
AdminGetApiKeysApiKeyParams,
|
||||
validateAndTransformBody(AdminRevokeApiKey),
|
||||
validateAndTransformQuery(
|
||||
AdminGetApiKeyParams,
|
||||
QueryConfig.retrieveTransformQueryConfig
|
||||
),
|
||||
transformBody(AdminRevokeApiKeysApiKeyReq),
|
||||
],
|
||||
},
|
||||
{
|
||||
method: ["POST"],
|
||||
matcher: "/admin/api-keys/:id/sales-channels/batch/add",
|
||||
matcher: "/admin/api-keys/:id/sales-channels/batch",
|
||||
middlewares: [
|
||||
transformQuery(
|
||||
AdminGetApiKeysApiKeyParams,
|
||||
validateAndTransformBody(
|
||||
createBatchBody(AdminApiKeySalesChannel, AdminApiKeySalesChannel)
|
||||
),
|
||||
validateAndTransformQuery(
|
||||
AdminGetApiKeyParams,
|
||||
QueryConfig.retrieveTransformQueryConfig
|
||||
),
|
||||
transformBody(AdminPostApiKeysApiKeySalesChannelsBatchAddReq),
|
||||
],
|
||||
},
|
||||
{
|
||||
method: ["POST"],
|
||||
matcher: "/admin/api-keys/:id/sales-channels/batch/remove",
|
||||
middlewares: [
|
||||
transformQuery(
|
||||
AdminGetApiKeysApiKeyParams,
|
||||
QueryConfig.retrieveTransformQueryConfig
|
||||
),
|
||||
transformBody(AdminPostApiKeysApiKeySalesChannelsBatchRemoveReq),
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { createApiKeysWorkflow } from "@medusajs/core-flows"
|
||||
import { CreateApiKeyDTO } from "@medusajs/types"
|
||||
import {
|
||||
ContainerRegistrationKeys,
|
||||
remoteQueryObjectFromString,
|
||||
@@ -8,6 +7,7 @@ import {
|
||||
AuthenticatedMedusaRequest,
|
||||
MedusaResponse,
|
||||
} from "../../../types/routing"
|
||||
import { AdminCreateApiKeyType } from "./validators"
|
||||
|
||||
export const GET = async (
|
||||
req: AuthenticatedMedusaRequest,
|
||||
@@ -35,14 +35,14 @@ export const GET = async (
|
||||
}
|
||||
|
||||
export const POST = async (
|
||||
req: AuthenticatedMedusaRequest<Omit<CreateApiKeyDTO, "created_by">>,
|
||||
req: AuthenticatedMedusaRequest<AdminCreateApiKeyType>,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
const input = [
|
||||
{
|
||||
...req.validatedBody,
|
||||
created_by: req.auth.actor_id,
|
||||
} as CreateApiKeyDTO,
|
||||
},
|
||||
]
|
||||
|
||||
const { result, errors } = await createApiKeysWorkflow(req.scope).run({
|
||||
@@ -54,7 +54,7 @@ export const POST = async (
|
||||
throw errors[0].error
|
||||
}
|
||||
|
||||
// We cannot use remoteQuery here, as we need to show the secret key in the response (and never again)
|
||||
// We should not refetch the api key here, as we need to show the secret key in the response (and never again)
|
||||
// And the only time we get to see the secret, is when we create it
|
||||
res.status(200).json({ api_key: result[0] })
|
||||
}
|
||||
|
||||
@@ -1,88 +1,48 @@
|
||||
import { ApiKeyType } from "@medusajs/utils"
|
||||
import { Type } from "class-transformer"
|
||||
import { z } from "zod"
|
||||
import {
|
||||
IsArray,
|
||||
IsEnum,
|
||||
IsNumber,
|
||||
IsOptional,
|
||||
IsString,
|
||||
ValidateNested,
|
||||
} from "class-validator"
|
||||
import { FindParams, extendedFindParamsMixin } from "../../../types/common"
|
||||
createFindParams,
|
||||
createOperatorMap,
|
||||
createSelectParams,
|
||||
} from "../../utils/validators"
|
||||
import { ApiKeyType } from "@medusajs/utils"
|
||||
|
||||
export class AdminGetApiKeysApiKeyParams extends FindParams {}
|
||||
/**
|
||||
* Parameters used to filter and configure the pagination of the retrieved api keys.
|
||||
*/
|
||||
export class AdminGetApiKeysParams extends extendedFindParamsMixin({
|
||||
limit: 50,
|
||||
export const AdminGetApiKeyParams = createSelectParams()
|
||||
|
||||
export type AdminGetApiKeysParamsType = z.infer<typeof AdminGetApiKeysParams>
|
||||
export const AdminGetApiKeysParams = createFindParams({
|
||||
offset: 0,
|
||||
}) {
|
||||
/**
|
||||
* Search parameter for api keys.
|
||||
*/
|
||||
@IsString({ each: true })
|
||||
@IsOptional()
|
||||
id?: string | string[]
|
||||
limit: 50,
|
||||
}).merge(
|
||||
z.object({
|
||||
id: z.union([z.string(), z.array(z.string())]).optional(),
|
||||
title: z.union([z.string(), z.array(z.string())]).optional(),
|
||||
token: z.union([z.string(), z.array(z.string())]).optional(),
|
||||
type: z.nativeEnum(ApiKeyType).optional(),
|
||||
created_at: createOperatorMap().optional(),
|
||||
updated_at: createOperatorMap().optional(),
|
||||
deleted_at: createOperatorMap().optional(),
|
||||
$and: z.lazy(() => AdminGetApiKeysParams.array()).optional(),
|
||||
$or: z.lazy(() => AdminGetApiKeysParams.array()).optional(),
|
||||
})
|
||||
)
|
||||
|
||||
/**
|
||||
* Filter by title
|
||||
*/
|
||||
@IsString({ each: true })
|
||||
@IsOptional()
|
||||
title?: string | string[]
|
||||
export type AdminCreateApiKeyType = z.infer<typeof AdminCreateApiKey>
|
||||
export const AdminCreateApiKey = z.object({
|
||||
title: z.string(),
|
||||
type: z.nativeEnum(ApiKeyType),
|
||||
})
|
||||
|
||||
/**
|
||||
* Filter by token
|
||||
*/
|
||||
@IsString({ each: true })
|
||||
@IsOptional()
|
||||
token?: string | string[]
|
||||
export type AdminUpdateApiKeyType = z.infer<typeof AdminUpdateApiKey>
|
||||
export const AdminUpdateApiKey = z.object({
|
||||
title: z.string(),
|
||||
})
|
||||
|
||||
/**
|
||||
* Filter by type
|
||||
*/
|
||||
@IsEnum(ApiKeyType, { each: true })
|
||||
@IsOptional()
|
||||
type?: ApiKeyType
|
||||
export type AdminRevokeApiKeyType = z.infer<typeof AdminRevokeApiKey>
|
||||
export const AdminRevokeApiKey = z.object({
|
||||
revoke_in: z.number().optional(),
|
||||
})
|
||||
|
||||
// Additional filters from BaseFilterable
|
||||
@IsOptional()
|
||||
@ValidateNested({ each: true })
|
||||
@Type(() => AdminGetApiKeysParams)
|
||||
$and?: AdminGetApiKeysParams[]
|
||||
|
||||
@IsOptional()
|
||||
@ValidateNested({ each: true })
|
||||
@Type(() => AdminGetApiKeysParams)
|
||||
$or?: AdminGetApiKeysParams[]
|
||||
}
|
||||
|
||||
export class AdminPostApiKeysReq {
|
||||
@IsString()
|
||||
title: string
|
||||
|
||||
@IsEnum(ApiKeyType, {})
|
||||
type: ApiKeyType
|
||||
}
|
||||
|
||||
export class AdminPostApiKeysApiKeyReq {
|
||||
@IsString()
|
||||
title: string
|
||||
}
|
||||
|
||||
export class AdminRevokeApiKeysApiKeyReq {
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
revoke_in?: number
|
||||
}
|
||||
|
||||
export class AdminDeleteApiKeysApiKeyReq {}
|
||||
|
||||
export class AdminPostApiKeysApiKeySalesChannelsBatchAddReq {
|
||||
@IsArray()
|
||||
sales_channel_ids: string[]
|
||||
}
|
||||
|
||||
// eslint-disable-next-line max-len
|
||||
export class AdminPostApiKeysApiKeySalesChannelsBatchRemoveReq extends AdminPostApiKeysApiKeySalesChannelsBatchAddReq {}
|
||||
export type AdminApiKeySalesChannelType = z.infer<
|
||||
typeof AdminApiKeySalesChannel
|
||||
>
|
||||
export const AdminApiKeySalesChannel = z.string()
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import {
|
||||
AuthenticatedMedusaRequest,
|
||||
MedusaRequest,
|
||||
MedusaResponse,
|
||||
} from "../../../../types/routing"
|
||||
import {
|
||||
@@ -8,32 +7,32 @@ import {
|
||||
updateCampaignsWorkflow,
|
||||
} from "@medusajs/core-flows"
|
||||
|
||||
import { AdminPostCampaignsReq } from "../validators"
|
||||
import { IPromotionModuleService } from "@medusajs/types"
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import { UpdateCampaignDTO } from "@medusajs/types"
|
||||
import { refetchCampaign } from "../helpers"
|
||||
import { AdminUpdateCampaignType } from "../validators"
|
||||
import { MedusaError } from "@medusajs/utils"
|
||||
|
||||
export const GET = async (
|
||||
req: AuthenticatedMedusaRequest,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
const promotionModuleService: IPromotionModuleService = req.scope.resolve(
|
||||
ModuleRegistrationName.PROMOTION
|
||||
const campaign = await refetchCampaign(
|
||||
req.params.id,
|
||||
req.scope,
|
||||
req.remoteQueryConfig.fields
|
||||
)
|
||||
|
||||
const campaign = await promotionModuleService.retrieveCampaign(
|
||||
req.params.id,
|
||||
{
|
||||
select: req.retrieveConfig.select,
|
||||
relations: req.retrieveConfig.relations,
|
||||
}
|
||||
)
|
||||
if (!campaign) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_FOUND,
|
||||
`Campaign with id: ${req.params.id} was not found`
|
||||
)
|
||||
}
|
||||
|
||||
res.status(200).json({ campaign })
|
||||
}
|
||||
|
||||
export const POST = async (
|
||||
req: AuthenticatedMedusaRequest<AdminPostCampaignsReq>,
|
||||
req: AuthenticatedMedusaRequest<AdminUpdateCampaignType>,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
const updateCampaigns = updateCampaignsWorkflow(req.scope)
|
||||
@@ -42,7 +41,7 @@ export const POST = async (
|
||||
id: req.params.id,
|
||||
...req.validatedBody,
|
||||
},
|
||||
] as UpdateCampaignDTO[]
|
||||
]
|
||||
|
||||
const { result, errors } = await updateCampaigns.run({
|
||||
input: { campaignsData },
|
||||
@@ -53,7 +52,12 @@ export const POST = async (
|
||||
throw errors[0].error
|
||||
}
|
||||
|
||||
res.status(200).json({ campaign: result[0] })
|
||||
const campaign = await refetchCampaign(
|
||||
req.params.id,
|
||||
req.scope,
|
||||
req.remoteQueryConfig.fields
|
||||
)
|
||||
res.status(200).json({ campaign })
|
||||
}
|
||||
|
||||
export const DELETE = async (
|
||||
|
||||
23
packages/medusa/src/api-v2/admin/campaigns/helpers.ts
Normal file
23
packages/medusa/src/api-v2/admin/campaigns/helpers.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { MedusaContainer } from "@medusajs/types"
|
||||
import {
|
||||
ContainerRegistrationKeys,
|
||||
remoteQueryObjectFromString,
|
||||
} from "@medusajs/utils"
|
||||
|
||||
export const refetchCampaign = async (
|
||||
campaignId: string,
|
||||
scope: MedusaContainer,
|
||||
fields: string[]
|
||||
) => {
|
||||
const remoteQuery = scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
|
||||
const queryObject = remoteQueryObjectFromString({
|
||||
entryPoint: "campaign",
|
||||
variables: {
|
||||
filters: { id: campaignId },
|
||||
},
|
||||
fields: fields,
|
||||
})
|
||||
|
||||
const campaigns = await remoteQuery(queryObject)
|
||||
return campaigns[0]
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
export * from "./types"
|
||||
export * from "./validators"
|
||||
@@ -1,31 +1,25 @@
|
||||
import * as QueryConfig from "./query-config"
|
||||
|
||||
import {
|
||||
AdminGetCampaignsCampaignParams,
|
||||
AdminGetCampaignsParams,
|
||||
AdminPostCampaignsCampaignReq,
|
||||
AdminPostCampaignsReq,
|
||||
} from "./validators"
|
||||
import {
|
||||
isFeatureFlagEnabled,
|
||||
transformBody,
|
||||
transformQuery,
|
||||
} from "../../../api/middlewares"
|
||||
|
||||
import { MedusaV2Flag } from "@medusajs/utils"
|
||||
import { MiddlewareRoute } from "../../../loaders/helpers/routing/types"
|
||||
import { authenticate } from "../../../utils/authenticate-middleware"
|
||||
import { validateAndTransformQuery } from "../../utils/validate-query"
|
||||
import {
|
||||
AdminCreateCampaign,
|
||||
AdminGetCampaignParams,
|
||||
AdminGetCampaignsParams,
|
||||
AdminUpdateCampaign,
|
||||
} from "./validators"
|
||||
import { validateAndTransformBody } from "../../utils/validate-body"
|
||||
|
||||
export const adminCampaignRoutesMiddlewares: MiddlewareRoute[] = [
|
||||
{
|
||||
matcher: "/admin/campaigns*",
|
||||
middlewares: [authenticate("admin", ["bearer", "session"])],
|
||||
middlewares: [authenticate("admin", ["bearer", "session", "api-key"])],
|
||||
},
|
||||
{
|
||||
method: ["GET"],
|
||||
matcher: "/admin/campaigns",
|
||||
middlewares: [
|
||||
transformQuery(
|
||||
validateAndTransformQuery(
|
||||
AdminGetCampaignsParams,
|
||||
QueryConfig.listTransformQueryConfig
|
||||
),
|
||||
@@ -34,14 +28,20 @@ export const adminCampaignRoutesMiddlewares: MiddlewareRoute[] = [
|
||||
{
|
||||
method: ["POST"],
|
||||
matcher: "/admin/campaigns",
|
||||
middlewares: [transformBody(AdminPostCampaignsReq)],
|
||||
middlewares: [
|
||||
validateAndTransformBody(AdminCreateCampaign),
|
||||
validateAndTransformQuery(
|
||||
AdminGetCampaignParams,
|
||||
QueryConfig.retrieveTransformQueryConfig
|
||||
),
|
||||
],
|
||||
},
|
||||
{
|
||||
method: ["GET"],
|
||||
matcher: "/admin/campaigns/:id",
|
||||
middlewares: [
|
||||
transformQuery(
|
||||
AdminGetCampaignsCampaignParams,
|
||||
validateAndTransformQuery(
|
||||
AdminGetCampaignParams,
|
||||
QueryConfig.retrieveTransformQueryConfig
|
||||
),
|
||||
],
|
||||
@@ -49,6 +49,12 @@ export const adminCampaignRoutesMiddlewares: MiddlewareRoute[] = [
|
||||
{
|
||||
method: ["POST"],
|
||||
matcher: "/admin/campaigns/:id",
|
||||
middlewares: [transformBody(AdminPostCampaignsCampaignReq)],
|
||||
middlewares: [
|
||||
validateAndTransformBody(AdminUpdateCampaign),
|
||||
validateAndTransformQuery(
|
||||
AdminGetCampaignParams,
|
||||
QueryConfig.retrieveTransformQueryConfig
|
||||
),
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
export const defaultAdminCampaignRelations = ["budget"]
|
||||
export const allowedAdminCampaignRelations = [
|
||||
...defaultAdminCampaignRelations,
|
||||
"promotions",
|
||||
]
|
||||
export const defaultAdminCampaignFields = [
|
||||
"id",
|
||||
"name",
|
||||
"description",
|
||||
"currency",
|
||||
"campaign_identifier",
|
||||
"*budget",
|
||||
"starts_at",
|
||||
"ends_at",
|
||||
"created_at",
|
||||
@@ -16,9 +13,7 @@ export const defaultAdminCampaignFields = [
|
||||
]
|
||||
|
||||
export const retrieveTransformQueryConfig = {
|
||||
defaultFields: defaultAdminCampaignFields,
|
||||
defaultRelations: defaultAdminCampaignRelations,
|
||||
allowedRelations: allowedAdminCampaignRelations,
|
||||
defaults: defaultAdminCampaignFields,
|
||||
isList: false,
|
||||
}
|
||||
|
||||
|
||||
@@ -1,38 +1,42 @@
|
||||
import {
|
||||
AuthenticatedMedusaRequest,
|
||||
MedusaRequest,
|
||||
MedusaResponse,
|
||||
} from "../../../types/routing"
|
||||
import { CreateCampaignDTO, IPromotionModuleService } from "@medusajs/types"
|
||||
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import { createCampaignsWorkflow } from "@medusajs/core-flows"
|
||||
import {
|
||||
ContainerRegistrationKeys,
|
||||
remoteQueryObjectFromString,
|
||||
} from "@medusajs/utils"
|
||||
import { AdminCreateCampaignType } from "./validators"
|
||||
import { refetchCampaign } from "./helpers"
|
||||
|
||||
export const GET = async (
|
||||
req: AuthenticatedMedusaRequest,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
const promotionModuleService: IPromotionModuleService = req.scope.resolve(
|
||||
ModuleRegistrationName.PROMOTION
|
||||
)
|
||||
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
|
||||
|
||||
const [campaigns, count] = await promotionModuleService.listAndCountCampaigns(
|
||||
req.filterableFields,
|
||||
req.listConfig
|
||||
)
|
||||
const query = remoteQueryObjectFromString({
|
||||
entryPoint: "campaign",
|
||||
variables: {
|
||||
filters: req.filterableFields,
|
||||
...req.remoteQueryConfig.pagination,
|
||||
},
|
||||
fields: req.remoteQueryConfig.fields,
|
||||
})
|
||||
|
||||
const { limit, offset } = req.validatedQuery
|
||||
const { rows: campaigns, metadata } = await remoteQuery(query)
|
||||
|
||||
res.json({
|
||||
count,
|
||||
campaigns,
|
||||
offset,
|
||||
limit,
|
||||
count: metadata.count,
|
||||
offset: metadata.skip,
|
||||
limit: metadata.take,
|
||||
})
|
||||
}
|
||||
|
||||
export const POST = async (
|
||||
req: AuthenticatedMedusaRequest<CreateCampaignDTO>,
|
||||
req: AuthenticatedMedusaRequest<AdminCreateCampaignType>,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
const createCampaigns = createCampaignsWorkflow(req.scope)
|
||||
@@ -50,5 +54,11 @@ export const POST = async (
|
||||
throw errors[0].error
|
||||
}
|
||||
|
||||
res.status(200).json({ campaign: result[0] })
|
||||
const campaign = await refetchCampaign(
|
||||
result[0].id,
|
||||
req.scope,
|
||||
req.remoteQueryConfig.fields
|
||||
)
|
||||
|
||||
res.status(200).json({ campaign })
|
||||
}
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
import { CampaignDTO, PaginatedResponse } from "@medusajs/types"
|
||||
|
||||
export type AdminCampaignsListRes = PaginatedResponse<{
|
||||
campaigns: CampaignDTO[]
|
||||
}>
|
||||
|
||||
export type AdminCampaignRes = {
|
||||
campaign: CampaignDTO
|
||||
}
|
||||
@@ -1,119 +1,54 @@
|
||||
import { Type } from "class-transformer"
|
||||
import {
|
||||
IsArray,
|
||||
IsDateString,
|
||||
IsEnum,
|
||||
IsNotEmpty,
|
||||
IsNumber,
|
||||
IsOptional,
|
||||
IsString,
|
||||
ValidateNested,
|
||||
} from "class-validator"
|
||||
import { FindParams, extendedFindParamsMixin } from "../../../types/common"
|
||||
|
||||
import { CampaignBudgetType } from "@medusajs/utils"
|
||||
import { createFindParams, createSelectParams } from "../../utils/validators"
|
||||
import { z } from "zod"
|
||||
|
||||
export class AdminGetCampaignsCampaignParams extends FindParams {}
|
||||
export const AdminGetCampaignParams = createSelectParams()
|
||||
|
||||
export class AdminGetCampaignsParams extends extendedFindParamsMixin({
|
||||
limit: 100,
|
||||
export type AdminGetCampaignsParamsType = z.infer<
|
||||
typeof AdminGetCampaignsParams
|
||||
>
|
||||
export const AdminGetCampaignsParams = createFindParams({
|
||||
offset: 0,
|
||||
}) {
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
campaign_identifier?: string
|
||||
limit: 50,
|
||||
}).merge(
|
||||
z.object({
|
||||
campaign_identifier: z.string().optional(),
|
||||
currency: z.string().optional(),
|
||||
$and: z.lazy(() => AdminGetCampaignsParams.array()).optional(),
|
||||
$or: z.lazy(() => AdminGetCampaignsParams.array()).optional(),
|
||||
})
|
||||
)
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
currency?: string
|
||||
}
|
||||
const CreateCampaignBudget = z.object({
|
||||
type: z.nativeEnum(CampaignBudgetType),
|
||||
limit: z.number(),
|
||||
})
|
||||
|
||||
export class AdminPostCampaignsReq {
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
name: string
|
||||
const UpdateCampaignBudget = z.object({
|
||||
type: z.nativeEnum(CampaignBudgetType).optional(),
|
||||
limit: z.number().optional(),
|
||||
})
|
||||
|
||||
@IsOptional()
|
||||
@IsNotEmpty()
|
||||
campaign_identifier?: string
|
||||
export type AdminCreateCampaignType = z.infer<typeof AdminCreateCampaign>
|
||||
export const AdminCreateCampaign = z.object({
|
||||
name: z.string(),
|
||||
campaign_identifier: z.string(),
|
||||
description: z.string().optional(),
|
||||
currency: z.string().optional(),
|
||||
budget: CreateCampaignBudget.optional(),
|
||||
starts_at: z.coerce.date(),
|
||||
ends_at: z.coerce.date(),
|
||||
promotions: z.array(z.object({ id: z.string() })).optional(),
|
||||
})
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
description?: string
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
currency?: string
|
||||
|
||||
@IsOptional()
|
||||
@ValidateNested()
|
||||
@Type(() => CampaignBudget)
|
||||
budget?: CampaignBudget
|
||||
|
||||
@IsOptional()
|
||||
@IsDateString()
|
||||
starts_at?: string
|
||||
|
||||
@IsOptional()
|
||||
@IsDateString()
|
||||
ends_at?: string
|
||||
|
||||
@IsOptional()
|
||||
@IsArray()
|
||||
@ValidateNested({ each: true })
|
||||
@Type(() => IdObject)
|
||||
promotions?: IdObject[]
|
||||
}
|
||||
|
||||
export class IdObject {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
id: string
|
||||
}
|
||||
|
||||
export class CampaignBudget {
|
||||
@IsOptional()
|
||||
@IsEnum(CampaignBudgetType)
|
||||
type?: CampaignBudgetType
|
||||
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
limit?: number
|
||||
}
|
||||
|
||||
export class AdminPostCampaignsCampaignReq {
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
name?: string
|
||||
|
||||
@IsOptional()
|
||||
@IsNotEmpty()
|
||||
campaign_identifier?: string
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
description?: string
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
currency?: string
|
||||
|
||||
@IsOptional()
|
||||
@ValidateNested()
|
||||
@Type(() => CampaignBudget)
|
||||
budget?: CampaignBudget
|
||||
|
||||
@IsOptional()
|
||||
@IsDateString()
|
||||
starts_at?: string
|
||||
|
||||
@IsOptional()
|
||||
@IsDateString()
|
||||
ends_at?: string
|
||||
|
||||
@IsOptional()
|
||||
@IsArray()
|
||||
@ValidateNested({ each: true })
|
||||
@Type(() => IdObject)
|
||||
promotions?: IdObject[]
|
||||
}
|
||||
export type AdminUpdateCampaignType = z.infer<typeof AdminUpdateCampaign>
|
||||
export const AdminUpdateCampaign = z.object({
|
||||
name: z.string().optional(),
|
||||
campaign_identifier: z.string().optional(),
|
||||
description: z.string().optional(),
|
||||
currency: z.string().optional(),
|
||||
budget: UpdateCampaignBudget.optional(),
|
||||
starts_at: z.coerce.date().optional(),
|
||||
ends_at: z.coerce.date().optional(),
|
||||
promotions: z.array(z.object({ id: z.string() })).optional(),
|
||||
})
|
||||
|
||||
@@ -7,30 +7,24 @@ import {
|
||||
updateCollectionsWorkflow,
|
||||
} from "@medusajs/core-flows"
|
||||
|
||||
import { UpdateProductCollectionDTO } from "@medusajs/types"
|
||||
import { remoteQueryObjectFromString } from "@medusajs/utils"
|
||||
import { AdminUpdateCollectionType } from "../validators"
|
||||
import { refetchCollection } from "../helpers"
|
||||
|
||||
export const GET = async (
|
||||
req: AuthenticatedMedusaRequest,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
const remoteQuery = req.scope.resolve("remoteQuery")
|
||||
|
||||
const variables = { id: req.params.id }
|
||||
|
||||
const queryObject = remoteQueryObjectFromString({
|
||||
entryPoint: "product_collection",
|
||||
variables,
|
||||
fields: req.retrieveConfig.select as string[],
|
||||
})
|
||||
|
||||
const [collection] = await remoteQuery(queryObject)
|
||||
const collection = await refetchCollection(
|
||||
req.params.id,
|
||||
req.scope,
|
||||
req.remoteQueryConfig.fields
|
||||
)
|
||||
|
||||
res.status(200).json({ collection })
|
||||
}
|
||||
|
||||
export const POST = async (
|
||||
req: AuthenticatedMedusaRequest<UpdateProductCollectionDTO>,
|
||||
req: AuthenticatedMedusaRequest<AdminUpdateCollectionType>,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
const { result, errors } = await updateCollectionsWorkflow(req.scope).run({
|
||||
@@ -45,7 +39,13 @@ export const POST = async (
|
||||
throw errors[0].error
|
||||
}
|
||||
|
||||
res.status(200).json({ collection: result[0] })
|
||||
const collection = await refetchCollection(
|
||||
req.params.id,
|
||||
req.scope,
|
||||
req.remoteQueryConfig.fields
|
||||
)
|
||||
|
||||
res.status(200).json({ collection })
|
||||
}
|
||||
|
||||
export const DELETE = async (
|
||||
|
||||
23
packages/medusa/src/api-v2/admin/collections/helpers.ts
Normal file
23
packages/medusa/src/api-v2/admin/collections/helpers.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { MedusaContainer } from "@medusajs/types"
|
||||
import {
|
||||
ContainerRegistrationKeys,
|
||||
remoteQueryObjectFromString,
|
||||
} from "@medusajs/utils"
|
||||
|
||||
export const refetchCollection = async (
|
||||
collectionId: string,
|
||||
scope: MedusaContainer,
|
||||
fields: string[]
|
||||
) => {
|
||||
const remoteQuery = scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
|
||||
const queryObject = remoteQueryObjectFromString({
|
||||
entryPoint: "product_collection",
|
||||
variables: {
|
||||
filters: { id: collectionId },
|
||||
},
|
||||
fields: fields,
|
||||
})
|
||||
|
||||
const collections = await remoteQuery(queryObject)
|
||||
return collections[0]
|
||||
}
|
||||
@@ -1,15 +1,14 @@
|
||||
import * as QueryConfig from "./query-config"
|
||||
|
||||
import {
|
||||
AdminGetCollectionsCollectionParams,
|
||||
AdminGetCollectionsParams,
|
||||
AdminPostCollectionsCollectionReq,
|
||||
AdminPostCollectionsReq,
|
||||
} from "./validators"
|
||||
import { transformBody, transformQuery } from "../../../api/middlewares"
|
||||
|
||||
import { MiddlewareRoute } from "../../../loaders/helpers/routing/types"
|
||||
import { authenticate } from "../../../utils/authenticate-middleware"
|
||||
import { validateAndTransformQuery } from "../../utils/validate-query"
|
||||
import {
|
||||
AdminCreateCollection,
|
||||
AdminGetCollectionParams,
|
||||
AdminGetCollectionsParams,
|
||||
AdminUpdateCollection,
|
||||
} from "./validators"
|
||||
import { validateAndTransformBody } from "../../utils/validate-body"
|
||||
|
||||
export const adminCollectionRoutesMiddlewares: MiddlewareRoute[] = [
|
||||
{
|
||||
@@ -22,7 +21,7 @@ export const adminCollectionRoutesMiddlewares: MiddlewareRoute[] = [
|
||||
method: ["GET"],
|
||||
matcher: "/admin/collections",
|
||||
middlewares: [
|
||||
transformQuery(
|
||||
validateAndTransformQuery(
|
||||
AdminGetCollectionsParams,
|
||||
QueryConfig.listTransformQueryConfig
|
||||
),
|
||||
@@ -32,8 +31,8 @@ export const adminCollectionRoutesMiddlewares: MiddlewareRoute[] = [
|
||||
method: ["GET"],
|
||||
matcher: "/admin/collections/:id",
|
||||
middlewares: [
|
||||
transformQuery(
|
||||
AdminGetCollectionsCollectionParams,
|
||||
validateAndTransformQuery(
|
||||
AdminGetCollectionParams,
|
||||
QueryConfig.retrieveTransformQueryConfig
|
||||
),
|
||||
],
|
||||
@@ -41,12 +40,24 @@ export const adminCollectionRoutesMiddlewares: MiddlewareRoute[] = [
|
||||
{
|
||||
method: ["POST"],
|
||||
matcher: "/admin/collections",
|
||||
middlewares: [transformBody(AdminPostCollectionsReq)],
|
||||
middlewares: [
|
||||
validateAndTransformBody(AdminCreateCollection),
|
||||
validateAndTransformQuery(
|
||||
AdminGetCollectionParams,
|
||||
QueryConfig.retrieveTransformQueryConfig
|
||||
),
|
||||
],
|
||||
},
|
||||
{
|
||||
method: ["POST"],
|
||||
matcher: "/admin/collections/:id",
|
||||
middlewares: [transformBody(AdminPostCollectionsCollectionReq)],
|
||||
middlewares: [
|
||||
validateAndTransformBody(AdminUpdateCollection),
|
||||
validateAndTransformQuery(
|
||||
AdminGetCollectionParams,
|
||||
QueryConfig.retrieveTransformQueryConfig
|
||||
),
|
||||
],
|
||||
},
|
||||
{
|
||||
method: ["DELETE"],
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
export const allowedAdminCollectionRelations = ["products.profiles"]
|
||||
|
||||
// TODO: See how these should look when expanded
|
||||
export const defaultAdminCollectionRelations = ["products.profiles"]
|
||||
|
||||
export const defaultAdminCollectionFields = [
|
||||
"id",
|
||||
"title",
|
||||
@@ -12,9 +7,7 @@ export const defaultAdminCollectionFields = [
|
||||
]
|
||||
|
||||
export const retrieveTransformQueryConfig = {
|
||||
defaultFields: defaultAdminCollectionFields,
|
||||
defaultRelations: defaultAdminCollectionRelations,
|
||||
allowedRelations: allowedAdminCollectionRelations,
|
||||
defaults: defaultAdminCollectionFields,
|
||||
isList: false,
|
||||
}
|
||||
|
||||
|
||||
@@ -3,28 +3,30 @@ import {
|
||||
MedusaResponse,
|
||||
} from "../../../types/routing"
|
||||
|
||||
import { CreateProductCollectionDTO } from "@medusajs/types"
|
||||
import { createCollectionsWorkflow } from "@medusajs/core-flows"
|
||||
import { remoteQueryObjectFromString } from "@medusajs/utils"
|
||||
import {
|
||||
ContainerRegistrationKeys,
|
||||
remoteQueryObjectFromString,
|
||||
} from "@medusajs/utils"
|
||||
import { AdminCreateCollectionType } from "./validators"
|
||||
import { refetchCollection } from "./helpers"
|
||||
|
||||
export const GET = async (
|
||||
req: AuthenticatedMedusaRequest,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
const remoteQuery = req.scope.resolve("remoteQuery")
|
||||
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
|
||||
|
||||
const queryObject = remoteQueryObjectFromString({
|
||||
const query = remoteQueryObjectFromString({
|
||||
entryPoint: "product_collection",
|
||||
variables: {
|
||||
filters: req.filterableFields,
|
||||
order: req.listConfig.order,
|
||||
skip: req.listConfig.skip,
|
||||
take: req.listConfig.take,
|
||||
...req.remoteQueryConfig.pagination,
|
||||
},
|
||||
fields: req.listConfig.select as string[],
|
||||
fields: req.remoteQueryConfig.fields,
|
||||
})
|
||||
|
||||
const { rows: collections, metadata } = await remoteQuery(queryObject)
|
||||
const { rows: collections, metadata } = await remoteQuery(query)
|
||||
|
||||
res.json({
|
||||
collections,
|
||||
@@ -35,7 +37,7 @@ export const GET = async (
|
||||
}
|
||||
|
||||
export const POST = async (
|
||||
req: AuthenticatedMedusaRequest<CreateProductCollectionDTO>,
|
||||
req: AuthenticatedMedusaRequest<AdminCreateCollectionType>,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
const input = [
|
||||
@@ -53,5 +55,11 @@ export const POST = async (
|
||||
throw errors[0].error
|
||||
}
|
||||
|
||||
res.status(200).json({ collection: result[0] })
|
||||
const collection = await refetchCollection(
|
||||
result[0].id,
|
||||
req.scope,
|
||||
req.remoteQueryConfig.fields
|
||||
)
|
||||
|
||||
res.status(200).json({ collection })
|
||||
}
|
||||
|
||||
@@ -1,115 +1,41 @@
|
||||
import { OperatorMap } from "@medusajs/types"
|
||||
import { Type } from "class-transformer"
|
||||
import {
|
||||
IsNotEmpty,
|
||||
IsObject,
|
||||
IsOptional,
|
||||
IsString,
|
||||
ValidateNested,
|
||||
} from "class-validator"
|
||||
import { FindParams, extendedFindParamsMixin } from "../../../types/common"
|
||||
import { OperatorMapValidator } from "../../../types/validators/operator-map"
|
||||
createFindParams,
|
||||
createOperatorMap,
|
||||
createSelectParams,
|
||||
} from "../../utils/validators"
|
||||
import { z } from "zod"
|
||||
|
||||
// TODO: Ensure these match the DTOs in the types
|
||||
export class AdminGetCollectionsCollectionParams extends FindParams {}
|
||||
export const AdminGetCollectionParams = createSelectParams()
|
||||
|
||||
/**
|
||||
* Parameters used to filter and configure the pagination of the retrieved regions.
|
||||
*/
|
||||
export class AdminGetCollectionsParams extends extendedFindParamsMixin({
|
||||
limit: 10,
|
||||
export type AdminGetCollectionsParamsType = z.infer<
|
||||
typeof AdminGetCollectionsParams
|
||||
>
|
||||
export const AdminGetCollectionsParams = createFindParams({
|
||||
offset: 0,
|
||||
}) {
|
||||
/**
|
||||
* Term to search product collections by their title and handle.
|
||||
*/
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
q?: string
|
||||
limit: 10,
|
||||
}).merge(
|
||||
z.object({
|
||||
q: z.string().optional(),
|
||||
title: z.union([z.string(), z.array(z.string())]).optional(),
|
||||
handle: z.union([z.string(), z.array(z.string())]).optional(),
|
||||
created_at: createOperatorMap().optional(),
|
||||
updated_at: createOperatorMap().optional(),
|
||||
deleted_at: createOperatorMap().optional(),
|
||||
$and: z.lazy(() => AdminGetCollectionsParams.array()).optional(),
|
||||
$or: z.lazy(() => AdminGetCollectionsParams.array()).optional(),
|
||||
})
|
||||
)
|
||||
|
||||
/**
|
||||
* Title to filter product collections by.
|
||||
*/
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
title?: string | string[]
|
||||
export type AdminCreateCollectionType = z.infer<typeof AdminCreateCollection>
|
||||
export const AdminCreateCollection = z.object({
|
||||
title: z.string(),
|
||||
handle: z.string().optional(),
|
||||
metadata: z.record(z.unknown()).optional(),
|
||||
})
|
||||
|
||||
/**
|
||||
* Handle to filter product collections by.
|
||||
*/
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
handle?: string | string[]
|
||||
|
||||
/**
|
||||
* Date filters to apply on the product collections' `created_at` date.
|
||||
*/
|
||||
@IsOptional()
|
||||
@ValidateNested()
|
||||
@Type(() => OperatorMapValidator)
|
||||
created_at?: OperatorMap<string>
|
||||
|
||||
/**
|
||||
* Date filters to apply on the product collections' `updated_at` date.
|
||||
*/
|
||||
@IsOptional()
|
||||
@ValidateNested()
|
||||
@Type(() => OperatorMapValidator)
|
||||
updated_at?: OperatorMap<string>
|
||||
|
||||
/**
|
||||
* Date filters to apply on the product collections' `deleted_at` date.
|
||||
*/
|
||||
@ValidateNested()
|
||||
@IsOptional()
|
||||
@Type(() => OperatorMapValidator)
|
||||
deleted_at?: OperatorMap<string>
|
||||
|
||||
// TODO: To be added in next iteration
|
||||
// /**
|
||||
// * Filter product collections by their associated discount condition's ID.
|
||||
// */
|
||||
// @IsString()
|
||||
// @IsOptional()
|
||||
// discount_condition_id?: string
|
||||
|
||||
// Note: These are new in v2
|
||||
// Additional filters from BaseFilterable
|
||||
@IsOptional()
|
||||
@ValidateNested({ each: true })
|
||||
@Type(() => AdminGetCollectionsParams)
|
||||
$and?: AdminGetCollectionsParams[]
|
||||
|
||||
@IsOptional()
|
||||
@ValidateNested({ each: true })
|
||||
@Type(() => AdminGetCollectionsParams)
|
||||
$or?: AdminGetCollectionsParams[]
|
||||
}
|
||||
|
||||
export class AdminPostCollectionsReq {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
title: string
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
handle?: string
|
||||
|
||||
@IsObject()
|
||||
@IsOptional()
|
||||
metadata?: Record<string, unknown>
|
||||
}
|
||||
|
||||
export class AdminPostCollectionsCollectionReq {
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
title?: string
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
handle?: string
|
||||
|
||||
@IsObject()
|
||||
@IsOptional()
|
||||
metadata?: Record<string, unknown>
|
||||
}
|
||||
export type AdminUpdateCollectionType = z.infer<typeof AdminUpdateCollection>
|
||||
export const AdminUpdateCollection = z.object({
|
||||
title: z.string().optional(),
|
||||
handle: z.string().optional(),
|
||||
metadata: z.record(z.unknown()).optional(),
|
||||
})
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import { remoteQueryObjectFromString } from "@medusajs/utils"
|
||||
import {
|
||||
ContainerRegistrationKeys,
|
||||
remoteQueryObjectFromString,
|
||||
} from "@medusajs/utils"
|
||||
import { MedusaRequest, MedusaResponse } from "../../../../types/routing"
|
||||
|
||||
export const GET = async (req: MedusaRequest, res: MedusaResponse) => {
|
||||
const remoteQuery = req.scope.resolve("remoteQuery")
|
||||
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
|
||||
|
||||
const variables = { code: req.params.code }
|
||||
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import { remoteQueryObjectFromString } from "@medusajs/utils"
|
||||
import {
|
||||
ContainerRegistrationKeys,
|
||||
remoteQueryObjectFromString,
|
||||
} from "@medusajs/utils"
|
||||
import { MedusaRequest, MedusaResponse } from "../../../types/routing"
|
||||
|
||||
export const GET = async (req: MedusaRequest, res: MedusaResponse) => {
|
||||
const remoteQuery = req.scope.resolve("remoteQuery")
|
||||
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
|
||||
|
||||
const queryObject = remoteQueryObjectFromString({
|
||||
entryPoint: "currency",
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
export * from "./campaigns"
|
||||
export * from "./promotions"
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
ApplicationMethodAllocation,
|
||||
ApplicationMethodTargetType,
|
||||
ApplicationMethodType,
|
||||
CampaignBudgetType,
|
||||
PromotionRuleOperator,
|
||||
PromotionType,
|
||||
} from "@medusajs/utils"
|
||||
@@ -11,6 +12,7 @@ import {
|
||||
ArrayNotEmpty,
|
||||
IsArray,
|
||||
IsBoolean,
|
||||
IsDateString,
|
||||
IsEnum,
|
||||
IsNotEmpty,
|
||||
IsNumber,
|
||||
@@ -26,7 +28,6 @@ import {
|
||||
extendedFindParamsMixin,
|
||||
} from "../../../types/common"
|
||||
import { XorConstraint } from "../../../types/validators/xor"
|
||||
import { AdminPostCampaignsReq } from "../campaigns/validators"
|
||||
|
||||
export class AdminGetPromotionsPromotionParams extends FindParams {}
|
||||
export class AdminGetPromotionRules extends FindParams {}
|
||||
@@ -92,6 +93,59 @@ export class AdminPostCreatePromotionRule {
|
||||
values: string[]
|
||||
}
|
||||
|
||||
export class AdminPostCampaignsReq {
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
name: string
|
||||
|
||||
@IsOptional()
|
||||
@IsNotEmpty()
|
||||
campaign_identifier?: string
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
description?: string
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
currency?: string
|
||||
|
||||
@IsOptional()
|
||||
@ValidateNested()
|
||||
@Type(() => CampaignBudget)
|
||||
budget?: CampaignBudget
|
||||
|
||||
@IsOptional()
|
||||
@IsDateString()
|
||||
starts_at?: string
|
||||
|
||||
@IsOptional()
|
||||
@IsDateString()
|
||||
ends_at?: string
|
||||
|
||||
@IsOptional()
|
||||
@IsArray()
|
||||
@ValidateNested({ each: true })
|
||||
@Type(() => IdObject)
|
||||
promotions?: IdObject[]
|
||||
}
|
||||
|
||||
export class IdObject {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
id: string
|
||||
}
|
||||
|
||||
export class CampaignBudget {
|
||||
@IsOptional()
|
||||
@IsEnum(CampaignBudgetType)
|
||||
type?: CampaignBudgetType
|
||||
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
limit?: number
|
||||
}
|
||||
|
||||
export class AdminPostPromotionsReq {
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
|
||||
Reference in New Issue
Block a user