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
@@ -0,0 +1,24 @@
|
|||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`/admin/product-tags GET /admin/product-tags returns a list of product tags 1`] = `
|
||||||
|
Array [
|
||||||
|
Object {
|
||||||
|
"created_at": Any<String>,
|
||||||
|
"id": "tag1",
|
||||||
|
"updated_at": Any<String>,
|
||||||
|
"value": "123",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"created_at": Any<String>,
|
||||||
|
"id": "tag3",
|
||||||
|
"updated_at": Any<String>,
|
||||||
|
"value": "123",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"created_at": Any<String>,
|
||||||
|
"id": "tag4",
|
||||||
|
"updated_at": Any<String>,
|
||||||
|
"value": "123",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
const path = require("path")
|
||||||
|
|
||||||
|
const setupServer = require("../../../helpers/setup-server")
|
||||||
|
const { useApi } = require("../../../helpers/use-api")
|
||||||
|
const { initDb, useDb } = require("../../../helpers/use-db")
|
||||||
|
|
||||||
|
const adminSeeder = require("../../helpers/admin-seeder")
|
||||||
|
const productSeeder = require("../../helpers/product-seeder")
|
||||||
|
|
||||||
|
jest.setTimeout(50000)
|
||||||
|
|
||||||
|
describe("/admin/product-tags", () => {
|
||||||
|
let medusaProcess
|
||||||
|
let dbConnection
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
const cwd = path.resolve(path.join(__dirname, "..", ".."))
|
||||||
|
dbConnection = await initDb({ cwd })
|
||||||
|
medusaProcess = await setupServer({ cwd })
|
||||||
|
})
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
const db = useDb()
|
||||||
|
await db.shutdown()
|
||||||
|
|
||||||
|
medusaProcess.kill()
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("GET /admin/product-tags", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
try {
|
||||||
|
await productSeeder(dbConnection)
|
||||||
|
await adminSeeder(dbConnection)
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err)
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
const db = useDb()
|
||||||
|
await db.teardown()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("returns a list of product tags", async () => {
|
||||||
|
const api = useApi()
|
||||||
|
|
||||||
|
const res = await api
|
||||||
|
.get("/admin/product-tags", {
|
||||||
|
headers: {
|
||||||
|
Authorization: "Bearer test_token",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.log(err)
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(res.status).toEqual(200)
|
||||||
|
|
||||||
|
const tagMatch = {
|
||||||
|
created_at: expect.any(String),
|
||||||
|
updated_at: expect.any(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(res.data.product_tags).toMatchSnapshot([
|
||||||
|
tagMatch,
|
||||||
|
tagMatch,
|
||||||
|
tagMatch,
|
||||||
|
])
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -20,6 +20,7 @@ import AdminShippingOptionsResource from "./shipping-options"
|
|||||||
import AdminRegionsResource from "./regions"
|
import AdminRegionsResource from "./regions"
|
||||||
import AdminNotificationsResource from "./notifications"
|
import AdminNotificationsResource from "./notifications"
|
||||||
import AdminUploadsResource from "./uploads"
|
import AdminUploadsResource from "./uploads"
|
||||||
|
import AdminProductTagsResource from "./product-tags"
|
||||||
|
|
||||||
class Admin extends BaseResource {
|
class Admin extends BaseResource {
|
||||||
public auth = new AdminAuthResource(this.client)
|
public auth = new AdminAuthResource(this.client)
|
||||||
@@ -31,6 +32,7 @@ class Admin extends BaseResource {
|
|||||||
public invites = new AdminInvitesResource(this.client)
|
public invites = new AdminInvitesResource(this.client)
|
||||||
public notes = new AdminNotesResource(this.client)
|
public notes = new AdminNotesResource(this.client)
|
||||||
public products = new AdminProductsResource(this.client)
|
public products = new AdminProductsResource(this.client)
|
||||||
|
public productTags = new AdminProductTagsResource(this.client)
|
||||||
public users = new AdminUsersResource(this.client)
|
public users = new AdminUsersResource(this.client)
|
||||||
public returns = new AdminReturnsResource(this.client)
|
public returns = new AdminReturnsResource(this.client)
|
||||||
public orders = new AdminOrdersResource(this.client)
|
public orders = new AdminOrdersResource(this.client)
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import {
|
||||||
|
AdminGetProductTagsParams,
|
||||||
|
AdminProductTagsListRes,
|
||||||
|
} from "@medusajs/medusa"
|
||||||
|
import qs from "qs"
|
||||||
|
import { ResponsePromise } from "../../typings"
|
||||||
|
import BaseResource from "../base"
|
||||||
|
|
||||||
|
class AdminProductTagsResource extends BaseResource {
|
||||||
|
list(
|
||||||
|
query?: AdminGetProductTagsParams
|
||||||
|
): ResponsePromise<AdminProductTagsListRes> {
|
||||||
|
let path = `/admin/product-tags`
|
||||||
|
|
||||||
|
if (query) {
|
||||||
|
const queryString = qs.stringify(query)
|
||||||
|
path = `/admin/product-tags?${queryString}`
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.client.request("GET", path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AdminProductTagsResource
|
||||||
@@ -7,6 +7,7 @@ export * from "./draft-orders"
|
|||||||
export * from "./gift-cards"
|
export * from "./gift-cards"
|
||||||
export * from "./orders"
|
export * from "./orders"
|
||||||
export * from "./products"
|
export * from "./products"
|
||||||
|
export * from "./product-tags"
|
||||||
export * from "./return-reasons"
|
export * from "./return-reasons"
|
||||||
export * from "./regions"
|
export * from "./regions"
|
||||||
export * from "./shipping-options"
|
export * from "./shipping-options"
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from "./queries"
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
import {
|
||||||
|
AdminProductTagsListRes,
|
||||||
|
AdminGetProductTagsParams,
|
||||||
|
} from "@medusajs/medusa"
|
||||||
|
import { Response } from "@medusajs/medusa-js"
|
||||||
|
import { useQuery } from "react-query"
|
||||||
|
import { useMedusa } from "../../../contexts"
|
||||||
|
import { UseQueryOptionsWrapper } from "../../../types"
|
||||||
|
import { queryKeysFactory } from "../../utils/index"
|
||||||
|
|
||||||
|
const ADMIN_PRODUCT_TAGS_QUERY_KEY = `admin_product_tags` as const
|
||||||
|
|
||||||
|
export const adminProductTagKeys = queryKeysFactory(
|
||||||
|
ADMIN_PRODUCT_TAGS_QUERY_KEY
|
||||||
|
)
|
||||||
|
|
||||||
|
type ProductQueryKeys = typeof adminProductTagKeys
|
||||||
|
|
||||||
|
export const useAdminProductTags = (
|
||||||
|
query?: AdminGetProductTagsParams,
|
||||||
|
options?: UseQueryOptionsWrapper<
|
||||||
|
Response<AdminProductTagsListRes>,
|
||||||
|
Error,
|
||||||
|
ReturnType<ProductQueryKeys["list"]>
|
||||||
|
>
|
||||||
|
) => {
|
||||||
|
const { client } = useMedusa()
|
||||||
|
const { data, ...rest } = useQuery(
|
||||||
|
adminProductTagKeys.list(query),
|
||||||
|
() => client.admin.productTags.list(query),
|
||||||
|
options
|
||||||
|
)
|
||||||
|
return { ...data, ...rest } as const
|
||||||
|
}
|
||||||
@@ -67,7 +67,7 @@ export const useAdminProductTypes = (
|
|||||||
return { ...data, ...rest } as const
|
return { ...data, ...rest } as const
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useAdminProductTags = (
|
export const useAdminProductTagUsage = (
|
||||||
options?: UseQueryOptionsWrapper<
|
options?: UseQueryOptionsWrapper<
|
||||||
Response<AdminProductsListTagsRes>,
|
Response<AdminProductsListTagsRes>,
|
||||||
Error,
|
Error,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import {
|
import {
|
||||||
useAdminProduct,
|
useAdminProduct,
|
||||||
useAdminProducts,
|
useAdminProducts,
|
||||||
useAdminProductTags,
|
useAdminProductTagUsage,
|
||||||
useAdminProductTypes,
|
useAdminProductTypes,
|
||||||
} from "../../../../src"
|
} from "../../../../src"
|
||||||
import { renderHook } from "@testing-library/react-hooks"
|
import { renderHook } from "@testing-library/react-hooks"
|
||||||
@@ -36,10 +36,10 @@ describe("useAdminProductTypes hook", () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("useAdminProductTags hook", () => {
|
describe("useAdminProductTagUsage hook", () => {
|
||||||
test("returns a list of product tags", async () => {
|
test("returns a list of product tags", async () => {
|
||||||
const tags = fixtures.list("product_tag")
|
const tags = fixtures.list("product_tag")
|
||||||
const { result, waitFor } = renderHook(() => useAdminProductTags(), {
|
const { result, waitFor } = renderHook(() => useAdminProductTagUsage(), {
|
||||||
wrapper: createWrapper(),
|
wrapper: createWrapper(),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ export * from "./routes/admin/uploads"
|
|||||||
export * from "./routes/admin/returns"
|
export * from "./routes/admin/returns"
|
||||||
export * from "./routes/admin/shipping-options"
|
export * from "./routes/admin/shipping-options"
|
||||||
export * from "./routes/admin/regions"
|
export * from "./routes/admin/regions"
|
||||||
|
export * from "./routes/admin/product-tags"
|
||||||
|
|
||||||
// Store
|
// Store
|
||||||
export * from "./routes/store/auth"
|
export * from "./routes/store/auth"
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import returnRoutes from "./returns"
|
|||||||
import variantRoutes from "./variants"
|
import variantRoutes from "./variants"
|
||||||
import draftOrderRoutes from "./draft-orders"
|
import draftOrderRoutes from "./draft-orders"
|
||||||
import collectionRoutes from "./collections"
|
import collectionRoutes from "./collections"
|
||||||
|
import productTagRoutes from "./product-tags"
|
||||||
import notificationRoutes from "./notifications"
|
import notificationRoutes from "./notifications"
|
||||||
import noteRoutes from "./notes"
|
import noteRoutes from "./notes"
|
||||||
|
|
||||||
@@ -76,6 +77,7 @@ export default (app, container, config) => {
|
|||||||
collectionRoutes(route)
|
collectionRoutes(route)
|
||||||
notificationRoutes(route)
|
notificationRoutes(route)
|
||||||
returnReasonRoutes(route)
|
returnReasonRoutes(route)
|
||||||
|
productTagRoutes(route)
|
||||||
noteRoutes(route)
|
noteRoutes(route)
|
||||||
inviteRoutes(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,
|
IsString,
|
||||||
ValidateNested,
|
ValidateNested,
|
||||||
} from "class-validator"
|
} from "class-validator"
|
||||||
import * as _ from "lodash"
|
import { pickBy, omit } from "lodash"
|
||||||
import { identity } from "lodash"
|
import { MedusaError } from "medusa-core-utils"
|
||||||
import { defaultAdminProductFields, defaultAdminProductRelations } from "."
|
import { Product } from "../../../../models/product"
|
||||||
|
import {
|
||||||
|
allowedAdminProductFields,
|
||||||
|
defaultAdminProductFields,
|
||||||
|
defaultAdminProductRelations,
|
||||||
|
} from "."
|
||||||
import { ProductService } from "../../../../services"
|
import { ProductService } from "../../../../services"
|
||||||
import { DateComparisonOperator } from "../../../../types/common"
|
import { FindConfig, DateComparisonOperator } from "../../../../types/common"
|
||||||
import { validator } from "../../../../utils/validator"
|
import { validator } from "../../../../utils/validator"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -78,8 +83,10 @@ export default async (req, res) => {
|
|||||||
expandFields = validatedParams.expand!.split(",")
|
expandFields = validatedParams.expand!.split(",")
|
||||||
}
|
}
|
||||||
|
|
||||||
const listConfig = {
|
const listConfig: FindConfig<Product> = {
|
||||||
select: includeFields.length ? includeFields : defaultAdminProductFields,
|
select: (includeFields.length
|
||||||
|
? includeFields
|
||||||
|
: defaultAdminProductFields) as (keyof Product)[],
|
||||||
relations: expandFields.length
|
relations: expandFields.length
|
||||||
? expandFields
|
? expandFields
|
||||||
: defaultAdminProductRelations,
|
: defaultAdminProductRelations,
|
||||||
@@ -87,7 +94,25 @@ export default async (req, res) => {
|
|||||||
take: validatedParams.limit,
|
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",
|
"limit",
|
||||||
"offset",
|
"offset",
|
||||||
"expand",
|
"expand",
|
||||||
@@ -96,7 +121,7 @@ export default async (req, res) => {
|
|||||||
])
|
])
|
||||||
|
|
||||||
const [products, count] = await productService.listAndCount(
|
const [products, count] = await productService.listAndCount(
|
||||||
_.pickBy(filterableFields, (val) => typeof val !== "undefined"),
|
pickBy(filterableFields, (val) => typeof val !== "undefined"),
|
||||||
listConfig
|
listConfig
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,115 @@
|
|||||||
|
import { EntityManager } from "typeorm"
|
||||||
|
import { BaseService } from "medusa-interfaces"
|
||||||
|
import { MedusaError } from "medusa-core-utils"
|
||||||
|
import { ProductTagRepository } from "../repositories/product-tag"
|
||||||
|
import { ProductTag } from "../models/product-tag"
|
||||||
|
import { FindConfig } from "../types/common"
|
||||||
|
import { FilterableProductTagProps } from "../types/product"
|
||||||
|
|
||||||
|
type ProductTagConstructorProps = {
|
||||||
|
manager: EntityManager
|
||||||
|
productTagRepository: typeof ProductTagRepository
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides layer to manipulate product tags.
|
||||||
|
* @extends BaseService
|
||||||
|
*/
|
||||||
|
class ProductTagService extends BaseService {
|
||||||
|
private manager_: EntityManager
|
||||||
|
private tagRepo_: typeof ProductTagRepository
|
||||||
|
|
||||||
|
constructor({ manager, productTagRepository }: ProductTagConstructorProps) {
|
||||||
|
super()
|
||||||
|
this.manager_ = manager
|
||||||
|
this.tagRepo_ = productTagRepository
|
||||||
|
}
|
||||||
|
|
||||||
|
withTransaction(transactionManager: EntityManager): ProductTagService {
|
||||||
|
if (!transactionManager) {
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
const cloned = new ProductTagService({
|
||||||
|
manager: transactionManager,
|
||||||
|
productTagRepository: this.tagRepo_,
|
||||||
|
})
|
||||||
|
|
||||||
|
cloned.transactionManager_ = transactionManager
|
||||||
|
|
||||||
|
return cloned
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a product tag by id.
|
||||||
|
* @param {string} tagId - the id of the product tag to retrieve
|
||||||
|
* @param {Object} config - the config to retrieve the tag by
|
||||||
|
* @return {Promise<ProductTag>} the collection.
|
||||||
|
*/
|
||||||
|
async retrieve(
|
||||||
|
tagId: string,
|
||||||
|
config: FindConfig<ProductTag> = {}
|
||||||
|
): Promise<ProductTag> {
|
||||||
|
const tagRepo = this.manager_.getCustomRepository(this.tagRepo_)
|
||||||
|
|
||||||
|
const query = this.buildQuery_({ id: tagId }, config)
|
||||||
|
const tag = await tagRepo.findOne(query)
|
||||||
|
|
||||||
|
if (!tag) {
|
||||||
|
throw new MedusaError(
|
||||||
|
MedusaError.Types.NOT_FOUND,
|
||||||
|
`Product tag with id: ${tagId} was not found`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return tag
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a product tag
|
||||||
|
* @param {object} tag - the product tag to create
|
||||||
|
* @return {Promise<ProductTag>} created product tag
|
||||||
|
*/
|
||||||
|
async create(tag: Partial<ProductTag>): Promise<ProductTag> {
|
||||||
|
return await this.atomicPhase_(async (manager: EntityManager) => {
|
||||||
|
const tagRepo = manager.getCustomRepository(this.tagRepo_)
|
||||||
|
|
||||||
|
const productTag = tagRepo.create(tag)
|
||||||
|
return await tagRepo.save(productTag)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lists product tags
|
||||||
|
* @param {Object} selector - the query object for find
|
||||||
|
* @param {Object} config - the config to be used for find
|
||||||
|
* @return {Promise} the result of the find operation
|
||||||
|
*/
|
||||||
|
async list(
|
||||||
|
selector: FilterableProductTagProps = {},
|
||||||
|
config: FindConfig<ProductTag> = { skip: 0, take: 20 }
|
||||||
|
): Promise<ProductTag[]> {
|
||||||
|
const tagRepo = this.manager_.getCustomRepository(this.tagRepo_)
|
||||||
|
|
||||||
|
const query = this.buildQuery_(selector, config)
|
||||||
|
return await tagRepo.find(query)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lists product tags and adds count.
|
||||||
|
* @param {Object} selector - the query object for find
|
||||||
|
* @param {Object} config - the config to be used for find
|
||||||
|
* @return {Promise} the result of the find operation
|
||||||
|
*/
|
||||||
|
async listAndCount(
|
||||||
|
selector: FilterableProductTagProps = {},
|
||||||
|
config: FindConfig<ProductTag> = { skip: 0, take: 20 }
|
||||||
|
): Promise<[ProductTag[], number]> {
|
||||||
|
const tagRepo = this.manager_.getCustomRepository(this.tagRepo_)
|
||||||
|
|
||||||
|
const query = this.buildQuery_(selector, config)
|
||||||
|
return await tagRepo.findAndCount(query)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ProductTagService
|
||||||
@@ -23,7 +23,7 @@ export interface FindConfig<Entity> {
|
|||||||
skip?: number
|
skip?: number
|
||||||
take?: number
|
take?: number
|
||||||
relations?: string[]
|
relations?: string[]
|
||||||
order?: "ASC" | "DESC"
|
order?: { [k: string]: "ASC" | "DESC" }
|
||||||
}
|
}
|
||||||
|
|
||||||
export type PaginatedResponse = { limit: number; offset: number; count: number }
|
export type PaginatedResponse = { limit: number; offset: number; count: number }
|
||||||
|
|||||||
@@ -1,6 +1,26 @@
|
|||||||
|
import { ValidateNested } from "class-validator"
|
||||||
|
import { IsType } from "../utils/validators/is-type"
|
||||||
|
import { DateComparisonOperator, StringComparisonOperator } from "./common"
|
||||||
|
|
||||||
export enum ProductStatus {
|
export enum ProductStatus {
|
||||||
DRAFT = "draft",
|
DRAFT = "draft",
|
||||||
PROPOSED = "proposed",
|
PROPOSED = "proposed",
|
||||||
PUBLISHED = "published",
|
PUBLISHED = "published",
|
||||||
REJECTED = "rejected",
|
REJECTED = "rejected",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class FilterableProductTagProps {
|
||||||
|
@ValidateNested()
|
||||||
|
@IsType([String, [String], StringComparisonOperator])
|
||||||
|
id?: string | string[] | StringComparisonOperator
|
||||||
|
|
||||||
|
@ValidateNested()
|
||||||
|
@IsType([String, [String], StringComparisonOperator])
|
||||||
|
value?: string | string[] | StringComparisonOperator
|
||||||
|
|
||||||
|
@IsType([DateComparisonOperator])
|
||||||
|
created_at?: DateComparisonOperator
|
||||||
|
|
||||||
|
@IsType([DateComparisonOperator])
|
||||||
|
updated_at?: DateComparisonOperator
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user