feat(region): Add admin region get + list endpoints (#6322)

**What**
Add `GET /admin/regions`
Add `GET /admin/regions/:id`

Blocked by #6320 

Co-authored-by: Adrien de Peretti <25098370+adrien2p@users.noreply.github.com>
This commit is contained in:
Oli Juhl
2024-02-11 18:13:49 +01:00
committed by GitHub
parent b91a1ca5b8
commit 95d0e58d31
28 changed files with 566 additions and 87 deletions

View File

@@ -11,6 +11,7 @@ const { dropDatabase } = require("pg-god")
const { DataSource } = require("typeorm")
const dbFactory = require("./use-template-db")
const { ContainerRegistrationKeys } = require("@medusajs/utils")
const { migrateMedusaApp } = require("@medusajs/medusa/dist/loaders/medusa-app")
const DB_HOST = process.env.DB_HOST
const DB_USERNAME = process.env.DB_USERNAME
@@ -155,35 +156,26 @@ module.exports = {
const featureFlagLoader =
require("@medusajs/medusa/dist/loaders/feature-flags").default
const medusaAppLoader =
require("@medusajs/medusa/dist/loaders/medusa-app").default
const container = createMedusaContainer()
const featureFlagRouter = await featureFlagLoader(configModule)
const pgConnection = await pgConnectionLoader({ configModule, container })
container.register({
[ContainerRegistrationKeys.CONFIG_MODULE]: asValue(configModule),
[ContainerRegistrationKeys.LOGGER]: asValue(console),
[ContainerRegistrationKeys.MANAGER]: asValue(dbDataSource.manager),
[ContainerRegistrationKeys.PG_CONNECTION]: asValue(pgConnection),
featureFlagRouter: asValue(featureFlagRouter),
})
const pgConnection = await pgConnectionLoader({ configModule, container })
instance.setPgConnection(pgConnection)
const { runMigrations } = await medusaAppLoader(
await migrateMedusaApp(
{ configModule, container },
{ registerInContainer: false }
)
const options = {
database: {
clientUrl: DB_URL,
connection: pgConnection,
},
}
await runMigrations(options)
}
return dbDataSource

View File

@@ -1,12 +1,14 @@
import { initDb, useDb } from "../../../../environment-helpers/use-db"
import { ICustomerModuleService } from "@medusajs/types"
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
import { createAuthenticatedCustomer } from "../../../helpers/create-authenticated-customer"
import { getContainer } from "../../../../environment-helpers/use-container"
import { ICustomerModuleService } from "@medusajs/types"
import path from "path"
import { startBootstrapApp } from "../../../../environment-helpers/bootstrap-app"
import { useApi } from "../../../../environment-helpers/use-api"
import { getContainer } from "../../../../environment-helpers/use-container"
import { createAuthenticatedCustomer } from "../../../helpers/create-authenticated-customer"
jest.setTimeout(50000)
const env = { MEDUSA_FF_MEDUSA_V2: true }

View File

@@ -0,0 +1,66 @@
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
import { IRegionModuleService } from "@medusajs/types"
import path from "path"
import { startBootstrapApp } from "../../../../environment-helpers/bootstrap-app"
import { useApi } from "../../../../environment-helpers/use-api"
import { getContainer } from "../../../../environment-helpers/use-container"
import { initDb, useDb } from "../../../../environment-helpers/use-db"
import adminSeeder from "../../../../helpers/admin-seeder"
jest.setTimeout(50000)
const env = { MEDUSA_FF_MEDUSA_V2: true }
const adminHeaders = {
headers: { "x-medusa-access-token": "test_token" },
}
describe("GET /admin/regions/:id", () => {
let dbConnection
let appContainer
let shutdownServer
let regionModuleService: IRegionModuleService
beforeAll(async () => {
const cwd = path.resolve(path.join(__dirname, "..", "..", ".."))
dbConnection = await initDb({ cwd, env } as any)
shutdownServer = await startBootstrapApp({ cwd, env })
appContainer = getContainer()
regionModuleService = appContainer.resolve(ModuleRegistrationName.REGION)
})
afterAll(async () => {
const db = useDb()
await db.shutdown()
await shutdownServer()
})
beforeEach(async () => {
await adminSeeder(dbConnection)
})
afterEach(async () => {
const db = useDb()
await db.teardown()
})
it("should get a region", async () => {
const [region] = await regionModuleService.create([
{
name: "Test",
currency_code: "usd",
},
])
const api = useApi() as any
const response = await api.get(`/admin/regions/${region.id}`, adminHeaders)
expect(response.status).toEqual(200)
expect(response.data.region).toEqual(
expect.objectContaining({
id: region.id,
name: "Test",
currency_code: "usd",
})
)
})
})

View File

@@ -0,0 +1,66 @@
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
import { IRegionModuleService } from "@medusajs/types"
import path from "path"
import { startBootstrapApp } from "../../../../environment-helpers/bootstrap-app"
import { useApi } from "../../../../environment-helpers/use-api"
import { getContainer } from "../../../../environment-helpers/use-container"
import { initDb, useDb } from "../../../../environment-helpers/use-db"
import adminSeeder from "../../../../helpers/admin-seeder"
jest.setTimeout(50000)
const env = { MEDUSA_FF_MEDUSA_V2: true }
const adminHeaders = {
headers: { "x-medusa-access-token": "test_token" },
}
describe("GET /admin/regions", () => {
let dbConnection
let appContainer
let shutdownServer
let regionModuleService: IRegionModuleService
beforeAll(async () => {
const cwd = path.resolve(path.join(__dirname, "..", "..", ".."))
dbConnection = await initDb({ cwd, env } as any)
shutdownServer = await startBootstrapApp({ cwd, env })
appContainer = getContainer()
regionModuleService = appContainer.resolve(ModuleRegistrationName.REGION)
})
afterAll(async () => {
const db = useDb()
await db.shutdown()
await shutdownServer()
})
beforeEach(async () => {
await adminSeeder(dbConnection)
})
afterEach(async () => {
const db = useDb()
await db.teardown()
})
it("should get all regions and count", async () => {
await regionModuleService.create([
{
name: "Test",
currency_code: "usd",
},
])
const api = useApi() as any
const response = await api.get(`/admin/regions`, adminHeaders)
expect(response.status).toEqual(200)
expect(response.data.regions).toEqual([
expect.objectContaining({
id: expect.any(String),
name: "Test",
currency_code: "usd",
}),
])
})
})

View File

@@ -91,5 +91,10 @@ module.exports = {
resources: "shared",
resolve: "@medusajs/cart",
},
[Modules.REGION]: {
scope: "internal",
resources: "shared",
resolve: "@medusajs/region",
},
},
}

View File

@@ -19,6 +19,7 @@
"@medusajs/pricing": "workspace:^",
"@medusajs/product": "workspace:^",
"@medusajs/promotion": "workspace:^",
"@medusajs/region": "workspace:^",
"@medusajs/utils": "workspace:^",
"faker": "^5.5.3",
"medusa-fulfillment-webshipper": "workspace:*",

View File

@@ -0,0 +1,19 @@
import { remoteQueryObjectFromString } from "@medusajs/utils"
import { MedusaRequest, MedusaResponse } from "../../../../types/routing"
import { defaultAdminRegionFields } from "../query-config"
export const GET = async (req: MedusaRequest, res: MedusaResponse) => {
const remoteQuery = req.scope.resolve("remoteQuery")
const variables = { id: req.params.id }
const queryObject = remoteQueryObjectFromString({
entryPoint: "region",
variables,
fields: defaultAdminRegionFields,
})
const [region] = await remoteQuery(queryObject)
res.status(200).json({ region })
}

View File

@@ -0,0 +1,30 @@
import { transformQuery } from "../../../api/middlewares"
import { MiddlewareRoute } from "../../../loaders/helpers/routing/types"
import * as QueryConfig from "./query-config"
import {
AdminGetRegionsParams,
AdminGetRegionsRegionParams,
} from "./validators"
export const adminRegionRoutesMiddlewares: MiddlewareRoute[] = [
{
method: ["GET"],
matcher: "/admin/regions",
middlewares: [
transformQuery(
AdminGetRegionsParams,
QueryConfig.listTransformQueryConfig
),
],
},
{
method: ["GET"],
matcher: "/admin/regions/:id",
middlewares: [
transformQuery(
AdminGetRegionsRegionParams,
QueryConfig.retrieveTransformQueryConfig
),
],
},
]

View File

@@ -0,0 +1,27 @@
export const defaultAdminRegionFields = [
"id",
"name",
"currency_code",
"created_at",
"updated_at",
"deleted_at",
"metadata",
"countries.id",
"countries.iso_2",
"countries.iso_3",
"countries.num_code",
"countries.name",
"currency.code",
"currency.symbol",
"currency.symbol_native",
"currency.name",
]
export const retrieveTransformQueryConfig = {
isList: false,
}
export const listTransformQueryConfig = {
defaultLimit: 20,
isList: true,
}

View File

@@ -0,0 +1,20 @@
import { remoteQueryObjectFromString } from "@medusajs/utils"
import { MedusaRequest, MedusaResponse } from "../../../types/routing"
import { defaultAdminRegionFields } from "./query-config"
export const GET = async (req: MedusaRequest, res: MedusaResponse) => {
const remoteQuery = req.scope.resolve("remoteQuery")
const variables = { filters: req.filterableFields }
const queryObject = remoteQueryObjectFromString({
entryPoint: "region",
variables,
fields: defaultAdminRegionFields,
})
// TODO: Add count, offset, limit
const regions = await remoteQuery(queryObject)
res.json({ regions })
}

View File

@@ -0,0 +1,71 @@
import { OperatorMap } from "@medusajs/types"
import { Type } from "class-transformer"
import { IsOptional, IsString, ValidateNested } from "class-validator"
import { FindParams, extendedFindParamsMixin } from "../../../types/common"
import { OperatorMapValidator } from "../../../types/validators/operator-map"
export class AdminGetRegionsRegionParams extends FindParams {}
/**
* Parameters used to filter and configure the pagination of the retrieved regions.
*/
export class AdminGetRegionsParams extends extendedFindParamsMixin({
limit: 50,
offset: 0,
}) {
/**
* Search parameter for regions.
*/
@IsString({ each: true })
@IsOptional()
id?: string | string[]
/**
* Filter by currency code
*/
@IsString({ each: true })
@IsOptional()
code?: string | string[]
/**
* Filter by region name
*/
@IsString({ each: true })
@IsOptional()
name?: string | string[]
/**
* Date filters to apply on the regions' `created_at` date.
*/
@IsOptional()
@ValidateNested()
@Type(() => OperatorMapValidator)
created_at?: OperatorMap<string>
/**
* Date filters to apply on the regions' `updated_at` date.
*/
@IsOptional()
@ValidateNested()
@Type(() => OperatorMapValidator)
updated_at?: OperatorMap<string>
/**
* Date filters to apply on the regions' `deleted_at` date.
*/
@ValidateNested()
@IsOptional()
@Type(() => OperatorMapValidator)
deleted_at?: OperatorMap<string>
// Additional filters from BaseFilterable
@IsOptional()
@ValidateNested({ each: true })
@Type(() => AdminGetRegionsParams)
$and?: AdminGetRegionsParams[]
@IsOptional()
@ValidateNested({ each: true })
@Type(() => AdminGetRegionsParams)
$or?: AdminGetRegionsParams[]
}

View File

@@ -1,11 +1,12 @@
import { MiddlewaresConfig } from "../loaders/helpers/routing/types"
import { adminCampaignRoutesMiddlewares } from "./admin/campaigns/middlewares"
import { adminCustomerGroupRoutesMiddlewares } from "./admin/customer-groups/middlewares"
import { storeCustomerRoutesMiddlewares } from "./store/customers/middlewares"
import { adminCustomerRoutesMiddlewares } from "./admin/customers/middlewares"
import { adminPromotionRoutesMiddlewares } from "./admin/promotions/middlewares"
import { storeCartRoutesMiddlewares } from "./store/carts/middlewares"
import { adminRegionRoutesMiddlewares } from "./admin/regions/middlewares"
import { authRoutesMiddlewares } from "./auth/middlewares"
import { storeCartRoutesMiddlewares } from "./store/carts/middlewares"
import { storeCustomerRoutesMiddlewares } from "./store/customers/middlewares"
export const config: MiddlewaresConfig = {
routes: [
@@ -16,5 +17,6 @@ export const config: MiddlewaresConfig = {
...storeCustomerRoutesMiddlewares,
...storeCartRoutesMiddlewares,
...authRoutesMiddlewares,
...adminRegionRoutesMiddlewares,
],
}

View File

@@ -8,7 +8,10 @@ import { Express, NextFunction, Request, Response } from "express"
import databaseLoader, { dataSource } from "./database"
import pluginsLoader, { registerPluginModels } from "./plugins"
import { ContainerRegistrationKeys, isString } from "@medusajs/utils"
import {
ContainerRegistrationKeys,
isString
} from "@medusajs/utils"
import { asValue } from "awilix"
import { createMedusaContainer } from "medusa-core-utils"
import { track } from "medusa-telemetry"

View File

@@ -1,8 +1,9 @@
import {
MODULE_PACKAGE_NAMES,
MedusaApp,
MedusaAppMigrateUp,
MedusaAppOutput,
MedusaModule,
MODULE_PACKAGE_NAMES,
Modules,
ModulesDefinition,
} from "@medusajs/modules-sdk"
@@ -12,9 +13,12 @@ import {
MedusaContainer,
ModuleDefinition,
} from "@medusajs/types"
import { FlagRouter, MedusaV2Flag } from "@medusajs/utils"
import { ContainerRegistrationKeys, isObject } from "@medusajs/utils"
import {
ContainerRegistrationKeys,
FlagRouter,
isObject,
MedusaV2Flag,
} from "@medusajs/utils"
import { asValue } from "awilix"
import { remoteQueryFetchData } from ".."
import { joinerConfig } from "../joiner-config"
@@ -37,6 +41,67 @@ export function mergeDefaultModules(
return configModules
}
export async function migrateMedusaApp(
{
configModule,
container,
}: {
configModule: {
modules?: CommonTypes.ConfigModule["modules"]
projectConfig: CommonTypes.ConfigModule["projectConfig"]
}
container: MedusaContainer
},
config = { registerInContainer: true }
): Promise<void> {
const featureFlagRouter = container.resolve<FlagRouter>("featureFlagRouter")
const isMedusaV2Enabled = featureFlagRouter.isFeatureEnabled(MedusaV2Flag.key)
const injectedDependencies = {
[ContainerRegistrationKeys.PG_CONNECTION]: container.resolve(
ContainerRegistrationKeys.PG_CONNECTION
),
}
const sharedResourcesConfig = {
database: {
clientUrl: configModule.projectConfig.database_url,
driverOptions: configModule.projectConfig.database_extra,
},
}
const configModules = mergeDefaultModules(configModule.modules)
// Apply default options to legacy modules
for (const moduleKey of Object.keys(configModules)) {
if (!ModulesDefinition[moduleKey].isLegacy) {
continue
}
if (isObject(configModules[moduleKey])) {
;(
configModules[moduleKey] as Partial<InternalModuleDeclaration>
).options ??= {
database: {
type: "postgres",
url: configModule.projectConfig.database_url,
extra: configModule.projectConfig.database_extra,
schema: configModule.projectConfig.database_schema,
logging: configModule.projectConfig.database_logging,
},
}
}
}
await MedusaAppMigrateUp({
modulesConfig: configModules,
servicesConfig: joinerConfig,
remoteFetchData: remoteQueryFetchData(container),
sharedContainer: container,
sharedResourcesConfig,
injectedDependencies,
})
}
export const loadMedusaApp = async (
{
configModule,

View File

@@ -13,13 +13,20 @@ export const moduleLoader = async ({
container,
moduleResolutions,
logger,
migrationOnly,
}: {
container: MedusaContainer
moduleResolutions: Record<string, ModuleResolution>
logger: Logger
migrationOnly?: boolean
}): Promise<void> => {
for (const resolution of Object.values(moduleResolutions ?? {})) {
const registrationResult = await loadModule(container, resolution, logger!)
const registrationResult = await loadModule(
container,
resolution,
logger!,
migrationOnly
)
if (registrationResult?.error) {
const { error } = registrationResult
@@ -40,7 +47,8 @@ export const moduleLoader = async ({
async function loadModule(
container: MedusaContainer,
resolution: ModuleResolution,
logger: Logger
logger: Logger,
migrationOnly?: boolean
): Promise<{ error?: Error } | void> {
const modDefinition = resolution.definition
const registrationName = modDefinition.registrationName
@@ -77,5 +85,5 @@ async function loadModule(
return
}
return await loadInternalModule(container, resolution, logger)
return await loadInternalModule(container, resolution, logger, migrationOnly)
}

View File

@@ -16,7 +16,8 @@ import { asFunction, asValue } from "awilix"
export async function loadInternalModule(
container: MedusaContainer,
resolution: ModuleResolution,
logger: Logger
logger: Logger,
migrationOnly?: boolean
): Promise<{ error?: Error } | void> {
const registrationName = resolution.definition.registrationName
@@ -64,6 +65,17 @@ export async function loadInternalModule(
}
}
if (migrationOnly) {
// Partially loaded module, only register the service __joinerConfig function to be able to resolve it later
const moduleService = {
__joinerConfig: loadedModule.service.prototype.__joinerConfig,
}
container.register({
[registrationName]: asValue(moduleService),
})
return
}
const localContainer = createMedusaContainer()
const dependencies = resolution?.dependencies ?? []

View File

@@ -67,7 +67,11 @@ export type SharedResources = {
}
}
async function loadModules(modulesConfig, sharedContainer) {
async function loadModules(
modulesConfig,
sharedContainer,
migrationOnly = false
) {
const allModules = {}
await Promise.all(
@@ -106,6 +110,7 @@ async function loadModules(modulesConfig, sharedContainer) {
sharedContainer,
moduleDefinition: definition as ModuleDefinition,
moduleExports,
migrationOnly,
})) as LoadedModule
const service = loaded[moduleName]
@@ -190,18 +195,7 @@ export type MedusaAppOutput = {
runMigrations: RunMigrationFn
}
export async function MedusaApp({
sharedContainer,
sharedResourcesConfig,
servicesConfig,
modulesConfigPath,
modulesConfigFileName,
modulesConfig,
linkModules,
remoteFetchData,
injectedDependencies,
onApplicationStartCb,
}: {
export type MedusaAppOptions = {
sharedContainer?: MedusaContainer
sharedResourcesConfig?: SharedResources
loadedModules?: LoadedModule[]
@@ -213,7 +207,21 @@ export async function MedusaApp({
remoteFetchData?: RemoteFetchDataCallback
injectedDependencies?: any
onApplicationStartCb?: () => void
} = {}): Promise<{
}
async function MedusaApp_({
sharedContainer,
sharedResourcesConfig,
servicesConfig,
modulesConfigPath,
modulesConfigFileName,
modulesConfig,
linkModules,
remoteFetchData,
injectedDependencies = {},
onApplicationStartCb,
migrationOnly = false,
}: MedusaAppOptions & { migrationOnly?: boolean } = {}): Promise<{
modules: Record<string, LoadedModule | LoadedModule[]>
link: RemoteLink | undefined
query: (
@@ -224,8 +232,6 @@ export async function MedusaApp({
notFound?: Record<string, Record<string, string>>
runMigrations: RunMigrationFn
}> {
injectedDependencies ??= {}
const sharedContainer_ = createMedusaContainer({}, sharedContainer)
const modules: MedusaModuleConfig =
@@ -279,7 +285,7 @@ export async function MedusaApp({
})
}
const allModules = await loadModules(modules, sharedContainer_)
const allModules = await loadModules(modules, sharedContainer_, migrationOnly)
// Share Event bus with link modules
injectedDependencies[ModuleRegistrationName.EVENT_BUS] =
@@ -346,16 +352,35 @@ export async function MedusaApp({
}))
}
try {
return {
modules: allModules,
link: remoteLink,
query,
entitiesMap: schema.getTypeMap(),
notFound,
runMigrations,
}
} finally {
MedusaModule.onApplicationStart(onApplicationStartCb)
return {
modules: allModules,
link: remoteLink,
query,
entitiesMap: schema.getTypeMap(),
notFound,
runMigrations,
}
}
export async function MedusaApp(
options: MedusaAppOptions = {}
): Promise<MedusaAppOutput> {
try {
return await MedusaApp_(options)
} finally {
MedusaModule.onApplicationStart(options.onApplicationStartCb)
}
}
export async function MedusaAppMigrateUp(
options: MedusaAppOptions = {}
): Promise<void> {
const migrationOnly = true
const { runMigrations } = await MedusaApp_({
...options,
migrationOnly,
})
await runMigrations().finally(MedusaModule.clearInstances)
}

View File

@@ -4,9 +4,9 @@ import {
InternalModuleDeclaration,
LinkModuleDefinition,
LoadedModule,
MedusaContainer,
MODULE_RESOURCE_TYPE,
MODULE_SCOPE,
MedusaContainer,
ModuleBootstrapDeclaration,
ModuleDefinition,
ModuleExports,
@@ -59,6 +59,11 @@ export type ModuleBootstrapOptions = {
sharedContainer?: MedusaContainer
moduleDefinition?: ModuleDefinition
injectedDependencies?: Record<string, any>
/**
* In this mode, all instances are partially loaded, meaning that the module will not be fully loaded and the services will not be available.
* Don't forget to clear the instances (MedusaModule.clearInstances()) after the migration are done.
*/
migrationOnly?: boolean
}
export type LinkModuleBootstrapOptions = {
@@ -213,6 +218,7 @@ export class MedusaModule {
sharedContainer,
moduleDefinition,
injectedDependencies,
migrationOnly,
}: ModuleBootstrapOptions): Promise<{
[key: string]: T
}> {
@@ -283,6 +289,7 @@ export class MedusaModule {
container,
moduleResolutions,
logger,
migrationOnly,
})
} catch (err) {
errorLoading(err)

View File

@@ -4,3 +4,5 @@ if (typeof process.env.DB_TEMP_NAME === "undefined") {
}
process.env.MEDUSA_REGION_DB_SCHEMA = "public"
// TODO: Remove this when all modules are migrated to v2
process.env.MEDUSA_FF_MEDUSA_V2 = true

View File

@@ -1,28 +1,10 @@
import { Modules } from "@medusajs/modules-sdk"
import { ModulesSdkUtils } from "@medusajs/utils"
import * as RegionModels from "@models"
import { moduleDefinition } from "./module-definition"
import {
moduleDefinition,
revertMigration,
runMigrations,
} from "./module-definition"
export default moduleDefinition
const migrationScriptOptions = {
moduleName: Modules.REGION,
models: RegionModels,
pathToMigrations: __dirname + "/migrations",
}
export const runMigrations = ModulesSdkUtils.buildMigrationScript(
migrationScriptOptions
)
export const revertMigration = ModulesSdkUtils.buildRevertMigrationScript(
migrationScriptOptions
)
export { revertMigration, runMigrations }
export * from "./initialize"
export * from "./loaders"
export * from "./models"
export * from "./services"
export * from "./types"

View File

@@ -2,6 +2,12 @@ import { ModuleRegistrationName } from "@medusajs/modules-sdk"
import { IRegionModuleService, LoaderOptions } from "@medusajs/types"
export default async ({ container }: LoaderOptions): Promise<void> => {
const service: IRegionModuleService = container.resolve(ModuleRegistrationName.REGION)
await service.createDefaultCountriesAndCurrencies()
const service: IRegionModuleService = container.resolve(
ModuleRegistrationName.REGION
)
// TODO: Remove when legacy modules have been migrated
if (!!process.env.MEDUSA_FF_MEDUSA_V2) {
await service.createDefaultCountriesAndCurrencies()
}
}

View File

@@ -1,3 +1,4 @@
import { generatePostgresAlterColummnIfExistStatement } from "@medusajs/utils"
import { Migration } from "@mikro-orm/migrations"
export class RegionModuleSetup20240205173216 extends Migration {
@@ -27,6 +28,13 @@ CREATE TABLE IF NOT EXISTS "region_currency" (
-- Adjust "region" table
ALTER TABLE "region" DROP CONSTRAINT IF EXISTS "FK_3bdd5896ec93be2f1c62a3309a5";
ALTER TABLE "region" DROP CONSTRAINT IF EXISTS "FK_91f88052197680f9790272aaf5b";
${generatePostgresAlterColummnIfExistStatement(
"region",
["tax_rate", "gift_cards_taxable", "automatic_taxes", "includes_tax"],
"DROP NOT NULL"
)}
ALTER TABLE "region" ADD CONSTRAINT "region_currency_code_foreign" FOREIGN KEY ("currency_code") REFERENCES "region_currency" ("code") ON UPDATE CASCADE;
CREATE INDEX IF NOT EXISTS "IDX_region_currency_code" ON "region" ("currency_code");

View File

@@ -1,14 +1,32 @@
import { ModuleExports } from "@medusajs/types"
import { RegionModuleService } from "@services"
import { Modules } from "@medusajs/modules-sdk"
import { ModulesSdkUtils } from "@medusajs/utils"
import * as RegionModels from "@models"
import loadConnection from "./loaders/connection"
import loadContainer from "./loaders/container"
import loadDefaults from "./loaders/defaults"
const migrationScriptOptions = {
moduleName: Modules.REGION,
models: RegionModels,
pathToMigrations: __dirname + "/migrations",
}
export const runMigrations = ModulesSdkUtils.buildMigrationScript(
migrationScriptOptions
)
export const revertMigration = ModulesSdkUtils.buildRevertMigrationScript(
migrationScriptOptions
)
const service = RegionModuleService
const loaders = [loadContainer, loadConnection, loadDefaults] as any
export const moduleDefinition: ModuleExports = {
service,
loaders,
runMigrations,
revertMigration,
}

View File

@@ -172,7 +172,7 @@ export default class RegionModuleService<
): Promise<void> {
const [countries, count] = await this.countryService_.listAndCount(
{},
{ select: ["id", "iso_2"], take: COUNTRIES_LIMIT },
{ select: ["iso_2"], take: COUNTRIES_LIMIT },
sharedContext
)

View File

@@ -1,4 +1,4 @@
import { BaseFilterable } from "../dal"
import { BaseFilterable, OperatorMap } from "../dal"
export interface RegionDTO {
id: string
@@ -6,6 +6,9 @@ export interface RegionDTO {
currency_code: string
currency: RegionCurrencyDTO
countries: CountryDTO[]
metadata?: Record<string, any>
created_at: string
updated_at: string
}
export interface CountryDTO {
@@ -19,8 +22,12 @@ export interface CountryDTO {
export interface FilterableRegionProps
extends BaseFilterable<FilterableRegionProps> {
id?: string[]
name?: string[]
id?: string[] | string
name?: string | OperatorMap<string>
currency_code?: string | OperatorMap<string>
metadata?: Record<string, unknown> | OperatorMap<Record<string, unknown>>
created_at?: OperatorMap<string>
updated_at?: OperatorMap<string>
}
export interface RegionCountryDTO {

View File

@@ -0,0 +1,32 @@
export function generatePostgresAlterColummnIfExistStatement(
tableName: string,
columns: string[],
alterExpression: string
) {
let script = `
DO $$
DECLARE
current_column text;
BEGIN`
columns.forEach((column) => {
script += `
current_column := '${column}';
IF EXISTS (
SELECT 1
FROM information_schema.columns
WHERE table_name = '${tableName}'
AND column_name = current_column
) THEN
EXECUTE format('ALTER TABLE %I ALTER COLUMN %I ${alterExpression}', '${tableName}', current_column);
ELSE
RAISE NOTICE 'Column % does not exist or alteration condition not met.', current_column;
END IF;`
})
script += `
END$$;
`
return script
}

View File

@@ -1,3 +1,4 @@
export * from "./alter-columns-helper"
export * from "./array-difference"
export * from "./build-query"
export * from "./camel-to-snake-case"
@@ -45,3 +46,4 @@ export * from "./to-pascal-case"
export * from "./transaction"
export * from "./upper-case-first"
export * from "./wrap-handler"

View File

@@ -8605,7 +8605,7 @@ __metadata:
languageName: unknown
linkType: soft
"@medusajs/region@workspace:packages/region":
"@medusajs/region@workspace:^, @medusajs/region@workspace:packages/region":
version: 0.0.0-use.local
resolution: "@medusajs/region@workspace:packages/region"
dependencies:
@@ -31573,6 +31573,7 @@ __metadata:
"@medusajs/pricing": "workspace:^"
"@medusajs/product": "workspace:^"
"@medusajs/promotion": "workspace:^"
"@medusajs/region": "workspace:^"
"@medusajs/types": "workspace:^"
"@medusajs/utils": "workspace:^"
babel-preset-medusa-package: "*"