chore(medusa,orchestration,link-modules,modules-sdk): internal services as modules (#4925)
This commit is contained in:
1 parent
85fa90d9c6
commit
a4906d0ac0
15 files changed
+328
-90
No files matched your search
@@ -0,0 +1,8 @@
|
|||||||
|
---
|
||||||
|
"@medusajs/orchestration": minor
|
||||||
|
"@medusajs/link-modules": minor
|
||||||
|
"@medusajs/modules-sdk": minor
|
||||||
|
"@medusajs/medusa": minor
|
||||||
|
---
|
||||||
|
|
||||||
|
Use MedusaApp on core and initial JoinerConfig for internal services
|
||||||
@@ -2,6 +2,7 @@ const path = require("path")
|
|||||||
const express = require("express")
|
const express = require("express")
|
||||||
const importFrom = require("import-from")
|
const importFrom = require("import-from")
|
||||||
const chokidar = require("chokidar")
|
const chokidar = require("chokidar")
|
||||||
|
const { WorkflowManager } = require("@medusajs/orchestration")
|
||||||
|
|
||||||
process.env.DEV_MODE = !!process[Symbol.for("ts-node.register.instance")]
|
process.env.DEV_MODE = !!process[Symbol.for("ts-node.register.instance")]
|
||||||
process.env.NODE_ENV = process.env.DEV_MODE && "development"
|
process.env.NODE_ENV = process.env.DEV_MODE && "development"
|
||||||
@@ -107,6 +108,8 @@ const watchFiles = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WorkflowManager.unregisterAll()
|
||||||
|
|
||||||
await bootstrapApp()
|
await bootstrapApp()
|
||||||
IS_RELOADING = false
|
IS_RELOADING = false
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
import { Modules } from "@medusajs/modules-sdk"
|
||||||
|
import { ModuleJoinerConfig } from "@medusajs/types"
|
||||||
|
import { LINKS } from "../links"
|
||||||
|
|
||||||
|
export const ProductShippingProfile: ModuleJoinerConfig = {
|
||||||
|
serviceName: LINKS.ProductShippingProfile,
|
||||||
|
isLink: true,
|
||||||
|
databaseConfig: {
|
||||||
|
tableName: "product_shipping_profile",
|
||||||
|
idPrefix: "psprof",
|
||||||
|
},
|
||||||
|
alias: [
|
||||||
|
{
|
||||||
|
name: "product_shipping_profile",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
primaryKeys: ["id", "product_id", "profile_id"],
|
||||||
|
relationships: [
|
||||||
|
{
|
||||||
|
serviceName: Modules.PRODUCT,
|
||||||
|
primaryKey: "id",
|
||||||
|
foreignKey: "product_id",
|
||||||
|
alias: "product",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
serviceName: "shippingProfileService",
|
||||||
|
primaryKey: "id",
|
||||||
|
foreignKey: "profile_id",
|
||||||
|
alias: "shipping_profile",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
extends: [
|
||||||
|
{
|
||||||
|
serviceName: Modules.PRODUCT,
|
||||||
|
relationship: {
|
||||||
|
serviceName: LINKS.ProductShippingProfile,
|
||||||
|
primaryKey: "product_id",
|
||||||
|
foreignKey: "id",
|
||||||
|
alias: "shipping_profile",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
serviceName: "shippingProfileService",
|
||||||
|
relationship: {
|
||||||
|
serviceName: LINKS.ProductShippingProfile,
|
||||||
|
primaryKey: "profile_id",
|
||||||
|
foreignKey: "id",
|
||||||
|
alias: "product_link",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
@@ -14,4 +14,12 @@ export const LINKS = {
|
|||||||
Modules.PRICING,
|
Modules.PRICING,
|
||||||
"money_amount_id"
|
"money_amount_id"
|
||||||
),
|
),
|
||||||
|
|
||||||
|
// Internal services
|
||||||
|
ProductShippingProfile: composeLinkName(
|
||||||
|
Modules.PRODUCT,
|
||||||
|
"variant_id",
|
||||||
|
"shippingProfileService",
|
||||||
|
"profile_id"
|
||||||
|
),
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
import { Modules } from "@medusajs/modules-sdk"
|
||||||
|
import { ModuleJoinerConfig } from "@medusajs/types"
|
||||||
|
|
||||||
|
export const joinerConfig: ModuleJoinerConfig[] = [
|
||||||
|
{
|
||||||
|
serviceName: "cartService",
|
||||||
|
primaryKeys: ["id"],
|
||||||
|
linkableKeys: ["cart_id"],
|
||||||
|
alias: [
|
||||||
|
{
|
||||||
|
name: "cart",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
relationships: [
|
||||||
|
{
|
||||||
|
serviceName: Modules.PRODUCT,
|
||||||
|
primaryKey: "id",
|
||||||
|
foreignKey: "variant_id",
|
||||||
|
alias: "variant",
|
||||||
|
args: {
|
||||||
|
methodSuffix: "Variants",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
serviceName: "regionService",
|
||||||
|
primaryKey: "id",
|
||||||
|
foreignKey: "region_id",
|
||||||
|
alias: "region",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
serviceName: "customerService",
|
||||||
|
primaryKey: "id",
|
||||||
|
foreignKey: "customer_id",
|
||||||
|
alias: "customer",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
serviceName: "shippingProfileService",
|
||||||
|
primaryKeys: ["id"],
|
||||||
|
linkableKeys: ["profile_id"],
|
||||||
|
alias: [
|
||||||
|
{
|
||||||
|
name: "shipping_profile",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "shipping_profiles",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
serviceName: "regionService",
|
||||||
|
primaryKeys: ["id"],
|
||||||
|
linkableKeys: ["region_id"],
|
||||||
|
alias: [
|
||||||
|
{
|
||||||
|
name: "region",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "regions",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
serviceName: "customerService",
|
||||||
|
primaryKeys: ["id"],
|
||||||
|
linkableKeys: ["customer_id"],
|
||||||
|
alias: [
|
||||||
|
{
|
||||||
|
name: "customer",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "customers",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import { FlagSettings } from "../../types/feature-flags"
|
||||||
|
|
||||||
|
const IsolateProductDomainFeatureFlag: FlagSettings = {
|
||||||
|
key: "isolate_product_domain",
|
||||||
|
default_val: false,
|
||||||
|
env_key: "MEDUSA_FF_ISOLATE_PRODUCT_DOMAIN",
|
||||||
|
description: "[WIP] Isolate product domain dependencies from the core",
|
||||||
|
}
|
||||||
|
|
||||||
|
export default IsolateProductDomainFeatureFlag
|
||||||
@@ -1,20 +1,28 @@
|
|||||||
|
import { MedusaApp, moduleLoader, registerModules } from "@medusajs/modules-sdk"
|
||||||
|
import { ContainerRegistrationKeys } from "@medusajs/utils"
|
||||||
import { asValue } from "awilix"
|
import { asValue } from "awilix"
|
||||||
import { Express, NextFunction, Request, Response } from "express"
|
import { Express, NextFunction, Request, Response } from "express"
|
||||||
|
import { createMedusaContainer } from "medusa-core-utils"
|
||||||
import { track } from "medusa-telemetry"
|
import { track } from "medusa-telemetry"
|
||||||
import { EOL } from "os"
|
import { EOL } from "os"
|
||||||
import "reflect-metadata"
|
import "reflect-metadata"
|
||||||
import requestIp from "request-ip"
|
import requestIp from "request-ip"
|
||||||
import { Connection } from "typeorm"
|
import { Connection } from "typeorm"
|
||||||
|
import { joinerConfig } from "../joiner-config"
|
||||||
|
import modulesConfig from "../modules-config"
|
||||||
import { MedusaContainer } from "../types/global"
|
import { MedusaContainer } from "../types/global"
|
||||||
|
import { remoteQueryFetchData } from "../utils"
|
||||||
import apiLoader from "./api"
|
import apiLoader from "./api"
|
||||||
import loadConfig from "./config"
|
import loadConfig from "./config"
|
||||||
import databaseLoader, { dataSource } from "./database"
|
import databaseLoader, { dataSource } from "./database"
|
||||||
import defaultsLoader from "./defaults"
|
import defaultsLoader from "./defaults"
|
||||||
import expressLoader from "./express"
|
import expressLoader from "./express"
|
||||||
import featureFlagsLoader from "./feature-flags"
|
import featureFlagsLoader from "./feature-flags"
|
||||||
|
import IsolateProductDomainFeatureFlag from "./feature-flags/isolate-product-domain"
|
||||||
import Logger from "./logger"
|
import Logger from "./logger"
|
||||||
import modelsLoader from "./models"
|
import modelsLoader from "./models"
|
||||||
import passportLoader from "./passport"
|
import passportLoader from "./passport"
|
||||||
|
import pgConnectionLoader from "./pg-connection"
|
||||||
import pluginsLoader, { registerPluginModels } from "./plugins"
|
import pluginsLoader, { registerPluginModels } from "./plugins"
|
||||||
import redisLoader from "./redis"
|
import redisLoader from "./redis"
|
||||||
import repositoriesLoader from "./repositories"
|
import repositoriesLoader from "./repositories"
|
||||||
@@ -23,11 +31,6 @@ import servicesLoader from "./services"
|
|||||||
import strategiesLoader from "./strategies"
|
import strategiesLoader from "./strategies"
|
||||||
import subscribersLoader from "./subscribers"
|
import subscribersLoader from "./subscribers"
|
||||||
|
|
||||||
import { moduleLoader, registerModules } from "@medusajs/modules-sdk"
|
|
||||||
import { createMedusaContainer } from "medusa-core-utils"
|
|
||||||
import pgConnectionLoader from "./pg-connection"
|
|
||||||
import { ContainerRegistrationKeys } from "@medusajs/utils"
|
|
||||||
|
|
||||||
type Options = {
|
type Options = {
|
||||||
directory: string
|
directory: string
|
||||||
expressApp: Express
|
expressApp: Express
|
||||||
@@ -182,5 +185,20 @@ export default async ({
|
|||||||
Logger.success(searchActivity, "Indexing event emitted") || {}
|
Logger.success(searchActivity, "Indexing event emitted") || {}
|
||||||
track("SEARCH_ENGINE_INDEXING_COMPLETED", { duration: searchAct.duration })
|
track("SEARCH_ENGINE_INDEXING_COMPLETED", { duration: searchAct.duration })
|
||||||
|
|
||||||
|
if (featureFlagRouter.isFeatureEnabled(IsolateProductDomainFeatureFlag.key)) {
|
||||||
|
const { query } = await MedusaApp({
|
||||||
|
modulesConfig,
|
||||||
|
servicesConfig: joinerConfig,
|
||||||
|
remoteFetchData: remoteQueryFetchData(container),
|
||||||
|
injectedDependencies: {
|
||||||
|
[ContainerRegistrationKeys.PG_CONNECTION]: container.resolve(
|
||||||
|
ContainerRegistrationKeys.PG_CONNECTION
|
||||||
|
),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
container.register("remoteQuery", asValue(query))
|
||||||
|
}
|
||||||
|
|
||||||
return { container, dbConnection, app: expressApp }
|
return { container, dbConnection, app: expressApp }
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import { MedusaModuleConfig, Modules } from "@medusajs/modules-sdk"
|
||||||
|
|
||||||
|
const modules: MedusaModuleConfig = [
|
||||||
|
{
|
||||||
|
module: Modules.PRODUCT,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
export default modules
|
||||||
@@ -11,8 +11,9 @@ export * from "./is-object"
|
|||||||
export * from "./is-string"
|
export * from "./is-string"
|
||||||
export * from "./omit-deep"
|
export * from "./omit-deep"
|
||||||
export * from "./product-category"
|
export * from "./product-category"
|
||||||
|
export * from "./remote-query-fetch-data"
|
||||||
export * from "./remove-undefined-properties"
|
export * from "./remove-undefined-properties"
|
||||||
export * from "./set-metadata"
|
export * from "./set-metadata"
|
||||||
export * from "./validate-id"
|
export * from "./validate-id"
|
||||||
export * from "./validators/is-type"
|
|
||||||
export { registerOverriddenValidators, validator } from "./validator"
|
export { registerOverriddenValidators, validator } from "./validator"
|
||||||
|
export * from "./validators/is-type"
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
import { MedusaModule, RemoteQuery } from "@medusajs/modules-sdk"
|
||||||
|
import { MedusaContainer } from "@medusajs/types"
|
||||||
|
|
||||||
|
export function remoteQueryFetchData(container: MedusaContainer) {
|
||||||
|
return async (expand, keyField, ids, relationship) => {
|
||||||
|
const serviceConfig = expand.serviceConfig
|
||||||
|
const service = container.resolve(serviceConfig.serviceName, {
|
||||||
|
allowUnregistered: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (MedusaModule.isInstalled(serviceConfig.serviceName)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const filters = {}
|
||||||
|
const options = {
|
||||||
|
...RemoteQuery.getAllFieldsAndRelations(expand),
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ids) {
|
||||||
|
filters[keyField] = ids
|
||||||
|
}
|
||||||
|
|
||||||
|
const hasPagination = Object.keys(options).some((key) =>
|
||||||
|
["skip"].includes(key)
|
||||||
|
)
|
||||||
|
|
||||||
|
let methodName = hasPagination ? "listAndCount" : "list"
|
||||||
|
|
||||||
|
if (relationship?.args?.methodSuffix) {
|
||||||
|
methodName += relationship.args.methodSuffix
|
||||||
|
} else if (serviceConfig?.args?.methodSuffix) {
|
||||||
|
methodName += serviceConfig.args.methodSuffix
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await service[methodName](filters, options)
|
||||||
|
|
||||||
|
if (hasPagination) {
|
||||||
|
const [data, count] = result
|
||||||
|
return {
|
||||||
|
data: {
|
||||||
|
rows: data,
|
||||||
|
metadata: {},
|
||||||
|
},
|
||||||
|
path: "rows",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: result,
|
||||||
|
} as any
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -36,20 +36,30 @@ export type SharedResources = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function MedusaApp({
|
export async function MedusaApp(
|
||||||
|
{
|
||||||
sharedResourcesConfig,
|
sharedResourcesConfig,
|
||||||
|
servicesConfig,
|
||||||
modulesConfigPath,
|
modulesConfigPath,
|
||||||
|
modulesConfigFileName,
|
||||||
modulesConfig,
|
modulesConfig,
|
||||||
linkModules,
|
linkModules,
|
||||||
remoteFetchData,
|
remoteFetchData,
|
||||||
}: {
|
injectedDependencies,
|
||||||
|
}: {
|
||||||
sharedResourcesConfig?: SharedResources
|
sharedResourcesConfig?: SharedResources
|
||||||
loadedModules?: LoadedModule[]
|
loadedModules?: LoadedModule[]
|
||||||
|
servicesConfig?: ModuleJoinerConfig[]
|
||||||
modulesConfigPath?: string
|
modulesConfigPath?: string
|
||||||
|
modulesConfigFileName?: string
|
||||||
modulesConfig?: MedusaModuleConfig
|
modulesConfig?: MedusaModuleConfig
|
||||||
linkModules?: ModuleJoinerConfig | ModuleJoinerConfig[]
|
linkModules?: ModuleJoinerConfig | ModuleJoinerConfig[]
|
||||||
remoteFetchData?: RemoteFetchDataCallback
|
remoteFetchData?: RemoteFetchDataCallback
|
||||||
} = {}): Promise<{
|
injectedDependencies?: any
|
||||||
|
} = {
|
||||||
|
injectedDependencies: {},
|
||||||
|
}
|
||||||
|
): Promise<{
|
||||||
modules: Record<string, LoadedModule | LoadedModule[]>
|
modules: Record<string, LoadedModule | LoadedModule[]>
|
||||||
link: RemoteLink | undefined
|
link: RemoteLink | undefined
|
||||||
query: (
|
query: (
|
||||||
@@ -59,10 +69,12 @@ export async function MedusaApp({
|
|||||||
}> {
|
}> {
|
||||||
const modules: MedusaModuleConfig =
|
const modules: MedusaModuleConfig =
|
||||||
modulesConfig ??
|
modulesConfig ??
|
||||||
(await import(process.cwd() + (modulesConfigPath ?? "/modules-config")))
|
(
|
||||||
.default
|
await import(
|
||||||
|
modulesConfigPath ??
|
||||||
const injectedDependencies: any = {}
|
process.cwd() + (modulesConfigFileName ?? "/modules-config")
|
||||||
|
)
|
||||||
|
).default
|
||||||
|
|
||||||
const dbData = ModulesSdkUtils.loadDatabaseConfig(
|
const dbData = ModulesSdkUtils.loadDatabaseConfig(
|
||||||
"medusa",
|
"medusa",
|
||||||
@@ -167,7 +179,10 @@ export async function MedusaApp({
|
|||||||
console.warn("Error initializing link modules.", err)
|
console.warn("Error initializing link modules.", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
const remoteQuery = new RemoteQuery(undefined, remoteFetchData)
|
const remoteQuery = new RemoteQuery({
|
||||||
|
servicesConfig,
|
||||||
|
customRemoteFetchData: remoteFetchData,
|
||||||
|
})
|
||||||
query = async (
|
query = async (
|
||||||
query: string | RemoteJoinerQuery,
|
query: string | RemoteJoinerQuery,
|
||||||
variables?: Record<string, unknown>
|
variables?: Record<string, unknown>
|
||||||
|
|||||||
@@ -17,8 +17,17 @@ export class RemoteQuery {
|
|||||||
private customRemoteFetchData?: RemoteFetchDataCallback
|
private customRemoteFetchData?: RemoteFetchDataCallback
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
modulesLoaded?: LoadedModule[],
|
{
|
||||||
|
modulesLoaded,
|
||||||
|
customRemoteFetchData,
|
||||||
|
servicesConfig,
|
||||||
|
}: {
|
||||||
|
modulesLoaded?: LoadedModule[]
|
||||||
customRemoteFetchData?: RemoteFetchDataCallback
|
customRemoteFetchData?: RemoteFetchDataCallback
|
||||||
|
servicesConfig?: ModuleJoinerConfig[]
|
||||||
|
} = {
|
||||||
|
servicesConfig: [],
|
||||||
|
}
|
||||||
) {
|
) {
|
||||||
if (!modulesLoaded?.length) {
|
if (!modulesLoaded?.length) {
|
||||||
modulesLoaded = MedusaModule.getLoadedModules().map(
|
modulesLoaded = MedusaModule.getLoadedModules().map(
|
||||||
@@ -26,7 +35,6 @@ export class RemoteQuery {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const servicesConfig: ModuleJoinerConfig[] = []
|
|
||||||
for (const mod of modulesLoaded) {
|
for (const mod of modulesLoaded) {
|
||||||
if (!mod.__definition.isQueryable) {
|
if (!mod.__definition.isQueryable) {
|
||||||
continue
|
continue
|
||||||
@@ -41,7 +49,7 @@ export class RemoteQuery {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.modulesMap.set(serviceName, mod)
|
this.modulesMap.set(serviceName, mod)
|
||||||
servicesConfig.push(mod.__joinerConfig)
|
servicesConfig!.push(mod.__joinerConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.customRemoteFetchData = customRemoteFetchData
|
this.customRemoteFetchData = customRemoteFetchData
|
||||||
@@ -65,7 +73,7 @@ export class RemoteQuery {
|
|||||||
this.remoteJoiner.setFetchDataCallback(remoteFetchData)
|
this.remoteJoiner.setFetchDataCallback(remoteFetchData)
|
||||||
}
|
}
|
||||||
|
|
||||||
private static getAllFieldsAndRelations(
|
public static getAllFieldsAndRelations(
|
||||||
data: any,
|
data: any,
|
||||||
prefix = "",
|
prefix = "",
|
||||||
args: Record<string, unknown[]> = {}
|
args: Record<string, unknown[]> = {}
|
||||||
|
|||||||
@@ -317,13 +317,13 @@ describe("RemoteJoiner", () => {
|
|||||||
|
|
||||||
expect(serviceMock.productService).toHaveBeenCalledTimes(2)
|
expect(serviceMock.productService).toHaveBeenCalledTimes(2)
|
||||||
expect(serviceMock.productService).toHaveBeenNthCalledWith(1, {
|
expect(serviceMock.productService).toHaveBeenNthCalledWith(1, {
|
||||||
fields: ["name", "id"],
|
|
||||||
options: { id: expect.arrayContaining([103, 102]) },
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(serviceMock.productService).toHaveBeenNthCalledWith(2, {
|
|
||||||
fields: ["handler", "id"],
|
fields: ["handler", "id"],
|
||||||
options: { id: expect.arrayContaining([101, 103]) },
|
options: { id: expect.arrayContaining([101, 103]) },
|
||||||
})
|
})
|
||||||
|
|
||||||
|
expect(serviceMock.productService).toHaveBeenNthCalledWith(2, {
|
||||||
|
fields: ["name", "id"],
|
||||||
|
options: { id: expect.arrayContaining([103, 102]) },
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -290,46 +290,38 @@ export class RemoteJoiner {
|
|||||||
if (!parsedExpands) {
|
if (!parsedExpands) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
const resolvedPaths = new Set<string>()
|
||||||
const stack: [
|
const stack: [any[], Partial<RemoteJoinerQuery>, string][] = [
|
||||||
any[],
|
[items, query, BASE_PATH],
|
||||||
Partial<RemoteJoinerQuery>,
|
]
|
||||||
Map<string, RemoteExpandProperty>,
|
|
||||||
string,
|
|
||||||
Set<string>
|
|
||||||
][] = [[items, query, parsedExpands, BASE_PATH, new Set()]]
|
|
||||||
|
|
||||||
while (stack.length > 0) {
|
while (stack.length > 0) {
|
||||||
const [
|
const [currentItems, currentQuery, basePath] = stack.pop()!
|
||||||
currentItems,
|
|
||||||
currentQuery,
|
|
||||||
currentParsedExpands,
|
|
||||||
basePath,
|
|
||||||
resolvedPaths,
|
|
||||||
] = stack.pop()!
|
|
||||||
|
|
||||||
for (const [expandedPath, expand] of currentParsedExpands.entries()) {
|
for (const [expandedPath, expand] of parsedExpands.entries()) {
|
||||||
const isImmediateChildPath = basePath === expand.parent
|
const isParentPath = expandedPath.startsWith(basePath)
|
||||||
|
|
||||||
if (!isImmediateChildPath || resolvedPaths.has(expandedPath)) {
|
if (!isParentPath || resolvedPaths.has(expandedPath)) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
resolvedPaths.add(expandedPath)
|
resolvedPaths.add(expandedPath)
|
||||||
|
|
||||||
const property = expand.property || ""
|
const property = expand.property || ""
|
||||||
const parentServiceConfig = this.getServiceConfig(
|
|
||||||
currentQuery.service,
|
let curItems = currentItems
|
||||||
currentQuery.alias
|
const expandedPathLevels = expandedPath.split(".")
|
||||||
|
for (let idx = 1; idx < expandedPathLevels.length - 1; idx++) {
|
||||||
|
curItems = RemoteJoiner.getNestedItems(
|
||||||
|
curItems,
|
||||||
|
expandedPathLevels[idx]
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
await this.expandProperty(currentItems, parentServiceConfig!, expand)
|
await this.expandProperty(curItems, expand.parentConfig!, expand)
|
||||||
|
|
||||||
const nestedItems = RemoteJoiner.getNestedItems(currentItems, property)
|
const nestedItems = RemoteJoiner.getNestedItems(currentItems, property)
|
||||||
|
|
||||||
if (nestedItems.length > 0) {
|
if (nestedItems.length > 0) {
|
||||||
const relationship = expand.serviceConfig
|
const relationship = expand.serviceConfig
|
||||||
|
|
||||||
let nextProp = currentQuery
|
let nextProp = currentQuery
|
||||||
if (relationship) {
|
if (relationship) {
|
||||||
const relQuery = {
|
const relQuery = {
|
||||||
@@ -337,14 +329,7 @@ export class RemoteJoiner {
|
|||||||
}
|
}
|
||||||
nextProp = relQuery
|
nextProp = relQuery
|
||||||
}
|
}
|
||||||
|
stack.push([nestedItems, nextProp, expandedPath])
|
||||||
stack.push([
|
|
||||||
nestedItems,
|
|
||||||
nextProp,
|
|
||||||
currentParsedExpands,
|
|
||||||
expandedPath,
|
|
||||||
new Set(),
|
|
||||||
])
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -538,12 +523,14 @@ export class RemoteJoiner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!parsedExpands.has(fullPath)) {
|
if (!parsedExpands.has(fullPath)) {
|
||||||
|
const parentPath = [BASE_PATH, ...currentPath].join(".")
|
||||||
parsedExpands.set(fullPath, {
|
parsedExpands.set(fullPath, {
|
||||||
property: prop,
|
property: prop,
|
||||||
serviceConfig: currentServiceConfig,
|
serviceConfig: currentServiceConfig,
|
||||||
fields,
|
fields,
|
||||||
args,
|
args,
|
||||||
parent: [BASE_PATH, ...currentPath].join("."),
|
parent: parentPath,
|
||||||
|
parentConfig: parsedExpands.get(parentPath).serviceConfig,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -556,53 +543,41 @@ export class RemoteJoiner {
|
|||||||
private groupExpands(
|
private groupExpands(
|
||||||
parsedExpands: Map<string, RemoteExpandProperty>
|
parsedExpands: Map<string, RemoteExpandProperty>
|
||||||
): Map<string, RemoteExpandProperty> {
|
): Map<string, RemoteExpandProperty> {
|
||||||
const sortedParsedExpands = new Map(
|
const mergedExpands = new Map<string, RemoteExpandProperty>(parsedExpands)
|
||||||
Array.from(parsedExpands.entries()).sort()
|
|
||||||
)
|
|
||||||
|
|
||||||
const mergedExpands = new Map<string, RemoteExpandProperty>(
|
|
||||||
sortedParsedExpands
|
|
||||||
)
|
|
||||||
const mergedPaths = new Map<string, string>()
|
const mergedPaths = new Map<string, string>()
|
||||||
|
|
||||||
let lastServiceName = ""
|
for (const [path, expand] of mergedExpands.entries()) {
|
||||||
|
|
||||||
for (const [path, expand] of sortedParsedExpands.entries()) {
|
|
||||||
const currentServiceName = expand.serviceConfig.serviceName
|
const currentServiceName = expand.serviceConfig.serviceName
|
||||||
|
|
||||||
let parentPath = expand.parent
|
let parentPath = expand.parent
|
||||||
|
|
||||||
// Check if the parentPath was merged before
|
while (parentPath) {
|
||||||
while (mergedPaths.has(parentPath)) {
|
const parentExpand = mergedExpands.get(parentPath)
|
||||||
parentPath = mergedPaths.get(parentPath)!
|
if (
|
||||||
|
!parentExpand ||
|
||||||
|
parentExpand.serviceConfig.serviceName !== currentServiceName
|
||||||
|
) {
|
||||||
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
const canMerge = currentServiceName === lastServiceName
|
// Merge the current expand into its parent
|
||||||
|
|
||||||
if (mergedExpands.has(parentPath) && canMerge) {
|
|
||||||
const parentExpand = mergedExpands.get(parentPath)!
|
|
||||||
|
|
||||||
if (parentExpand.serviceConfig.serviceName === currentServiceName) {
|
|
||||||
const nestedKeys = path.split(".").slice(parentPath.split(".").length)
|
const nestedKeys = path.split(".").slice(parentPath.split(".").length)
|
||||||
|
let targetExpand = parentExpand as Omit<
|
||||||
|
RemoteExpandProperty,
|
||||||
|
"expands"
|
||||||
|
> & { expands?: {} }
|
||||||
|
|
||||||
let targetExpand: any = parentExpand
|
for (const key of nestedKeys) {
|
||||||
|
targetExpand.expands ??= {}
|
||||||
for (let key of nestedKeys) {
|
targetExpand = targetExpand.expands[key] ??= {}
|
||||||
if (!targetExpand.expands) {
|
|
||||||
targetExpand.expands = {}
|
|
||||||
}
|
|
||||||
if (!targetExpand.expands[key]) {
|
|
||||||
targetExpand.expands[key] = {} as any
|
|
||||||
}
|
|
||||||
targetExpand = targetExpand.expands[key]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
targetExpand.fields = expand.fields
|
targetExpand.fields = expand.fields
|
||||||
targetExpand.args = expand.args
|
targetExpand.args = expand.args
|
||||||
|
|
||||||
|
mergedExpands.delete(path)
|
||||||
mergedPaths.set(path, parentPath)
|
mergedPaths.set(path, parentPath)
|
||||||
}
|
|
||||||
} else {
|
parentPath = parentExpand.parent
|
||||||
lastServiceName = currentServiceName
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ export interface RemoteNestedExpands {
|
|||||||
export interface RemoteExpandProperty {
|
export interface RemoteExpandProperty {
|
||||||
property: string
|
property: string
|
||||||
parent: string
|
parent: string
|
||||||
|
parentConfig?: JoinerServiceConfig
|
||||||
serviceConfig: JoinerServiceConfig
|
serviceConfig: JoinerServiceConfig
|
||||||
fields: string[]
|
fields: string[]
|
||||||
args?: JoinerArgument[]
|
args?: JoinerArgument[]
|
||||||
|
|||||||
Reference in new issue
Block a user