chore(orchestrator): remote joiner using entitymap (#9205)
This commit is contained in:
@@ -170,7 +170,7 @@ medusaIntegrationTestRunner({
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
).rejects.toThrow(
|
).rejects.toThrow(
|
||||||
"Payment id not found: pp_system_default_non_existent"
|
"PaymentProvider id not found: pp_system_default_non_existent"
|
||||||
)
|
)
|
||||||
|
|
||||||
// everything is fine
|
// everything is fine
|
||||||
|
|||||||
@@ -72,7 +72,10 @@ export class RemoteQuery {
|
|||||||
this.remoteJoiner = new RemoteJoiner(
|
this.remoteJoiner = new RemoteJoiner(
|
||||||
servicesConfig_ as JoinerServiceConfig[],
|
servicesConfig_ as JoinerServiceConfig[],
|
||||||
this.remoteFetchData.bind(this),
|
this.remoteFetchData.bind(this),
|
||||||
{ autoCreateServiceNameAlias: false }
|
{
|
||||||
|
autoCreateServiceNameAlias: false,
|
||||||
|
entitiesMap,
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,13 @@ import {
|
|||||||
} from "@medusajs/types"
|
} from "@medusajs/types"
|
||||||
|
|
||||||
import { RemoteJoinerOptions } from "@medusajs/types"
|
import { RemoteJoinerOptions } from "@medusajs/types"
|
||||||
import { MedusaError, deduplicate, isDefined, isString } from "@medusajs/utils"
|
import {
|
||||||
|
MedusaError,
|
||||||
|
deduplicate,
|
||||||
|
extractRelationsFromGQL,
|
||||||
|
isDefined,
|
||||||
|
isString,
|
||||||
|
} from "@medusajs/utils"
|
||||||
import GraphQLParser from "./graphql-ast"
|
import GraphQLParser from "./graphql-ast"
|
||||||
|
|
||||||
const BASE_PATH = "_root"
|
const BASE_PATH = "_root"
|
||||||
@@ -35,6 +41,8 @@ export class RemoteJoiner {
|
|||||||
private serviceConfigCache: Map<string, InternalJoinerServiceConfig> =
|
private serviceConfigCache: Map<string, InternalJoinerServiceConfig> =
|
||||||
new Map()
|
new Map()
|
||||||
|
|
||||||
|
private entityMap: Map<string, Map<string, string>> = new Map()
|
||||||
|
|
||||||
private static filterFields(
|
private static filterFields(
|
||||||
data: any,
|
data: any,
|
||||||
fields?: string[],
|
fields?: string[],
|
||||||
@@ -142,12 +150,21 @@ export class RemoteJoiner {
|
|||||||
private remoteFetchData: RemoteFetchDataCallback,
|
private remoteFetchData: RemoteFetchDataCallback,
|
||||||
private options: {
|
private options: {
|
||||||
autoCreateServiceNameAlias?: boolean
|
autoCreateServiceNameAlias?: boolean
|
||||||
|
entitiesMap?: Map<string, any>
|
||||||
} = {}
|
} = {}
|
||||||
) {
|
) {
|
||||||
this.options.autoCreateServiceNameAlias ??= true
|
this.options.autoCreateServiceNameAlias ??= true
|
||||||
|
if (this.options.entitiesMap) {
|
||||||
|
this.entityMap = extractRelationsFromGQL(this.options.entitiesMap)
|
||||||
|
}
|
||||||
|
|
||||||
this.serviceConfigs = this.buildReferences(
|
this.serviceConfigs = this.buildReferences(
|
||||||
JSON.parse(JSON.stringify(serviceConfigs))
|
JSON.parse(JSON.stringify(serviceConfigs), (key, value) => {
|
||||||
|
if (key === "schema") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
})
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,12 +175,15 @@ export class RemoteJoiner {
|
|||||||
private buildReferences(serviceConfigs: ModuleJoinerConfig[]) {
|
private buildReferences(serviceConfigs: ModuleJoinerConfig[]) {
|
||||||
const expandedRelationships: Map<
|
const expandedRelationships: Map<
|
||||||
string,
|
string,
|
||||||
{ fieldAlias; relationships: Map<string, JoinerRelationship> }
|
{
|
||||||
|
fieldAlias
|
||||||
|
relationships: Map<string, JoinerRelationship | JoinerRelationship[]>
|
||||||
|
}
|
||||||
> = new Map()
|
> = new Map()
|
||||||
|
|
||||||
for (const service of serviceConfigs) {
|
for (const service of serviceConfigs) {
|
||||||
const service_ = service as Omit<ModuleJoinerConfig, "relationships"> & {
|
const service_ = service as Omit<ModuleJoinerConfig, "relationships"> & {
|
||||||
relationships?: Map<string, JoinerRelationship>
|
relationships?: Map<string, JoinerRelationship | JoinerRelationship[]>
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.serviceConfigCache.has(service_.serviceName!)) {
|
if (this.serviceConfigCache.has(service_.serviceName!)) {
|
||||||
@@ -233,18 +253,33 @@ export class RemoteJoiner {
|
|||||||
? { ...service_.args, ...alias.args }
|
? { ...service_.args, ...alias.args }
|
||||||
: undefined
|
: undefined
|
||||||
|
|
||||||
service_.relationships?.set(alias.name as string, {
|
const aliasName = alias.name as string
|
||||||
alias: alias.name as string,
|
const rel = {
|
||||||
|
alias: aliasName,
|
||||||
entity: alias.entity,
|
entity: alias.entity,
|
||||||
foreignKey: alias.name + "_id",
|
foreignKey: alias.name + "_id",
|
||||||
primaryKey: "id",
|
primaryKey: "id",
|
||||||
serviceName: service_.serviceName!,
|
serviceName: service_.serviceName!,
|
||||||
args,
|
args,
|
||||||
})
|
}
|
||||||
this.cacheServiceConfig(serviceConfigs, undefined, alias)
|
|
||||||
|
if (service_.relationships?.has(aliasName)) {
|
||||||
|
const existing = service_.relationships.get(aliasName)!
|
||||||
|
const newRelation = Array.isArray(existing)
|
||||||
|
? existing.concat(rel)
|
||||||
|
: [existing, rel]
|
||||||
|
|
||||||
|
service_.relationships?.set(aliasName, newRelation)
|
||||||
|
} else {
|
||||||
|
service_.relationships?.set(aliasName, rel)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.cacheServiceConfig(serviceConfigs, { serviceAlias: alias })
|
||||||
}
|
}
|
||||||
|
|
||||||
this.cacheServiceConfig(serviceConfigs, service_.serviceName)
|
this.cacheServiceConfig(serviceConfigs, {
|
||||||
|
serviceName: service_.serviceName,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const extend of service_.extends) {
|
for (const extend of service_.extends) {
|
||||||
@@ -256,10 +291,20 @@ export class RemoteJoiner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const service_ = expandedRelationships.get(extend.serviceName)!
|
const service_ = expandedRelationships.get(extend.serviceName)!
|
||||||
service_.relationships.set(
|
|
||||||
extend.relationship.alias,
|
const aliasName = extend.relationship.alias
|
||||||
extend.relationship
|
const rel = extend.relationship
|
||||||
)
|
if (service_.relationships?.has(aliasName)) {
|
||||||
|
const existing = service_.relationships.get(aliasName)!
|
||||||
|
const newRelation = Array.isArray(existing)
|
||||||
|
? existing.concat(rel)
|
||||||
|
: [existing, rel]
|
||||||
|
|
||||||
|
service_.relationships?.set(aliasName, newRelation)
|
||||||
|
} else {
|
||||||
|
service_.relationships?.set(aliasName, rel)
|
||||||
|
}
|
||||||
|
|
||||||
Object.assign(service_.fieldAlias ?? {}, extend.fieldAlias)
|
Object.assign(service_.fieldAlias ?? {}, extend.fieldAlias)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -274,8 +319,19 @@ export class RemoteJoiner {
|
|||||||
|
|
||||||
const service_ = this.serviceConfigCache.get(serviceName)!
|
const service_ = this.serviceConfigCache.get(serviceName)!
|
||||||
relationships.forEach((relationship, alias) => {
|
relationships.forEach((relationship, alias) => {
|
||||||
service_.relationships!.set(alias, relationship)
|
const rel = relationship as JoinerRelationship
|
||||||
|
if (service_.relationships?.has(alias)) {
|
||||||
|
const existing = service_.relationships.get(alias)!
|
||||||
|
const newRelation = Array.isArray(existing)
|
||||||
|
? existing.concat(rel)
|
||||||
|
: [existing, rel]
|
||||||
|
|
||||||
|
service_.relationships?.set(alias, newRelation)
|
||||||
|
} else {
|
||||||
|
service_.relationships?.set(alias, rel)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
Object.assign(service_.fieldAlias!, fieldAlias ?? {})
|
Object.assign(service_.fieldAlias!, fieldAlias ?? {})
|
||||||
|
|
||||||
if (Object.keys(service_.fieldAlias!).length) {
|
if (Object.keys(service_.fieldAlias!).length) {
|
||||||
@@ -296,10 +352,23 @@ export class RemoteJoiner {
|
|||||||
return serviceConfigs
|
return serviceConfigs
|
||||||
}
|
}
|
||||||
|
|
||||||
private getServiceConfig(
|
private getServiceConfig({
|
||||||
serviceName?: string,
|
serviceName,
|
||||||
|
serviceAlias,
|
||||||
|
entity,
|
||||||
|
}: {
|
||||||
|
serviceName?: string
|
||||||
serviceAlias?: string
|
serviceAlias?: string
|
||||||
): InternalJoinerServiceConfig | undefined {
|
entity?: string
|
||||||
|
}): InternalJoinerServiceConfig | undefined {
|
||||||
|
if (entity) {
|
||||||
|
const name = `entity_${entity}`
|
||||||
|
const serviceConfig = this.serviceConfigCache.get(name)
|
||||||
|
if (serviceConfig) {
|
||||||
|
return serviceConfig
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (serviceAlias) {
|
if (serviceAlias) {
|
||||||
const name = `alias_${serviceAlias}`
|
const name = `alias_${serviceAlias}`
|
||||||
return this.serviceConfigCache.get(name)
|
return this.serviceConfigCache.get(name)
|
||||||
@@ -309,10 +378,14 @@ export class RemoteJoiner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private cacheServiceConfig(
|
private cacheServiceConfig(
|
||||||
serviceConfigs,
|
serviceConfigs: ModuleJoinerConfig[],
|
||||||
serviceName?: string,
|
params: {
|
||||||
serviceAlias?: JoinerServiceConfigAlias
|
serviceName?: string
|
||||||
|
serviceAlias?: JoinerServiceConfigAlias
|
||||||
|
}
|
||||||
): void {
|
): void {
|
||||||
|
const { serviceName, serviceAlias } = params
|
||||||
|
|
||||||
if (serviceAlias) {
|
if (serviceAlias) {
|
||||||
const name = `alias_${serviceAlias.name}`
|
const name = `alias_${serviceAlias.name}`
|
||||||
if (!this.serviceConfigCache.has(name)) {
|
if (!this.serviceConfigCache.has(name)) {
|
||||||
@@ -331,7 +404,19 @@ export class RemoteJoiner {
|
|||||||
if (aliasConfig) {
|
if (aliasConfig) {
|
||||||
serviceConfig.args = { ...config?.args, ...aliasConfig?.args }
|
serviceConfig.args = { ...config?.args, ...aliasConfig?.args }
|
||||||
}
|
}
|
||||||
this.serviceConfigCache.set(name, serviceConfig)
|
this.serviceConfigCache.set(
|
||||||
|
name,
|
||||||
|
serviceConfig as InternalJoinerServiceConfig
|
||||||
|
)
|
||||||
|
|
||||||
|
const entity = serviceAlias.entity
|
||||||
|
if (entity) {
|
||||||
|
const name = `entity_${entity}`
|
||||||
|
this.serviceConfigCache.set(
|
||||||
|
name,
|
||||||
|
serviceConfig as InternalJoinerServiceConfig
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
@@ -339,20 +424,22 @@ export class RemoteJoiner {
|
|||||||
|
|
||||||
const config = serviceConfigs.find(
|
const config = serviceConfigs.find(
|
||||||
(config) => config.serviceName === serviceName
|
(config) => config.serviceName === serviceName
|
||||||
)
|
) as InternalJoinerServiceConfig
|
||||||
this.serviceConfigCache.set(serviceName!, config)
|
this.serviceConfigCache.set(serviceName!, config)
|
||||||
}
|
}
|
||||||
|
|
||||||
private async fetchData(
|
private async fetchData(params: {
|
||||||
expand: RemoteExpandProperty,
|
expand: RemoteExpandProperty
|
||||||
pkField: string,
|
pkField: string
|
||||||
ids?: (unknown | unknown[])[],
|
ids?: (unknown | unknown[])[]
|
||||||
relationship?: any,
|
relationship?: any
|
||||||
options?: RemoteJoinerOptions
|
options?: RemoteJoinerOptions
|
||||||
): Promise<{
|
}): Promise<{
|
||||||
data: unknown[] | { [path: string]: unknown }
|
data: unknown[] | { [path: string]: unknown }
|
||||||
path?: string
|
path?: string
|
||||||
}> {
|
}> {
|
||||||
|
const { expand, pkField, ids, relationship, options } = params
|
||||||
|
|
||||||
let uniqueIds = Array.isArray(ids) ? ids : ids ? [ids] : undefined
|
let uniqueIds = Array.isArray(ids) ? ids : ids ? [ids] : undefined
|
||||||
|
|
||||||
if (uniqueIds) {
|
if (uniqueIds) {
|
||||||
@@ -372,15 +459,16 @@ export class RemoteJoiner {
|
|||||||
uniqueIds = uniqueIds.filter((id) => isDefined(id))
|
uniqueIds = uniqueIds.filter((id) => isDefined(id))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let pkFieldAdjusted = pkField
|
||||||
if (relationship) {
|
if (relationship) {
|
||||||
pkField = relationship.inverse
|
pkFieldAdjusted = relationship.inverse
|
||||||
? relationship.foreignKey.split(".").pop()!
|
? relationship.foreignKey.split(".").pop()!
|
||||||
: relationship.primaryKey
|
: relationship.primaryKey
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await this.remoteFetchData(
|
const response = await this.remoteFetchData(
|
||||||
expand,
|
expand,
|
||||||
pkField,
|
pkFieldAdjusted,
|
||||||
uniqueIds,
|
uniqueIds,
|
||||||
relationship
|
relationship
|
||||||
)
|
)
|
||||||
@@ -394,14 +482,14 @@ export class RemoteJoiner {
|
|||||||
: [resData]
|
: [resData]
|
||||||
: []
|
: []
|
||||||
|
|
||||||
this.checkIfKeysExist(
|
this.checkIfKeysExist({
|
||||||
uniqueIds,
|
uniqueIds,
|
||||||
resData,
|
resData,
|
||||||
expand,
|
expand,
|
||||||
pkField,
|
pkField: pkFieldAdjusted,
|
||||||
relationship,
|
relationship,
|
||||||
options
|
options,
|
||||||
)
|
})
|
||||||
|
|
||||||
const filteredDataArray = resData.map((data: any) =>
|
const filteredDataArray = resData.map((data: any) =>
|
||||||
RemoteJoiner.filterFields(data, expand.fields, expand.expands)
|
RemoteJoiner.filterFields(data, expand.fields, expand.expands)
|
||||||
@@ -416,14 +504,17 @@ export class RemoteJoiner {
|
|||||||
return response
|
return response
|
||||||
}
|
}
|
||||||
|
|
||||||
private checkIfKeysExist(
|
private checkIfKeysExist(params: {
|
||||||
uniqueIds: unknown[] | undefined,
|
uniqueIds: unknown[] | undefined
|
||||||
resData: any[],
|
resData: any[]
|
||||||
expand: RemoteExpandProperty,
|
expand: RemoteExpandProperty
|
||||||
pkField: string,
|
pkField: string
|
||||||
relationship?: any,
|
relationship?: any
|
||||||
options?: RemoteJoinerOptions
|
options?: RemoteJoinerOptions
|
||||||
) {
|
}) {
|
||||||
|
const { uniqueIds, resData, expand, pkField, relationship, options } =
|
||||||
|
params
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!(
|
!(
|
||||||
isDefined(uniqueIds) &&
|
isDefined(uniqueIds) &&
|
||||||
@@ -461,11 +552,13 @@ export class RemoteJoiner {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleFieldAliases(
|
private handleFieldAliases(params: {
|
||||||
items: any[],
|
items: any[]
|
||||||
parsedExpands: Map<string, RemoteExpandProperty>,
|
parsedExpands: Map<string, RemoteExpandProperty>
|
||||||
implodeMapping: InternalImplodeMapping[]
|
implodeMapping: InternalImplodeMapping[]
|
||||||
) {
|
}) {
|
||||||
|
const { items, parsedExpands, implodeMapping } = params
|
||||||
|
|
||||||
const getChildren = (item: any, prop: string) => {
|
const getChildren = (item: any, prop: string) => {
|
||||||
if (Array.isArray(item)) {
|
if (Array.isArray(item)) {
|
||||||
return item.flatMap((currentItem) => currentItem[prop])
|
return item.flatMap((currentItem) => currentItem[prop])
|
||||||
@@ -541,12 +634,14 @@ export class RemoteJoiner {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleExpands(
|
private async handleExpands(params: {
|
||||||
items: any[],
|
items: any[]
|
||||||
parsedExpands: Map<string, RemoteExpandProperty>,
|
parsedExpands: Map<string, RemoteExpandProperty>
|
||||||
implodeMapping: InternalImplodeMapping[] = [],
|
implodeMapping?: InternalImplodeMapping[]
|
||||||
options?: RemoteJoinerOptions
|
options?: RemoteJoinerOptions
|
||||||
): Promise<void> {
|
}): Promise<void> {
|
||||||
|
const { items, parsedExpands, implodeMapping = [], options } = params
|
||||||
|
|
||||||
if (!parsedExpands) {
|
if (!parsedExpands) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -567,48 +662,87 @@ export class RemoteJoiner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (nestedItems.length > 0) {
|
if (nestedItems.length > 0) {
|
||||||
await this.expandProperty(
|
await this.expandProperty({
|
||||||
nestedItems,
|
items: nestedItems,
|
||||||
expand.parentConfig!,
|
parentServiceConfig: expand.parentConfig!,
|
||||||
expand,
|
expand,
|
||||||
options
|
options,
|
||||||
)
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.handleFieldAliases(items, parsedExpands, implodeMapping)
|
this.handleFieldAliases({ items, parsedExpands, implodeMapping })
|
||||||
}
|
}
|
||||||
|
|
||||||
private async expandProperty(
|
private getEntityRelationship(params: {
|
||||||
items: any[],
|
parentServiceConfig: InternalJoinerServiceConfig
|
||||||
parentServiceConfig: InternalJoinerServiceConfig,
|
property: string
|
||||||
expand?: RemoteExpandProperty,
|
entity?: string
|
||||||
|
}): JoinerRelationship {
|
||||||
|
const { parentServiceConfig, property, entity } = params
|
||||||
|
|
||||||
|
const propEntity = entity ?? parentServiceConfig?.entity
|
||||||
|
const rel = parentServiceConfig?.relationships?.get(property)
|
||||||
|
|
||||||
|
if (Array.isArray(rel)) {
|
||||||
|
if (!propEntity) {
|
||||||
|
return rel[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
const entityRel = rel.find((r) => r.entity === propEntity)
|
||||||
|
if (entityRel) {
|
||||||
|
return entityRel
|
||||||
|
}
|
||||||
|
|
||||||
|
// If entity is not found, return the relationship where the primary key matches
|
||||||
|
const serviceEntity = this.getServiceConfig({
|
||||||
|
entity: propEntity,
|
||||||
|
})!
|
||||||
|
|
||||||
|
return rel.find((r) => serviceEntity.primaryKeys.includes(r.primaryKey))!
|
||||||
|
}
|
||||||
|
|
||||||
|
return rel as JoinerRelationship
|
||||||
|
}
|
||||||
|
|
||||||
|
private async expandProperty(params: {
|
||||||
|
items: any[]
|
||||||
|
parentServiceConfig: InternalJoinerServiceConfig
|
||||||
|
expand?: RemoteExpandProperty
|
||||||
options?: RemoteJoinerOptions
|
options?: RemoteJoinerOptions
|
||||||
): Promise<void> {
|
}): Promise<void> {
|
||||||
|
const { items, parentServiceConfig, expand, options } = params
|
||||||
|
|
||||||
if (!expand) {
|
if (!expand) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const relationship = parentServiceConfig?.relationships?.get(
|
const relationship = this.getEntityRelationship({
|
||||||
expand.property
|
parentServiceConfig,
|
||||||
)
|
property: expand.property,
|
||||||
|
entity: expand.entity,
|
||||||
|
})
|
||||||
|
|
||||||
if (relationship) {
|
if (!relationship) {
|
||||||
await this.expandRelationshipProperty(
|
return
|
||||||
items,
|
|
||||||
expand,
|
|
||||||
relationship,
|
|
||||||
options
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await this.expandRelationshipProperty({
|
||||||
|
items,
|
||||||
|
expand,
|
||||||
|
relationship,
|
||||||
|
options,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private async expandRelationshipProperty(
|
private async expandRelationshipProperty(params: {
|
||||||
items: any[],
|
items: any[]
|
||||||
expand: RemoteExpandProperty,
|
expand: RemoteExpandProperty
|
||||||
relationship: JoinerRelationship,
|
relationship: JoinerRelationship
|
||||||
options?: RemoteJoinerOptions
|
options?: RemoteJoinerOptions
|
||||||
): Promise<void> {
|
}): Promise<void> {
|
||||||
|
const { items, expand, relationship, options } = params
|
||||||
|
|
||||||
const field = relationship.inverse
|
const field = relationship.inverse
|
||||||
? relationship.primaryKey
|
? relationship.primaryKey
|
||||||
: relationship.foreignKey.split(".").pop()!
|
: relationship.foreignKey.split(".").pop()!
|
||||||
@@ -639,13 +773,13 @@ export class RemoteJoiner {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const relatedDataArray = await this.fetchData(
|
const relatedDataArray = await this.fetchData({
|
||||||
expand,
|
expand,
|
||||||
field,
|
pkField: field,
|
||||||
idsToFetch,
|
ids: idsToFetch,
|
||||||
relationship,
|
relationship,
|
||||||
options
|
options,
|
||||||
)
|
})
|
||||||
|
|
||||||
const joinFields = relationship.inverse
|
const joinFields = relationship.inverse
|
||||||
? relationship.foreignKey.split(",")
|
? relationship.foreignKey.split(",")
|
||||||
@@ -689,36 +823,46 @@ export class RemoteJoiner {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private parseExpands(
|
private parseExpands(params: {
|
||||||
initialService: RemoteExpandProperty,
|
initialService: RemoteExpandProperty
|
||||||
query: RemoteJoinerQuery,
|
query: RemoteJoinerQuery
|
||||||
serviceConfig: InternalJoinerServiceConfig,
|
serviceConfig: InternalJoinerServiceConfig
|
||||||
expands: RemoteJoinerQuery["expands"],
|
expands: RemoteJoinerQuery["expands"]
|
||||||
implodeMapping: InternalImplodeMapping[],
|
implodeMapping: InternalImplodeMapping[]
|
||||||
options?: RemoteJoinerOptions
|
options?: RemoteJoinerOptions
|
||||||
): Map<string, RemoteExpandProperty> {
|
}): Map<string, RemoteExpandProperty> {
|
||||||
const parsedExpands = this.parseProperties(
|
const {
|
||||||
initialService,
|
initialService,
|
||||||
query,
|
query,
|
||||||
serviceConfig,
|
serviceConfig,
|
||||||
expands,
|
expands,
|
||||||
implodeMapping,
|
implodeMapping,
|
||||||
options
|
options,
|
||||||
)
|
} = params
|
||||||
|
|
||||||
|
const parsedExpands = this.parseProperties({
|
||||||
|
initialService,
|
||||||
|
query,
|
||||||
|
serviceConfig,
|
||||||
|
expands,
|
||||||
|
implodeMapping,
|
||||||
|
})
|
||||||
|
|
||||||
const groupedExpands = this.groupExpands(parsedExpands)
|
const groupedExpands = this.groupExpands(parsedExpands)
|
||||||
|
|
||||||
return groupedExpands
|
return groupedExpands
|
||||||
}
|
}
|
||||||
|
|
||||||
private parseProperties(
|
private parseProperties(params: {
|
||||||
initialService: RemoteExpandProperty,
|
initialService: RemoteExpandProperty
|
||||||
query: RemoteJoinerQuery,
|
query: RemoteJoinerQuery
|
||||||
serviceConfig: InternalJoinerServiceConfig,
|
serviceConfig: InternalJoinerServiceConfig
|
||||||
expands: RemoteJoinerQuery["expands"],
|
expands: RemoteJoinerQuery["expands"]
|
||||||
implodeMapping: InternalImplodeMapping[],
|
implodeMapping: InternalImplodeMapping[]
|
||||||
options?: RemoteJoinerOptions
|
}): Map<string, RemoteExpandProperty> {
|
||||||
): Map<string, RemoteExpandProperty> {
|
const { initialService, query, serviceConfig, expands, implodeMapping } =
|
||||||
|
params
|
||||||
|
|
||||||
const aliasRealPathMap = new Map<string, string[]>()
|
const aliasRealPathMap = new Map<string, string[]>()
|
||||||
const parsedExpands = new Map<string, any>()
|
const parsedExpands = new Map<string, any>()
|
||||||
parsedExpands.set(BASE_PATH, initialService)
|
parsedExpands.set(BASE_PATH, initialService)
|
||||||
@@ -760,7 +904,19 @@ export class RemoteJoiner {
|
|||||||
const fullPath = [BASE_PATH, ...currentPath, prop].join(".")
|
const fullPath = [BASE_PATH, ...currentPath, prop].join(".")
|
||||||
const fullAliasPath = [BASE_PATH, ...currentAliasPath, prop].join(".")
|
const fullAliasPath = [BASE_PATH, ...currentAliasPath, prop].join(".")
|
||||||
|
|
||||||
const relationship = currentServiceConfig.relationships?.get(prop)
|
let entity = currentServiceConfig.entity
|
||||||
|
if (entity) {
|
||||||
|
const completePath = fullPath.split(".")
|
||||||
|
for (let i = 1; i < completePath.length; i++) {
|
||||||
|
entity = this.getEntity({ entity, prop: completePath[i] }) ?? entity
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const relationship = this.getEntityRelationship({
|
||||||
|
parentServiceConfig: currentServiceConfig,
|
||||||
|
property: prop,
|
||||||
|
entity,
|
||||||
|
})
|
||||||
|
|
||||||
const isCurrentProp =
|
const isCurrentProp =
|
||||||
fullPath === BASE_PATH + "." + expand.property ||
|
fullPath === BASE_PATH + "." + expand.property ||
|
||||||
@@ -772,7 +928,6 @@ export class RemoteJoiner {
|
|||||||
if (relationship) {
|
if (relationship) {
|
||||||
const parentExpand =
|
const parentExpand =
|
||||||
parsedExpands.get([BASE_PATH, ...currentPath].join(".")) || query
|
parsedExpands.get([BASE_PATH, ...currentPath].join(".")) || query
|
||||||
|
|
||||||
if (parentExpand) {
|
if (parentExpand) {
|
||||||
const parRelField = relationship.inverse
|
const parRelField = relationship.inverse
|
||||||
? relationship.primaryKey
|
? relationship.primaryKey
|
||||||
@@ -792,9 +947,10 @@ export class RemoteJoiner {
|
|||||||
fields = fields.concat(relField.split(","))
|
fields = fields.concat(relField.split(","))
|
||||||
}
|
}
|
||||||
|
|
||||||
currentServiceConfig = this.getServiceConfig(
|
currentServiceConfig = this.getServiceConfig({
|
||||||
relationship.serviceName
|
serviceName: relationship.serviceName,
|
||||||
)!
|
entity: relationship.entity,
|
||||||
|
})!
|
||||||
|
|
||||||
if (!currentServiceConfig) {
|
if (!currentServiceConfig) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
@@ -817,6 +973,7 @@ export class RemoteJoiner {
|
|||||||
parsedExpands.set(fullPath, {
|
parsedExpands.set(fullPath, {
|
||||||
property: prop,
|
property: prop,
|
||||||
serviceConfig: currentServiceConfig,
|
serviceConfig: currentServiceConfig,
|
||||||
|
entity: entity,
|
||||||
fields,
|
fields,
|
||||||
args: isAliasMapping
|
args: isAliasMapping
|
||||||
? forwardArgumentsOnPath.includes(fullPath)
|
? forwardArgumentsOnPath.includes(fullPath)
|
||||||
@@ -848,6 +1005,10 @@ export class RemoteJoiner {
|
|||||||
return parsedExpands
|
return parsedExpands
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private getEntity({ entity, prop }: { entity: string; prop: string }) {
|
||||||
|
return this.entityMap.get(entity)?.get(prop)
|
||||||
|
}
|
||||||
|
|
||||||
private parseAlias({
|
private parseAlias({
|
||||||
aliasPath,
|
aliasPath,
|
||||||
aliasRealPathMap,
|
aliasRealPathMap,
|
||||||
@@ -875,7 +1036,6 @@ export class RemoteJoiner {
|
|||||||
return parsedExpands.get(fullPath).serviceConfig
|
return parsedExpands.get(fullPath).serviceConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove alias from fields
|
|
||||||
const parentPath = [BASE_PATH, ...currentPath].join(".")
|
const parentPath = [BASE_PATH, ...currentPath].join(".")
|
||||||
const parentExpands = parsedExpands.get(parentPath)
|
const parentExpands = parsedExpands.get(parentPath)
|
||||||
parentExpands.fields = parentExpands.fields?.filter(
|
parentExpands.fields = parentExpands.fields?.filter(
|
||||||
@@ -926,10 +1086,31 @@ export class RemoteJoiner {
|
|||||||
|
|
||||||
const partialPath: string[] = []
|
const partialPath: string[] = []
|
||||||
for (const partial of path.split(".")) {
|
for (const partial of path.split(".")) {
|
||||||
const relationship = currentServiceConfig.relationships?.get(partial)
|
const completePath = [
|
||||||
|
BASE_PATH,
|
||||||
|
...currentPath.concat(partialPath),
|
||||||
|
partial,
|
||||||
|
]
|
||||||
|
const parentPath = completePath.slice(0, -1).join(".")
|
||||||
|
|
||||||
|
let entity = serviceConfig.entity
|
||||||
|
if (entity) {
|
||||||
|
for (let i = 1; i < completePath.length; i++) {
|
||||||
|
entity = this.getEntity({ entity, prop: completePath[i] }) ?? entity
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const relationship = this.getEntityRelationship({
|
||||||
|
parentServiceConfig: currentServiceConfig,
|
||||||
|
property: partial,
|
||||||
|
entity,
|
||||||
|
})
|
||||||
|
|
||||||
if (relationship) {
|
if (relationship) {
|
||||||
currentServiceConfig = this.getServiceConfig(relationship.serviceName)!
|
currentServiceConfig = this.getServiceConfig({
|
||||||
|
serviceName: relationship.serviceName,
|
||||||
|
entity: relationship.entity,
|
||||||
|
})!
|
||||||
|
|
||||||
if (!currentServiceConfig) {
|
if (!currentServiceConfig) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
@@ -938,17 +1119,11 @@ export class RemoteJoiner {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const completePath = [
|
|
||||||
BASE_PATH,
|
|
||||||
...currentPath.concat(partialPath),
|
|
||||||
partial,
|
|
||||||
]
|
|
||||||
const parentPath = completePath.slice(0, -1).join(".")
|
|
||||||
|
|
||||||
partialPath.push(partial)
|
partialPath.push(partial)
|
||||||
parsedExpands.set(completePath.join("."), {
|
parsedExpands.set(completePath.join("."), {
|
||||||
property: partial,
|
property: partial,
|
||||||
serviceConfig: currentServiceConfig,
|
serviceConfig: currentServiceConfig,
|
||||||
|
entity: entity,
|
||||||
parent: parentPath,
|
parent: parentPath,
|
||||||
parentConfig: parsedExpands.get(parentPath).serviceConfig,
|
parentConfig: parsedExpands.get(parentPath).serviceConfig,
|
||||||
})
|
})
|
||||||
@@ -980,7 +1155,6 @@ export class RemoteJoiner {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
// Merge the current expand into its parent
|
|
||||||
const nestedKeys = path.split(".").slice(parentPath.split(".").length)
|
const nestedKeys = path.split(".").slice(parentPath.split(".").length)
|
||||||
let targetExpand = parentExpand as Omit<
|
let targetExpand = parentExpand as Omit<
|
||||||
RemoteExpandProperty,
|
RemoteExpandProperty,
|
||||||
@@ -1009,10 +1183,10 @@ export class RemoteJoiner {
|
|||||||
queryObj: RemoteJoinerQuery,
|
queryObj: RemoteJoinerQuery,
|
||||||
options?: RemoteJoinerOptions
|
options?: RemoteJoinerOptions
|
||||||
): Promise<any> {
|
): Promise<any> {
|
||||||
const serviceConfig = this.getServiceConfig(
|
const serviceConfig = this.getServiceConfig({
|
||||||
queryObj.service,
|
serviceName: queryObj.service,
|
||||||
queryObj.alias
|
serviceAlias: queryObj.alias,
|
||||||
)
|
})
|
||||||
|
|
||||||
if (!serviceConfig) {
|
if (!serviceConfig) {
|
||||||
if (queryObj.alias) {
|
if (queryObj.alias) {
|
||||||
@@ -1035,38 +1209,39 @@ export class RemoteJoiner {
|
|||||||
)
|
)
|
||||||
|
|
||||||
const implodeMapping: InternalImplodeMapping[] = []
|
const implodeMapping: InternalImplodeMapping[] = []
|
||||||
const parsedExpands = this.parseExpands(
|
const parsedExpands = this.parseExpands({
|
||||||
{
|
initialService: {
|
||||||
property: "",
|
property: "",
|
||||||
parent: "",
|
parent: "",
|
||||||
serviceConfig: serviceConfig,
|
serviceConfig,
|
||||||
|
entity: serviceConfig.entity,
|
||||||
fields: queryObj.fields,
|
fields: queryObj.fields,
|
||||||
args: otherArgs,
|
args: otherArgs,
|
||||||
},
|
},
|
||||||
queryObj,
|
query: queryObj,
|
||||||
serviceConfig,
|
serviceConfig,
|
||||||
queryObj.expands!,
|
expands: queryObj.expands!,
|
||||||
implodeMapping
|
implodeMapping,
|
||||||
)
|
options,
|
||||||
|
})
|
||||||
|
|
||||||
const root = parsedExpands.get(BASE_PATH)!
|
const root = parsedExpands.get(BASE_PATH)!
|
||||||
|
|
||||||
const response = await this.fetchData(
|
const response = await this.fetchData({
|
||||||
root,
|
expand: root,
|
||||||
pkName,
|
pkField: pkName,
|
||||||
primaryKeyArg?.value,
|
ids: primaryKeyArg?.value,
|
||||||
undefined,
|
options,
|
||||||
options
|
})
|
||||||
)
|
|
||||||
|
|
||||||
const data = response.path ? response.data[response.path!] : response.data
|
const data = response.path ? response.data[response.path!] : response.data
|
||||||
|
|
||||||
await this.handleExpands(
|
await this.handleExpands({
|
||||||
Array.isArray(data) ? data : [data],
|
items: Array.isArray(data) ? data : [data],
|
||||||
parsedExpands,
|
parsedExpands,
|
||||||
implodeMapping,
|
implodeMapping,
|
||||||
options
|
options,
|
||||||
)
|
})
|
||||||
|
|
||||||
return response.data
|
return response.data
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ export type InternalJoinerServiceConfig = Omit<
|
|||||||
JoinerServiceConfig,
|
JoinerServiceConfig,
|
||||||
"relationships"
|
"relationships"
|
||||||
> & {
|
> & {
|
||||||
relationships?: Map<string, JoinerRelationship>
|
relationships?: Map<string, JoinerRelationship | JoinerRelationship[]>
|
||||||
entity?: string
|
entity?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,6 +106,7 @@ export interface RemoteExpandProperty {
|
|||||||
parent: string
|
parent: string
|
||||||
parentConfig?: InternalJoinerServiceConfig
|
parentConfig?: InternalJoinerServiceConfig
|
||||||
serviceConfig: InternalJoinerServiceConfig
|
serviceConfig: InternalJoinerServiceConfig
|
||||||
|
entity?: string
|
||||||
fields?: string[]
|
fields?: string[]
|
||||||
args?: JoinerArgument[]
|
args?: JoinerArgument[]
|
||||||
expands?: RemoteNestedExpands
|
expands?: RemoteNestedExpands
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
import { isListType, isNonNullType, isObjectType } from "graphql"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts only the relation fields from the GraphQL type map.
|
||||||
|
* @param {Map<string, any>} typeMap - The GraphQL schema TypeMap.
|
||||||
|
* @returns {Map<string, Map<string, string>>} A map where each key is an entity name, and the values are a map of relation fields and their corresponding entity type.
|
||||||
|
*/
|
||||||
|
export function extractRelationsFromGQL(
|
||||||
|
typeMap: Map<string, any>
|
||||||
|
): Map<string, Map<string, string>> {
|
||||||
|
const relationMap = new Map()
|
||||||
|
|
||||||
|
// Extract the actual type
|
||||||
|
const getBaseType = (type) => {
|
||||||
|
if (isNonNullType(type) || isListType(type)) {
|
||||||
|
return getBaseType(type.ofType)
|
||||||
|
}
|
||||||
|
return type
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [typeName, graphqlType] of Object.entries(typeMap)) {
|
||||||
|
if (!isObjectType(graphqlType)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
const fields = graphqlType.getFields()
|
||||||
|
const entityRelations = new Map()
|
||||||
|
|
||||||
|
for (const [fieldName, fieldConfig] of Object.entries(fields)) {
|
||||||
|
const fieldType = getBaseType((fieldConfig as any).type)
|
||||||
|
|
||||||
|
// only add relation fields
|
||||||
|
if (isObjectType(fieldType)) {
|
||||||
|
entityRelations.set(fieldName, fieldType.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
relationMap.set(typeName, entityRelations)
|
||||||
|
}
|
||||||
|
|
||||||
|
return relationMap
|
||||||
|
}
|
||||||
@@ -26,6 +26,7 @@ export * from "./get-node-version"
|
|||||||
export * from "./get-selects-and-relations-from-object-array"
|
export * from "./get-selects-and-relations-from-object-array"
|
||||||
export * from "./get-set-difference"
|
export * from "./get-set-difference"
|
||||||
export * from "./graceful-shutdown-server"
|
export * from "./graceful-shutdown-server"
|
||||||
|
export * from "./graphql-relations-entity-map"
|
||||||
export * from "./group-by"
|
export * from "./group-by"
|
||||||
export * from "./handle-postgres-database-error"
|
export * from "./handle-postgres-database-error"
|
||||||
export * from "./is-big-number"
|
export * from "./is-big-number"
|
||||||
|
|||||||
Reference in New Issue
Block a user