load resources from starter kit and introduce srcDir configuration

This commit is contained in:
Harminder Virk
2024-05-21 15:02:53 +05:30
committed by GitHub
parent 73c917fdce
commit 3dbb256ddf
35 changed files with 177 additions and 123 deletions
@@ -862,6 +862,10 @@ export type ConfigModule = {
* :::
*/
featureFlags: Record<string, boolean | string>
directories?: {
srcDir?: string
}
}
export type PluginDetails = {
@@ -1,5 +1,4 @@
import { FindConfig } from "../common"
import { RestoreReturn, SoftDeleteReturn } from "../dal"
import { IModuleService } from "../modules-sdk"
import { Context } from "../shared-context"
import { FilterableCurrencyProps, CurrencyDTO } from "./common"
+1 -1
View File
@@ -1,4 +1,4 @@
import { Dictionary, FilterQuery, OperatorMap, Order } from "./utils"
import { Dictionary, FilterQuery, Order } from "./utils"
export { FilterQuery, OperatorMap } from "./utils"
@@ -33,8 +33,6 @@ interface BaseRepositoryService<T = any> {
): Promise<TOutput>
}
type DtoBasedMutationMethods = "create" | "update"
export interface RepositoryService<T = any> extends BaseRepositoryService<T> {
find(options?: FindOptions<T>, context?: Context): Promise<T[]>
@@ -1,4 +1,4 @@
import { BaseFilterable, OperatorMap } from "../../dal"
import { BaseFilterable } from "../../dal"
import { BaseProduct } from "../product/common"
export interface BaseProductCollection {
@@ -1,6 +1,4 @@
import { BaseFilterable, OperatorMap } from "../dal"
import { StringComparisonOperator } from "../common/common"
import { FulfillmentSetDTO } from "../fulfillment"
/**
@@ -3,7 +3,6 @@ import {
FilterableStockLocationProps,
StockLocationDTO,
UpdateStockLocationInput,
UpdateStockLocationNextInput,
UpsertStockLocationInput,
} from "./common"
import { RestoreReturn, SoftDeleteReturn } from "../dal"
@@ -7,10 +7,6 @@ import {
MedusaResponse,
} from "../../../../types/routing"
import {
ContainerRegistrationKeys,
remoteQueryObjectFromString,
} from "@medusajs/utils"
import { refetchApiKey } from "../helpers"
import { AdminUpdateApiKeyType } from "../validators"
@@ -31,7 +27,7 @@ export const POST = async (
req: AuthenticatedMedusaRequest<AdminUpdateApiKeyType>,
res: MedusaResponse
) => {
const { result, errors } = await updateApiKeysWorkflow(req.scope).run({
const { errors } = await updateApiKeysWorkflow(req.scope).run({
input: {
selector: { id: req.params.id },
update: req.validatedBody,
@@ -43,7 +43,7 @@ export const POST = async (
},
]
const { result, errors } = await updateCampaigns.run({
const { errors } = await updateCampaigns.run({
input: { campaignsData },
throwOnError: false,
})
@@ -14,7 +14,7 @@ export const POST = async (
const { add = [], remove = [] } = req.validatedBody
const workflow = batchLinkProductsToCollectionWorkflow(req.scope)
const { result, errors } = await workflow.run({
const { errors } = await workflow.run({
input: {
id,
add,
@@ -27,7 +27,7 @@ export const POST = async (
req: AuthenticatedMedusaRequest<AdminUpdateCollectionType>,
res: MedusaResponse
) => {
const { result, errors } = await updateCollectionsWorkflow(req.scope).run({
const { errors } = await updateCollectionsWorkflow(req.scope).run({
input: {
selector: { id: req.params.id },
update: req.validatedBody,
@@ -28,7 +28,7 @@ export const POST = async (
res: MedusaResponse
) => {
const updateGroups = updateCustomerGroupsWorkflow(req.scope)
const { result, errors } = await updateGroups.run({
const { errors } = await updateGroups.run({
input: {
selector: { id: req.params.id },
update: req.validatedBody,
@@ -37,7 +37,7 @@ export const POST = async (
res: MedusaResponse
) => {
const updateAddresses = updateCustomerAddressesWorkflow(req.scope)
const { result, errors } = await updateAddresses.run({
const { errors } = await updateAddresses.run({
input: {
selector: { id: req.params.address_id, customer_id: req.params.id },
update: req.validatedBody,
@@ -49,7 +49,7 @@ export const POST = async (
},
]
const { result, errors } = await createAddresses.run({
const { errors } = await createAddresses.run({
input: { addresses },
throwOnError: false,
})
@@ -34,7 +34,7 @@ export const POST = async (
)
const workflow = batchPriceListPricesWorkflow(req.scope)
const { result, errors } = await workflow.run({
const { errors } = await workflow.run({
input: {
data: {
id,
@@ -38,7 +38,7 @@ export const POST = async (
res: MedusaResponse
) => {
const workflow = updatePricingRuleTypesWorkflow(req.scope)
const { result, errors } = await workflow.run({
const { errors } = await workflow.run({
input: {
data: [{ ...req.validatedBody, id: req.params.id }],
},
@@ -42,7 +42,7 @@ export const POST = async (
const productId = req.params.id
const optionId = req.params.option_id
const { result, errors } = await updateProductOptionsWorkflow(req.scope).run({
const { errors } = await updateProductOptionsWorkflow(req.scope).run({
input: {
selector: { id: optionId, product_id: productId },
update: req.validatedBody,
@@ -51,7 +51,7 @@ export const POST = async (
},
]
const { result, errors } = await createProductOptionsWorkflow(req.scope).run({
const { errors } = await createProductOptionsWorkflow(req.scope).run({
input: { product_options: input },
throwOnError: false,
})
@@ -56,12 +56,10 @@ export const POST = async (
},
]
const { result, errors } = await createProductVariantsWorkflow(req.scope).run(
{
input: { product_variants: input },
throwOnError: false,
}
)
const { errors } = await createProductVariantsWorkflow(req.scope).run({
input: { product_variants: input },
throwOnError: false,
})
if (Array.isArray(errors) && errors[0]) {
throw errors[0].error
@@ -54,7 +54,7 @@ export const POST = async (
} as any,
]
const { result, errors } = await updatePromotions.run({
const { errors } = await updatePromotions.run({
input: { promotionsData },
throwOnError: false,
})
@@ -54,7 +54,7 @@ export const POST = async (
],
}
const { result } = await workflow.run({ input })
await workflow.run({ input })
const user = await refetchUser(
req.params.id,
+4 -6
View File
@@ -3,7 +3,7 @@ import { Express } from "express"
type Options = {
app: Express
configModule: ConfigModule
adminConfig: ConfigModule["admin"]
}
type IntializedOptions = Required<
@@ -11,17 +11,15 @@ type IntializedOptions = Required<
> &
AdminOptions
export default async function adminLoader({ app, configModule }: Options) {
const { admin } = configModule
export default async function adminLoader({ app, adminConfig }: Options) {
const adminOptions: IntializedOptions = {
disable: false,
path: "/app",
outDir: "./build",
...admin,
...adminConfig,
}
if (admin?.disable) {
if (adminOptions?.disable) {
return app
}
+21 -8
View File
@@ -1,19 +1,17 @@
import { ConfigModule } from "@medusajs/types"
import { FlagRouter } from "@medusajs/utils"
import { AwilixContainer } from "awilix"
import { ContainerRegistrationKeys } from "@medusajs/utils"
import { Express } from "express"
import path from "path"
import qs from "qs"
import { RoutesLoader } from "./helpers/routing"
import { MedusaContainer, PluginDetails } from "@medusajs/types"
type Options = {
app: Express
container: AwilixContainer
configModule: ConfigModule
featureFlagRouter?: FlagRouter
plugins: PluginDetails[]
container: MedusaContainer
}
export default async ({ app, configModule }: Options) => {
export default async ({ app, container, plugins }: Options) => {
// This is a workaround for the issue described here: https://github.com/expressjs/express/issues/3454
// We parse the url and get the qs to be parsed and override the query prop from the request
app.use(function (req, res, next) {
@@ -26,6 +24,10 @@ export default async ({ app, configModule }: Options) => {
next()
})
const configModule = container.resolve(
ContainerRegistrationKeys.CONFIG_MODULE
)
// TODO: Figure out why this is causing issues with test when placed inside ./api.ts
// Adding this here temporarily
// Test: (packages/medusa/src/api/routes/admin/currencies/update-currency.ts)
@@ -40,9 +42,20 @@ export default async ({ app, configModule }: Options) => {
}).load()
} catch (err) {
throw Error(
"An error occurred while registering Medusa Core API Routes. See error in logs for more details."
"An error occurred while registering Medusa Core API Routes. See error in logs for more details.",
{ cause: err }
)
}
await Promise.all(
plugins.map(async (pluginDetails) => {
return new RoutesLoader({
app: app,
configModule,
rootDir: path.join(pluginDetails.resolve, "api"),
}).load()
})
)
return app
}
+3
View File
@@ -102,5 +102,8 @@ export default (rootDirectory: string): ConfigModule => {
modules: configModule.modules ?? {},
featureFlags: configModule?.featureFlags ?? {},
plugins: configModule?.plugins ?? [],
directories: configModule?.directories ?? {
srcDir: "dist",
},
}
}
@@ -1,21 +1,22 @@
import { PluginDetails } from "@medusajs/types"
import { glob } from "glob"
import { getResolvedPlugins } from "./resolve-plugins"
import { PluginDetails } from "@medusajs/types"
/**
* import files from the workflows directory to run the registration of the wofklows
* @param pluginDetails
*/
async function registerWorkflows(pluginDetails: PluginDetails): Promise<void> {
const files = glob.sync(`${pluginDetails.resolve}/workflows/*.js`, {})
await Promise.all(files.map(async (file) => import(file)))
}
export async function registerProjectWorkflows({
rootDirectory,
configModule,
}) {
const [resolved] =
getResolvedPlugins(rootDirectory, configModule, "dist", true) || []
await registerWorkflows(resolved)
export async function registerWorkflows(
plugins: PluginDetails[]
): Promise<void> {
await Promise.all(
plugins.map(async (pluginDetails) => {
const files = glob.sync(
`${pluginDetails.resolve}/workflows/*.{ts,js,mjs,mts}`,
{
ignore: ["**/*.d.ts", "**/*.map"],
}
)
return Promise.all(files.map(async (file) => import(file)))
})
)
}
@@ -106,15 +106,22 @@ function resolvePlugin(pluginName: string): {
export function getResolvedPlugins(
rootDirectory: string,
configModule: ConfigModule,
extensionDirectoryPath = "dist",
configModule: {
plugins: ConfigModule["plugins"]
directories?: ConfigModule["directories"]
},
isMedusaProject = false
): undefined | PluginDetails[] {
const { plugins } = configModule
if (isMedusaProject) {
/**
* Grab directory for loading resources inside a starter kit from
* the medusa-config file.
*
* This is because, we do not have a "dist" directory inside a starter
* kit. Instead we discover resources from the "src" directory.
*/
const extensionDirectoryPath = configModule.directories?.srcDir ?? "dist"
const extensionDirectory = path.join(rootDirectory, extensionDirectoryPath)
return [
{
resolve: extensionDirectory,
@@ -126,7 +133,8 @@ export function getResolvedPlugins(
]
}
const resolved = plugins.map((plugin) => {
const extensionDirectoryPath = "dist"
const resolved = configModule?.plugins.map((plugin) => {
if (isString(plugin)) {
return resolvePlugin(plugin)
}
@@ -1,11 +1,14 @@
import { ConfigModule } from "@medusajs/types"
export const customersGlobalMiddlewareMock = jest.fn()
export const customersCreateMiddlewareMock = jest.fn()
export const storeGlobalMiddlewareMock = jest.fn()
export const config = {
export const config: ConfigModule = {
projectConfig: {
database_logging: false,
http: {
authCors: "http://localhost:9000",
storeCors: "http://localhost:8000",
adminCors: "http://localhost:7001",
jwtSecret: "supersecret",
@@ -3,14 +3,18 @@ import {
ModulesDefinition,
registerMedusaModule,
} from "@medusajs/modules-sdk"
import { ContainerRegistrationKeys } from "@medusajs/utils"
import { asValue, createContainer } from "awilix"
import {
ContainerRegistrationKeys,
createMedusaContainer,
} from "@medusajs/utils"
import { asValue } from "awilix"
import express from "express"
import jwt from "jsonwebtoken"
import { MockManager } from "medusa-test-utils"
import querystring from "querystring"
import supertest from "supertest"
import apiLoader from "../../../../api"
import { getResolvedPlugins } from "../../../../helpers/resolve-plugins"
import featureFlagLoader, { featureFlagRouter } from "../../../../feature-flags"
import passportLoader from "../../../../passport"
@@ -42,7 +46,7 @@ export const createServer = async (rootDir) => {
)[moduleKey]
})
const container = createContainer()
const container = createMedusaContainer()
container.registerAdd = function (name, registration) {
const storeKey = name + "_STORE"
@@ -83,8 +87,10 @@ export const createServer = async (rootDir) => {
next()
})
const plugins = getResolvedPlugins(rootDir, config) || []
featureFlagLoader(config)
await passportLoader({ app: app, container, configModule: config })
await passportLoader({ app: app, configModule: config })
await moduleLoader({ container, moduleResolutions })
app.use((req, res, next) => {
@@ -103,14 +109,13 @@ export const createServer = async (rootDir) => {
await apiLoader({
container,
app: app,
configModule: config,
featureFlagRouter,
plugins,
})
const superRequest = supertest(app)
return {
request: async (method, url, opts = {}) => {
request: async (method, url, opts: any = {}) => {
const { payload, query, headers = {} } = opts
const queryParams = query && querystring.stringify(query)
@@ -124,7 +129,7 @@ export const createServer = async (rootDir) => {
user_id: opts.adminSession.userId || opts.adminSession.jwt?.userId,
domain: "admin",
},
config.projectConfig.http.jwtSecret
config.projectConfig.http.jwtSecret!
)
headers.Authorization = `Bearer ${token}`
@@ -137,7 +142,7 @@ export const createServer = async (rootDir) => {
opts.clientSession.jwt?.customer_id,
domain: "store",
},
config.projectConfig.http.jwtSecret
config.projectConfig.http.jwtSecret!
)
headers.Authorization = `Bearer ${token}`
@@ -568,8 +568,6 @@ export class RoutesLoader {
}
if (mostSpecificConfig?.bodyParser) {
const sizeLimit = mostSpecificConfig?.bodyParser?.sizeLimit
this.router[method.toLowerCase()](
path,
...getBodyParserMiddleware(mostSpecificConfig?.bodyParser)
+72 -38
View File
@@ -1,5 +1,5 @@
import { createDefaultsWorkflow } from "@medusajs/core-flows"
import { ConfigModule } from "@medusajs/types"
import { ConfigModule, MedusaContainer } from "@medusajs/types"
import { ContainerRegistrationKeys, promiseAll } from "@medusajs/utils"
import { asValue } from "awilix"
import { Express, NextFunction, Request, Response } from "express"
@@ -7,17 +7,19 @@ import { createMedusaContainer } from "medusa-core-utils"
import requestIp from "request-ip"
import { v4 } from "uuid"
import path from "path"
import { MedusaContainer } from "../types/global"
import adminLoader from "./admin"
import apiLoader from "./api"
import loadConfig from "./config"
import expressLoader from "./express"
import featureFlagsLoader from "./feature-flags"
import { registerProjectWorkflows } from "./helpers/register-workflows"
import { registerWorkflows } from "./helpers/register-workflows"
import Logger from "./logger"
import loadMedusaApp from "./medusa-app"
import pgConnectionLoader from "./pg-connection"
import registerPgConnection from "./pg-connection"
import { SubscriberLoader } from "./helpers/subscribers"
import { getResolvedPlugins } from "./helpers/resolve-plugins"
import { PluginDetails } from "@medusajs/types"
import glob from "glob"
type Options = {
directory: string
@@ -28,17 +30,45 @@ const isWorkerMode = (configModule) => {
return configModule.projectConfig.worker_mode === "worker"
}
async function subscribersLoader(container) {
const subscribersPath = path.join(__dirname, "../subscribers")
await new SubscriberLoader(subscribersPath, container).load()
async function subscribersLoader(
plugins: PluginDetails[],
container: MedusaContainer
) {
/**
* Load subscribers from the medusa/medusa package
*/
await new SubscriberLoader(
path.join(__dirname, "../subscribers"),
container
).load()
/**
* Load subscribers from all the plugins.
*/
await Promise.all(
plugins.map(async (pluginDetails) => {
const files = glob.sync(
`${pluginDetails.resolve}/subscribers/*.{ts,js,mjs,mts}`,
{
ignore: ["**/*.d.ts", "**/*.map"],
}
)
return Promise.all(
files.map(async (file) => new SubscriberLoader(file, container).load())
)
})
)
}
async function loadEntrypoints(
configModule,
container,
expressApp,
featureFlagRouter
plugins: PluginDetails[],
container: MedusaContainer,
expressApp: Express
) {
const configModule: ConfigModule = container.resolve(
ContainerRegistrationKeys.CONFIG_MODULE
)
if (isWorkerMode(configModule)) {
return async () => {}
}
@@ -59,33 +89,22 @@ async function loadEntrypoints(
next()
})
await adminLoader({ app: expressApp, configModule })
await subscribersLoader(container)
await adminLoader({ app: expressApp, adminConfig: configModule.admin })
await subscribersLoader(plugins, container)
await apiLoader({
container,
plugins,
app: expressApp,
configModule,
featureFlagRouter,
})
return shutdown
}
export default async ({
directory: rootDirectory,
expressApp,
}: Options): Promise<{
configModule: ConfigModule
container: MedusaContainer
app: Express
pgConnection: unknown
shutdown: () => Promise<void>
prepareShutdown: () => Promise<void>
}> => {
async function initializeContainer(rootDirectory: string) {
const container = createMedusaContainer()
const configModule = loadConfig(rootDirectory)
const featureFlagRouter = featureFlagsLoader(configModule, Logger)
const container = createMedusaContainer()
const pgConnection = await pgConnectionLoader({ container, configModule })
container.register({
[ContainerRegistrationKeys.LOGGER]: asValue(Logger),
[ContainerRegistrationKeys.FEATURE_FLAG_ROUTER]: asValue(featureFlagRouter),
@@ -93,28 +112,45 @@ export default async ({
[ContainerRegistrationKeys.REMOTE_QUERY]: asValue(null),
})
// Workflows are registered before the app to allow modules to run workflows as part of bootstrapping
// e.g. the workflow engine will resume workflows that were running when the server was shut down
await registerProjectWorkflows({ rootDirectory, configModule })
await registerPgConnection({ container, configModule })
return container
}
export default async ({
directory: rootDirectory,
expressApp,
}: Options): Promise<{
container: MedusaContainer
app: Express
shutdown: () => Promise<void>
prepareShutdown: () => Promise<void>
}> => {
const container = await initializeContainer(rootDirectory)
const configModule = container.resolve(
ContainerRegistrationKeys.CONFIG_MODULE
)
const plugins = getResolvedPlugins(rootDirectory, configModule, true) || []
await registerWorkflows(plugins)
const {
onApplicationShutdown: medusaAppOnApplicationShutdown,
onApplicationPrepareShutdown: medusaAppOnApplicationPrepareShutdown,
} = await loadMedusaApp({
configModule,
container,
})
const entrypointsShutdown = await loadEntrypoints(
configModule,
plugins,
container,
expressApp,
featureFlagRouter
expressApp
)
await createDefaultsWorkflow(container).run()
const shutdown = async () => {
const pgConnection = container.resolve(
ContainerRegistrationKeys.PG_CONNECTION
)
await medusaAppOnApplicationShutdown()
await promiseAll([
@@ -126,10 +162,8 @@ export default async ({
}
return {
configModule,
container,
app: expressApp,
pgConnection,
shutdown,
prepareShutdown: medusaAppOnApplicationPrepareShutdown,
}
+5 -5
View File
@@ -7,6 +7,7 @@ import {
} from "@medusajs/modules-sdk"
import {
CommonTypes,
ConfigModule,
InternalModuleDeclaration,
LoadedModule,
MedusaContainer,
@@ -143,13 +144,8 @@ export async function revertMedusaApp({
export const loadMedusaApp = async (
{
configModule,
container,
}: {
configModule: {
modules?: CommonTypes.ConfigModule["modules"]
projectConfig: CommonTypes.ConfigModule["projectConfig"]
}
container: MedusaContainer
},
config = { registerInContainer: true }
@@ -163,6 +159,10 @@ export const loadMedusaApp = async (
),
}
const configModule: ConfigModule = container.resolve(
ContainerRegistrationKeys.CONFIG_MODULE
)
const sharedResourcesConfig = {
database: {
clientUrl: configModule.projectConfig.database_url,
+1 -1
View File
@@ -3,8 +3,8 @@ import { ContainerRegistrationKeys, ModulesSdkUtils } from "@medusajs/utils"
import { asValue, AwilixContainer } from "awilix"
type Options = {
configModule: ConfigModule
container: AwilixContainer
configModule: ConfigModule
}
export default async ({ container, configModule }: Options): Promise<any> => {
@@ -11,7 +11,6 @@ import { toCamelCase, upperCaseFirst } from "@medusajs/utils"
export function formatRegistrationName(path: string): string {
const parsed = parse(path)
const parsedDir = parse(parsed.dir)
const rawname = parsed.name
let directoryNamespace = parsedDir.name
if (directoryNamespace.startsWith("__")) {
@@ -90,7 +90,6 @@ export function prepareListQuery<T extends RequestQueryFields, TEntity>(
}
})
const allAllowedFields = new Set(allowedFields) // In case there is no allowedFields, allow all fields
const notAllowedFields: string[] = []
if (allowedFields.length) {
+1
View File
@@ -13,6 +13,7 @@
"noImplicitReturns": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"noUnusedLocals": true,
"noImplicitThis": true,
"allowJs": true,
"skipLibCheck": true,