Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Changelog

## 0.47.0

### Features

- Update admin/merchants route. Now it call entity: "store" instead of "user_store".
- processing of store search params moved to middleware

## 0.46.0

### Misc
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@techlabi/medusa-marketplace-plugin",
"version": "0.46.0",
"version": "0.47.0",
"description": "A multi-vendor Medusa plugin",
"author": "TechLabi",
"license": "Apache 2.0",
Expand Down
15 changes: 13 additions & 2 deletions src/api/admin/merchants/middlewares.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,23 @@
import { validateAndTransformQuery } from "@medusajs/framework";
import { MiddlewareRoute } from "@medusajs/framework/http";
import { maybeApplyLinkFilter, MiddlewareRoute } from "@medusajs/framework/http";
import * as QueryConfig from "./query-config";
import { AdminGetMerchantsParams } from "./validators";
import { addUserIdToFilterableFieldsForAdmin } from "../../middlewares/add-userId-filter-for-admin";
import { maybeApplyStoreSearchFilter } from "../../middlewares/maybe-apply-store-filter";

export const adminMerchantsRoutesMiddlewares: MiddlewareRoute[] = [
{
method: ["GET"],
matcher: "/admin/merchants",
middlewares: [validateAndTransformQuery(AdminGetMerchantsParams, QueryConfig.listTransformQueryConfig)],
middlewares: [
validateAndTransformQuery(AdminGetMerchantsParams, QueryConfig.listTransformQueryConfig),
addUserIdToFilterableFieldsForAdmin,
maybeApplyStoreSearchFilter(),
maybeApplyLinkFilter({
entryPoint: "user_store",
resourceId: "store_id",
filterableField: "user_id",
}),
],
},
];
3 changes: 3 additions & 0 deletions src/api/admin/merchants/query-config.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
export const defaultMerchantsFields = ["id", "name", "created_at", "users.*"];

export const listTransformQueryConfig = {
defaults: defaultMerchantsFields,
defaultLimit: 10,
isList: true,
};
36 changes: 24 additions & 12 deletions src/api/admin/merchants/route.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,35 @@
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http";
import { MedusaRequest, MedusaResponse, refetchEntities } from "@medusajs/framework/http";
import { UserDTO } from "@medusajs/framework/types";
import { getMerchantsListWorkflow } from "../../../workflows/get-merchants-list";
import { AdminGetMerchantsParamsType } from "./validators";

export const GET = async (req: MedusaRequest<AdminGetMerchantsParamsType>, res: MedusaResponse) => {
const loggedInUser = req.scope.resolve("loggedInUser") as UserDTO;
const { result } = await getMerchantsListWorkflow(req.scope).run({
input: {
userId: loggedInUser.id,
isSuperAdmin: !!loggedInUser.metadata?.is_super_admin,
...req.queryConfig.pagination,
...req.filterableFields,

const response = await refetchEntities({
entity: "store",
idOrFilter: req?.filterableFields,
scope: req.scope,
fields: req.queryConfig?.fields,
pagination: {
...req.queryConfig?.pagination,
order: { created_at: "DESC" },
},
});
const { merchants, metadata } = result;

const merchants = response.data.map((merchant) => ({
...merchant,
store_id: merchant.id,
user_id: merchant?.users[0]?.id,
store_name: merchant.name,
user_email: merchant?.users[0]?.email,
status: "active",
can_impersonate: !!loggedInUser.metadata?.is_super_admin,
}));

res.json({
merchants,
count: metadata.count,
offset: metadata.skip,
limit: metadata.take,
count: response.metadata.count,
offset: response.metadata.skip,
limit: response.metadata.take,
});
};
24 changes: 24 additions & 0 deletions src/api/middlewares/add-userId-filter-for-admin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { type MedusaNextFunction, type MedusaRequest, type MedusaResponse } from "@medusajs/framework/http";
import { UserDTO } from "@medusajs/framework/types";
import { addUserIdToFilterableFields } from "./add-userId-filter";

export async function addUserIdToFilterableFieldsForAdmin(
req: MedusaRequest,
res: MedusaResponse,
next: MedusaNextFunction
) {
const loggedInUser = req.scope.resolve("loggedInUser", {
allowUnregistered: true,
}) as UserDTO;
if (!loggedInUser) {
return next();
}

const isSuperAdmin = loggedInUser.metadata?.is_super_admin && !req.session.impersonate_user_id;

if (isSuperAdmin) {
return next();
}

return addUserIdToFilterableFields(req, res, next);
}
18 changes: 18 additions & 0 deletions src/api/middlewares/add-userId-filter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { type MedusaNextFunction, type MedusaRequest, type MedusaResponse } from "@medusajs/framework/http";
import { UserDTO } from "@medusajs/framework/types";

export async function addUserIdToFilterableFields(req: MedusaRequest, res: MedusaResponse, next: MedusaNextFunction) {
const loggedInUser = req.scope.resolve("loggedInUser", {
allowUnregistered: true,
}) as UserDTO;
if (!loggedInUser) {
return next();
}

if (!req.filterableFields) {
req.filterableFields = {};
}

req.filterableFields["user_id"] = loggedInUser.id;
return next();
}
47 changes: 47 additions & 0 deletions src/api/middlewares/maybe-apply-store-filter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { MedusaRequest } from "@medusajs/framework/http";
import { OperatorMap } from "@medusajs/framework/types";
import { ContainerRegistrationKeys } from "@medusajs/framework/utils";
import { NextFunction } from "express";

type MerchantStoresFilter = {
q?: string;
store_id?: OperatorMap<string>;
user_id?: string;
id?: string[] | string;
};

export function maybeApplyStoreSearchFilter() {
return async function applyDateRangeFilter(req: MedusaRequest, _, next: NextFunction) {
const filterableFields: MerchantStoresFilter = req.filterableFields;

const query = req.scope.resolve(ContainerRegistrationKeys.QUERY);
const q = filterableFields.q;
delete filterableFields.q;

if (!q) {
return next();
} else {
const { data: users } = await query.graph({
entity: "user",
fields: ["id", "email"],
filters: {
email: { $ilike: `%${q}%` },
},
});

const { data: usersStore } = await query.graph({
entity: "user_store",
fields: ["id", "user_id", "store_id"],
filters: {
user_id: [...users.map(user => user.id)],
},
});

Object.assign(filterableFields, {
$or: [{ id: [...usersStore.map(u => u.store_id)] }, { name: { $ilike: `%${q}%` } }],
});
}

return next();
};
}