diff --git a/integration-tests/api/__tests__/admin/__snapshots__/sales-channels.js.snap b/integration-tests/api/__tests__/admin/__snapshots__/sales-channels.js.snap new file mode 100644 index 0000000000..2ea2d96eb4 --- /dev/null +++ b/integration-tests/api/__tests__/admin/__snapshots__/sales-channels.js.snap @@ -0,0 +1,13 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`sales channels GET /admin/sales-channels/:id should retrieve the requested sales channel 1`] = ` +Object { + "created_at": Any, + "deleted_at": null, + "description": "test description", + "id": Any, + "is_disabled": false, + "name": "test name", + "updated_at": Any, +} +`; diff --git a/integration-tests/api/__tests__/admin/sales-channels.js b/integration-tests/api/__tests__/admin/sales-channels.js index 44f849906e..cc5729f567 100644 --- a/integration-tests/api/__tests__/admin/sales-channels.js +++ b/integration-tests/api/__tests__/admin/sales-channels.js @@ -2,10 +2,19 @@ const path = require("path") const { useApi } = require("../../../helpers/use-api") const { useDb } = require("../../../helpers/use-db") + const adminSeeder = require("../../helpers/admin-seeder") +const { simpleSalesChannelFactory, } = require("../../factories") + const startServerWithEnvironment = require("../../../helpers/start-server-with-environment").default +const adminReqConfig = { + headers: { + Authorization: "Bearer test_token", + }, +} + jest.setTimeout(30000) describe("sales channels", () => { @@ -36,7 +45,46 @@ describe("sales channels", () => { }) }) describe("POST /admin/sales-channels", () => {}) - describe("GET /admin/sales-channels/:id", () => {}) + + describe("GET /admin/sales-channels/:id", () => { + let salesChannel + + beforeEach(async() => { + try { + await adminSeeder(dbConnection) + salesChannel = await simpleSalesChannelFactory(dbConnection, { + name: "test name", + description: "test description", + }) + } catch (e) { + console.error(e) + } + }) + + afterEach(async() => { + const db = useDb() + await db.teardown() + }) + + it("should retrieve the requested sales channel", async() => { + const api = useApi() + const response = await api.get( + `/admin/sales-channels/${salesChannel.id}`, + adminReqConfig + ) + + expect(response.status).toEqual(200) + expect(response.data.sales_channel).toBeTruthy() + expect(response.data.sales_channel).toMatchSnapshot({ + id: expect.any(String), + name: salesChannel.name, + description: salesChannel.description, + created_at: expect.any(String), + updated_at: expect.any(String), + }) + }) + }) + describe("POST /admin/sales-channels/:id", () => {}) describe("DELETE /admin/sales-channels/:id", () => {}) }) diff --git a/integration-tests/api/factories/index.ts b/integration-tests/api/factories/index.ts index 56b0298a4f..720640d3ce 100644 --- a/integration-tests/api/factories/index.ts +++ b/integration-tests/api/factories/index.ts @@ -15,3 +15,4 @@ export * from "./simple-shipping-method-factory" export * from "./simple-product-type-tax-rate-factory" export * from "./simple-price-list-factory" export * from "./simple-batch-job-factory" +export * from "./simple-sales-channel-factory" diff --git a/integration-tests/api/factories/simple-sales-channel-factory.ts b/integration-tests/api/factories/simple-sales-channel-factory.ts new file mode 100644 index 0000000000..38115b47b2 --- /dev/null +++ b/integration-tests/api/factories/simple-sales-channel-factory.ts @@ -0,0 +1,31 @@ +import { Connection } from "typeorm" +import faker from "faker" +import { SalesChannel } from "@medusajs/medusa" + +export type SalesChannelFactoryData = { + name?: string + description?: string + is_disabled?: boolean +} + +export const simpleSalesChannelFactory = async ( + connection: Connection, + data: SalesChannelFactoryData = {}, + seed?: number +): Promise => { + if (typeof seed !== "undefined") { + faker.seed(seed) + } + + const manager = connection.manager + + const salesChannel = manager.create(SalesChannel, { + id: `simple-id-${Math.random() * 1000}`, + name: data.name || faker.name.firstName(), + description: data.description || faker.name.lastName(), + is_disabled: + typeof data.is_disabled !== undefined ? data.is_disabled : false, + }) + + return await manager.save(salesChannel) +} \ No newline at end of file diff --git a/packages/medusa-js/src/resources/admin/index.ts b/packages/medusa-js/src/resources/admin/index.ts index 25b3c8a632..c44709d11b 100644 --- a/packages/medusa-js/src/resources/admin/index.ts +++ b/packages/medusa-js/src/resources/admin/index.ts @@ -18,6 +18,7 @@ import AdminProductsResource from "./products" import AdminRegionsResource from "./regions" import AdminReturnReasonsResource from "./return-reasons" import AdminReturnsResource from "./returns" +import AdminSalesChannelsResource from "./sales-channels" import AdminShippingOptionsResource from "./shipping-options" import AdminShippingProfilesResource from "./shipping-profiles" import AdminStoresResource from "./store" @@ -47,6 +48,7 @@ class Admin extends BaseResource { public orders = new AdminOrdersResource(this.client) public returnReasons = new AdminReturnReasonsResource(this.client) public variants = new AdminVariantsResource(this.client) + public salesChannels = new AdminSalesChannelsResource(this.client) public swaps = new AdminSwapsResource(this.client) public shippingProfiles = new AdminShippingProfilesResource(this.client) public store = new AdminStoresResource(this.client) diff --git a/packages/medusa-js/src/resources/admin/sales-channels.ts b/packages/medusa-js/src/resources/admin/sales-channels.ts new file mode 100644 index 0000000000..87b782ecd7 --- /dev/null +++ b/packages/medusa-js/src/resources/admin/sales-channels.ts @@ -0,0 +1,44 @@ +import { + AdminSalesChannelRes, +} from "@medusajs/medusa" +import { ResponsePromise } from "../../typings" +import BaseResource from "../base" + +class AdminSalesChannelsResource extends BaseResource { + /** + * @description gets a sales channel + * @returns a medusa sales channel + */ + retrieve( + salesChannelId: string, + customHeaders: Record = {} + ): ResponsePromise { + const path = `/admin/sales-channels/${salesChannelId}` + return this.client.request("GET", path, {}, {}, customHeaders) + } + + /*create( + payload: any, + customHeaders: Record = {} + ): ResponsePromise {}*/ + + /*update( + id: string, + payload: any, + customHeaders: Record = {} + ): ResponsePromise {}*/ + + /*delete( + id: string, + customHeaders: Record = {} + ): ResponsePromise { + }*/ + + /*list( + query?: any, + customHeaders: Record = {} + ): ResponsePromise { + }*/ +} + +export default AdminSalesChannelsResource diff --git a/packages/medusa-react/mocks/data/fixtures.json b/packages/medusa-react/mocks/data/fixtures.json index 99fc9da78d..950da43985 100644 --- a/packages/medusa-react/mocks/data/fixtures.json +++ b/packages/medusa-react/mocks/data/fixtures.json @@ -1261,6 +1261,15 @@ }, "upload": { "url": "test-url" + }, + "sales_channel": { + "id": "sc_01F0YES4R67TXXC1QBQ8P54A8Y", + "name": "sales channel 1 name", + "description": "sales channel 1 description", + "is_disabled": false, + "updated_at": "2022-07-05T15:16:01.959Z", + "created_at": "2022-07-05T15:16:01.959Z", + "deleted_at": null } } } diff --git a/packages/medusa-react/mocks/handlers/admin.ts b/packages/medusa-react/mocks/handlers/admin.ts index 49ec7d072a..1fcc3644f9 100644 --- a/packages/medusa-react/mocks/handlers/admin.ts +++ b/packages/medusa-react/mocks/handlers/admin.ts @@ -1672,4 +1672,13 @@ export const adminHandlers = [ }) ) }), + + rest.get("/admin/sales-channels/:id", (req, res, ctx) => { + return res( + ctx.status(200), + ctx.json({ + sales_channel: fixtures.get("sales_channel"), + }) + ) + }), ] diff --git a/packages/medusa-react/src/hooks/admin/index.ts b/packages/medusa-react/src/hooks/admin/index.ts index 89873d1ebe..7b45420c5a 100644 --- a/packages/medusa-react/src/hooks/admin/index.ts +++ b/packages/medusa-react/src/hooks/admin/index.ts @@ -18,6 +18,7 @@ export * from "./products" export * from "./regions" export * from "./return-reasons" export * from "./returns" +export * from "./sales-channels" export * from "./shipping-options" export * from "./shipping-profiles" export * from "./store" diff --git a/packages/medusa-react/src/hooks/admin/sales-channels/index.ts b/packages/medusa-react/src/hooks/admin/sales-channels/index.ts new file mode 100644 index 0000000000..a494946b87 --- /dev/null +++ b/packages/medusa-react/src/hooks/admin/sales-channels/index.ts @@ -0,0 +1,2 @@ +export * from "./queries" +export * from "./mutations" diff --git a/packages/medusa-react/src/hooks/admin/sales-channels/mutations.ts b/packages/medusa-react/src/hooks/admin/sales-channels/mutations.ts new file mode 100644 index 0000000000..e6c1b200ee --- /dev/null +++ b/packages/medusa-react/src/hooks/admin/sales-channels/mutations.ts @@ -0,0 +1,54 @@ +export {} +/*export const useAdminCreateSalesChannel = ( + options?: UseMutationOptions< + Response, + Error, + AdminPostSalesChannelsReq + > +) => { + const { client } = useMedusa() + const queryClient = useQueryClient() + return useMutation( + (payload: AdminPostSalesChannelsReq) => client.admin.salesChannels.create(payload), + buildOptions(queryClient, adminSalesChannelsKeys.lists(), options) + ) +}*/ + +/*export const useAdminUpdateSalesChannel = ( + id: string, + options?: UseMutationOptions< + Response, + Error, + AdminPostSalesChannelsSalesChannelReq + > +) => { + const { client } = useMedusa() + const queryClient = useQueryClient() + + return useMutation( + (payload: AdminPostSalesChannelsSalesChannelReq) => + client.admin.salesChannels.update(id, payload), + buildOptions( + queryClient, + [adminSalesChannelsKeys.lists(), adminSalesChannelsKeys.detail(id)], + options + ) + ) +}*/ + +/*export const useAdminDeleteSalesChannel = ( + id: string, + options?: UseMutationOptions, Error, void> +) => { + const { client } = useMedusa() + const queryClient = useQueryClient() + + return useMutation( + () => client.admin.salesChannels.delete(id), + buildOptions( + queryClient, + [adminSalesChannelsKeys.lists(), adminSalesChannelsKeys.detail(id)], + options + ) + ) +}*/ diff --git a/packages/medusa-react/src/hooks/admin/sales-channels/queries.ts b/packages/medusa-react/src/hooks/admin/sales-channels/queries.ts new file mode 100644 index 0000000000..de1d65b0e9 --- /dev/null +++ b/packages/medusa-react/src/hooks/admin/sales-channels/queries.ts @@ -0,0 +1,51 @@ +import { + AdminSalesChannelRes +} 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" + +const ADMIN_SALES_CHANNELS_QUERY_KEY = `admin_sales_channels` as const + +export const adminSalesChannelsKeys = queryKeysFactory(ADMIN_SALES_CHANNELS_QUERY_KEY) + +type SalesChannelsQueryKeys = typeof adminSalesChannelsKeys + +export const useAdminSalesChannel = ( + id: string, + options?: UseQueryOptionsWrapper< + Response, + Error, + ReturnType + > +) => { + const { client } = useMedusa() + const { data, ...rest } = useQuery( + adminSalesChannelsKeys.detail(id), + () => client.admin.salesChannels.retrieve(id), + options + ) + return { ...data, ...rest } as const +} + +/* +export const useAdminSalesChannels = ( + query?: AdminGetSalesChannelsParams, + options?: UseQueryOptionsWrapper< + Response, + Error, + ReturnType + > +) => { + const { client } = useMedusa() + const { data, ...rest } = useQuery( + adminProductKeys.list(query), + () => client.admin.salesChannels.list(query), + options + ) + return { ...data, ...rest } as const +} +*/ + diff --git a/packages/medusa-react/test/hooks/admin/sales-channels/queries.test.ts b/packages/medusa-react/test/hooks/admin/sales-channels/queries.test.ts new file mode 100644 index 0000000000..c865a80fd4 --- /dev/null +++ b/packages/medusa-react/test/hooks/admin/sales-channels/queries.test.ts @@ -0,0 +1,20 @@ +import { + useAdminSalesChannel, +} from "../../../../src" +import { renderHook } from "@testing-library/react-hooks" +import { fixtures } from "../../../../mocks/data" +import { createWrapper } from "../../../utils" + +describe("useAdminSalesChannel hook", () => { + test("returns a product", async () => { + const salesChannel = fixtures.get("sales_channel") + const { result, waitFor } = renderHook(() => useAdminSalesChannel(salesChannel.id), { + wrapper: createWrapper(), + }) + + await waitFor(() => result.current.isSuccess) + + expect(result.current.response.status).toEqual(200) + expect(result.current.sales_channel).toEqual(salesChannel) + }) +}) diff --git a/packages/medusa/src/api/index.js b/packages/medusa/src/api/index.js index 63f19acd92..0ac8f38db0 100644 --- a/packages/medusa/src/api/index.js +++ b/packages/medusa/src/api/index.js @@ -35,6 +35,7 @@ export * from "./routes/admin/products" export * from "./routes/admin/regions" export * from "./routes/admin/return-reasons" export * from "./routes/admin/returns" +export * from "./routes/admin/sales-channels" export * from "./routes/admin/shipping-options" export * from "./routes/admin/shipping-profiles" export * from "./routes/admin/store" diff --git a/packages/medusa/src/api/routes/admin/index.js b/packages/medusa/src/api/routes/admin/index.js index 31d5359deb..c6005c9af7 100644 --- a/packages/medusa/src/api/routes/admin/index.js +++ b/packages/medusa/src/api/routes/admin/index.js @@ -20,6 +20,7 @@ import productTypesRoutes from "./product-types" import productRoutes from "./products" import regionRoutes from "./regions" import returnReasonRoutes from "./return-reasons" +import salesChannelRoutes from "./sales-channels" import returnRoutes from "./returns" import shippingOptionRoutes from "./shipping-options" import shippingProfileRoutes from "./shipping-profiles" @@ -63,32 +64,33 @@ export default (app, container, config) => { middlewareService.usePostAuthentication(app) appRoutes(route) - productRoutes(route) batchRoutes(route) - userRoutes(route) - regionRoutes(route) - shippingOptionRoutes(route) - shippingProfileRoutes(route) - discountRoutes(route) - giftCardRoutes(route) - orderRoutes(route) - storeRoutes(route) - uploadRoutes(route) - customerRoutes(route) - swapRoutes(route) - returnRoutes(route) - variantRoutes(route) - draftOrderRoutes(route) collectionRoutes(route) + customerGroupRoutes(route) + customerRoutes(route) + discountRoutes(route) + draftOrderRoutes(route) + giftCardRoutes(route) + inviteRoutes(route) + noteRoutes(route) notificationRoutes(route) - returnReasonRoutes(route) + orderRoutes(route) + priceListRoutes(route) + productRoutes(route) productTagRoutes(route) productTypesRoutes(route) - noteRoutes(route) - inviteRoutes(route) + regionRoutes(route) + returnReasonRoutes(route) + returnRoutes(route) + salesChannelRoutes(route) + shippingOptionRoutes(route) + shippingProfileRoutes(route) + storeRoutes(route) + swapRoutes(route) taxRateRoutes(route) - customerGroupRoutes(route) - priceListRoutes(route) + uploadRoutes(route) + userRoutes(route) + variantRoutes(route) return app } diff --git a/packages/medusa/src/api/routes/admin/sales-channels/__tests__/get-sales-channel.js b/packages/medusa/src/api/routes/admin/sales-channels/__tests__/get-sales-channel.js new file mode 100644 index 0000000000..04b4f1142e --- /dev/null +++ b/packages/medusa/src/api/routes/admin/sales-channels/__tests__/get-sales-channel.js @@ -0,0 +1,44 @@ +import { IdMap } from "medusa-test-utils" +import { request } from "../../../../../helpers/test-request" +import { SalesChannelServiceMock } from "../../../../../services/__mocks__/sales-channel" + +describe("GET /admin/sales-channels/:id", () => { + describe("successfully get a sales channel", () => { + let subject + + beforeAll(async () => { + subject = await request( + "GET", + `/admin/sales-channels/${IdMap.getId("sales_channel_1")}`, + { + adminSession: { + jwt: { + userId: IdMap.getId("admin_user"), + }, + }, + flags: ["sales_channels"], + } + ) + }) + + afterAll(() => { + jest.clearAllMocks() + }) + + it("calls the retrieve method from the sales channel service", () => { + expect(SalesChannelServiceMock.retrieve).toHaveBeenCalledTimes(1) + expect(SalesChannelServiceMock.retrieve).toHaveBeenCalledWith( + IdMap.getId("sales_channel_1"), + ) + }) + + it("returns the expected sales channel", () => { + expect(subject.body.sales_channel).toEqual({ + id: IdMap.getId("sales_channel_1"), + name: "sales channel 1 name", + description: "sales channel 1 description", + is_disabled: false, + }) + }) + }) +}) diff --git a/packages/medusa/src/api/routes/admin/sales-channels/get-sales-channel.ts b/packages/medusa/src/api/routes/admin/sales-channels/get-sales-channel.ts new file mode 100644 index 0000000000..ad27feed5a --- /dev/null +++ b/packages/medusa/src/api/routes/admin/sales-channels/get-sales-channel.ts @@ -0,0 +1,33 @@ +import { Request, Response } from "express" +import SalesChannelService from "../../../../services/sales-channel" + +/** + * @oas [get] /sales-channels/{id} + * operationId: "GetSalesChannelsSalesChannel" + * summary: "Retrieve a sales channel" + * description: "Retrieves the sales channel." + * x-authenticated: true + * parameters: + * - (path) id=* {string} The id of the Sales channel. + * tags: + * - Sales channel + * responses: + * 200: + * description: OK + * content: + * application/json: + * schema: + * properties: + * sales_channel: + * $ref: "#/components/schemas/sales-channel" + */ +export default async (req: Request, res: Response): Promise => { + const { id } = req.params + + const salesChannelService: SalesChannelService = req.scope.resolve( + "salesChannelService" + ) + + const salesChannel = await salesChannelService.retrieve(id) + res.status(200).json({ sales_channel: salesChannel }) +} diff --git a/packages/medusa/src/api/routes/admin/sales-channels/index.ts b/packages/medusa/src/api/routes/admin/sales-channels/index.ts index 7befc43306..e7c2af1c41 100644 --- a/packages/medusa/src/api/routes/admin/sales-channels/index.ts +++ b/packages/medusa/src/api/routes/admin/sales-channels/index.ts @@ -1,17 +1,22 @@ -/* eslint-disable @typescript-eslint/no-empty-function */ import { Router } from "express" import { DeleteResponse, PaginatedResponse } from "../../../../types/common" -import middlewares from "../../../middlewares" import "reflect-metadata" import { isFeatureFlagEnabled } from "../../../middlewares/feature-flag-enabled" -import { SalesChannel } from "../../../../models/sales-channel" +import { SalesChannel } from "../../../../models" +import middlewares from "../../../middlewares" const route = Router() export default (app) => { app.use("/sales-channels", isFeatureFlagEnabled("sales_channels"), route) - route.get("/:id", (req, res) => {}) + const salesChannelRouter = Router({ mergeParams: true }) + route.use("/:id", salesChannelRouter) + + salesChannelRouter.get( + "/", + middlewares.wrap(require("./get-sales-channel").default) + ) route.get("/", (req, res) => {}) @@ -24,7 +29,7 @@ export default (app) => { return app } -export type AdminSalesChanenlRes = { +export type AdminSalesChannelRes = { sales_channel: SalesChannel } @@ -34,7 +39,7 @@ export type AdminSalesChannelListRes = PaginatedResponse & { sales_channels: SalesChannel[] } -// export * from './' +export * from "./get-sales-channel" // export * from './' // export * from './' // export * from './' diff --git a/packages/medusa/src/loaders/feature-flags/index.ts b/packages/medusa/src/loaders/feature-flags/index.ts index 33844084a4..368365da26 100644 --- a/packages/medusa/src/loaders/feature-flags/index.ts +++ b/packages/medusa/src/loaders/feature-flags/index.ts @@ -19,9 +19,9 @@ export default ( ): FlagRouter => { const { featureFlags: projectConfigFlags = {} } = configModule - const flagDir = path.join(flagDirectory || __dirname, "*.js") + const flagDir = path.join(flagDirectory || __dirname, "*.{j,t}s") const supportedFlags = glob.sync(flagDir, { - ignore: ["**/index.js"], + ignore: ["**/index.js", "**/index.ts", "**/*.d.ts"], }) const flagConfig: Record = {} diff --git a/packages/medusa/src/models/index.ts b/packages/medusa/src/models/index.ts index 498ea0da40..89068cda28 100644 --- a/packages/medusa/src/models/index.ts +++ b/packages/medusa/src/models/index.ts @@ -53,6 +53,7 @@ export * from "./region" export * from "./return" export * from "./return-item" export * from "./return-reason" +export * from "./sales-channel" export * from "./shipping-method" export * from "./shipping-method-tax-line" export * from "./shipping-option" diff --git a/packages/medusa/src/repositories/sales-channel.ts b/packages/medusa/src/repositories/sales-channel.ts index 701c3fd661..08e5d4d682 100644 --- a/packages/medusa/src/repositories/sales-channel.ts +++ b/packages/medusa/src/repositories/sales-channel.ts @@ -1,5 +1,5 @@ import { EntityRepository, Repository } from "typeorm" -import { SalesChannel } from "../models/sales-channel" +import { SalesChannel } from "../models" @EntityRepository(SalesChannel) export class SalesChannelRepository extends Repository {} diff --git a/packages/medusa/src/services/__mocks__/sales-channel.js b/packages/medusa/src/services/__mocks__/sales-channel.js new file mode 100644 index 0000000000..46854780cc --- /dev/null +++ b/packages/medusa/src/services/__mocks__/sales-channel.js @@ -0,0 +1,20 @@ +export const SalesChannelServiceMock = { + withTransaction: function () { + return this + }, + + retrieve: jest.fn().mockImplementation((id, config) => { + return Promise.resolve({ + id: id, + name: "sales channel 1 name", + description: "sales channel 1 description", + is_disabled: false, + }) + }), +} + +const mock = jest.fn().mockImplementation(() => { + return SalesChannelServiceMock +}) + +export default mock diff --git a/packages/medusa/src/services/__tests__/batch-job.ts b/packages/medusa/src/services/__tests__/batch-job.ts index 3c7b41a3f6..90c0491f57 100644 --- a/packages/medusa/src/services/__tests__/batch-job.ts +++ b/packages/medusa/src/services/__tests__/batch-job.ts @@ -22,7 +22,7 @@ describe('BatchJobService', () => { manager: MockManager, eventBusService: eventBusServiceMock, batchJobRepository: batchJobRepositoryMock - }) + } as any) afterEach(() => { jest.clearAllMocks() diff --git a/packages/medusa/src/services/__tests__/sales-channel.ts b/packages/medusa/src/services/__tests__/sales-channel.ts new file mode 100644 index 0000000000..26c7ec6e16 --- /dev/null +++ b/packages/medusa/src/services/__tests__/sales-channel.ts @@ -0,0 +1,57 @@ +import { IdMap, MockManager, MockRepository } from "medusa-test-utils" +import SalesChannelService from "../sales-channel" +import { EventBusServiceMock } from "../__mocks__/event-bus" +import { EventBusService } from "../index" +import { FindConditions, FindOneOptions } from "typeorm" +import { SalesChannel } from "../../models" + +describe('SalesChannelService', () => { + describe("retrieve", () => { + const salesChannelData = { + name: "sales channel 1 name", + description: "sales channel 1 description", + is_disabled: false, + } + + const salesChannelRepositoryMock = MockRepository({ + findOne: jest.fn().mockImplementation((queryOrId: string | FindOneOptions): any => { + return Promise.resolve({ + id: + typeof queryOrId === "string" + ? queryOrId + : ((queryOrId?.where as FindConditions)?.id ?? IdMap.getId("sc_adjhlukiaeswhfae")), + ...salesChannelData + }) + }), + }) + + const salesChannelService = new SalesChannelService({ + manager: MockManager, + eventBusService: EventBusServiceMock as unknown as EventBusService, + salesChannelRepository: salesChannelRepositoryMock + }) + + afterEach(() => { + jest.clearAllMocks() + }) + + it('should retrieve a sales channel', async () => { + const salesChannel = await salesChannelService.retrieve( + IdMap.getId("sales_channel_1") + ) + + expect(salesChannel).toBeTruthy() + expect(salesChannel).toEqual({ + id: IdMap.getId("sales_channel_1"), + ...salesChannelData + }) + + expect(salesChannelRepositoryMock.findOne) + .toHaveBeenCalledTimes(1) + expect(salesChannelRepositoryMock.findOne) + .toHaveBeenLastCalledWith( + { where: { id: IdMap.getId("sales_channel_1") } }, + ) + }) + }) +}) diff --git a/packages/medusa/src/services/sales-channel.ts b/packages/medusa/src/services/sales-channel.ts index 3f38aa7efb..952a41df70 100644 --- a/packages/medusa/src/services/sales-channel.ts +++ b/packages/medusa/src/services/sales-channel.ts @@ -1,6 +1,6 @@ import { EntityManager } from "typeorm" import { TransactionBaseService } from "../interfaces" -import { SalesChannel } from "../models/sales-channel" +import { SalesChannel } from "../models" import { SalesChannelRepository } from "../repositories/sales-channel" import { FindConfig, QuerySelector } from "../types/common" import { @@ -8,6 +8,8 @@ import { UpdateSalesChannelInput, } from "../types/sales-channels" import EventBusService from "./event-bus" +import { buildQuery } from "../utils" +import { MedusaError } from "medusa-core-utils" type InjectedDependencies = { salesChannelRepository: typeof SalesChannelRepository @@ -35,8 +37,33 @@ class SalesChannelService extends TransactionBaseService { this.eventBusService_ = eventBusService } - async retrieve(id: string): Promise { - throw new Error("Method not implemented.") + async retrieve( + salesChannelId: string, + config: FindConfig = {} + ): Promise { + return await this.atomicPhase_(async (manager) => { + const salesChannelRepo = manager.getCustomRepository( + this.salesChannelRepository_ + ) + + const query = buildQuery( + { + id: salesChannelId, + }, + config + ) + + const salesChannel = await salesChannelRepo.findOne(query) + + if (!salesChannel) { + throw new MedusaError( + MedusaError.Types.NOT_FOUND, + `Sales channel with id ${salesChannelId} was not found` + ) + } + + return salesChannel + }) } async listAndCount( diff --git a/packages/medusa/src/types/global.ts b/packages/medusa/src/types/global.ts index 5b55dde52d..fa69b8dbb3 100644 --- a/packages/medusa/src/types/global.ts +++ b/packages/medusa/src/types/global.ts @@ -3,6 +3,7 @@ import { Logger as _Logger } from "winston" import { LoggerOptions } from "typeorm" import { Customer, User } from "../models" import { FindConfig, RequestQueryFields } from "./common" +import { Request } from "express" declare global { // eslint-disable-next-line @typescript-eslint/no-namespace @@ -19,6 +20,7 @@ declare global { } } +export type ExtendedRequest = Request & { resource: TEntity } export type ClassConstructor = { new (...args: unknown[]): T