chore(medusa): cleanup medusa package (#7206)
This commit is contained in:
@@ -1,40 +0,0 @@
|
||||
import { IsString } from "class-validator"
|
||||
import { AdminPostProductsReq as MedusaAdminPostProductsReq } from "../../api/routes/admin/products/create-product"
|
||||
import { registerOverriddenValidators, validator } from "../validator"
|
||||
|
||||
class AdminPostProductsReq extends MedusaAdminPostProductsReq {
|
||||
@IsString()
|
||||
custom_attribute: string
|
||||
}
|
||||
|
||||
describe("Validator", function () {
|
||||
it("should override the original validator", async function () {
|
||||
let err = await validator(MedusaAdminPostProductsReq, {
|
||||
title: "test",
|
||||
})
|
||||
.then(() => void 0)
|
||||
.catch((err) => err)
|
||||
|
||||
expect(err).not.toBeDefined()
|
||||
|
||||
registerOverriddenValidators(AdminPostProductsReq)
|
||||
|
||||
err = await validator(MedusaAdminPostProductsReq, {
|
||||
title: "test",
|
||||
})
|
||||
.then(() => void 0)
|
||||
.catch((err) => err)
|
||||
|
||||
expect(err).toBeDefined()
|
||||
expect(err.message).toEqual("custom_attribute must be a string")
|
||||
|
||||
err = await validator(MedusaAdminPostProductsReq, {
|
||||
title: "test",
|
||||
custom_attribute: "test",
|
||||
})
|
||||
.then(() => void 0)
|
||||
.catch((err) => err)
|
||||
|
||||
expect(err).not.toBeDefined()
|
||||
})
|
||||
})
|
||||
@@ -10,8 +10,6 @@ export * from "./is-date"
|
||||
export * from "./is-object"
|
||||
export * from "./is-string"
|
||||
export * from "./omit-deep"
|
||||
export * from "./product-category"
|
||||
export * from "./queries"
|
||||
export * from "./remote-query-fetch-data"
|
||||
export * from "./remove-undefined-properties"
|
||||
export * from "./set-metadata"
|
||||
|
||||
@@ -0,0 +1,719 @@
|
||||
import { NextFunction, Request, Response } from "express"
|
||||
import { transformQuery } from "../transform-query"
|
||||
import { extendedFindParamsMixin } from "../../../types/common"
|
||||
import { MedusaError } from "medusa-core-utils"
|
||||
|
||||
describe("transformQuery", () => {
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
it("should transform the input query", async () => {
|
||||
let mockRequest = {
|
||||
query: {},
|
||||
} as Request
|
||||
const mockResponse = {} as Response
|
||||
const nextFunction: NextFunction = jest.fn()
|
||||
|
||||
const expectations = ({
|
||||
offset,
|
||||
limit,
|
||||
inputOrder,
|
||||
transformedOrder,
|
||||
}: {
|
||||
offset: number
|
||||
limit: number
|
||||
inputOrder: string | undefined
|
||||
transformedOrder: Record<string, "ASC" | "DESC">
|
||||
relations?: string[]
|
||||
}) => {
|
||||
expect(mockRequest.validatedQuery).toEqual({
|
||||
offset,
|
||||
limit,
|
||||
order: inputOrder,
|
||||
})
|
||||
expect(mockRequest.filterableFields).toEqual({})
|
||||
expect(mockRequest.allowedProperties).toEqual([
|
||||
"id",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"deleted_at",
|
||||
"metadata.id",
|
||||
"metadata.parent.id",
|
||||
"metadata.children.id",
|
||||
"metadata.product.id",
|
||||
"metadata",
|
||||
"metadata.parent",
|
||||
"metadata.children",
|
||||
"metadata.product",
|
||||
])
|
||||
expect(mockRequest.listConfig).toEqual({
|
||||
take: limit,
|
||||
skip: offset,
|
||||
select: [
|
||||
"id",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"deleted_at",
|
||||
"metadata.id",
|
||||
"metadata.parent.id",
|
||||
"metadata.children.id",
|
||||
"metadata.product.id",
|
||||
],
|
||||
relations: [
|
||||
"metadata",
|
||||
"metadata.parent",
|
||||
"metadata.children",
|
||||
"metadata.product",
|
||||
],
|
||||
order: transformedOrder,
|
||||
})
|
||||
expect(mockRequest.remoteQueryConfig).toEqual({
|
||||
fields: [
|
||||
"id",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"deleted_at",
|
||||
"metadata.id",
|
||||
"metadata.parent.id",
|
||||
"metadata.children.id",
|
||||
"metadata.product.id",
|
||||
],
|
||||
pagination: {
|
||||
order: transformedOrder,
|
||||
skip: offset,
|
||||
take: limit,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
let queryConfig: any = {
|
||||
defaultFields: [
|
||||
"id",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"deleted_at",
|
||||
"metadata.id",
|
||||
"metadata.parent.id",
|
||||
"metadata.children.id",
|
||||
"metadata.product.id",
|
||||
],
|
||||
defaultRelations: [
|
||||
"metadata",
|
||||
"metadata.parent",
|
||||
"metadata.children",
|
||||
"metadata.product",
|
||||
],
|
||||
isList: true,
|
||||
}
|
||||
|
||||
let middleware = transformQuery(extendedFindParamsMixin(), queryConfig)
|
||||
|
||||
await middleware(mockRequest, mockResponse, nextFunction)
|
||||
|
||||
expectations({
|
||||
limit: 20,
|
||||
offset: 0,
|
||||
inputOrder: undefined,
|
||||
transformedOrder: {
|
||||
created_at: "DESC",
|
||||
},
|
||||
})
|
||||
|
||||
//////////////////////////////
|
||||
|
||||
mockRequest = {
|
||||
query: {
|
||||
limit: "10",
|
||||
offset: "5",
|
||||
order: "created_at",
|
||||
},
|
||||
} as unknown as Request
|
||||
|
||||
middleware = transformQuery(extendedFindParamsMixin(), queryConfig)
|
||||
|
||||
await middleware(mockRequest, mockResponse, nextFunction)
|
||||
|
||||
expectations({
|
||||
limit: 10,
|
||||
offset: 5,
|
||||
inputOrder: "created_at",
|
||||
transformedOrder: { created_at: "ASC" },
|
||||
})
|
||||
|
||||
//////////////////////////////
|
||||
|
||||
mockRequest = {
|
||||
query: {
|
||||
limit: "10",
|
||||
offset: "5",
|
||||
order: "created_at",
|
||||
},
|
||||
} as unknown as Request
|
||||
|
||||
queryConfig = {
|
||||
defaults: [
|
||||
"id",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"deleted_at",
|
||||
"metadata.id",
|
||||
"metadata.parent.id",
|
||||
"metadata.children.id",
|
||||
"metadata.product.id",
|
||||
],
|
||||
isList: true,
|
||||
}
|
||||
|
||||
middleware = transformQuery(extendedFindParamsMixin(), queryConfig)
|
||||
|
||||
await middleware(mockRequest, mockResponse, nextFunction)
|
||||
|
||||
expectations({
|
||||
limit: 10,
|
||||
offset: 5,
|
||||
inputOrder: "created_at",
|
||||
transformedOrder: { created_at: "ASC" },
|
||||
})
|
||||
})
|
||||
|
||||
it("should transform the input query taking into account the fields symbols (+,- or no symbol)", async () => {
|
||||
let mockRequest = {
|
||||
query: {
|
||||
fields: "id",
|
||||
},
|
||||
} as unknown as Request
|
||||
const mockResponse = {} as Response
|
||||
const nextFunction: NextFunction = jest.fn()
|
||||
|
||||
let queryConfig: any = {
|
||||
defaultFields: [
|
||||
"id",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"deleted_at",
|
||||
"metadata.id",
|
||||
"metadata.parent.id",
|
||||
"metadata.children.id",
|
||||
"metadata.product.id",
|
||||
],
|
||||
defaultRelations: [
|
||||
"metadata",
|
||||
"metadata.parent",
|
||||
"metadata.children",
|
||||
"metadata.product",
|
||||
],
|
||||
isList: true,
|
||||
}
|
||||
|
||||
let middleware = transformQuery(extendedFindParamsMixin(), queryConfig)
|
||||
|
||||
await middleware(mockRequest, mockResponse, nextFunction)
|
||||
|
||||
expect(mockRequest.listConfig).toEqual(
|
||||
expect.objectContaining({
|
||||
select: ["id", "created_at"],
|
||||
})
|
||||
)
|
||||
|
||||
//////////////////////////////
|
||||
|
||||
mockRequest = {
|
||||
query: {
|
||||
fields: "+test_prop,-prop-test-something",
|
||||
},
|
||||
} as unknown as Request
|
||||
|
||||
queryConfig = {
|
||||
defaultFields: [
|
||||
"id",
|
||||
"prop-test-something",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"deleted_at",
|
||||
"metadata.id",
|
||||
"metadata.parent.id",
|
||||
"metadata.children.id",
|
||||
"metadata.product.id",
|
||||
],
|
||||
defaultRelations: [
|
||||
"metadata",
|
||||
"metadata.parent",
|
||||
"metadata.children",
|
||||
"metadata.product",
|
||||
],
|
||||
isList: true,
|
||||
}
|
||||
|
||||
middleware = transformQuery(extendedFindParamsMixin(), queryConfig)
|
||||
|
||||
await middleware(mockRequest, mockResponse, nextFunction)
|
||||
|
||||
expect(mockRequest.listConfig).toEqual(
|
||||
expect.objectContaining({
|
||||
select: [
|
||||
"id",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"deleted_at",
|
||||
"metadata.id",
|
||||
"metadata.parent.id",
|
||||
"metadata.children.id",
|
||||
"metadata.product.id",
|
||||
"test_prop",
|
||||
],
|
||||
})
|
||||
)
|
||||
|
||||
//////////////////////////////
|
||||
|
||||
mockRequest = {
|
||||
query: {
|
||||
fields: "+test_prop,-updated_at",
|
||||
},
|
||||
} as unknown as Request
|
||||
|
||||
queryConfig = {
|
||||
defaults: [
|
||||
"id",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"deleted_at",
|
||||
"metadata.id",
|
||||
"metadata.parent.id",
|
||||
"metadata.children.id",
|
||||
"metadata.product.id",
|
||||
],
|
||||
isList: true,
|
||||
}
|
||||
|
||||
middleware = transformQuery(extendedFindParamsMixin(), queryConfig)
|
||||
|
||||
await middleware(mockRequest, mockResponse, nextFunction)
|
||||
|
||||
expect(mockRequest.listConfig).toEqual(
|
||||
expect.objectContaining({
|
||||
select: [
|
||||
"id",
|
||||
"created_at",
|
||||
"deleted_at",
|
||||
"metadata.id",
|
||||
"metadata.parent.id",
|
||||
"metadata.children.id",
|
||||
"metadata.product.id",
|
||||
"test_prop",
|
||||
],
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it(`should transform the input and manage the allowed fields and relations properly without error`, async () => {
|
||||
let mockRequest = {
|
||||
query: {
|
||||
fields: "*product.variants,+product.id",
|
||||
},
|
||||
} as unknown as Request
|
||||
const mockResponse = {} as Response
|
||||
const nextFunction: NextFunction = jest.fn()
|
||||
|
||||
let queryConfig: any = {
|
||||
defaults: [
|
||||
"id",
|
||||
"created_at",
|
||||
"deleted_at",
|
||||
"metadata.id",
|
||||
"metadata.parent.id",
|
||||
"metadata.children.id",
|
||||
"metadata.product.id",
|
||||
],
|
||||
allowed: [
|
||||
"id",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"deleted_at",
|
||||
"metadata.id",
|
||||
"metadata.parent.id",
|
||||
"metadata.children.id",
|
||||
"metadata.product.id",
|
||||
"product",
|
||||
"product.variants",
|
||||
],
|
||||
isList: true,
|
||||
}
|
||||
|
||||
let middleware = transformQuery(extendedFindParamsMixin(), queryConfig)
|
||||
|
||||
await middleware(mockRequest, mockResponse, nextFunction)
|
||||
|
||||
expect(mockRequest.listConfig).toEqual(
|
||||
expect.objectContaining({
|
||||
select: [
|
||||
"id",
|
||||
"created_at",
|
||||
"deleted_at",
|
||||
"metadata.id",
|
||||
"metadata.parent.id",
|
||||
"metadata.children.id",
|
||||
"metadata.product.id",
|
||||
"product.id",
|
||||
],
|
||||
relations: [
|
||||
"metadata",
|
||||
"metadata.parent",
|
||||
"metadata.children",
|
||||
"metadata.product",
|
||||
"product",
|
||||
"product.variants",
|
||||
],
|
||||
})
|
||||
)
|
||||
expect(mockRequest.remoteQueryConfig).toEqual(
|
||||
expect.objectContaining({
|
||||
fields: [
|
||||
"id",
|
||||
"created_at",
|
||||
"deleted_at",
|
||||
"metadata.id",
|
||||
"metadata.parent.id",
|
||||
"metadata.children.id",
|
||||
"metadata.product.id",
|
||||
"product.id",
|
||||
"product.variants.*",
|
||||
],
|
||||
})
|
||||
)
|
||||
|
||||
//////////////////////////////
|
||||
|
||||
mockRequest = {
|
||||
query: {
|
||||
fields: "store.name",
|
||||
},
|
||||
} as unknown as Request
|
||||
|
||||
queryConfig = {
|
||||
defaultFields: [
|
||||
"id",
|
||||
"created_at",
|
||||
"deleted_at",
|
||||
"metadata.id",
|
||||
"metadata.parent.id",
|
||||
"metadata.children.id",
|
||||
"metadata.product.id",
|
||||
],
|
||||
allowedFields: [
|
||||
"id",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"deleted_at",
|
||||
"metadata.id",
|
||||
"metadata.parent.id",
|
||||
"metadata.children.id",
|
||||
"metadata.product.id",
|
||||
"product",
|
||||
"product.variants",
|
||||
"store.name",
|
||||
],
|
||||
allowedRelations: ["metadata", "product"],
|
||||
isList: true,
|
||||
}
|
||||
|
||||
middleware = transformQuery(extendedFindParamsMixin(), queryConfig)
|
||||
|
||||
await middleware(mockRequest, mockResponse, nextFunction)
|
||||
|
||||
expect(mockRequest.listConfig).toEqual(
|
||||
expect.objectContaining({
|
||||
select: ["store.name", "created_at", "id"],
|
||||
relations: ["store"],
|
||||
})
|
||||
)
|
||||
expect(mockRequest.remoteQueryConfig).toEqual(
|
||||
expect.objectContaining({
|
||||
fields: ["store.name", "created_at", "id"],
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("should throw when attempting to transform the input if disallowed fields are requested", async () => {
|
||||
let mockRequest = {
|
||||
query: {
|
||||
fields: "+test_prop",
|
||||
},
|
||||
} as unknown as Request
|
||||
const mockResponse = {} as Response
|
||||
const nextFunction: NextFunction = jest.fn()
|
||||
|
||||
let queryConfig: any = {
|
||||
defaultFields: [
|
||||
"id",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"deleted_at",
|
||||
"metadata.id",
|
||||
"metadata.parent.id",
|
||||
"metadata.children.id",
|
||||
"metadata.product.id",
|
||||
],
|
||||
defaultRelations: [
|
||||
"metadata",
|
||||
"metadata.parent",
|
||||
"metadata.children",
|
||||
"metadata.product",
|
||||
],
|
||||
allowedFields: [
|
||||
"id",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"deleted_at",
|
||||
"metadata.id",
|
||||
"metadata.parent.id",
|
||||
"metadata.children.id",
|
||||
"metadata.product.id",
|
||||
],
|
||||
isList: true,
|
||||
}
|
||||
|
||||
let middleware = transformQuery(extendedFindParamsMixin(), queryConfig)
|
||||
|
||||
await middleware(mockRequest, mockResponse, nextFunction)
|
||||
|
||||
expect(nextFunction).toHaveBeenCalledWith(
|
||||
new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Requested fields [test_prop] are not valid`
|
||||
)
|
||||
)
|
||||
|
||||
//////////////////////////////
|
||||
|
||||
mockRequest = {
|
||||
query: {
|
||||
expand: "product",
|
||||
},
|
||||
} as unknown as Request
|
||||
|
||||
queryConfig = {
|
||||
defaultFields: [
|
||||
"id",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"deleted_at",
|
||||
"metadata.id",
|
||||
"metadata.parent.id",
|
||||
"metadata.children.id",
|
||||
"metadata.product.id",
|
||||
],
|
||||
defaultRelations: [
|
||||
"metadata",
|
||||
"metadata.parent",
|
||||
"metadata.children",
|
||||
"metadata.product",
|
||||
],
|
||||
allowedFields: [
|
||||
"id",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"deleted_at",
|
||||
"metadata.id",
|
||||
"metadata.parent.id",
|
||||
"metadata.children.id",
|
||||
"metadata.product.id",
|
||||
],
|
||||
allowedRelations: [
|
||||
"metadata",
|
||||
"metadata.parent",
|
||||
"metadata.children",
|
||||
"metadata.product",
|
||||
],
|
||||
isList: true,
|
||||
}
|
||||
|
||||
middleware = transformQuery(extendedFindParamsMixin(), queryConfig)
|
||||
|
||||
await middleware(mockRequest, mockResponse, nextFunction)
|
||||
|
||||
expect(nextFunction).toHaveBeenCalledWith(
|
||||
new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Requested fields [product] are not valid`
|
||||
)
|
||||
)
|
||||
|
||||
//////////////////////////////
|
||||
|
||||
mockRequest = {
|
||||
query: {
|
||||
expand: "store",
|
||||
},
|
||||
} as unknown as Request
|
||||
|
||||
queryConfig = {
|
||||
defaultFields: [
|
||||
"id",
|
||||
"created_at",
|
||||
"deleted_at",
|
||||
"metadata.id",
|
||||
"metadata.parent.id",
|
||||
"metadata.children.id",
|
||||
"metadata.product.id",
|
||||
],
|
||||
allowedFields: [
|
||||
"id",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"deleted_at",
|
||||
"metadata.id",
|
||||
"metadata.parent.id",
|
||||
"metadata.children.id",
|
||||
"metadata.product.id",
|
||||
"product",
|
||||
"product.variants",
|
||||
"store.name",
|
||||
],
|
||||
allowedRelations: ["metadata", "product"],
|
||||
isList: true,
|
||||
}
|
||||
|
||||
middleware = transformQuery(extendedFindParamsMixin(), queryConfig)
|
||||
|
||||
await middleware(mockRequest, mockResponse, nextFunction)
|
||||
|
||||
expect(nextFunction).toHaveBeenCalledWith(
|
||||
new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Requested fields [store] are not valid`
|
||||
)
|
||||
)
|
||||
|
||||
//////////////////////////////
|
||||
|
||||
mockRequest = {
|
||||
query: {
|
||||
fields: "*product",
|
||||
},
|
||||
} as unknown as Request
|
||||
|
||||
queryConfig = {
|
||||
defaults: [
|
||||
"id",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"deleted_at",
|
||||
"metadata.id",
|
||||
"metadata.parent.id",
|
||||
"metadata.children.id",
|
||||
"metadata.product.id",
|
||||
],
|
||||
allowed: [
|
||||
"id",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"deleted_at",
|
||||
"metadata.id",
|
||||
"metadata.parent.id",
|
||||
"metadata.children.id",
|
||||
"metadata.product.id",
|
||||
],
|
||||
isList: true,
|
||||
}
|
||||
|
||||
middleware = transformQuery(extendedFindParamsMixin(), queryConfig)
|
||||
|
||||
await middleware(mockRequest, mockResponse, nextFunction)
|
||||
|
||||
expect(nextFunction).toHaveBeenCalledWith(
|
||||
new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Requested fields [product] are not valid`
|
||||
)
|
||||
)
|
||||
|
||||
//////////////////////////////
|
||||
|
||||
mockRequest = {
|
||||
query: {
|
||||
fields: "*product.variants",
|
||||
},
|
||||
} as unknown as Request
|
||||
|
||||
queryConfig = {
|
||||
defaults: [
|
||||
"id",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"deleted_at",
|
||||
"metadata.id",
|
||||
"metadata.parent.id",
|
||||
"metadata.children.id",
|
||||
"metadata.product.id",
|
||||
],
|
||||
allowed: [
|
||||
"id",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"deleted_at",
|
||||
"metadata.id",
|
||||
"metadata.parent.id",
|
||||
"metadata.children.id",
|
||||
"metadata.product.id",
|
||||
"product",
|
||||
],
|
||||
isList: true,
|
||||
}
|
||||
|
||||
middleware = transformQuery(extendedFindParamsMixin(), queryConfig)
|
||||
|
||||
await middleware(mockRequest, mockResponse, nextFunction)
|
||||
|
||||
expect(nextFunction).toHaveBeenCalledWith(
|
||||
new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Requested fields [product.variants] are not valid`
|
||||
)
|
||||
)
|
||||
|
||||
//////////////////////////////
|
||||
|
||||
mockRequest = {
|
||||
query: {
|
||||
fields: "product",
|
||||
},
|
||||
} as unknown as Request
|
||||
|
||||
queryConfig = {
|
||||
defaults: [
|
||||
"id",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"deleted_at",
|
||||
"metadata.id",
|
||||
"metadata.parent.id",
|
||||
"metadata.children.id",
|
||||
"metadata.product.id",
|
||||
],
|
||||
allowed: [
|
||||
"id",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"deleted_at",
|
||||
"metadata.id",
|
||||
"metadata.parent.id",
|
||||
"metadata.children.id",
|
||||
"metadata.product.id",
|
||||
],
|
||||
isList: true,
|
||||
}
|
||||
|
||||
middleware = transformQuery(extendedFindParamsMixin(), queryConfig)
|
||||
|
||||
await middleware(mockRequest, mockResponse, nextFunction)
|
||||
|
||||
expect(nextFunction).toHaveBeenCalledWith(
|
||||
new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Requested fields [product] are not valid`
|
||||
)
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,34 @@
|
||||
import { ContainerRegistrationKeys, MedusaV2Flag } from "@medusajs/utils"
|
||||
import { NextFunction, Request, RequestHandler, Response } from "express"
|
||||
|
||||
import passport from "passport"
|
||||
|
||||
// Optional customer authentication
|
||||
// If authenticated, middleware attaches customer to request (as user) otherwise we pass through
|
||||
// If you want to require authentication, use `requireCustomerAuthentication` in `packages/medusa/src/api/middlewares/require-customer-authentication.ts`
|
||||
export default (): RequestHandler => {
|
||||
return (req: Request, res: Response, next: NextFunction): void => {
|
||||
const featureFlagRouter = req.scope.resolve(
|
||||
ContainerRegistrationKeys.FEATURE_FLAG_ROUTER
|
||||
)
|
||||
if (featureFlagRouter.isFeatureEnabled(MedusaV2Flag.key)) {
|
||||
return next()
|
||||
}
|
||||
|
||||
passport.authenticate(
|
||||
["store-session", "store-bearer"],
|
||||
{ session: false },
|
||||
(err, user) => {
|
||||
if (err) {
|
||||
return next(err)
|
||||
}
|
||||
|
||||
if (user) {
|
||||
req.user = user
|
||||
}
|
||||
|
||||
return next()
|
||||
}
|
||||
)(req, res, next)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import { ContainerRegistrationKeys, MedusaV2Flag } from "@medusajs/utils"
|
||||
import { NextFunction, Request, RequestHandler, Response } from "express"
|
||||
|
||||
import passport from "passport"
|
||||
|
||||
export default (): RequestHandler => {
|
||||
return (req: Request, res: Response, next: NextFunction): void => {
|
||||
const featureFlagRouter = req.scope.resolve(
|
||||
ContainerRegistrationKeys.FEATURE_FLAG_ROUTER
|
||||
)
|
||||
if (featureFlagRouter.isFeatureEnabled(MedusaV2Flag.key)) {
|
||||
return next()
|
||||
}
|
||||
passport.authenticate(
|
||||
["admin-session", "admin-bearer", "admin-api-token"],
|
||||
{ session: false }
|
||||
)(req, res, next)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
import { NextFunction, Request, RequestHandler, Response } from "express"
|
||||
|
||||
type handler = (req: Request, res: Response) => Promise<void>
|
||||
|
||||
/**
|
||||
* @deprecated use `import { wrapHandler } from "@medusajs/utils"`
|
||||
*/
|
||||
export default (fn: handler): RequestHandler => {
|
||||
return async (req: Request, res: Response, next: NextFunction) => {
|
||||
if (req?.errors?.length) {
|
||||
return res.status(400).json({
|
||||
errors: req.errors,
|
||||
message:
|
||||
"Provided request body contains errors. Please check the data and retry the request",
|
||||
})
|
||||
}
|
||||
|
||||
try {
|
||||
return await fn(req, res)
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @schema MultipleErrors
|
||||
* title: "Multiple Errors"
|
||||
* type: object
|
||||
* properties:
|
||||
* errors:
|
||||
* type: array
|
||||
* description: Array of errors
|
||||
* items:
|
||||
* $ref: "#/components/schemas/Error"
|
||||
* message:
|
||||
* type: string
|
||||
* default: "Provided request body contains errors. Please check the data and retry the request"
|
||||
*/
|
||||
@@ -0,0 +1,15 @@
|
||||
import { NextFunction, Request, Response } from "express"
|
||||
|
||||
export function checkRegisteredModules(services: {
|
||||
[serviceName: string]: string
|
||||
}): (req: Request, res: Response, next: NextFunction) => Promise<void> {
|
||||
return async (req: Request, res: Response, next: NextFunction) => {
|
||||
for (const service of Object.keys(services)) {
|
||||
if (!req.scope.resolve(service, { allowUnregistered: true })) {
|
||||
return next(new Error(services[service]))
|
||||
}
|
||||
}
|
||||
|
||||
next()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
import { NextFunction, Request, Response } from "express"
|
||||
|
||||
import { MedusaError } from "medusa-core-utils"
|
||||
import { Logger } from "../../types/global"
|
||||
import { formatException } from "../../utils"
|
||||
|
||||
const QUERY_RUNNER_RELEASED = "QueryRunnerAlreadyReleasedError"
|
||||
const TRANSACTION_STARTED = "TransactionAlreadyStartedError"
|
||||
const TRANSACTION_NOT_STARTED = "TransactionNotStartedError"
|
||||
|
||||
const API_ERROR = "api_error"
|
||||
const INVALID_REQUEST_ERROR = "invalid_request_error"
|
||||
const INVALID_STATE_ERROR = "invalid_state_error"
|
||||
|
||||
export default () => {
|
||||
return (
|
||||
err: MedusaError,
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
) => {
|
||||
const logger: Logger = req.scope.resolve("logger")
|
||||
|
||||
err = formatException(err)
|
||||
|
||||
logger.error(err)
|
||||
|
||||
const errorType = err.type || err.name
|
||||
|
||||
const errObj = {
|
||||
code: err.code,
|
||||
type: err.type,
|
||||
message: err.message,
|
||||
}
|
||||
|
||||
let statusCode = 500
|
||||
switch (errorType) {
|
||||
case QUERY_RUNNER_RELEASED:
|
||||
case TRANSACTION_STARTED:
|
||||
case TRANSACTION_NOT_STARTED:
|
||||
case MedusaError.Types.CONFLICT:
|
||||
statusCode = 409
|
||||
errObj.code = INVALID_STATE_ERROR
|
||||
errObj.message =
|
||||
"The request conflicted with another request. You may retry the request with the provided Idempotency-Key."
|
||||
break
|
||||
case MedusaError.Types.UNAUTHORIZED:
|
||||
statusCode = 401
|
||||
break
|
||||
case MedusaError.Types.PAYMENT_AUTHORIZATION_ERROR:
|
||||
statusCode = 422
|
||||
break
|
||||
case MedusaError.Types.DUPLICATE_ERROR:
|
||||
statusCode = 422
|
||||
errObj.code = INVALID_REQUEST_ERROR
|
||||
break
|
||||
case MedusaError.Types.NOT_ALLOWED:
|
||||
case MedusaError.Types.INVALID_DATA:
|
||||
statusCode = 400
|
||||
break
|
||||
case MedusaError.Types.NOT_FOUND:
|
||||
statusCode = 404
|
||||
break
|
||||
case MedusaError.Types.DB_ERROR:
|
||||
statusCode = 500
|
||||
errObj.code = API_ERROR
|
||||
break
|
||||
case MedusaError.Types.UNEXPECTED_STATE:
|
||||
case MedusaError.Types.INVALID_ARGUMENT:
|
||||
break
|
||||
default:
|
||||
errObj.code = "unknown_error"
|
||||
errObj.message = "An unknown error occurred."
|
||||
errObj.type = "unknown_error"
|
||||
break
|
||||
}
|
||||
|
||||
res.status(statusCode).json(errObj)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @schema Error
|
||||
* title: "Response Error"
|
||||
* type: object
|
||||
* properties:
|
||||
* code:
|
||||
* type: string
|
||||
* description: A slug code to indicate the type of the error.
|
||||
* enum: [invalid_state_error, invalid_request_error, api_error, unknown_error]
|
||||
* message:
|
||||
* type: string
|
||||
* description: Description of the error that occurred.
|
||||
* example: "first_name must be a string"
|
||||
* type:
|
||||
* type: string
|
||||
* description: A slug indicating the type of the error.
|
||||
* enum: [QueryRunnerAlreadyReleasedError, TransactionAlreadyStartedError, TransactionNotStartedError, conflict, unauthorized, payment_authorization_error, duplicate_error, not_allowed, invalid_data, not_found, database_error, unexpected_state, invalid_argument, unknown_error]
|
||||
*/
|
||||
@@ -0,0 +1,18 @@
|
||||
import { FlagRouter } from "@medusajs/utils"
|
||||
import { NextFunction, Request, Response } from "express"
|
||||
|
||||
export function isFeatureFlagEnabled(
|
||||
flagKey: string
|
||||
): (req: Request, res: Response, next: NextFunction) => Promise<void> {
|
||||
return async (req: Request, res: Response, next: NextFunction) => {
|
||||
const featureFlagRouter = req.scope.resolve(
|
||||
"featureFlagRouter"
|
||||
) as FlagRouter
|
||||
|
||||
if (!featureFlagRouter.isFeatureEnabled(flagKey)) {
|
||||
res.sendStatus(404)
|
||||
} else {
|
||||
next()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
export { default as authenticate } from "./authenticate"
|
||||
export { default as authenticateCustomer } from "./authenticate-customer"
|
||||
export { default as wrapHandler } from "./await-middleware"
|
||||
export { default as errorHandler } from "./error-handler"
|
||||
export { isFeatureFlagEnabled } from "./feature-flag-enabled"
|
||||
export { default as normalizeQuery } from "./normalized-query"
|
||||
export { default as requireCustomerAuthentication } from "./require-customer-authentication"
|
||||
export { transformBody } from "./transform-body"
|
||||
export { transformIncludesOptions } from "./transform-includes-options"
|
||||
export { transformQuery, transformStoreQuery } from "./transform-query"
|
||||
@@ -0,0 +1,24 @@
|
||||
import { NextFunction, Request, Response } from "express"
|
||||
|
||||
/**
|
||||
* Normalize an input query, especially from array like query params to an array type
|
||||
* e.g: /admin/orders/?fields[]=id,status,cart_id becomes { fields: ["id", "status", "cart_id"] }
|
||||
*/
|
||||
export default (): ((
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
) => void) => {
|
||||
return (req: Request, res: Response, next: NextFunction): void => {
|
||||
req.query = Object.entries(req.query).reduce((acc, [key, val]) => {
|
||||
if (Array.isArray(val) && val.length === 1) {
|
||||
acc[key] = (val as string[])[0].split(",")
|
||||
} else {
|
||||
acc[key] = val
|
||||
}
|
||||
return acc
|
||||
}, {})
|
||||
|
||||
next()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { NextFunction, Request, RequestHandler, Response } from "express"
|
||||
import passport from "passport"
|
||||
|
||||
export default (): RequestHandler => {
|
||||
return (req: Request, res: Response, next: NextFunction): void => {
|
||||
if (req.user) {
|
||||
return next()
|
||||
}
|
||||
|
||||
passport.authenticate(["store-session", "store-bearer"], { session: false })(
|
||||
req,
|
||||
res,
|
||||
next
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import { ValidatorOptions } from "class-validator"
|
||||
import { NextFunction, Request, Response } from "express"
|
||||
import { ClassConstructor } from "../../types/global"
|
||||
import { validator } from "../../utils/validator"
|
||||
|
||||
export function transformBody<T>(
|
||||
plainToClass: ClassConstructor<T>,
|
||||
config: ValidatorOptions = {
|
||||
forbidUnknownValues: false,
|
||||
}
|
||||
): (req: Request, res: Response, next: NextFunction) => Promise<void> {
|
||||
return async (req: Request, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
req.validatedBody = await validator(plainToClass, req.body, config)
|
||||
next()
|
||||
} catch (e) {
|
||||
next(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
import { NextFunction, Request, Response } from "express"
|
||||
import { MedusaError } from "medusa-core-utils"
|
||||
|
||||
/**
|
||||
* Retrieve the includes options from the fields query param.
|
||||
* If the include option is present then assigned it to includes on req
|
||||
* @param allowedIncludes The list of fields that can be passed and assign to req.includes
|
||||
* @param expectedIncludes The list of fields that the consumer can pass to the end point using this middleware. It is a subset of `allowedIncludes`
|
||||
*/
|
||||
export function transformIncludesOptions(
|
||||
allowedIncludes: string[] = [],
|
||||
expectedIncludes: string[] = []
|
||||
) {
|
||||
return (req: Request, res: Response, next: NextFunction): void => {
|
||||
if (!allowedIncludes.length || !req.query.expand) {
|
||||
return next()
|
||||
}
|
||||
|
||||
const expand = (req.query.expand as string).split(",") ?? []
|
||||
|
||||
for (const includes of allowedIncludes) {
|
||||
const fieldIndex = expand.indexOf(includes) ?? -1
|
||||
|
||||
const isPresent = fieldIndex !== -1
|
||||
|
||||
if (isPresent) {
|
||||
expand.splice(fieldIndex, 1)
|
||||
|
||||
if (!expectedIncludes.includes(includes)) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`The field "${includes}" is not supported by this end point. ${
|
||||
expectedIncludes.length
|
||||
? `The includes fields can be one of entity properties or in [${expectedIncludes.join(
|
||||
", "
|
||||
)}]`
|
||||
: ""
|
||||
}`
|
||||
)
|
||||
}
|
||||
|
||||
req.includes = req.includes ?? {}
|
||||
req.includes[includes] = true
|
||||
}
|
||||
}
|
||||
|
||||
if (req.query.expand) {
|
||||
if (expand.length) {
|
||||
req.query.expand = expand.join(",")
|
||||
} else {
|
||||
delete req.query.expand
|
||||
}
|
||||
}
|
||||
|
||||
next()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
import { ValidatorOptions } from "class-validator"
|
||||
import { NextFunction, Request, Response } from "express"
|
||||
import { omit } from "lodash"
|
||||
import { BaseEntity } from "../../interfaces"
|
||||
import { FindConfig, QueryConfig, RequestQueryFields } from "../../types/common"
|
||||
import { ClassConstructor } from "../../types/global"
|
||||
import { removeUndefinedProperties } from "../../utils"
|
||||
import {
|
||||
prepareListQuery,
|
||||
prepareRetrieveQuery,
|
||||
} from "../../utils/get-query-config"
|
||||
import { validator } from "../../utils/validator"
|
||||
import { default as normalizeQuery } from "./normalized-query"
|
||||
|
||||
/**
|
||||
* Middleware that transform the query input for the admin end points
|
||||
* @param plainToClass
|
||||
* @param queryConfig
|
||||
* @param config
|
||||
*/
|
||||
export function transformQuery<
|
||||
T extends RequestQueryFields,
|
||||
TEntity extends BaseEntity
|
||||
>(
|
||||
plainToClass: ClassConstructor<T>,
|
||||
queryConfig: QueryConfig<TEntity> = {},
|
||||
config: ValidatorOptions = {}
|
||||
): (req: Request, res: Response, next: NextFunction) => Promise<void> {
|
||||
return async (req: Request, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
normalizeQuery()(req, res, () => void 0)
|
||||
const validated: T = await validator<T, Record<string, unknown>>(
|
||||
plainToClass,
|
||||
req.query,
|
||||
config
|
||||
)
|
||||
|
||||
req.validatedQuery = validated
|
||||
req.filterableFields = getFilterableFields(validated)
|
||||
|
||||
attachListOrRetrieveConfig<TEntity>(req, {
|
||||
...queryConfig,
|
||||
allowed:
|
||||
req.allowed ?? queryConfig.allowed ?? queryConfig.allowedFields ?? [],
|
||||
})
|
||||
|
||||
/**
|
||||
* TODO: the bellow allowedProperties should probably need to be reworked which would create breaking changes everywhere
|
||||
* cleanResponseData is used. It is in fact, what is expected to be returned which IMO
|
||||
* should correspond to the select/relations
|
||||
*
|
||||
* Kept it as it is to maintain backward compatibility
|
||||
*/
|
||||
const queryConfigRes = !queryConfig.isList
|
||||
? req.retrieveConfig
|
||||
: req.listConfig
|
||||
const includesRelations = Object.keys(req.includes ?? {})
|
||||
req.allowedProperties = Array.from(
|
||||
new Set(
|
||||
[
|
||||
...(req.validatedQuery.fields
|
||||
? queryConfigRes.select ?? []
|
||||
: req.allowed ??
|
||||
queryConfig.allowed ??
|
||||
queryConfig.allowedFields ??
|
||||
(queryConfig.defaults as string[]) ??
|
||||
queryConfig.defaultFields ??
|
||||
[]),
|
||||
...(req.validatedQuery.expand || includesRelations.length
|
||||
? [...(validated.expand?.split(",") || []), ...includesRelations] // For backward compatibility, the includes takes precedence over the relations for the returnable fields
|
||||
: queryConfig.allowedRelations ?? queryConfigRes.relations ?? []), // For backward compatibility, the allowedRelations takes precedence over the relations for the returnable fields
|
||||
].filter(Boolean)
|
||||
)
|
||||
)
|
||||
|
||||
next()
|
||||
} catch (e) {
|
||||
next(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Middleware that transform the query input for the store endpoints
|
||||
* @param plainToClass
|
||||
* @param queryConfig
|
||||
* @param config
|
||||
*
|
||||
* @deprecated use `transformQuery` instead
|
||||
*/
|
||||
export function transformStoreQuery<
|
||||
T extends RequestQueryFields,
|
||||
TEntity extends BaseEntity
|
||||
>(
|
||||
plainToClass: ClassConstructor<T>,
|
||||
queryConfig?: QueryConfig<TEntity>,
|
||||
config: ValidatorOptions = {}
|
||||
): (req: Request, res: Response, next: NextFunction) => Promise<void> {
|
||||
return transformQuery(plainToClass, queryConfig, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Omit the non filterable config from the validated object
|
||||
* @param obj
|
||||
*/
|
||||
function getFilterableFields<T extends RequestQueryFields>(obj: T): T {
|
||||
const result = omit(obj, [
|
||||
"limit",
|
||||
"offset",
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
"expand",
|
||||
"fields",
|
||||
"order",
|
||||
]) as T
|
||||
return removeUndefinedProperties(result)
|
||||
}
|
||||
|
||||
/**
|
||||
* build and attach the `retrieveConfig` or `listConfig` and remoteQueryConfig to the request object
|
||||
* @param req
|
||||
* @param queryConfig
|
||||
*/
|
||||
function attachListOrRetrieveConfig<TEntity extends BaseEntity>(
|
||||
req: Request,
|
||||
queryConfig: QueryConfig<TEntity> = {}
|
||||
) {
|
||||
const validated = req.validatedQuery
|
||||
const config = queryConfig.isList
|
||||
? prepareListQuery(validated, queryConfig)
|
||||
: prepareRetrieveQuery(validated, queryConfig)
|
||||
|
||||
req.listConfig = ("listConfig" in config &&
|
||||
config.listConfig) as FindConfig<any>
|
||||
req.retrieveConfig = ("retrieveConfig" in config &&
|
||||
config.retrieveConfig) as FindConfig<any>
|
||||
req.remoteQueryConfig = config.remoteQueryConfig
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
// Since typeorm require us to use ES6 and that migrating require a lot of work
|
||||
// one solution is to override directly the one from typeorm so that there is no complain about
|
||||
// the output build
|
||||
import { DefaultNamingStrategy } from "typeorm"
|
||||
|
||||
DefaultNamingStrategy.prototype.eagerJoinRelationAlias = function (
|
||||
alias: string,
|
||||
propertyPath: string
|
||||
): string {
|
||||
const path = propertyPath
|
||||
.split(".")
|
||||
.map((p) => p.substring(0, 2))
|
||||
.join("_")
|
||||
const out = alias + "_" + path
|
||||
const match = out.match(/_/g) || []
|
||||
return out + match.length
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
import { FindOptionsWhere } from "typeorm"
|
||||
import { ProductCategory } from "../../models"
|
||||
import { isDefined } from "medusa-core-utils"
|
||||
|
||||
export const categoryMatchesScope = (
|
||||
category: ProductCategory,
|
||||
query: FindOptionsWhere<ProductCategory>
|
||||
): boolean => {
|
||||
return Object.keys(query ?? {}).every(key => category[key] === query[key])
|
||||
}
|
||||
|
||||
export const fetchCategoryDescendantsIds = (
|
||||
productCategory: ProductCategory,
|
||||
query: FindOptionsWhere<ProductCategory>
|
||||
) => {
|
||||
let result = [productCategory.id]
|
||||
|
||||
;(productCategory.category_children || []).forEach((child) => {
|
||||
if (categoryMatchesScope(child, query)) {
|
||||
result = result.concat(fetchCategoryDescendantsIds(child, query))
|
||||
}
|
||||
})
|
||||
|
||||
return result
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
export * from "./products"
|
||||
@@ -1,33 +0,0 @@
|
||||
import { MedusaContainer, ProductVariantDTO } from "@medusajs/types"
|
||||
|
||||
export async function getVariantsFromPriceList(
|
||||
container: MedusaContainer,
|
||||
priceListId: string
|
||||
) {
|
||||
const remoteQuery = container.resolve("remoteQuery")
|
||||
const query = {
|
||||
price_list: {
|
||||
__args: { id: [priceListId] },
|
||||
prices: {
|
||||
price_set: {
|
||||
variant_link: { variant: { fields: ["id", "product_id"] } },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const priceLists = await remoteQuery(query)
|
||||
const variants: ProductVariantDTO[] = []
|
||||
|
||||
priceLists.forEach((priceList) => {
|
||||
priceList.prices?.forEach((price) => {
|
||||
const variant = price.price_set?.variant_link?.variant
|
||||
|
||||
if (variant) {
|
||||
variants.push(variant)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
return variants
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
export * from "./get-variants-from-price-list"
|
||||
export * from "./list-products"
|
||||
export * from "./retrieve-product"
|
||||
@@ -1,235 +0,0 @@
|
||||
import { MedusaContainer } from "@medusajs/types"
|
||||
import { MedusaV2Flag, promiseAll } from "@medusajs/utils"
|
||||
|
||||
import { PriceListService } from "../../../services"
|
||||
import { getVariantsFromPriceList } from "./get-variants-from-price-list"
|
||||
|
||||
export async function listProducts(
|
||||
container: MedusaContainer,
|
||||
filterableFields,
|
||||
listConfig
|
||||
) {
|
||||
// TODO: Add support for fields/expands
|
||||
|
||||
const remoteQuery = container.resolve("remoteQuery")
|
||||
const featureFlagRouter = container.resolve("featureFlagRouter")
|
||||
|
||||
const productIdsFilter: Set<string> = new Set()
|
||||
const variantIdsFilter: Set<string> = new Set()
|
||||
|
||||
const promises: Promise<void>[] = []
|
||||
|
||||
// This is not the best way of handling cross filtering but for now I would say it is fine
|
||||
const salesChannelIdFilter = filterableFields.sales_channel_id
|
||||
delete filterableFields.sales_channel_id
|
||||
|
||||
const priceListId = filterableFields.price_list_id
|
||||
delete filterableFields.price_list_id
|
||||
|
||||
if (priceListId) {
|
||||
if (featureFlagRouter.isFeatureEnabled(MedusaV2Flag.key)) {
|
||||
const variants = await getVariantsFromPriceList(container, priceListId)
|
||||
|
||||
variants.forEach((pv) => variantIdsFilter.add(pv.id))
|
||||
} else {
|
||||
// TODO: it is working but validate the behaviour.
|
||||
// e.g pricing context properly set.
|
||||
// At the moment filtering by price list but not having any customer id or
|
||||
// include discount forces the query to filter with price list id is null
|
||||
const priceListService = container.resolve(
|
||||
"priceListService"
|
||||
) as PriceListService
|
||||
|
||||
promises.push(
|
||||
priceListService
|
||||
.listPriceListsVariantIdsMap(priceListId)
|
||||
.then((priceListVariantIdsMap) => {
|
||||
priceListVariantIdsMap[priceListId].map((variantId) =>
|
||||
variantIdsFilter.add(variantId)
|
||||
)
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const discountConditionId = filterableFields.discount_condition_id
|
||||
delete filterableFields.discount_condition_id
|
||||
|
||||
if (discountConditionId) {
|
||||
// TODO implement later
|
||||
}
|
||||
|
||||
await promiseAll(promises)
|
||||
|
||||
if (productIdsFilter.size > 0) {
|
||||
filterableFields.id = Array.from(productIdsFilter)
|
||||
}
|
||||
|
||||
if (variantIdsFilter.size > 0) {
|
||||
filterableFields.variants = { id: Array.from(variantIdsFilter) }
|
||||
}
|
||||
|
||||
const variables = {
|
||||
filters: filterableFields,
|
||||
order: listConfig.order,
|
||||
skip: listConfig.skip,
|
||||
take: listConfig.take,
|
||||
}
|
||||
|
||||
const query = {
|
||||
product: {
|
||||
__args: variables,
|
||||
...defaultAdminProductRemoteQueryObject,
|
||||
},
|
||||
}
|
||||
|
||||
if (salesChannelIdFilter) {
|
||||
query.product["sales_channels"]["__args"] = { id: salesChannelIdFilter }
|
||||
}
|
||||
|
||||
const {
|
||||
rows: products,
|
||||
metadata: { count },
|
||||
} = await remoteQuery(query)
|
||||
|
||||
products.forEach((product) => {
|
||||
product.profile_id = product.profile?.id
|
||||
})
|
||||
|
||||
return [products, count]
|
||||
}
|
||||
|
||||
export const defaultAdminProductRemoteQueryObject = {
|
||||
fields: [
|
||||
"id",
|
||||
"title",
|
||||
"subtitle",
|
||||
"status",
|
||||
"external_id",
|
||||
"description",
|
||||
"handle",
|
||||
"is_giftcard",
|
||||
"discountable",
|
||||
"thumbnail",
|
||||
"collection_id",
|
||||
"type_id",
|
||||
"weight",
|
||||
"length",
|
||||
"height",
|
||||
"width",
|
||||
"hs_code",
|
||||
"origin_country",
|
||||
"mid_code",
|
||||
"material",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"deleted_at",
|
||||
"metadata",
|
||||
],
|
||||
images: {
|
||||
fields: ["id", "created_at", "updated_at", "deleted_at", "url", "metadata"],
|
||||
},
|
||||
tags: {
|
||||
fields: ["id", "created_at", "updated_at", "deleted_at", "value"],
|
||||
},
|
||||
|
||||
type: {
|
||||
fields: ["id", "created_at", "updated_at", "deleted_at", "value"],
|
||||
},
|
||||
|
||||
collection: {
|
||||
fields: ["title", "handle", "id", "created_at", "updated_at", "deleted_at"],
|
||||
},
|
||||
|
||||
categories: {
|
||||
fields: [
|
||||
"id",
|
||||
"name",
|
||||
"description",
|
||||
"handle",
|
||||
"is_active",
|
||||
"is_internal",
|
||||
"parent_category_id",
|
||||
],
|
||||
},
|
||||
|
||||
options: {
|
||||
fields: [
|
||||
"id",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"deleted_at",
|
||||
"title",
|
||||
"product_id",
|
||||
"metadata",
|
||||
],
|
||||
values: {
|
||||
fields: [
|
||||
"id",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"deleted_at",
|
||||
"value",
|
||||
"option_id",
|
||||
"variant_id",
|
||||
"metadata",
|
||||
],
|
||||
},
|
||||
},
|
||||
|
||||
variants: {
|
||||
fields: [
|
||||
"id",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"deleted_at",
|
||||
"title",
|
||||
"product_id",
|
||||
"sku",
|
||||
"barcode",
|
||||
"ean",
|
||||
"upc",
|
||||
"variant_rank",
|
||||
"inventory_quantity",
|
||||
"allow_backorder",
|
||||
"manage_inventory",
|
||||
"hs_code",
|
||||
"origin_country",
|
||||
"mid_code",
|
||||
"material",
|
||||
"weight",
|
||||
"length",
|
||||
"height",
|
||||
"width",
|
||||
"metadata",
|
||||
],
|
||||
|
||||
options: {
|
||||
fields: [
|
||||
"id",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"deleted_at",
|
||||
"value",
|
||||
"option_id",
|
||||
"variant_id",
|
||||
"metadata",
|
||||
],
|
||||
},
|
||||
},
|
||||
profile: {
|
||||
fields: ["id", "created_at", "updated_at", "deleted_at", "name", "type"],
|
||||
},
|
||||
sales_channels: {
|
||||
fields: [
|
||||
"id",
|
||||
"name",
|
||||
"description",
|
||||
"is_disabled",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"deleted_at",
|
||||
"metadata",
|
||||
],
|
||||
},
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
import { MedusaError } from "@medusajs/utils"
|
||||
|
||||
export async function retrieveProduct(container, id, remoteQueryObject = {}) {
|
||||
// TODO: Add support for fields/expands
|
||||
const remoteQuery = container.resolve("remoteQuery")
|
||||
|
||||
const variables = { id }
|
||||
|
||||
const query = {
|
||||
product: {
|
||||
__args: variables,
|
||||
...remoteQueryObject,
|
||||
},
|
||||
}
|
||||
|
||||
const [product] = await remoteQuery(query)
|
||||
|
||||
if (!product) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_FOUND,
|
||||
`Product with id: ${id} not found`
|
||||
)
|
||||
}
|
||||
|
||||
product.profile_id = product.profile?.id
|
||||
|
||||
return product
|
||||
}
|
||||
Reference in New Issue
Block a user