feat(medusa): Create default sales channel associated to the store (#1830)
**What** Add support for default sales channel **How** - Implement a new method in the salesChannelService `createDefault` - call the new method above in the default loader **Test** - Unit tests of the sales channel service method createDefaulta - Init default loader unit tests Fixes CORE-316
This commit is contained in:
committed by
GitHub
parent
19f35ba6aa
commit
b402b9f159
55
packages/medusa/src/loaders/__tests__/default.spec.ts
Normal file
55
packages/medusa/src/loaders/__tests__/default.spec.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import { asValue, createContainer } from "awilix";
|
||||
import { MockRepository, MockManager } from "medusa-test-utils"
|
||||
import { StoreServiceMock } from "../../services/__mocks__/store";
|
||||
import { ShippingProfileServiceMock } from "../../services/__mocks__/shipping-profile";
|
||||
import Logger from "../logger";
|
||||
import featureFlagsLoader from "../feature-flags";
|
||||
import { default as defaultLoader } from "../defaults"
|
||||
import { SalesChannelServiceMock } from "../../services/__mocks__/sales-channel";
|
||||
import { PaymentProviderServiceMock } from "../../services/__mocks__/payment-provider";
|
||||
|
||||
describe('default', () => {
|
||||
describe('sales channel default', () => {
|
||||
let featureFlagRouter
|
||||
const container = createContainer()
|
||||
|
||||
beforeAll(async () => {
|
||||
featureFlagRouter = await featureFlagsLoader({
|
||||
featureFlags: {
|
||||
sales_channels: true,
|
||||
},
|
||||
}, Logger)
|
||||
|
||||
container.register({
|
||||
storeService: asValue(StoreServiceMock),
|
||||
currencyRepository: asValue(MockRepository()),
|
||||
countryRepository: asValue(MockRepository()),
|
||||
shippingProfileService: asValue(ShippingProfileServiceMock),
|
||||
salesChannelService: asValue(SalesChannelServiceMock),
|
||||
logger: asValue(Logger),
|
||||
featureFlagRouter: asValue(featureFlagRouter),
|
||||
manager: asValue(MockManager),
|
||||
paymentProviders: asValue([]),
|
||||
paymentProviderService: asValue(PaymentProviderServiceMock),
|
||||
notificationProviders: asValue([]),
|
||||
notificationService: asValue({
|
||||
registerInstalledProviders: jest.fn(),
|
||||
}),
|
||||
fulfillmentProviders: asValue([]),
|
||||
fulfillmentProviderService: asValue({
|
||||
registerInstalledProviders: jest.fn(),
|
||||
}),
|
||||
taxProviders: asValue([]),
|
||||
taxProviderService: asValue({
|
||||
registerInstalledProviders: jest.fn(),
|
||||
}),
|
||||
})
|
||||
})
|
||||
|
||||
it("should create a new default sales channel attach to the store", async () => {
|
||||
await defaultLoader({ container })
|
||||
expect(SalesChannelServiceMock.createDefault).toHaveBeenCalledTimes(1)
|
||||
expect(SalesChannelServiceMock.createDefault).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -9,11 +9,15 @@ import {
|
||||
FulfillmentProviderService,
|
||||
NotificationService,
|
||||
PaymentProviderService,
|
||||
SalesChannelService,
|
||||
ShippingProfileService,
|
||||
StoreService, TaxProviderService,
|
||||
StoreService,
|
||||
TaxProviderService,
|
||||
} from "../services"
|
||||
import { CurrencyRepository } from "../repositories/currency"
|
||||
import { AbstractTaxService } from "../interfaces"
|
||||
import { FlagRouter } from "../utils/flag-router";
|
||||
import SalesChannelFeatureFlag from "./feature-flags/sales-channels";
|
||||
|
||||
const silentResolution = <T>(container: AwilixContainer, name: string, logger: Logger): T | never | undefined => {
|
||||
try {
|
||||
@@ -49,7 +53,9 @@ export default async ({ container }: { container: AwilixContainer }): Promise<vo
|
||||
const currencyRepository = container.resolve<typeof CurrencyRepository>("currencyRepository")
|
||||
const countryRepository = container.resolve<typeof CountryRepository>("countryRepository")
|
||||
const profileService = container.resolve<ShippingProfileService>("shippingProfileService")
|
||||
const salesChannelService = container.resolve<SalesChannelService>("salesChannelService")
|
||||
const logger = container.resolve<Logger>("logger")
|
||||
const featureFlagRouter = container.resolve<FlagRouter>("featureFlagRouter")
|
||||
|
||||
const entityManager = container.resolve<EntityManager>("manager")
|
||||
|
||||
@@ -97,7 +103,6 @@ export default async ({ container }: { container: AwilixContainer }): Promise<vo
|
||||
await entityManager.transaction(async (manager: EntityManager) => {
|
||||
await storeService.withTransaction(manager).create()
|
||||
|
||||
|
||||
const payProviders =
|
||||
silentResolution<typeof BasePaymentService[]>(container, "paymentProviders", logger) || []
|
||||
const payIds = payProviders.map((p) => p.getIdentifier())
|
||||
@@ -129,5 +134,10 @@ export default async ({ container }: { container: AwilixContainer }): Promise<vo
|
||||
|
||||
await profileService.withTransaction(manager).createDefault()
|
||||
await profileService.withTransaction(manager).createGiftCardDefault()
|
||||
|
||||
const isSalesChannelEnabled = featureFlagRouter.isFeatureEnabled(SalesChannelFeatureFlag.key)
|
||||
if (isSalesChannelEnabled) {
|
||||
await salesChannelService.withTransaction(manager).createDefault()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ import { BeforeInsert, Column } from "typeorm"
|
||||
|
||||
import { SoftDeletableEntity } from "../interfaces"
|
||||
import { FeatureFlagEntity } from "../utils/feature-flag-decorators"
|
||||
import { resolveDbType } from "../utils/db-aware-column"
|
||||
import { generateEntityId } from "../utils"
|
||||
|
||||
@FeatureFlagEntity("sales_channels")
|
||||
|
||||
@@ -31,6 +31,9 @@ export const PaymentProviderServiceMock = {
|
||||
list: jest.fn().mockImplementation(() => {
|
||||
return Promise.resolve()
|
||||
}),
|
||||
registerInstalledProviders: jest.fn().mockImplementation(() => {
|
||||
return Promise.resolve()
|
||||
}),
|
||||
createSession: jest.fn().mockImplementation((providerId, cart) => {
|
||||
return Promise.resolve({
|
||||
id: `${providerId}_session`,
|
||||
|
||||
@@ -27,6 +27,14 @@ export const SalesChannelServiceMock = {
|
||||
delete: jest.fn().mockImplementation((id, config) => {
|
||||
return Promise.resolve()
|
||||
}),
|
||||
|
||||
createDefault: jest.fn().mockImplementation(() => {
|
||||
return Promise.resolve({
|
||||
name: "sales channel 1 name",
|
||||
description: "sales channel 1 description",
|
||||
is_disabled: false,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const mock = jest.fn().mockImplementation(() => {
|
||||
|
||||
@@ -16,12 +16,21 @@ export const profiles = {
|
||||
}
|
||||
|
||||
export const ShippingProfileServiceMock = {
|
||||
withTransaction: function () {
|
||||
return this
|
||||
},
|
||||
update: jest.fn().mockImplementation(data => {
|
||||
return Promise.resolve()
|
||||
}),
|
||||
create: jest.fn().mockImplementation(data => {
|
||||
return Promise.resolve(data)
|
||||
}),
|
||||
createDefault: jest.fn().mockImplementation(() => {
|
||||
return Promise.resolve()
|
||||
}),
|
||||
createGiftCardDefault: jest.fn().mockImplementation(() => {
|
||||
return Promise.resolve()
|
||||
}),
|
||||
retrieve: jest.fn().mockImplementation(data => {
|
||||
if (data === IdMap.getId("default")) {
|
||||
return Promise.resolve(profiles.default)
|
||||
|
||||
@@ -7,6 +7,12 @@ export const store = {
|
||||
}
|
||||
|
||||
export const StoreServiceMock = {
|
||||
withTransaction: function () {
|
||||
return this
|
||||
},
|
||||
create: jest.fn().mockImplementation(data => {
|
||||
return Promise.resolve(data)
|
||||
}),
|
||||
addCurrency: jest.fn().mockImplementation(data => {
|
||||
return Promise.resolve()
|
||||
}),
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { IdMap, MockManager, MockRepository } from "medusa-test-utils"
|
||||
import SalesChannelService from "../sales-channel"
|
||||
import { EventBusServiceMock } from "../__mocks__/event-bus"
|
||||
import { EventBusService } from "../index"
|
||||
import { EventBusService, StoreService } from "../index"
|
||||
import { FindConditions, FindOneOptions } from "typeorm"
|
||||
import { SalesChannel } from "../../models"
|
||||
import { store, StoreServiceMock } from "../__mocks__/store";
|
||||
|
||||
describe("SalesChannelService", () => {
|
||||
const salesChannelData = {
|
||||
@@ -27,17 +28,76 @@ describe("SalesChannelService", () => {
|
||||
})
|
||||
}
|
||||
),
|
||||
save: (salesChannel) => Promise.resolve(salesChannel),
|
||||
create: jest.fn().mockImplementation((data) => data),
|
||||
save: (salesChannel) => Promise.resolve({
|
||||
id: IdMap.getId("sales_channel_1"),
|
||||
...salesChannel
|
||||
}),
|
||||
softRemove: jest.fn().mockImplementation((id: string): any => {
|
||||
return Promise.resolve()
|
||||
}),
|
||||
})
|
||||
|
||||
describe("create default", async () => {
|
||||
const salesChannelService = new SalesChannelService({
|
||||
manager: MockManager,
|
||||
eventBusService: EventBusServiceMock as unknown as EventBusService,
|
||||
salesChannelRepository: salesChannelRepositoryMock,
|
||||
storeService: StoreServiceMock as unknown as StoreService
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
it("should call the save method if the store does not have a default sales channel", async () => {
|
||||
await salesChannelService.createDefault()
|
||||
|
||||
expect(salesChannelRepositoryMock.save).toHaveBeenCalledTimes(1)
|
||||
expect(salesChannelRepositoryMock.save).toHaveBeenCalledWith({
|
||||
description: "Created by Medusa",
|
||||
name: "Default Sales Channel",
|
||||
is_disabled: false,
|
||||
})
|
||||
})
|
||||
|
||||
it("should return the default sales channel if it already exists", async () => {
|
||||
const localSalesChannelService = new SalesChannelService({
|
||||
manager: MockManager,
|
||||
eventBusService: EventBusServiceMock as unknown as EventBusService,
|
||||
salesChannelRepository: salesChannelRepositoryMock,
|
||||
storeService: {
|
||||
...StoreServiceMock,
|
||||
retrieve: jest.fn().mockImplementation(() => {
|
||||
return Promise.resolve({
|
||||
...store,
|
||||
default_sales_channel_id: IdMap.getId("sales_channel_1"),
|
||||
default_sales_channel: {
|
||||
id: IdMap.getId("sales_channel_1"),
|
||||
...salesChannelData,
|
||||
}
|
||||
})
|
||||
})
|
||||
} as any
|
||||
})
|
||||
|
||||
const salesChannel = await localSalesChannelService.createDefault()
|
||||
|
||||
expect(salesChannelRepositoryMock.save).toHaveBeenCalledTimes(0)
|
||||
expect(salesChannelRepositoryMock.save).not.toHaveBeenCalledTimes(1)
|
||||
expect(salesChannel).toEqual({
|
||||
id: IdMap.getId("sales_channel_1"),
|
||||
...salesChannelData,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("retrieve", () => {
|
||||
const salesChannelService = new SalesChannelService({
|
||||
manager: MockManager,
|
||||
eventBusService: EventBusServiceMock as unknown as EventBusService,
|
||||
salesChannelRepository: salesChannelRepositoryMock,
|
||||
storeService: StoreServiceMock as unknown as StoreService
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -67,6 +127,7 @@ describe("SalesChannelService", () => {
|
||||
manager: MockManager,
|
||||
eventBusService: EventBusServiceMock as unknown as EventBusService,
|
||||
salesChannelRepository: salesChannelRepositoryMock,
|
||||
storeService: StoreServiceMock as unknown as StoreService
|
||||
})
|
||||
|
||||
const update = {
|
||||
@@ -100,7 +161,8 @@ describe("SalesChannelService", () => {
|
||||
const salesChannelService = new SalesChannelService({
|
||||
manager: MockManager,
|
||||
eventBusService: EventBusServiceMock as unknown as EventBusService,
|
||||
salesChannelRepository: salesChannelRepositoryMock
|
||||
salesChannelRepository: salesChannelRepositoryMock,
|
||||
storeService: StoreServiceMock as unknown as StoreService
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
|
||||
@@ -11,12 +11,13 @@ import {
|
||||
} from "../types/sales-channels"
|
||||
import EventBusService from "./event-bus"
|
||||
import { buildQuery } from "../utils"
|
||||
import { PostgresError } from "../utils/exception-formatter"
|
||||
import StoreService from "./store"
|
||||
|
||||
type InjectedDependencies = {
|
||||
salesChannelRepository: typeof SalesChannelRepository
|
||||
eventBusService: EventBusService
|
||||
manager: EntityManager
|
||||
storeService: StoreService
|
||||
}
|
||||
|
||||
class SalesChannelService extends TransactionBaseService<SalesChannelService> {
|
||||
@@ -31,11 +32,13 @@ class SalesChannelService extends TransactionBaseService<SalesChannelService> {
|
||||
|
||||
protected readonly salesChannelRepository_: typeof SalesChannelRepository
|
||||
protected readonly eventBusService_: EventBusService
|
||||
protected readonly storeService_: StoreService
|
||||
|
||||
constructor({
|
||||
salesChannelRepository,
|
||||
eventBusService,
|
||||
manager,
|
||||
storeService,
|
||||
}: InjectedDependencies) {
|
||||
// eslint-disable-next-line prefer-rest-params
|
||||
super(arguments[0])
|
||||
@@ -43,6 +46,7 @@ class SalesChannelService extends TransactionBaseService<SalesChannelService> {
|
||||
this.manager_ = manager
|
||||
this.salesChannelRepository_ = salesChannelRepository
|
||||
this.eventBusService_ = eventBusService
|
||||
this.storeService_ = storeService
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -170,6 +174,36 @@ class SalesChannelService extends TransactionBaseService<SalesChannelService> {
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a default sales channel, if this does not already exist.
|
||||
* @return the sales channel
|
||||
*/
|
||||
async createDefault(): Promise<SalesChannel> {
|
||||
return this.atomicPhase_(async (transactionManager) => {
|
||||
const store = await this.storeService_
|
||||
.withTransaction(transactionManager)
|
||||
.retrieve({
|
||||
relations: ["default_sales_channel"],
|
||||
})
|
||||
|
||||
if (store.default_sales_channel_id) {
|
||||
return store.default_sales_channel
|
||||
}
|
||||
|
||||
const defaultSalesChannel = await this.create({
|
||||
description: "Created by Medusa",
|
||||
name: "Default Sales Channel",
|
||||
is_disabled: false,
|
||||
})
|
||||
|
||||
await this.storeService_.withTransaction(transactionManager).update({
|
||||
default_sales_channel_id: defaultSalesChannel.id,
|
||||
})
|
||||
|
||||
return defaultSalesChannel
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export default SalesChannelService
|
||||
|
||||
@@ -6,4 +6,5 @@ export type UpdateStoreInput = {
|
||||
default_currency_code?: string
|
||||
currencies?: string[]
|
||||
metadata?: Record<string, unknown>
|
||||
default_sales_channel_id?: string
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user