diff --git a/.changeset/old-plums-cough.md b/.changeset/old-plums-cough.md new file mode 100644 index 0000000000..8ab530fac7 --- /dev/null +++ b/.changeset/old-plums-cough.md @@ -0,0 +1,6 @@ +--- +"@medusajs/medusa": patch +"@medusajs/types": patch +--- + +feat(medusa,types): add fulfillment provider list api diff --git a/integration-tests/modules/__tests__/fulfillment-providers/index.spec.ts b/integration-tests/modules/__tests__/fulfillment-providers/index.spec.ts new file mode 100644 index 0000000000..ff6ba5b697 --- /dev/null +++ b/integration-tests/modules/__tests__/fulfillment-providers/index.spec.ts @@ -0,0 +1,42 @@ +import { ModuleRegistrationName } from "@medusajs/modules-sdk" +import { IFulfillmentModuleService } from "@medusajs/types" +import { medusaIntegrationTestRunner } from "medusa-test-utils/dist" +import { createAdminUser } from "../../../helpers/create-admin-user" + +jest.setTimeout(100000) + +const env = { MEDUSA_FF_MEDUSA_V2: true } +const adminHeaders = { + headers: { "x-medusa-access-token": "test_token" }, +} + +medusaIntegrationTestRunner({ + env, + testSuite: ({ getContainer, api, dbConnection }) => { + let service: IFulfillmentModuleService + let container + + beforeAll(() => { + container = getContainer() + service = container.resolve(ModuleRegistrationName.FULFILLMENT) + }) + + beforeEach(async () => { + await createAdminUser(dbConnection, adminHeaders, container) + }) + + describe("GET /admin/fulfillment-providers", () => { + it("should list all fulfillment providers", async () => { + const response = await api.get( + `/admin/fulfillment-providers`, + adminHeaders + ) + + expect(response.status).toEqual(200) + expect(response.data.fulfillment_providers).toEqual([ + { id: "manual_test-provider", is_enabled: true }, + ]) + }) + }) + }, +}) diff --git a/packages/fulfillment/src/joiner-config.ts b/packages/fulfillment/src/joiner-config.ts index 58ba9c05bd..ed43af7aa7 100644 --- a/packages/fulfillment/src/joiner-config.ts +++ b/packages/fulfillment/src/joiner-config.ts @@ -3,6 +3,7 @@ import { ModuleJoinerConfig } from "@medusajs/types" import { MapToConfig } from "@medusajs/utils" import { Fulfillment, + FulfillmentProvider, FulfillmentSet, GeoZone, ServiceZone, @@ -59,6 +60,13 @@ export const joinerConfig: ModuleJoinerConfig = { methodSuffix: "Fulfillments", }, }, + { + name: ["fulfillment_provider", "fulfillment_providers"], + args: { + entity: FulfillmentProvider.name, + methodSuffix: "FulfillmentProviders", + }, + }, { name: ["service_zone", "service_zones"], args: { diff --git a/packages/fulfillment/src/services/fulfillment-module-service.ts b/packages/fulfillment/src/services/fulfillment-module-service.ts index c8861ff0e5..086a61d4a5 100644 --- a/packages/fulfillment/src/services/fulfillment-module-service.ts +++ b/packages/fulfillment/src/services/fulfillment-module-service.ts @@ -20,15 +20,16 @@ import { InjectTransactionManager, MedusaContext, MedusaError, + Modules, ModulesSdkUtils, arrayDifference, getSetDifference, isString, promiseAll, - Modules } from "@medusajs/utils" import { Fulfillment, + FulfillmentProvider, FulfillmentSet, GeoZone, ServiceZone, @@ -39,8 +40,8 @@ import { } from "@models" import { isContextValid, validateRules } from "@utils" import { entityNameToLinkableKeysMap, joinerConfig } from "../joiner-config" -import FulfillmentProviderService from "./fulfillment-provider" import { UpdateShippingOptionsInput } from "../types/service" +import FulfillmentProviderService from "./fulfillment-provider" const generateMethodForModels = [ ServiceZone, @@ -49,6 +50,7 @@ const generateMethodForModels = [ ShippingProfile, ShippingOptionRule, ShippingOptionType, + FulfillmentProvider, // Not adding Fulfillment to not auto generate the methods under the hood and only provide the methods we want to expose8 ] @@ -86,6 +88,7 @@ export default class FulfillmentModuleService< ShippingProfile: { dto: FulfillmentTypes.ShippingProfileDTO } ShippingOptionRule: { dto: FulfillmentTypes.ShippingOptionRuleDTO } ShippingOptionType: { dto: FulfillmentTypes.ShippingOptionTypeDTO } + FulfillmentProvider: { dto: FulfillmentTypes.FulfillmentProviderDTO } } >(FulfillmentSet, generateMethodForModels, entityNameToLinkableKeysMap) implements IFulfillmentModuleService diff --git a/packages/medusa/src/api-v2/admin/fulfillment-providers/middlewares.ts b/packages/medusa/src/api-v2/admin/fulfillment-providers/middlewares.ts new file mode 100644 index 0000000000..31c924d3f4 --- /dev/null +++ b/packages/medusa/src/api-v2/admin/fulfillment-providers/middlewares.ts @@ -0,0 +1,23 @@ +import { MiddlewareRoute } from "../../../types/middlewares" +import { authenticate } from "../../../utils/authenticate-middleware" +import { validateAndTransformQuery } from "../../utils/validate-query" +import * as QueryConfig from "./query-config" +import { AdminFulfillmentProvidersParams } from "./validators" + +export const adminFulfillmentProvidersRoutesMiddlewares: MiddlewareRoute[] = [ + { + method: "ALL", + matcher: "/admin/fulfillment-providers*", + middlewares: [authenticate("admin", ["session", "bearer", "api-key"])], + }, + { + method: ["GET"], + matcher: "/admin/fulfillment-providers", + middlewares: [ + validateAndTransformQuery( + AdminFulfillmentProvidersParams, + QueryConfig.listTransformQueryConfig + ), + ], + }, +] diff --git a/packages/medusa/src/api-v2/admin/fulfillment-providers/query-config.ts b/packages/medusa/src/api-v2/admin/fulfillment-providers/query-config.ts new file mode 100644 index 0000000000..ed333677f4 --- /dev/null +++ b/packages/medusa/src/api-v2/admin/fulfillment-providers/query-config.ts @@ -0,0 +1,11 @@ +export const defaultAdminFulfillmentProvidersFields = ["id", "is_enabled"] + +export const retrieveTransformQueryConfig = { + defaults: defaultAdminFulfillmentProvidersFields, + isList: false, +} + +export const listTransformQueryConfig = { + ...retrieveTransformQueryConfig, + isList: true, +} diff --git a/packages/medusa/src/api-v2/admin/fulfillment-providers/route.ts b/packages/medusa/src/api-v2/admin/fulfillment-providers/route.ts new file mode 100644 index 0000000000..fab51a8739 --- /dev/null +++ b/packages/medusa/src/api-v2/admin/fulfillment-providers/route.ts @@ -0,0 +1,35 @@ +import { + ContainerRegistrationKeys, + remoteQueryObjectFromString, +} from "@medusajs/utils" +import { + AuthenticatedMedusaRequest, + MedusaResponse, +} from "../../../types/routing" +import { AdminFulfillmentProvidersParamsType } from "./validators" + +export const GET = async ( + req: AuthenticatedMedusaRequest, + res: MedusaResponse +) => { + const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY) + const queryObject = remoteQueryObjectFromString({ + entryPoint: "fulfillment_provider", + variables: { + filters: req.filterableFields, + ...req.remoteQueryConfig.pagination, + }, + fields: req.remoteQueryConfig.fields, + }) + + const { rows: fulfillment_providers, metadata } = await remoteQuery( + queryObject + ) + + res.json({ + fulfillment_providers, + count: metadata.count, + offset: metadata.skip, + limit: metadata.take, + }) +} diff --git a/packages/medusa/src/api-v2/admin/fulfillment-providers/validators.ts b/packages/medusa/src/api-v2/admin/fulfillment-providers/validators.ts new file mode 100644 index 0000000000..11ae13dae2 --- /dev/null +++ b/packages/medusa/src/api-v2/admin/fulfillment-providers/validators.ts @@ -0,0 +1,7 @@ +import { z } from "zod" +import { createSelectParams } from "../../utils/validators" + +export type AdminFulfillmentProvidersParamsType = z.infer< + typeof AdminFulfillmentProvidersParams +> +export const AdminFulfillmentProvidersParams = createSelectParams() diff --git a/packages/medusa/src/api-v2/middlewares.ts b/packages/medusa/src/api-v2/middlewares.ts index 176994133e..3d3a0a120c 100644 --- a/packages/medusa/src/api-v2/middlewares.ts +++ b/packages/medusa/src/api-v2/middlewares.ts @@ -6,6 +6,7 @@ import { adminCurrencyRoutesMiddlewares } from "./admin/currencies/middlewares" import { adminCustomerGroupRoutesMiddlewares } from "./admin/customer-groups/middlewares" import { adminCustomerRoutesMiddlewares } from "./admin/customers/middlewares" import { adminDraftOrderRoutesMiddlewares } from "./admin/draft-orders/middlewares" +import { adminFulfillmentProvidersRoutesMiddlewares } from "./admin/fulfillment-providers/middlewares" import { adminFulfillmentSetsRoutesMiddlewares } from "./admin/fulfillment-sets/middlewares" import { adminFulfillmentsRoutesMiddlewares } from "./admin/fulfillments/middlewares" import { adminInventoryRoutesMiddlewares } from "./admin/inventory-items/middlewares" @@ -78,5 +79,6 @@ export const config: MiddlewaresConfig = { ...adminReservationRoutesMiddlewares, ...adminShippingProfilesMiddlewares, ...adminFulfillmentsRoutesMiddlewares, + ...adminFulfillmentProvidersRoutesMiddlewares, ], } diff --git a/packages/types/src/fulfillment/common/fulfillment-provider.ts b/packages/types/src/fulfillment/common/fulfillment-provider.ts index 34ba71f6c9..e42a9fe323 100644 --- a/packages/types/src/fulfillment/common/fulfillment-provider.ts +++ b/packages/types/src/fulfillment/common/fulfillment-provider.ts @@ -1,3 +1,4 @@ +import { BaseFilterable, OperatorMap } from "../../dal" import { ShippingOptionDTO } from "./shipping-option" /** @@ -39,3 +40,14 @@ export interface FulfillmentProviderDTO { */ deleted_at: Date | null } + +/** + * The filters to apply on the retrieved fulfillment providers. + */ +export interface FilterableFulfillmentProviderProps + extends BaseFilterable { + /** + * The IDs to filter the provider by. + */ + id?: string | string[] | OperatorMap +} diff --git a/packages/types/src/fulfillment/service.ts b/packages/types/src/fulfillment/service.ts index 743ac4220b..62734bfa47 100644 --- a/packages/types/src/fulfillment/service.ts +++ b/packages/types/src/fulfillment/service.ts @@ -4,6 +4,7 @@ import { IModuleService } from "../modules-sdk" import { Context } from "../shared-context" import { FilterableFulfillmentProps, + FilterableFulfillmentProviderProps, FilterableFulfillmentSetProps, FilterableGeoZoneProps, FilterableServiceZoneProps, @@ -13,6 +14,7 @@ import { FilterableShippingOptionTypeProps, FilterableShippingProfileProps, FulfillmentDTO, + FulfillmentProviderDTO, FulfillmentSetDTO, GeoZoneDTO, ServiceZoneDTO, @@ -2498,4 +2500,29 @@ export interface IFulfillmentModuleService extends IModuleService { shippingOptionId: string, context: Record ): Promise + + /** + * This method retrieves a paginated list of fulfillment providers based on optional filters and configuration. + * + * @param {FilterableFulfillmentProviderProps} filters - The filters to apply on the retrieved fulfillment providers. + * @param {FindConfig} config - The configurations determining how the fulfillment provider is retrieved. Its properties, such as `select` or `relations`, accept the + * attributes or relations associated with a fulfillment provider. + * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. + * @returns {Promise} The list of fulfillment providers. + * + * @example + * To retrieve a list of fulfillment providers using their IDs: + * + * ```ts + * const providers = await fulfillmentModuleService.listFulfillmentProviders({ + * id: ["sepro_123", "sepro_321"], + * }) + * ``` + * + */ + listFulfillmentProviders( + filters?: FilterableFulfillmentProviderProps, + config?: FindConfig, + sharedContext?: Context + ): Promise }