fix: Move default country loading for region to the loader, fix a bug with cascade (#6559)
This commit is contained in:
1 parent
56d97ebef9
commit
347aba719c
16 files changed
+83
-117
No files matched your search
@@ -32,6 +32,7 @@ const keepTables = [
|
|||||||
"fulfillment_provider",
|
"fulfillment_provider",
|
||||||
"payment_provider",
|
"payment_provider",
|
||||||
"country",
|
"country",
|
||||||
|
"region_country",
|
||||||
"currency",
|
"currency",
|
||||||
"migrations",
|
"migrations",
|
||||||
"mikro_orm_migrations",
|
"mikro_orm_migrations",
|
||||||
|
|||||||
@@ -40,13 +40,13 @@ describe("API Keys - Admin", () => {
|
|||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await createAdminUser(dbConnection, adminHeaders)
|
await createAdminUser(dbConnection, adminHeaders)
|
||||||
|
|
||||||
// Used for testing cross-module authentication checks
|
|
||||||
await regionService.createDefaultCountries()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
afterEach(async () => {
|
afterEach(async () => {
|
||||||
const db = useDb()
|
const db = useDb()
|
||||||
|
// TODO: Once teardown doesn't skip constraint checks and cascades, we can remove this
|
||||||
|
const existingRegions = await regionService.list({})
|
||||||
|
await regionService.delete(existingRegions.map((r) => r.id))
|
||||||
await db.teardown()
|
await db.teardown()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -63,7 +63,6 @@ describe("Carts workflows", () => {
|
|||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await adminSeeder(dbConnection)
|
await adminSeeder(dbConnection)
|
||||||
await regionModuleService.createDefaultCountries()
|
|
||||||
|
|
||||||
// Here, so we don't have to create a region for each test
|
// Here, so we don't have to create a region for each test
|
||||||
defaultRegion = await regionModuleService.create({
|
defaultRegion = await regionModuleService.create({
|
||||||
|
|||||||
@@ -59,7 +59,6 @@ describe("Store Carts API", () => {
|
|||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await adminSeeder(dbConnection)
|
await adminSeeder(dbConnection)
|
||||||
await regionModuleService.createDefaultCountries()
|
|
||||||
|
|
||||||
// Here, so we don't have to create a region for each test
|
// Here, so we don't have to create a region for each test
|
||||||
defaultRegion = await regionModuleService.create({
|
defaultRegion = await regionModuleService.create({
|
||||||
|
|||||||
@@ -47,11 +47,6 @@ describe("Cart links", () => {
|
|||||||
await shutdownServer()
|
await shutdownServer()
|
||||||
})
|
})
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
// @ts-ignore
|
|
||||||
await regionModule.createDefaultCountries()
|
|
||||||
})
|
|
||||||
|
|
||||||
afterEach(async () => {
|
afterEach(async () => {
|
||||||
const db = useDb()
|
const db = useDb()
|
||||||
await db.teardown()
|
await db.teardown()
|
||||||
|
|||||||
@@ -33,11 +33,6 @@ describe("Link: Cart Region", () => {
|
|||||||
await shutdownServer()
|
await shutdownServer()
|
||||||
})
|
})
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
// @ts-ignore
|
|
||||||
await regionModule.createDefaultCountries()
|
|
||||||
})
|
|
||||||
|
|
||||||
afterEach(async () => {
|
afterEach(async () => {
|
||||||
const db = useDb()
|
const db = useDb()
|
||||||
await db.teardown()
|
await db.teardown()
|
||||||
|
|||||||
@@ -36,12 +36,13 @@ describe("Regions - Admin", () => {
|
|||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await createAdminUser(dbConnection, adminHeaders)
|
await createAdminUser(dbConnection, adminHeaders)
|
||||||
|
|
||||||
await service.createDefaultCountries()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
afterEach(async () => {
|
afterEach(async () => {
|
||||||
const db = useDb()
|
const db = useDb()
|
||||||
|
// TODO: Once teardown doesn't skip constraint checks and cascades, we can remove this
|
||||||
|
const existingRegions = await service.list({})
|
||||||
|
await service.delete(existingRegions.map((r) => r.id))
|
||||||
await db.teardown()
|
await db.teardown()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -37,9 +37,6 @@ describe("Store - Admin", () => {
|
|||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await createAdminUser(dbConnection, adminHeaders)
|
await createAdminUser(dbConnection, adminHeaders)
|
||||||
|
|
||||||
const existingStores = await service.list({})
|
|
||||||
await service.delete(existingStores.map((s) => s.id))
|
|
||||||
})
|
})
|
||||||
|
|
||||||
afterEach(async () => {
|
afterEach(async () => {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||||
import { ModulesSdkTypes, LoaderOptions } from "@medusajs/types"
|
import { ModulesSdkTypes, LoaderOptions, Logger } from "@medusajs/types"
|
||||||
import { defaultCurrencies } from "@medusajs/utils"
|
import { ContainerRegistrationKeys, defaultCurrencies } from "@medusajs/utils"
|
||||||
import { Currency } from "@models"
|
import { Currency } from "@models"
|
||||||
|
|
||||||
export default async ({
|
export default async ({
|
||||||
@@ -10,20 +10,23 @@ export default async ({
|
|||||||
| ModulesSdkTypes.ModuleServiceInitializeOptions
|
| ModulesSdkTypes.ModuleServiceInitializeOptions
|
||||||
| ModulesSdkTypes.ModuleServiceInitializeCustomDataLayerOptions
|
| ModulesSdkTypes.ModuleServiceInitializeCustomDataLayerOptions
|
||||||
>): Promise<void> => {
|
>): Promise<void> => {
|
||||||
try {
|
// TODO: Add default logger to the container when running tests
|
||||||
|
const logger =
|
||||||
|
container.resolve<Logger>(ContainerRegistrationKeys.LOGGER) ?? console
|
||||||
const {
|
const {
|
||||||
currencyService_,
|
currencyService_,
|
||||||
}: { currencyService_: ModulesSdkTypes.InternalModuleService<Currency> } =
|
}: { currencyService_: ModulesSdkTypes.InternalModuleService<Currency> } =
|
||||||
container.resolve(ModuleRegistrationName.CURRENCY)
|
container.resolve(ModuleRegistrationName.CURRENCY)
|
||||||
|
|
||||||
|
try {
|
||||||
const normalizedCurrencies = Object.values(defaultCurrencies).map((c) => ({
|
const normalizedCurrencies = Object.values(defaultCurrencies).map((c) => ({
|
||||||
...c,
|
...c,
|
||||||
code: c.code.toLowerCase(),
|
code: c.code.toLowerCase(),
|
||||||
}))
|
}))
|
||||||
const resp = await currencyService_.upsert(normalizedCurrencies)
|
const resp = await currencyService_.upsert(normalizedCurrencies)
|
||||||
console.log(`Loaded ${resp.length} currencies`)
|
logger.info(`Loaded ${resp.length} currencies`)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(
|
logger.warn(
|
||||||
`Failed to load currencies, skipping loader. Original error: ${error.message}`
|
`Failed to load currencies, skipping loader. Original error: ${error.message}`
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { Modules } from "@medusajs/modules-sdk"
|
import { Modules } from "@medusajs/modules-sdk"
|
||||||
import { IRegionModuleService } from "@medusajs/types"
|
import { IRegionModuleService } from "@medusajs/types"
|
||||||
import { DefaultsUtils } from "@medusajs/utils"
|
|
||||||
import { initModules } from "medusa-test-utils"
|
import { initModules } from "medusa-test-utils"
|
||||||
import { MikroOrmWrapper } from "../utils"
|
import { MikroOrmWrapper } from "../utils"
|
||||||
import { getInitModuleConfig } from "../utils/get-init-module-config"
|
import { getInitModuleConfig } from "../utils/get-init-module-config"
|
||||||
@@ -27,27 +26,8 @@ describe("Region Module Service", () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it("should create countries on application start", async () => {
|
it("should create countries on application start", async () => {
|
||||||
const countries = await service.listCountries()
|
const countries = await service.listCountries({}, { take: null })
|
||||||
expect(countries.length).toBeGreaterThan(0)
|
expect(countries.length).toEqual(250)
|
||||||
})
|
|
||||||
|
|
||||||
it("should create countries added to default ones", async () => {
|
|
||||||
const [, count] = await service.listAndCountCountries()
|
|
||||||
const initialCountries = DefaultsUtils.defaultCountries.length
|
|
||||||
|
|
||||||
expect(count).toEqual(initialCountries)
|
|
||||||
|
|
||||||
DefaultsUtils.defaultCountries.push({
|
|
||||||
name: "Dogecoin",
|
|
||||||
alpha2: "DOGE",
|
|
||||||
alpha3: "DOGE",
|
|
||||||
numeric: "420",
|
|
||||||
})
|
|
||||||
|
|
||||||
await service.createDefaultCountries()
|
|
||||||
|
|
||||||
const [, newCount] = await service.listAndCountCountries()
|
|
||||||
expect(newCount).toEqual(initialCountries + 1)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should create and list a region", async () => {
|
it("should create and list a region", async () => {
|
||||||
@@ -356,4 +336,22 @@ describe("Region Module Service", () => {
|
|||||||
'Countries with codes: "mx" are already assigned to a region'
|
'Countries with codes: "mx" are already assigned to a region'
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("should unset the region ID on the country when deleting a region", async () => {
|
||||||
|
const createdRegion = await service.create({
|
||||||
|
name: "North America",
|
||||||
|
currency_code: "USD",
|
||||||
|
countries: ["us", "ca"],
|
||||||
|
})
|
||||||
|
|
||||||
|
await service.delete(createdRegion.id)
|
||||||
|
|
||||||
|
const resp = await service.create({
|
||||||
|
name: "North America",
|
||||||
|
currency_code: "USD",
|
||||||
|
countries: ["us", "ca"],
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(resp.countries).toHaveLength(2)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
@@ -1,13 +1,30 @@
|
|||||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
import { Logger } from "@medusajs/types"
|
||||||
import { IRegionModuleService, LoaderOptions } from "@medusajs/types"
|
import { LoaderOptions, ModulesSdkTypes } from "@medusajs/types"
|
||||||
|
import { ContainerRegistrationKeys } from "@medusajs/utils"
|
||||||
|
import { DefaultsUtils } from "@medusajs/utils"
|
||||||
|
import { Country } from "@models"
|
||||||
|
|
||||||
export default async ({ container }: LoaderOptions): Promise<void> => {
|
export default async ({ container }: LoaderOptions): Promise<void> => {
|
||||||
const service: IRegionModuleService = container.resolve(
|
// TODO: Add default logger to the container when running tests
|
||||||
ModuleRegistrationName.REGION
|
const logger =
|
||||||
)
|
container.resolve<Logger>(ContainerRegistrationKeys.LOGGER) ?? console
|
||||||
|
const countryService_: ModulesSdkTypes.InternalModuleService<Country> =
|
||||||
|
container.resolve("countryService")
|
||||||
|
|
||||||
// TODO: Remove when legacy modules have been migrated
|
try {
|
||||||
if (!!process.env.MEDUSA_FF_MEDUSA_V2) {
|
const normalizedCountries = DefaultsUtils.defaultCountries.map((c) => ({
|
||||||
await service.createDefaultCountries()
|
iso_2: c.alpha2.toLowerCase(),
|
||||||
|
iso_3: c.alpha3.toLowerCase(),
|
||||||
|
num_code: c.numeric,
|
||||||
|
name: c.name.toUpperCase(),
|
||||||
|
display_name: c.name,
|
||||||
|
}))
|
||||||
|
|
||||||
|
const resp = await countryService_.upsert(normalizedCountries)
|
||||||
|
logger.info(`Loaded ${resp.length} countries`)
|
||||||
|
} catch (error) {
|
||||||
|
logger.warn(
|
||||||
|
`Failed to load countries, skipping loader. Original error: ${error.message}`
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
{
|
{
|
||||||
"namespaces": ["public"],
|
"namespaces": [
|
||||||
|
"public"
|
||||||
|
],
|
||||||
"name": "public",
|
"name": "public",
|
||||||
"tables": [
|
"tables": [
|
||||||
{
|
{
|
||||||
@@ -77,7 +79,9 @@
|
|||||||
"schema": "public",
|
"schema": "public",
|
||||||
"indexes": [
|
"indexes": [
|
||||||
{
|
{
|
||||||
"columnNames": ["deleted_at"],
|
"columnNames": [
|
||||||
|
"deleted_at"
|
||||||
|
],
|
||||||
"composite": false,
|
"composite": false,
|
||||||
"keyName": "IDX_region_deleted_at",
|
"keyName": "IDX_region_deleted_at",
|
||||||
"primary": false,
|
"primary": false,
|
||||||
@@ -85,7 +89,9 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"keyName": "region_pkey",
|
"keyName": "region_pkey",
|
||||||
"columnNames": ["id"],
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
"composite": false,
|
"composite": false,
|
||||||
"primary": true,
|
"primary": true,
|
||||||
"unique": true
|
"unique": true
|
||||||
@@ -165,7 +171,9 @@
|
|||||||
"indexes": [
|
"indexes": [
|
||||||
{
|
{
|
||||||
"keyName": "region_country_pkey",
|
"keyName": "region_country_pkey",
|
||||||
"columnNames": ["id"],
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
"composite": false,
|
"composite": false,
|
||||||
"primary": true,
|
"primary": true,
|
||||||
"unique": true
|
"unique": true
|
||||||
@@ -175,9 +183,13 @@
|
|||||||
"foreignKeys": {
|
"foreignKeys": {
|
||||||
"region_country_region_id_foreign": {
|
"region_country_region_id_foreign": {
|
||||||
"constraintName": "region_country_region_id_foreign",
|
"constraintName": "region_country_region_id_foreign",
|
||||||
"columnNames": ["region_id"],
|
"columnNames": [
|
||||||
|
"region_id"
|
||||||
|
],
|
||||||
"localTableName": "public.region_country",
|
"localTableName": "public.region_country",
|
||||||
"referencedColumnNames": ["id"],
|
"referencedColumnNames": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
"referencedTableName": "public.region",
|
"referencedTableName": "public.region",
|
||||||
"deleteRule": "set null",
|
"deleteRule": "set null",
|
||||||
"updateRule": "cascade"
|
"updateRule": "cascade"
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ CREATE TABLE IF NOT EXISTS "region_country" (
|
|||||||
CREATE UNIQUE INDEX IF NOT EXISTS "IDX_region_country_region_id_iso_2_unique" ON "region_country" (region_id, iso_2);
|
CREATE UNIQUE INDEX IF NOT EXISTS "IDX_region_country_region_id_iso_2_unique" ON "region_country" (region_id, iso_2);
|
||||||
-- Adjust foreign keys for "region_country"
|
-- Adjust foreign keys for "region_country"
|
||||||
ALTER TABLE "region_country" DROP CONSTRAINT IF EXISTS "FK_91f88052197680f9790272aaf5b";
|
ALTER TABLE "region_country" DROP CONSTRAINT IF EXISTS "FK_91f88052197680f9790272aaf5b";
|
||||||
ALTER TABLE "region_country" ADD CONSTRAINT "region_country_region_id_foreign" FOREIGN KEY ("region_id") REFERENCES "region" ("id") ON UPDATE CASCADE;
|
ALTER TABLE "region_country" ADD CONSTRAINT "region_country_region_id_foreign" FOREIGN KEY ("region_id") REFERENCES "region" ("id") ON UPDATE CASCADE ON DELETE SET NULL;
|
||||||
`)
|
`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -50,7 +50,9 @@ export default class Country {
|
|||||||
|
|
||||||
@ManyToOne({
|
@ManyToOne({
|
||||||
entity: () => Region,
|
entity: () => Region,
|
||||||
|
fieldName: "region_id",
|
||||||
nullable: true,
|
nullable: true,
|
||||||
|
onDelete: "set null",
|
||||||
})
|
})
|
||||||
region?: Region | null
|
region?: Region | null
|
||||||
|
|
||||||
|
|||||||
@@ -21,18 +21,15 @@ import {
|
|||||||
MedusaError,
|
MedusaError,
|
||||||
ModulesSdkUtils,
|
ModulesSdkUtils,
|
||||||
promiseAll,
|
promiseAll,
|
||||||
DefaultsUtils,
|
|
||||||
removeUndefined,
|
removeUndefined,
|
||||||
getDuplicates,
|
getDuplicates,
|
||||||
} from "@medusajs/utils"
|
} from "@medusajs/utils"
|
||||||
|
|
||||||
import { Country, Region } from "@models"
|
import { Country, Region } from "@models"
|
||||||
|
|
||||||
import { CreateCountryDTO, UpdateRegionInput } from "@types"
|
import { UpdateRegionInput } from "@types"
|
||||||
import { entityNameToLinkableKeysMap, joinerConfig } from "../joiner-config"
|
import { entityNameToLinkableKeysMap, joinerConfig } from "../joiner-config"
|
||||||
|
|
||||||
const COUNTRIES_LIMIT = 1000
|
|
||||||
|
|
||||||
type InjectedDependencies = {
|
type InjectedDependencies = {
|
||||||
baseRepository: DAL.RepositoryService
|
baseRepository: DAL.RepositoryService
|
||||||
regionService: ModulesSdkTypes.InternalModuleService<any>
|
regionService: ModulesSdkTypes.InternalModuleService<any>
|
||||||
@@ -280,7 +277,7 @@ export default class RegionModuleService<
|
|||||||
|
|
||||||
const countriesInDb = await this.countryService_.list(
|
const countriesInDb = await this.countryService_.list(
|
||||||
{ iso_2: uniqueCountries },
|
{ iso_2: uniqueCountries },
|
||||||
{ select: ["iso_2", "region_id"] },
|
{ select: ["iso_2", "region_id"], take: null },
|
||||||
sharedContext
|
sharedContext
|
||||||
)
|
)
|
||||||
const countryCodesInDb = countriesInDb.map((c) => c.iso_2.toLowerCase())
|
const countryCodesInDb = countriesInDb.map((c) => c.iso_2.toLowerCase())
|
||||||
@@ -311,43 +308,4 @@ export default class RegionModuleService<
|
|||||||
|
|
||||||
return countriesInDb
|
return countriesInDb
|
||||||
}
|
}
|
||||||
|
|
||||||
@InjectManager("baseRepository_")
|
|
||||||
public async createDefaultCountries(
|
|
||||||
@MedusaContext() sharedContext: Context = {}
|
|
||||||
): Promise<void> {
|
|
||||||
await this.maybeCreateCountries(sharedContext)
|
|
||||||
}
|
|
||||||
|
|
||||||
@InjectTransactionManager("baseRepository_")
|
|
||||||
private async maybeCreateCountries(
|
|
||||||
@MedusaContext() sharedContext: Context
|
|
||||||
): Promise<void> {
|
|
||||||
const [countries, count] = await this.countryService_.listAndCount(
|
|
||||||
{},
|
|
||||||
{ select: ["iso_2"], take: COUNTRIES_LIMIT },
|
|
||||||
sharedContext
|
|
||||||
)
|
|
||||||
|
|
||||||
let countsToCreate: CreateCountryDTO[] = []
|
|
||||||
if (count !== DefaultsUtils.defaultCountries.length) {
|
|
||||||
const countriesInDb = new Set(countries.map((c) => c.iso_2))
|
|
||||||
|
|
||||||
const countriesToAdd = DefaultsUtils.defaultCountries.filter(
|
|
||||||
(c) => !countriesInDb.has(c.alpha2.toLowerCase())
|
|
||||||
)
|
|
||||||
|
|
||||||
countsToCreate = countriesToAdd.map((c) => ({
|
|
||||||
iso_2: c.alpha2.toLowerCase(),
|
|
||||||
iso_3: c.alpha3.toLowerCase(),
|
|
||||||
num_code: c.numeric,
|
|
||||||
name: c.name.toUpperCase(),
|
|
||||||
display_name: c.name,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
if (countsToCreate.length) {
|
|
||||||
await this.countryService_.create(countsToCreate, sharedContext)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -356,15 +356,4 @@ export interface IRegionModuleService extends IModuleService {
|
|||||||
config?: RestoreReturn<TReturnableLinkableKeys>,
|
config?: RestoreReturn<TReturnableLinkableKeys>,
|
||||||
sharedContext?: Context
|
sharedContext?: Context
|
||||||
): Promise<Record<string, string[]> | void>
|
): Promise<Record<string, string[]> | void>
|
||||||
|
|
||||||
/**
|
|
||||||
* This method creates default countries.
|
|
||||||
*
|
|
||||||
* @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module.
|
|
||||||
* @returns {Promise<void>} Resolves when the default countries are created.
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* {example-code}
|
|
||||||
*/
|
|
||||||
createDefaultCountries(sharedContext?: Context): Promise<void>
|
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user