diff --git a/integration-tests/environment-helpers/use-db.js b/integration-tests/environment-helpers/use-db.js index 3581933d90..cb0c46be10 100644 --- a/integration-tests/environment-helpers/use-db.js +++ b/integration-tests/environment-helpers/use-db.js @@ -32,6 +32,7 @@ const keepTables = [ "fulfillment_provider", "payment_provider", "country", + "region_country", "currency", "migrations", "mikro_orm_migrations", diff --git a/integration-tests/modules/__tests__/api-key/admin/api-key.spec.ts b/integration-tests/modules/__tests__/api-key/admin/api-key.spec.ts index 77d2dea406..8d3bdc5d32 100644 --- a/integration-tests/modules/__tests__/api-key/admin/api-key.spec.ts +++ b/integration-tests/modules/__tests__/api-key/admin/api-key.spec.ts @@ -40,13 +40,13 @@ describe("API Keys - Admin", () => { beforeEach(async () => { await createAdminUser(dbConnection, adminHeaders) - - // Used for testing cross-module authentication checks - await regionService.createDefaultCountries() }) afterEach(async () => { 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() }) diff --git a/integration-tests/modules/__tests__/cart/store/cart.workflows.spec.ts b/integration-tests/modules/__tests__/cart/store/cart.workflows.spec.ts index 2f76b75d45..111346478b 100644 --- a/integration-tests/modules/__tests__/cart/store/cart.workflows.spec.ts +++ b/integration-tests/modules/__tests__/cart/store/cart.workflows.spec.ts @@ -63,7 +63,6 @@ describe("Carts workflows", () => { beforeEach(async () => { await adminSeeder(dbConnection) - await regionModuleService.createDefaultCountries() // Here, so we don't have to create a region for each test defaultRegion = await regionModuleService.create({ diff --git a/integration-tests/modules/__tests__/cart/store/carts.spec.ts b/integration-tests/modules/__tests__/cart/store/carts.spec.ts index b6641a9fd6..4249de1705 100644 --- a/integration-tests/modules/__tests__/cart/store/carts.spec.ts +++ b/integration-tests/modules/__tests__/cart/store/carts.spec.ts @@ -59,7 +59,6 @@ describe("Store Carts API", () => { beforeEach(async () => { await adminSeeder(dbConnection) - await regionModuleService.createDefaultCountries() // Here, so we don't have to create a region for each test defaultRegion = await regionModuleService.create({ diff --git a/integration-tests/modules/__tests__/link-modules/cart-links.spec.ts b/integration-tests/modules/__tests__/link-modules/cart-links.spec.ts index 84281a7452..c4fb270033 100644 --- a/integration-tests/modules/__tests__/link-modules/cart-links.spec.ts +++ b/integration-tests/modules/__tests__/link-modules/cart-links.spec.ts @@ -47,11 +47,6 @@ describe("Cart links", () => { await shutdownServer() }) - beforeEach(async () => { - // @ts-ignore - await regionModule.createDefaultCountries() - }) - afterEach(async () => { const db = useDb() await db.teardown() diff --git a/integration-tests/modules/__tests__/link-modules/cart-region.spec.ts b/integration-tests/modules/__tests__/link-modules/cart-region.spec.ts index 795124d48d..d0c67991b5 100644 --- a/integration-tests/modules/__tests__/link-modules/cart-region.spec.ts +++ b/integration-tests/modules/__tests__/link-modules/cart-region.spec.ts @@ -33,11 +33,6 @@ describe("Link: Cart Region", () => { await shutdownServer() }) - beforeEach(async () => { - // @ts-ignore - await regionModule.createDefaultCountries() - }) - afterEach(async () => { const db = useDb() await db.teardown() diff --git a/integration-tests/modules/__tests__/regions/admin/regions.spec.ts b/integration-tests/modules/__tests__/regions/admin/regions.spec.ts index eaf7c35a97..c24b00d5f2 100644 --- a/integration-tests/modules/__tests__/regions/admin/regions.spec.ts +++ b/integration-tests/modules/__tests__/regions/admin/regions.spec.ts @@ -36,12 +36,13 @@ describe("Regions - Admin", () => { beforeEach(async () => { await createAdminUser(dbConnection, adminHeaders) - - await service.createDefaultCountries() }) afterEach(async () => { 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() }) diff --git a/integration-tests/modules/__tests__/store/admin/store.spec.ts b/integration-tests/modules/__tests__/store/admin/store.spec.ts index 1491e45d4c..8b3d5c2709 100644 --- a/integration-tests/modules/__tests__/store/admin/store.spec.ts +++ b/integration-tests/modules/__tests__/store/admin/store.spec.ts @@ -37,9 +37,6 @@ describe("Store - Admin", () => { beforeEach(async () => { await createAdminUser(dbConnection, adminHeaders) - - const existingStores = await service.list({}) - await service.delete(existingStores.map((s) => s.id)) }) afterEach(async () => { diff --git a/packages/currency/src/loaders/initial-data.ts b/packages/currency/src/loaders/initial-data.ts index 5c1980cec9..b06d12438b 100644 --- a/packages/currency/src/loaders/initial-data.ts +++ b/packages/currency/src/loaders/initial-data.ts @@ -1,6 +1,6 @@ import { ModuleRegistrationName } from "@medusajs/modules-sdk" -import { ModulesSdkTypes, LoaderOptions } from "@medusajs/types" -import { defaultCurrencies } from "@medusajs/utils" +import { ModulesSdkTypes, LoaderOptions, Logger } from "@medusajs/types" +import { ContainerRegistrationKeys, defaultCurrencies } from "@medusajs/utils" import { Currency } from "@models" export default async ({ @@ -10,20 +10,23 @@ export default async ({ | ModulesSdkTypes.ModuleServiceInitializeOptions | ModulesSdkTypes.ModuleServiceInitializeCustomDataLayerOptions >): Promise => { - try { - const { - currencyService_, - }: { currencyService_: ModulesSdkTypes.InternalModuleService } = - container.resolve(ModuleRegistrationName.CURRENCY) + // TODO: Add default logger to the container when running tests + const logger = + container.resolve(ContainerRegistrationKeys.LOGGER) ?? console + const { + currencyService_, + }: { currencyService_: ModulesSdkTypes.InternalModuleService } = + container.resolve(ModuleRegistrationName.CURRENCY) + try { const normalizedCurrencies = Object.values(defaultCurrencies).map((c) => ({ ...c, code: c.code.toLowerCase(), })) const resp = await currencyService_.upsert(normalizedCurrencies) - console.log(`Loaded ${resp.length} currencies`) + logger.info(`Loaded ${resp.length} currencies`) } catch (error) { - console.error( + logger.warn( `Failed to load currencies, skipping loader. Original error: ${error.message}` ) } diff --git a/packages/region/integration-tests/__tests__/region-module.spec.ts b/packages/region/integration-tests/__tests__/region-module.spec.ts index f691e64947..e65aa62e59 100644 --- a/packages/region/integration-tests/__tests__/region-module.spec.ts +++ b/packages/region/integration-tests/__tests__/region-module.spec.ts @@ -1,6 +1,5 @@ import { Modules } from "@medusajs/modules-sdk" import { IRegionModuleService } from "@medusajs/types" -import { DefaultsUtils } from "@medusajs/utils" import { initModules } from "medusa-test-utils" import { MikroOrmWrapper } from "../utils" import { getInitModuleConfig } from "../utils/get-init-module-config" @@ -27,27 +26,8 @@ describe("Region Module Service", () => { }) it("should create countries on application start", async () => { - const countries = await service.listCountries() - expect(countries.length).toBeGreaterThan(0) - }) - - 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) + const countries = await service.listCountries({}, { take: null }) + expect(countries.length).toEqual(250) }) 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' ) }) + + 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) + }) }) diff --git a/packages/region/src/loaders/defaults.ts b/packages/region/src/loaders/defaults.ts index 434403eb7d..2375b51dda 100644 --- a/packages/region/src/loaders/defaults.ts +++ b/packages/region/src/loaders/defaults.ts @@ -1,13 +1,30 @@ -import { ModuleRegistrationName } from "@medusajs/modules-sdk" -import { IRegionModuleService, LoaderOptions } from "@medusajs/types" +import { Logger } 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 => { - const service: IRegionModuleService = container.resolve( - ModuleRegistrationName.REGION - ) + // TODO: Add default logger to the container when running tests + const logger = + container.resolve(ContainerRegistrationKeys.LOGGER) ?? console + const countryService_: ModulesSdkTypes.InternalModuleService = + container.resolve("countryService") - // TODO: Remove when legacy modules have been migrated - if (!!process.env.MEDUSA_FF_MEDUSA_V2) { - await service.createDefaultCountries() + try { + const normalizedCountries = DefaultsUtils.defaultCountries.map((c) => ({ + 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}` + ) } } diff --git a/packages/region/src/migrations/.snapshot-medusa-region.json b/packages/region/src/migrations/.snapshot-medusa-region.json index 85e93f5a52..b48a175d6c 100644 --- a/packages/region/src/migrations/.snapshot-medusa-region.json +++ b/packages/region/src/migrations/.snapshot-medusa-region.json @@ -1,5 +1,7 @@ { - "namespaces": ["public"], + "namespaces": [ + "public" + ], "name": "public", "tables": [ { @@ -77,7 +79,9 @@ "schema": "public", "indexes": [ { - "columnNames": ["deleted_at"], + "columnNames": [ + "deleted_at" + ], "composite": false, "keyName": "IDX_region_deleted_at", "primary": false, @@ -85,7 +89,9 @@ }, { "keyName": "region_pkey", - "columnNames": ["id"], + "columnNames": [ + "id" + ], "composite": false, "primary": true, "unique": true @@ -165,7 +171,9 @@ "indexes": [ { "keyName": "region_country_pkey", - "columnNames": ["id"], + "columnNames": [ + "id" + ], "composite": false, "primary": true, "unique": true @@ -175,9 +183,13 @@ "foreignKeys": { "region_country_region_id_foreign": { "constraintName": "region_country_region_id_foreign", - "columnNames": ["region_id"], + "columnNames": [ + "region_id" + ], "localTableName": "public.region_country", - "referencedColumnNames": ["id"], + "referencedColumnNames": [ + "id" + ], "referencedTableName": "public.region", "deleteRule": "set null", "updateRule": "cascade" diff --git a/packages/region/src/migrations/RegionModuleSetup20240205173216.ts b/packages/region/src/migrations/RegionModuleSetup20240205173216.ts index 36b56b04f7..2eb6b65a09 100644 --- a/packages/region/src/migrations/RegionModuleSetup20240205173216.ts +++ b/packages/region/src/migrations/RegionModuleSetup20240205173216.ts @@ -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); -- Adjust foreign keys for "region_country" 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; `) } } diff --git a/packages/region/src/models/country.ts b/packages/region/src/models/country.ts index 65a565149f..1e1ae59f36 100644 --- a/packages/region/src/models/country.ts +++ b/packages/region/src/models/country.ts @@ -50,7 +50,9 @@ export default class Country { @ManyToOne({ entity: () => Region, + fieldName: "region_id", nullable: true, + onDelete: "set null", }) region?: Region | null diff --git a/packages/region/src/services/region-module.ts b/packages/region/src/services/region-module.ts index b0bd5ede3a..6e8140b1ae 100644 --- a/packages/region/src/services/region-module.ts +++ b/packages/region/src/services/region-module.ts @@ -21,18 +21,15 @@ import { MedusaError, ModulesSdkUtils, promiseAll, - DefaultsUtils, removeUndefined, getDuplicates, } from "@medusajs/utils" import { Country, Region } from "@models" -import { CreateCountryDTO, UpdateRegionInput } from "@types" +import { UpdateRegionInput } from "@types" import { entityNameToLinkableKeysMap, joinerConfig } from "../joiner-config" -const COUNTRIES_LIMIT = 1000 - type InjectedDependencies = { baseRepository: DAL.RepositoryService regionService: ModulesSdkTypes.InternalModuleService @@ -280,7 +277,7 @@ export default class RegionModuleService< const countriesInDb = await this.countryService_.list( { iso_2: uniqueCountries }, - { select: ["iso_2", "region_id"] }, + { select: ["iso_2", "region_id"], take: null }, sharedContext ) const countryCodesInDb = countriesInDb.map((c) => c.iso_2.toLowerCase()) @@ -311,43 +308,4 @@ export default class RegionModuleService< return countriesInDb } - - @InjectManager("baseRepository_") - public async createDefaultCountries( - @MedusaContext() sharedContext: Context = {} - ): Promise { - await this.maybeCreateCountries(sharedContext) - } - - @InjectTransactionManager("baseRepository_") - private async maybeCreateCountries( - @MedusaContext() sharedContext: Context - ): Promise { - 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) - } - } } diff --git a/packages/types/src/region/service.ts b/packages/types/src/region/service.ts index c8d5ecad9a..214912bfb7 100644 --- a/packages/types/src/region/service.ts +++ b/packages/types/src/region/service.ts @@ -356,15 +356,4 @@ export interface IRegionModuleService extends IModuleService { config?: RestoreReturn, sharedContext?: Context ): Promise | 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} Resolves when the default countries are created. - * - * @example - * {example-code} - */ - createDefaultCountries(sharedContext?: Context): Promise }