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:
@@ -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
|
||||
|
||||
@@ -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 }
|
||||
|
||||
|
||||
@@ -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",
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -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",
|
||||
}),
|
||||
])
|
||||
})
|
||||
})
|
||||
@@ -91,5 +91,10 @@ module.exports = {
|
||||
resources: "shared",
|
||||
resolve: "@medusajs/cart",
|
||||
},
|
||||
[Modules.REGION]: {
|
||||
scope: "internal",
|
||||
resources: "shared",
|
||||
resolve: "@medusajs/region",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -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:*",
|
||||
|
||||
19
packages/medusa/src/api-v2/admin/regions/[id]/route.ts
Normal file
19
packages/medusa/src/api-v2/admin/regions/[id]/route.ts
Normal 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 })
|
||||
}
|
||||
30
packages/medusa/src/api-v2/admin/regions/middlewares.ts
Normal file
30
packages/medusa/src/api-v2/admin/regions/middlewares.ts
Normal 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
|
||||
),
|
||||
],
|
||||
},
|
||||
]
|
||||
27
packages/medusa/src/api-v2/admin/regions/query-config.ts
Normal file
27
packages/medusa/src/api-v2/admin/regions/query-config.ts
Normal 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,
|
||||
}
|
||||
20
packages/medusa/src/api-v2/admin/regions/route.ts
Normal file
20
packages/medusa/src/api-v2/admin/regions/route.ts
Normal 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 })
|
||||
}
|
||||
71
packages/medusa/src/api-v2/admin/regions/validators.ts
Normal file
71
packages/medusa/src/api-v2/admin/regions/validators.ts
Normal 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[]
|
||||
}
|
||||
@@ -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,
|
||||
],
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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 ?? []
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
32
packages/utils/src/common/alter-columns-helper.ts
Normal file
32
packages/utils/src/common/alter-columns-helper.ts
Normal 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
|
||||
}
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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: "*"
|
||||
|
||||
Reference in New Issue
Block a user