feat(medusa, medusa-js, medusa-react): Implement Sales Channel retrieval (#1793)

This commit is contained in:
Adrien de Peretti authored and GitHub committed 2022-07-06 12:17:26 +02:00
1 parent e115518dda
commit 263a661031
26 files changed
+511 -34

No files matched your search

+1
View File
@@ -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"
+22 -20
View File
@@ -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
}
@@ -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,
})
})
})
})
@@ -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<void> => {
const { id } = req.params
const salesChannelService: SalesChannelService = req.scope.resolve(
"salesChannelService"
)
const salesChannel = await salesChannelService.retrieve(id)
res.status(200).json({ sales_channel: salesChannel })
}
@@ -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 './'
@@ -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<string, boolean> = {}
+1
View File
@@ -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"
@@ -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<SalesChannel> {}
@@ -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
@@ -22,7 +22,7 @@ describe('BatchJobService', () => {
manager: MockManager,
eventBusService: eventBusServiceMock,
batchJobRepository: batchJobRepositoryMock
})
} as any)
afterEach(() => {
jest.clearAllMocks()
@@ -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<SalesChannel>): any => {
return Promise.resolve({
id:
typeof queryOrId === "string"
? queryOrId
: ((queryOrId?.where as FindConditions<SalesChannel>)?.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") } },
)
})
})
})
+30 -3
View File
@@ -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<SalesChannelService> {
this.eventBusService_ = eventBusService
}
async retrieve(id: string): Promise<SalesChannel> {
throw new Error("Method not implemented.")
async retrieve(
salesChannelId: string,
config: FindConfig<SalesChannel> = {}
): Promise<SalesChannel | never> {
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(
+2
View File
@@ -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<TEntity> = Request & { resource: TEntity }
export type ClassConstructor<T> = {
new (...args: unknown[]): T