fix: Move default country loading for region to the loader, fix a bug with cascade (#6559)

This commit is contained in:
Stevche Radevski authored and GitHub committed 2024-03-01 14:58:55 +01:00
1 parent 56d97ebef9
commit 347aba719c
16 files changed
+87 -121

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 () => {
+12 -9
View File
@@ -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 { const logger =
currencyService_, container.resolve<Logger>(ContainerRegistrationKeys.LOGGER) ?? console
}: { currencyService_: ModulesSdkTypes.InternalModuleService<Currency> } = const {
container.resolve(ModuleRegistrationName.CURRENCY) currencyService_,
}: { currencyService_: ModulesSdkTypes.InternalModuleService<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)
})
}) })
+25 -8
View File
@@ -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;
`) `)
} }
} }
+2
View File
@@ -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
+2 -44
View File
@@ -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)
}
}
} }
-11
View File
@@ -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>
} }