fix(medusa): Clean response data usage for admin and store fields/expand (#3323)

* fix(medusa): Clean response data usage for admin and store fields/expand

* cleanup

* Create mighty-ads-fold.md

* fix integration

* fix integration

* refactor transform query and cleanup

* fix missing re naming

* Update packages/medusa/src/api/middlewares/transform-query.ts

---------

Co-authored-by: Oliver Windall Juhl <59018053+olivermrbl@users.noreply.github.com>
This commit is contained in:
Adrien de PerettiandOliver Windall Juhl authored and GitHub committed 2023-02-28 09:48:08 +01:00
1 parent 0a02b70e59
commit cbbf3ca054
22 files changed
+287 -96

No files matched your search

+5
View File
@@ -0,0 +1,5 @@
---
"@medusajs/medusa": patch
---
fix(medusa): Clean response data usage for admin and store fields/expand
@@ -1615,6 +1615,16 @@ describe("/admin/orders", () => {
id: "test-billing-address", id: "test-billing-address",
first_name: "lebron", first_name: "lebron",
}), }),
shipping_total: expect.any(Number),
discount_total: expect.any(Number),
tax_total: expect.any(Number),
refunded_total: expect.any(Number),
total: expect.any(Number),
subtotal: expect.any(Number),
paid_total: expect.any(Number),
refundable_amount: expect.any(Number),
gift_card_total: expect.any(Number),
gift_card_tax_total: expect.any(Number),
}, },
]) ])
) )
@@ -177,6 +177,17 @@ describe("/store/carts", () => {
"customer", "customer",
"payments", "payments",
"region", "region",
// default
"shipping_total",
"discount_total",
"tax_total",
"refunded_total",
"total",
"subtotal",
"paid_total",
"refundable_amount",
"gift_card_total",
"gift_card_tax_total",
]) ])
}) })
@@ -197,6 +208,17 @@ describe("/store/carts", () => {
"customer", "customer",
"payments", "payments",
"region", "region",
// default
"shipping_total",
"discount_total",
"tax_total",
"refunded_total",
"total",
"subtotal",
"paid_total",
"refundable_amount",
"gift_card_total",
"gift_card_tax_total",
]) ])
}) })
@@ -212,6 +234,17 @@ describe("/store/carts", () => {
"status", "status",
// selected relations // selected relations
"billing_address", "billing_address",
// default
"shipping_total",
"discount_total",
"tax_total",
"refunded_total",
"total",
"subtotal",
"paid_total",
"refundable_amount",
"gift_card_total",
"gift_card_tax_total",
]) ])
}) })
+1 -1
View File
@@ -9,7 +9,7 @@ export { getRequestedBatchJob } from "./batch-job/get-requested-batch-job"
export { doesConditionBelongToDiscount } from "./discount/does-condition-belong-to-discount" export { doesConditionBelongToDiscount } from "./discount/does-condition-belong-to-discount"
export { transformIncludesOptions } from "./transform-includes-options" export { transformIncludesOptions } from "./transform-includes-options"
export { transformBody } from "./transform-body" export { transformBody } from "./transform-body"
export { transformQuery } from "./transform-query" export { transformQuery, transformStoreQuery } from "./transform-query"
export default { export default {
authenticate, authenticate,
@@ -13,11 +13,11 @@ export function transformIncludesOptions(
expectedIncludesFields: string[] = [] expectedIncludesFields: string[] = []
) { ) {
return (req: Request, res: Response, next: NextFunction): void => { return (req: Request, res: Response, next: NextFunction): void => {
if (!allowedIncludesFields.length || !req.query["fields"]) { if (!allowedIncludesFields.length || !req.query.fields) {
return next() return next()
} }
const fields = (req.query["fields"] as string).split(",") ?? [] const fields = (req.query.fields as string).split(",") ?? []
for (const includesField of allowedIncludesFields) { for (const includesField of allowedIncludesFields) {
const fieldIndex = fields.indexOf(includesField as keyof Order) ?? -1 const fieldIndex = fields.indexOf(includesField as keyof Order) ?? -1
@@ -40,16 +40,16 @@ export function transformIncludesOptions(
) )
} }
req["includes"] = req["includes"] ?? {} req.includes = req.includes ?? {}
req["includes"][includesField] = true req.includes[includesField] = true
} }
} }
if (req.query["fields"]) { if (req.query.fields) {
if (fields.length) { if (fields.length) {
req.query["fields"] = fields.join(",") req.query.fields = fields.join(",")
} else { } else {
delete req.query["fields"] delete req.query.fields
} }
} }
@@ -12,9 +12,56 @@ import { FindConfig, QueryConfig, RequestQueryFields } from "../../types/common"
import { omit } from "lodash" import { omit } from "lodash"
import { removeUndefinedProperties } from "../../utils" import { removeUndefinedProperties } from "../../utils"
/**
* Middleware that transform the query input for the admin end points
* @param plainToClass
* @param queryConfig
* @param config
*/
export function transformQuery< export function transformQuery<
T extends RequestQueryFields, T extends RequestQueryFields,
TEntity extends BaseEntity TEntity extends BaseEntity
>(
plainToClass: ClassConstructor<T>,
queryConfig?: Omit<
QueryConfig<TEntity>,
"allowedRelations" | "allowedFields"
>,
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)
req.allowedProperties = getAllowedProperties(
validated,
req.includes ?? {},
queryConfig
)
attachListOrRetrieveConfig<TEntity>(req, queryConfig)
next()
} catch (e) {
next(e)
}
}
}
/**
* Middleware that transform the query input for the store endpoints
* @param plainToClass
* @param queryConfig
* @param config
*/
export function transformStoreQuery<
T extends RequestQueryFields,
TEntity extends BaseEntity
>( >(
plainToClass: ClassConstructor<T>, plainToClass: ClassConstructor<T>,
queryConfig?: QueryConfig<TEntity>, queryConfig?: QueryConfig<TEntity>,
@@ -29,47 +76,13 @@ export function transformQuery<
config config
) )
req.validatedQuery = validated req.validatedQuery = validated
req.filterableFields = getFilterableFields(validated)
req.filterableFields = omit(validated, [ req.allowedProperties = getStoreAllowedProperties(
"limit", validated,
"offset", req.includes ?? {},
"expand", queryConfig
"fields", )
"order", attachListOrRetrieveConfig<TEntity>(req, queryConfig)
])
req.filterableFields = removeUndefinedProperties(req.filterableFields)
if (
(queryConfig?.defaultFields || validated.fields) &&
(queryConfig?.defaultRelations || validated.expand)
) {
req.allowedProperties = [
...(validated.fields
? validated.fields.split(",")
: queryConfig?.allowedFields || [])!,
...(validated.expand
? validated.expand.split(",")
: queryConfig?.allowedRelations || [])!,
] as unknown as string[]
}
const includesFields = Object.keys(req["includes"] ?? {})
if (includesFields.length) {
req.allowedProperties = req.allowedProperties ?? []
req.allowedProperties.push(...includesFields)
}
if (queryConfig?.isList) {
req.listConfig = prepareListQuery(
validated,
queryConfig
) as FindConfig<unknown>
} else {
req.retrieveConfig = prepareRetrieveQuery(
validated,
queryConfig
) as FindConfig<unknown>
}
next() next()
} catch (e) { } catch (e) {
@@ -77,3 +90,97 @@ export function transformQuery<
} }
} }
} }
/**
* 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",
"expand",
"fields",
"order",
]) as T
return removeUndefinedProperties(result)
}
/**
* build and attach the `retrieveConfig` or `listConfig`
* @param req
* @param queryConfig
*/
function attachListOrRetrieveConfig<TEntity extends BaseEntity>(
req: Request,
queryConfig?: QueryConfig<TEntity>
) {
const validated = req.validatedQuery
if (queryConfig?.isList) {
req.listConfig = prepareListQuery(
validated,
queryConfig
) as FindConfig<unknown>
} else {
req.retrieveConfig = prepareRetrieveQuery(
validated,
queryConfig
) as FindConfig<unknown>
}
}
/**
* Build the store allowed props based on the custom fields query params, the defaults and the includes options.
* This can be used later with `cleanResponseData` in order to clean up the returned objects to the client.
* @param queryConfig
* @param validated
* @param includesOptions
*/
function getStoreAllowedProperties<TEntity extends BaseEntity>(
validated: RequestQueryFields,
includesOptions: Record<string, boolean>,
queryConfig?: QueryConfig<TEntity>
): string[] {
const allowed: string[] = []
allowed.push(
...(validated.fields
? validated.fields.split(",")
: queryConfig?.allowedFields || []),
...(validated.expand
? validated.expand.split(",")
: queryConfig?.allowedRelations || [])
)
const includeKeys = Object.keys(includesOptions)
if (includeKeys) {
allowed.push(...includeKeys)
}
return allowed
}
/**
* Build the admin allowed props based on the custom fields query params, the defaults and the includes options.
* Since admin can access everything, it is only in order to return what the user asked for through fields and expand query params.
* This can be used later with `cleanResponseData` in order to clean up the returned objects to the client.
* @param queryConfig
* @param validated
* @param includesOptions
*/
function getAllowedProperties<TEntity extends BaseEntity>(
validated: RequestQueryFields,
includesOptions: Record<string, boolean>,
queryConfig?: QueryConfig<TEntity>
): string[] {
const allowed: string[] = []
allowed.push(
...(validated.fields?.split(",") ?? []),
...(validated.expand?.split(",") ?? [])
)
const includeKeys = Object.keys(includesOptions)
if (includeKeys) {
allowed.push(...includeKeys)
}
return allowed
}
@@ -1,5 +1,7 @@
import { OrderService } from "../../../../services" import { OrderService } from "../../../../services"
import { FindParams } from "../../../../types/common" import { FindParams } from "../../../../types/common"
import { cleanResponseData } from "../../../../utils/clean-response-data"
import { Order } from "../../../../models"
/** /**
* @oas [get] /admin/orders/{id} * @oas [get] /admin/orders/{id}
@@ -60,9 +62,15 @@ export default async (req, res) => {
const orderService: OrderService = req.scope.resolve("orderService") const orderService: OrderService = req.scope.resolve("orderService")
const order = await orderService.retrieveWithTotals(id, req.retrieveConfig, { let order: Partial<Order> = await orderService.retrieveWithTotals(
includes: req.includes, id,
}) req.retrieveConfig,
{
includes: req.includes,
}
)
order = cleanResponseData(order, req.allowedProperties)
res.json({ order: order }) res.json({ order: order })
} }
@@ -1,10 +1,9 @@
import { IsNumber, IsOptional, IsString } from "class-validator" import { IsNumber, IsOptional, IsString } from "class-validator"
import { AdminListOrdersSelector } from "../../../../types/orders" import { AdminListOrdersSelector } from "../../../../types/orders"
import { Order } from "../../../../models"
import { OrderService } from "../../../../services" import { OrderService } from "../../../../services"
import { Type } from "class-transformer" import { Type } from "class-transformer"
import { pick } from "lodash" import { cleanResponseData } from "../../../../utils/clean-response-data"
/** /**
* @oas [get] /admin/orders * @oas [get] /admin/orders
@@ -200,19 +199,14 @@ import { pick } from "lodash"
export default async (req, res) => { export default async (req, res) => {
const orderService: OrderService = req.scope.resolve("orderService") const orderService: OrderService = req.scope.resolve("orderService")
const { skip, take, select, relations } = req.listConfig const { skip, take } = req.listConfig
const [orders, count] = await orderService.listAndCount( const [orders, count] = await orderService.listAndCount(
req.filterableFields, req.filterableFields,
req.listConfig req.listConfig
) )
let data: Partial<Order>[] = orders const data = cleanResponseData(orders, req.allowedProperties)
const fields = [...select, ...relations]
if (fields.length) {
data = orders.map((o) => pick(o, fields))
}
res.json({ orders: data, count, offset: skip, limit: take }) res.json({ orders: data, count, offset: skip, limit: take })
} }
@@ -1,11 +1,11 @@
import "reflect-metadata" import "reflect-metadata"
import { RequestHandler, Router } from "express" import { Router } from "express"
import { Cart, Order, Swap } from "../../../../" import { Cart, Order, Swap } from "../../../../"
import { FindParams } from "../../../../types/common" import { FindParams } from "../../../../types/common"
import middlewares, { import middlewares, {
transformBody, transformBody,
transformQuery, transformStoreQuery,
} from "../../../middlewares" } from "../../../middlewares"
import { StorePostCartsCartReq } from "./update-cart" import { StorePostCartsCartReq } from "./update-cart"
import { StorePostCartReq } from "./create-cart" import { StorePostCartReq } from "./create-cart"
@@ -33,7 +33,7 @@ export default (app, container) => {
route.get( route.get(
"/:id", "/:id",
transformQuery(FindParams, { transformStoreQuery(FindParams, {
defaultRelations: defaultStoreCartRelations, defaultRelations: defaultStoreCartRelations,
defaultFields: defaultStoreCartFields, defaultFields: defaultStoreCartFields,
isList: false, isList: false,
@@ -1,7 +1,7 @@
import { Router } from "express" import { Router } from "express"
import { PaginatedResponse } from "../../../../types/common" import { PaginatedResponse } from "../../../../types/common"
import { ProductCollection } from "../../../../" import { ProductCollection } from "../../../../"
import middlewares, { transformQuery } from "../../../middlewares" import middlewares, { transformStoreQuery } from "../../../middlewares"
import { StoreGetCollectionsParams } from "./list-collections" import { StoreGetCollectionsParams } from "./list-collections"
const route = Router() const route = Router()
@@ -11,7 +11,7 @@ export default (app) => {
route.get( route.get(
"/", "/",
transformQuery(StoreGetCollectionsParams, { transformStoreQuery(StoreGetCollectionsParams, {
allowedFields, allowedFields,
isList: true, isList: true,
}), }),
@@ -1,7 +1,7 @@
import { Router } from "express" import { Router } from "express"
import { Customer, Order } from "../../../.." import { Customer, Order } from "../../../.."
import { PaginatedResponse } from "../../../../types/common" import { PaginatedResponse } from "../../../../types/common"
import middlewares, { transformQuery } from "../../../middlewares" import middlewares, { transformStoreQuery } from "../../../middlewares"
import { import {
defaultStoreOrdersFields, defaultStoreOrdersFields,
defaultStoreOrdersRelations, defaultStoreOrdersRelations,
@@ -41,7 +41,7 @@ export default (app, container) => {
route.get( route.get(
"/me/orders", "/me/orders",
transformQuery(StoreGetCustomersCustomerOrdersParams, { transformStoreQuery(StoreGetCustomersCustomerOrdersParams, {
defaultFields: defaultStoreOrdersFields, defaultFields: defaultStoreOrdersFields,
defaultRelations: defaultStoreOrdersRelations, defaultRelations: defaultStoreOrdersRelations,
isList: true, isList: true,
@@ -7,7 +7,7 @@ import {
} from "../../../../types/order-edit" } from "../../../../types/order-edit"
import middlewares, { import middlewares, {
transformBody, transformBody,
transformQuery, transformStoreQuery,
} from "../../../middlewares" } from "../../../middlewares"
import { StorePostOrderEditsOrderEditDecline } from "./decline-order-edit" import { StorePostOrderEditsOrderEditDecline } from "./decline-order-edit"
@@ -18,7 +18,7 @@ export default (app) => {
route.get( route.get(
"/:id", "/:id",
transformQuery(FindParams, { transformStoreQuery(FindParams, {
defaultRelations: defaultStoreOrderEditRelations, defaultRelations: defaultStoreOrderEditRelations,
defaultFields: defaultStoreOrderEditFields, defaultFields: defaultStoreOrderEditFields,
allowedFields: defaultStoreOrderEditFields, allowedFields: defaultStoreOrderEditFields,
@@ -3,7 +3,7 @@ import "reflect-metadata"
import { Order } from "../../../.." import { Order } from "../../../.."
import middlewares, { import middlewares, {
transformBody, transformBody,
transformQuery, transformStoreQuery,
} from "../../../middlewares" } from "../../../middlewares"
import requireCustomerAuthentication from "../../../middlewares/require-customer-authentication" import requireCustomerAuthentication from "../../../middlewares/require-customer-authentication"
import { StorePostCustomersCustomerOrderClaimReq } from "./request-order" import { StorePostCustomersCustomerOrderClaimReq } from "./request-order"
@@ -21,7 +21,7 @@ export default (app) => {
*/ */
route.get( route.get(
"/", "/",
transformQuery(StoreGetOrdersParams, { transformStoreQuery(StoreGetOrdersParams, {
defaultFields: defaultStoreOrdersFields, defaultFields: defaultStoreOrdersFields,
defaultRelations: defaultStoreOrdersRelations, defaultRelations: defaultStoreOrdersRelations,
allowedFields: allowedStoreOrdersFields, allowedFields: allowedStoreOrdersFields,
@@ -36,7 +36,7 @@ export default (app) => {
*/ */
route.get( route.get(
"/:id", "/:id",
transformQuery(StoreGetOrderParams, { transformStoreQuery(StoreGetOrderParams, {
defaultFields: defaultStoreOrdersFields, defaultFields: defaultStoreOrdersFields,
defaultRelations: defaultStoreOrdersRelations, defaultRelations: defaultStoreOrdersRelations,
allowedFields: allowedStoreOrdersFields, allowedFields: allowedStoreOrdersFields,
@@ -100,7 +100,9 @@ export default async (req, res) => {
const order = orders[0] const order = orders[0]
res.json({ order: cleanResponseData(order, req.allowedProperties || []) }) res.json({
order: cleanResponseData(order, req.allowedProperties || []),
})
} }
export class ShippingAddressPayload { export class ShippingAddressPayload {
@@ -2,7 +2,7 @@ import { Router } from "express"
import "reflect-metadata" import "reflect-metadata"
import middlewares, { import middlewares, {
transformBody, transformBody,
transformQuery, transformStoreQuery,
} from "../../../middlewares" } from "../../../middlewares"
import { PaymentCollection, PaymentSession } from "../../../../models" import { PaymentCollection, PaymentSession } from "../../../../models"
@@ -18,7 +18,7 @@ export default (app, container) => {
route.get( route.get(
"/:id", "/:id",
transformQuery(StoreGetPaymentCollectionsParams, { transformStoreQuery(StoreGetPaymentCollectionsParams, {
defaultFields: defaultPaymentCollectionFields, defaultFields: defaultPaymentCollectionFields,
defaultRelations: defaultPaymentCollectionRelations, defaultRelations: defaultPaymentCollectionRelations,
isList: false, isList: false,
@@ -1,5 +1,5 @@
import { Router } from "express" import { Router } from "express"
import middlewares, { transformQuery } from "../../../middlewares" import middlewares, { transformStoreQuery } from "../../../middlewares"
import { ProductCategory } from "../../../../models" import { ProductCategory } from "../../../../models"
import { PaginatedResponse } from "../../../../types/common" import { PaginatedResponse } from "../../../../types/common"
@@ -18,7 +18,7 @@ export default (app) => {
route.get( route.get(
"/", "/",
transformQuery(StoreGetProductCategoriesParams, { transformStoreQuery(StoreGetProductCategoriesParams, {
defaultFields: defaultStoreProductCategoryFields, defaultFields: defaultStoreProductCategoryFields,
allowedFields: allowedStoreProductCategoryFields, allowedFields: allowedStoreProductCategoryFields,
defaultRelations: defaultStoreProductCategoryRelations, defaultRelations: defaultStoreProductCategoryRelations,
@@ -29,7 +29,7 @@ export default (app) => {
route.get( route.get(
"/:id", "/:id",
transformQuery(StoreGetProductCategoryParams, { transformStoreQuery(StoreGetProductCategoryParams, {
defaultFields: defaultStoreProductCategoryFields, defaultFields: defaultStoreProductCategoryFields,
allowedFields: allowedStoreProductCategoryFields, allowedFields: allowedStoreProductCategoryFields,
defaultRelations: defaultStoreProductCategoryRelations, defaultRelations: defaultStoreProductCategoryRelations,
@@ -1,7 +1,7 @@
import { Router } from "express" import { Router } from "express"
import { ProductTag } from "../../../../models" import { ProductTag } from "../../../../models"
import { PaginatedResponse } from "../../../../types/common" import { PaginatedResponse } from "../../../../types/common"
import middlewares, { transformQuery } from "../../../middlewares" import middlewares, { transformStoreQuery } from "../../../middlewares"
import { StoreGetProductTagsParams } from "./list-product-tags" import { StoreGetProductTagsParams } from "./list-product-tags"
const route = Router() const route = Router()
@@ -11,7 +11,7 @@ export default (app: Router) => {
route.get( route.get(
"/", "/",
transformQuery(StoreGetProductTagsParams, { transformStoreQuery(StoreGetProductTagsParams, {
defaultFields: defaultStoreProductTagFields, defaultFields: defaultStoreProductTagFields,
defaultRelations: defaultStoreProductTagRelations, defaultRelations: defaultStoreProductTagRelations,
allowedFields: allowedStoreProductTagFields, allowedFields: allowedStoreProductTagFields,
@@ -1,7 +1,7 @@
import { Router } from "express" import { Router } from "express"
import { ProductType } from "../../../.." import { ProductType } from "../../../.."
import { PaginatedResponse } from "../../../../types/common" import { PaginatedResponse } from "../../../../types/common"
import middlewares, { transformQuery } from "../../../middlewares" import middlewares, { transformStoreQuery } from "../../../middlewares"
import "reflect-metadata" import "reflect-metadata"
import { StoreGetProductTypesParams } from "./list-product-types" import { StoreGetProductTypesParams } from "./list-product-types"
@@ -12,7 +12,7 @@ export default (app) => {
route.get( route.get(
"/", "/",
transformQuery(StoreGetProductTypesParams, { transformStoreQuery(StoreGetProductTypesParams, {
defaultFields: defaultStoreProductTypeFields, defaultFields: defaultStoreProductTypeFields,
defaultRelations: defaultStoreProductTypeRelations, defaultRelations: defaultStoreProductTypeRelations,
allowedFields: allowedStoreProductTypeFields, allowedFields: allowedStoreProductTypeFields,
@@ -1,8 +1,8 @@
import { RequestHandler, Router } from "express" import { Router } from "express"
import "reflect-metadata" import "reflect-metadata"
import { Product } from "../../../.." import { Product } from "../../../.."
import middlewares, { transformQuery } from "../../../middlewares" import middlewares, { transformStoreQuery } from "../../../middlewares"
import { PaginatedResponse } from "../../../../types/common" import { PaginatedResponse } from "../../../../types/common"
import { extendRequestParams } from "../../../middlewares/publishable-api-key/extend-request-params" import { extendRequestParams } from "../../../middlewares/publishable-api-key/extend-request-params"
import { validateProductSalesChannelAssociation } from "../../../middlewares/publishable-api-key/validate-product-sales-channel-association" import { validateProductSalesChannelAssociation } from "../../../middlewares/publishable-api-key/validate-product-sales-channel-association"
@@ -19,7 +19,7 @@ export default (app) => {
route.get( route.get(
"/", "/",
transformQuery(StoreGetProductsParams, { transformStoreQuery(StoreGetProductsParams, {
defaultRelations: defaultStoreProductsRelations, defaultRelations: defaultStoreProductsRelations,
defaultFields: defaultStoreProductsFields, defaultFields: defaultStoreProductsFields,
allowedFields: allowedStoreProductsFields, allowedFields: allowedStoreProductsFields,
@@ -31,7 +31,7 @@ export default (app) => {
route.get( route.get(
"/:id", "/:id",
transformQuery(StoreGetProductsProductParams, { transformStoreQuery(StoreGetProductsProductParams, {
defaultRelations: defaultStoreProductsRelations, defaultRelations: defaultStoreProductsRelations,
defaultFields: defaultStoreProductsFields, defaultFields: defaultStoreProductsFields,
allowedFields: allowedStoreProductsFields, allowedFields: allowedStoreProductsFields,
+1
View File
@@ -21,6 +21,7 @@ declare global {
retrieveConfig: FindConfig<unknown> retrieveConfig: FindConfig<unknown>
filterableFields: Record<string, unknown> filterableFields: Record<string, unknown>
allowedProperties: string[] allowedProperties: string[]
includes?: Record<string, boolean>
errors: string[] errors: string[]
} }
} }
@@ -1,21 +1,53 @@
import { pick } from "lodash" import { pick } from "lodash"
// TODO: once the legacy totals decoration will be removed.
// We will be able to only compute the totals if one of the total fields is present
// and therefore avoid totals computation if the user don't want them to appear in the response
// and therefore the below const will be removed
const EXCLUDED_FIELDS = [
"shipping_total",
"discount_total",
"tax_total",
"refunded_total",
"total",
"subtotal",
"paid_total",
"refundable_amount",
"gift_card_total",
"gift_card_tax_total",
"item_tax_total",
"shipping_tax_total",
"refundable",
"original_total",
"original_tax_total",
]
/** /**
* Filter response data to contain props specified in the fields array. * Filter response data to contain props specified in the `allowedProperties`.
* You can read more in the transformQuery middleware utility methods.
* *
* @param data - record or an array of records in the response * @param data - record or an array of records in the response
* @param fields - record props allowed in the response * @param fields - record props allowed in the response
*/ */
function cleanResponseData<T>(data: T, fields: string[]) { function cleanResponseData<T extends unknown | unknown[]>(
data: T,
fields: string[]
): T extends [] ? Partial<T>[] : Partial<T> {
if (!fields.length) { if (!fields.length) {
return data return data as T extends [] ? Partial<T>[] : Partial<T>
} }
if (Array.isArray(data)) { const isDataArray = Array.isArray(data)
return data.map((record) => pick(record, fields)) const fieldsSet = new Set([...fields, ...EXCLUDED_FIELDS])
}
return pick(data, fields) fields = [...fieldsSet]
let arrayData: Partial<T>[] = isDataArray ? data : [data]
arrayData = arrayData.map((record) => pick(record, fields))
return (isDataArray ? arrayData : arrayData[0]) as T extends []
? Partial<T>[]
: Partial<T>
} }
export { cleanResponseData } export { cleanResponseData }
+1 -2
View File
@@ -32,6 +32,5 @@
"build": "tsc --build", "build": "tsc --build",
"test": "jest", "test": "jest",
"test:unit": "jest" "test:unit": "jest"
}, }
"peerDependencies": {}
} }