fix: adds order by functionality to products (#1021)
* fix: adds order by functionality to products * feat: adds product tags list * fix: adds client and react support for product tags * fix: unit test * Update packages/medusa/src/services/product-tag.ts Co-authored-by: Oliver Windall Juhl <59018053+olivermrbl@users.noreply.github.com> * Update packages/medusa/src/services/product-tag.ts Co-authored-by: Oliver Windall Juhl <59018053+olivermrbl@users.noreply.github.com> * Update packages/medusa/src/services/product-tag.ts Co-authored-by: Oliver Windall Juhl <59018053+olivermrbl@users.noreply.github.com> * Update packages/medusa/src/services/product-tag.ts Co-authored-by: Oliver Windall Juhl <59018053+olivermrbl@users.noreply.github.com> * Update packages/medusa/src/services/product-tag.ts Co-authored-by: Oliver Windall Juhl <59018053+olivermrbl@users.noreply.github.com> * Update packages/medusa/src/services/product-tag.ts Co-authored-by: Oliver Windall Juhl <59018053+olivermrbl@users.noreply.github.com> Co-authored-by: Oliver Windall Juhl <59018053+olivermrbl@users.noreply.github.com>
This commit is contained in:
co-authored by
Oliver Windall Juhl
parent
a81227fa74
commit
3bf32e5dc9
@@ -37,6 +37,7 @@ export * from "./routes/admin/uploads"
|
||||
export * from "./routes/admin/returns"
|
||||
export * from "./routes/admin/shipping-options"
|
||||
export * from "./routes/admin/regions"
|
||||
export * from "./routes/admin/product-tags"
|
||||
|
||||
// Store
|
||||
export * from "./routes/store/auth"
|
||||
|
||||
@@ -22,6 +22,7 @@ import returnRoutes from "./returns"
|
||||
import variantRoutes from "./variants"
|
||||
import draftOrderRoutes from "./draft-orders"
|
||||
import collectionRoutes from "./collections"
|
||||
import productTagRoutes from "./product-tags"
|
||||
import notificationRoutes from "./notifications"
|
||||
import noteRoutes from "./notes"
|
||||
|
||||
@@ -76,6 +77,7 @@ export default (app, container, config) => {
|
||||
collectionRoutes(route)
|
||||
notificationRoutes(route)
|
||||
returnReasonRoutes(route)
|
||||
productTagRoutes(route)
|
||||
noteRoutes(route)
|
||||
inviteRoutes(route)
|
||||
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
import { Router } from "express"
|
||||
import { ProductTag } from "../../../.."
|
||||
import { PaginatedResponse } from "../../../../types/common"
|
||||
import middlewares from "../../../middlewares"
|
||||
import "reflect-metadata"
|
||||
|
||||
const route = Router()
|
||||
|
||||
export default (app) => {
|
||||
app.use("/product-tags", route)
|
||||
|
||||
route.get("/", middlewares.wrap(require("./list-product-tags").default))
|
||||
|
||||
return app
|
||||
}
|
||||
|
||||
export const allowedAdminProductTagsFields = [
|
||||
"id",
|
||||
"value",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
]
|
||||
|
||||
export const defaultAdminProductTagsFields = [
|
||||
"id",
|
||||
"value",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
]
|
||||
export const defaultAdminProductTagsRelations = []
|
||||
|
||||
export type AdminProductTagsListRes = PaginatedResponse & {
|
||||
product_tags: ProductTag[]
|
||||
}
|
||||
|
||||
export type AdminProductTagsRes = {
|
||||
product_tag: ProductTag
|
||||
}
|
||||
|
||||
export * from "./list-product-tags"
|
||||
@@ -0,0 +1,124 @@
|
||||
import { Type } from "class-transformer"
|
||||
import { MedusaError } from "medusa-core-utils"
|
||||
import { IsNumber, IsString, IsOptional, ValidateNested } from "class-validator"
|
||||
import { omit, pickBy, identity } from "lodash"
|
||||
import {
|
||||
allowedAdminProductTagsFields,
|
||||
defaultAdminProductTagsFields,
|
||||
defaultAdminProductTagsRelations,
|
||||
} from "."
|
||||
import { ProductTag } from "../../../../models/product-tag"
|
||||
import ProductTagService from "../../../../services/product-tag"
|
||||
import {
|
||||
StringComparisonOperator,
|
||||
DateComparisonOperator,
|
||||
FindConfig,
|
||||
} from "../../../../types/common"
|
||||
import { validator } from "../../../../utils/validator"
|
||||
import { IsType } from "../../../../utils/validators/is-type"
|
||||
|
||||
/**
|
||||
* @oas [get] /product-tags
|
||||
* operationId: "GetProductTags"
|
||||
* summary: "List Product Tags"
|
||||
* description: "Retrieve a list of Product Tags."
|
||||
* x-authenticated: true
|
||||
* parameters:
|
||||
* - (query) limit {string} The number of tags to return.
|
||||
* - (query) offset {string} The offset of tags to return.
|
||||
* - (query) value {string} The value of tags to return.
|
||||
* - (query) id {string} The id of tags to return.
|
||||
* - (query) created_at {DateComparisonOperator} Date comparison for when resulting tas was created, i.e. less than, greater than etc.
|
||||
* - (query) updated_at {DateComparisonOperator} Date comparison for when resulting tas was updated, i.e. less than, greater than etc.
|
||||
* tags:
|
||||
* - Product Tag
|
||||
* responses:
|
||||
* "200":
|
||||
* description: OK
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* tags:
|
||||
* $ref: "#/components/schemas/product_tag"
|
||||
*/
|
||||
export default async (req, res) => {
|
||||
const validated = await validator(AdminGetProductTagsParams, req.query)
|
||||
|
||||
const tagService: ProductTagService = req.scope.resolve("productTagService")
|
||||
|
||||
const listConfig: FindConfig<ProductTag> = {
|
||||
select: defaultAdminProductTagsFields as (keyof ProductTag)[],
|
||||
relations: defaultAdminProductTagsRelations,
|
||||
skip: validated.offset,
|
||||
take: validated.limit,
|
||||
}
|
||||
|
||||
if (typeof validated.order !== "undefined") {
|
||||
let orderField = validated.order
|
||||
if (validated.order.startsWith("-")) {
|
||||
const [, field] = validated.order.split("-")
|
||||
orderField = field
|
||||
listConfig.order = { [field]: "DESC" }
|
||||
} else {
|
||||
listConfig.order = { [validated.order]: "ASC" }
|
||||
}
|
||||
|
||||
if (!allowedAdminProductTagsFields.includes(orderField)) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
"Order field must be a valid product tag field"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const filterableFields = omit(validated, ["limit", "offset"])
|
||||
|
||||
const [tags, count] = await tagService.listAndCount(
|
||||
pickBy(filterableFields, identity),
|
||||
listConfig
|
||||
)
|
||||
|
||||
res.status(200).json({
|
||||
product_tags: tags,
|
||||
count,
|
||||
offset: validated.offset,
|
||||
limit: validated.limit,
|
||||
})
|
||||
}
|
||||
|
||||
export class AdminGetProductTagsPaginationParams {
|
||||
@IsNumber()
|
||||
@IsOptional()
|
||||
@Type(() => Number)
|
||||
limit = 10
|
||||
|
||||
@IsNumber()
|
||||
@IsOptional()
|
||||
@Type(() => Number)
|
||||
offset = 0
|
||||
}
|
||||
|
||||
export class AdminGetProductTagsParams extends AdminGetProductTagsPaginationParams {
|
||||
@ValidateNested()
|
||||
@IsType([String, [String], StringComparisonOperator])
|
||||
@IsOptional()
|
||||
id?: string | string[] | StringComparisonOperator
|
||||
|
||||
@ValidateNested()
|
||||
@IsType([String, [String], StringComparisonOperator])
|
||||
@IsOptional()
|
||||
value?: string | string[] | StringComparisonOperator
|
||||
|
||||
@IsType([DateComparisonOperator])
|
||||
@IsOptional()
|
||||
created_at?: DateComparisonOperator
|
||||
|
||||
@IsType([DateComparisonOperator])
|
||||
@IsOptional()
|
||||
updated_at?: DateComparisonOperator
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
order?: string
|
||||
}
|
||||
@@ -8,11 +8,16 @@ import {
|
||||
IsString,
|
||||
ValidateNested,
|
||||
} from "class-validator"
|
||||
import * as _ from "lodash"
|
||||
import { identity } from "lodash"
|
||||
import { defaultAdminProductFields, defaultAdminProductRelations } from "."
|
||||
import { pickBy, omit } from "lodash"
|
||||
import { MedusaError } from "medusa-core-utils"
|
||||
import { Product } from "../../../../models/product"
|
||||
import {
|
||||
allowedAdminProductFields,
|
||||
defaultAdminProductFields,
|
||||
defaultAdminProductRelations,
|
||||
} from "."
|
||||
import { ProductService } from "../../../../services"
|
||||
import { DateComparisonOperator } from "../../../../types/common"
|
||||
import { FindConfig, DateComparisonOperator } from "../../../../types/common"
|
||||
import { validator } from "../../../../utils/validator"
|
||||
|
||||
/**
|
||||
@@ -78,8 +83,10 @@ export default async (req, res) => {
|
||||
expandFields = validatedParams.expand!.split(",")
|
||||
}
|
||||
|
||||
const listConfig = {
|
||||
select: includeFields.length ? includeFields : defaultAdminProductFields,
|
||||
const listConfig: FindConfig<Product> = {
|
||||
select: (includeFields.length
|
||||
? includeFields
|
||||
: defaultAdminProductFields) as (keyof Product)[],
|
||||
relations: expandFields.length
|
||||
? expandFields
|
||||
: defaultAdminProductRelations,
|
||||
@@ -87,7 +94,25 @@ export default async (req, res) => {
|
||||
take: validatedParams.limit,
|
||||
}
|
||||
|
||||
const filterableFields = _.omit(validatedParams, [
|
||||
if (typeof validatedParams.order !== "undefined") {
|
||||
let orderField = validatedParams.order
|
||||
if (validatedParams.order.startsWith("-")) {
|
||||
const [, field] = validatedParams.order.split("-")
|
||||
orderField = field
|
||||
listConfig.order = { [field]: "DESC" }
|
||||
} else {
|
||||
listConfig.order = { [validatedParams.order]: "ASC" }
|
||||
}
|
||||
|
||||
if (!allowedAdminProductFields.includes(orderField)) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
"Order field must be a valid product field"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const filterableFields = omit(validatedParams, [
|
||||
"limit",
|
||||
"offset",
|
||||
"expand",
|
||||
@@ -96,7 +121,7 @@ export default async (req, res) => {
|
||||
])
|
||||
|
||||
const [products, count] = await productService.listAndCount(
|
||||
_.pickBy(filterableFields, (val) => typeof val !== "undefined"),
|
||||
pickBy(filterableFields, (val) => typeof val !== "undefined"),
|
||||
listConfig
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user