feat(orchestration): Remote Joiner field aliases (#5013)
* initial commit * chore: unit tests and forward arguments
This commit is contained in:
committed by
GitHub
parent
834da5c41a
commit
0953bdfe84
@@ -56,6 +56,9 @@ export const serviceConfigs: JoinerServiceConfig[] = [
|
||||
alias: {
|
||||
name: "variant",
|
||||
},
|
||||
fieldAlias: {
|
||||
user_shortcut: "product.user",
|
||||
},
|
||||
primaryKeys: ["id"],
|
||||
relationships: [
|
||||
{
|
||||
@@ -75,6 +78,12 @@ export const serviceConfigs: JoinerServiceConfig[] = [
|
||||
},
|
||||
{
|
||||
serviceName: "order",
|
||||
fieldAlias: {
|
||||
product_user_alias: {
|
||||
path: "products.product.user",
|
||||
forwardArgumentsOnPath: ["products.product"],
|
||||
},
|
||||
},
|
||||
primaryKeys: ["id"],
|
||||
relationships: [
|
||||
{
|
||||
|
||||
@@ -38,31 +38,40 @@ const container = {
|
||||
},
|
||||
} as MedusaContainer
|
||||
|
||||
const fetchServiceDataCallback = async (
|
||||
expand: RemoteExpandProperty,
|
||||
pkField: string,
|
||||
ids?: (unknown | unknown[])[],
|
||||
relationship?: any
|
||||
) => {
|
||||
const serviceConfig = expand.serviceConfig
|
||||
const moduleRegistryName = !serviceConfig.serviceName.endsWith("Service")
|
||||
? lowerCaseFirst(serviceConfig.serviceName) + "Service"
|
||||
: serviceConfig.serviceName
|
||||
const callbacks = jest.fn()
|
||||
const fetchServiceDataCallback = jest.fn(
|
||||
async (
|
||||
expand: RemoteExpandProperty,
|
||||
pkField: string,
|
||||
ids?: (unknown | unknown[])[],
|
||||
relationship?: any
|
||||
) => {
|
||||
const serviceConfig = expand.serviceConfig
|
||||
const moduleRegistryName = !serviceConfig.serviceName.endsWith("Service")
|
||||
? lowerCaseFirst(serviceConfig.serviceName) + "Service"
|
||||
: serviceConfig.serviceName
|
||||
|
||||
const service = container.resolve(moduleRegistryName)
|
||||
const methodName = relationship?.inverse
|
||||
? `getBy${toPascalCase(pkField)}`
|
||||
: "list"
|
||||
const service = container.resolve(moduleRegistryName)
|
||||
const methodName = relationship?.inverse
|
||||
? `getBy${toPascalCase(pkField)}`
|
||||
: "list"
|
||||
|
||||
return await service[methodName]({
|
||||
fields: expand.fields,
|
||||
args: expand.args,
|
||||
expands: expand.expands,
|
||||
options: {
|
||||
[pkField]: ids,
|
||||
},
|
||||
})
|
||||
}
|
||||
callbacks({
|
||||
service: serviceConfig.serviceName,
|
||||
fieds: expand.fields,
|
||||
args: expand.args,
|
||||
})
|
||||
|
||||
return await service[methodName]({
|
||||
fields: expand.fields,
|
||||
args: expand.args,
|
||||
expands: expand.expands,
|
||||
options: {
|
||||
[pkField]: ids,
|
||||
},
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
describe("RemoteJoiner", () => {
|
||||
let joiner: RemoteJoiner
|
||||
@@ -161,13 +170,11 @@ describe("RemoteJoiner", () => {
|
||||
}
|
||||
|
||||
const data = await joiner.query(query)
|
||||
|
||||
expect(data).toEqual([
|
||||
{
|
||||
email: "johndoe@example.com",
|
||||
products: [
|
||||
{
|
||||
id: 1,
|
||||
product_id: 102,
|
||||
product: {
|
||||
name: "Product 2",
|
||||
@@ -180,7 +187,6 @@ describe("RemoteJoiner", () => {
|
||||
email: "janedoe@example.com",
|
||||
products: [
|
||||
{
|
||||
id: 2,
|
||||
product_id: [101, 102],
|
||||
product: [
|
||||
{
|
||||
@@ -202,7 +208,6 @@ describe("RemoteJoiner", () => {
|
||||
email: "444444@example.com",
|
||||
products: [
|
||||
{
|
||||
id: 4,
|
||||
product_id: 103,
|
||||
product: {
|
||||
name: "Product 3",
|
||||
@@ -272,7 +277,6 @@ describe("RemoteJoiner", () => {
|
||||
email: "444444@example.com",
|
||||
products: [
|
||||
{
|
||||
id: 4,
|
||||
product_id: 103,
|
||||
product: {
|
||||
name: "Product 3",
|
||||
@@ -307,7 +311,6 @@ describe("RemoteJoiner", () => {
|
||||
email: "johndoe@example.com",
|
||||
products: [
|
||||
{
|
||||
id: 1,
|
||||
product_id: 102,
|
||||
product: {
|
||||
name: "Product 2",
|
||||
@@ -489,4 +492,252 @@ describe("RemoteJoiner", () => {
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
it("Should query an field alias and cleanup unused nested levels", async () => {
|
||||
const query = RemoteJoiner.parseQuery(`
|
||||
query {
|
||||
order {
|
||||
product_user_alias {
|
||||
email
|
||||
}
|
||||
}
|
||||
}
|
||||
`)
|
||||
const data = await joiner.query(query)
|
||||
|
||||
expect(data).toEqual([
|
||||
expect.objectContaining({
|
||||
product_user_alias: [
|
||||
{
|
||||
email: "janedoe@example.com",
|
||||
id: 2,
|
||||
},
|
||||
{
|
||||
email: "janedoe@example.com",
|
||||
id: 2,
|
||||
},
|
||||
],
|
||||
}),
|
||||
expect.objectContaining({
|
||||
product_user_alias: [
|
||||
{
|
||||
email: "janedoe@example.com",
|
||||
id: 2,
|
||||
},
|
||||
{
|
||||
email: "aaa@example.com",
|
||||
id: 3,
|
||||
},
|
||||
],
|
||||
}),
|
||||
])
|
||||
expect(data[0].products[0].product).toEqual(undefined)
|
||||
})
|
||||
|
||||
it("Should query an field alias and keep queried nested levels", async () => {
|
||||
const query = RemoteJoiner.parseQuery(`
|
||||
query {
|
||||
order {
|
||||
product_user_alias {
|
||||
email
|
||||
}
|
||||
products {
|
||||
product {
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`)
|
||||
const data = await joiner.query(query)
|
||||
|
||||
expect(data).toEqual([
|
||||
expect.objectContaining({
|
||||
product_user_alias: [
|
||||
{
|
||||
email: "janedoe@example.com",
|
||||
id: 2,
|
||||
},
|
||||
{
|
||||
email: "janedoe@example.com",
|
||||
id: 2,
|
||||
},
|
||||
],
|
||||
}),
|
||||
expect.objectContaining({
|
||||
product_user_alias: [
|
||||
{
|
||||
email: "janedoe@example.com",
|
||||
id: 2,
|
||||
},
|
||||
{
|
||||
email: "aaa@example.com",
|
||||
id: 3,
|
||||
},
|
||||
],
|
||||
}),
|
||||
])
|
||||
expect(data[0].products[0].product).toEqual({
|
||||
name: "Product 1",
|
||||
id: 101,
|
||||
user_id: 2,
|
||||
})
|
||||
expect(data[0].products[0].product.user).toEqual(undefined)
|
||||
})
|
||||
|
||||
it("Should query an field alias and merge requested fields on alias and on the relationship", async () => {
|
||||
const query = RemoteJoiner.parseQuery(`
|
||||
query {
|
||||
order {
|
||||
product_user_alias {
|
||||
email
|
||||
}
|
||||
products {
|
||||
product {
|
||||
user {
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`)
|
||||
const data = await joiner.query(query)
|
||||
|
||||
expect(data).toEqual([
|
||||
expect.objectContaining({
|
||||
product_user_alias: [
|
||||
{
|
||||
name: "Jane Doe",
|
||||
id: 2,
|
||||
email: "janedoe@example.com",
|
||||
},
|
||||
{
|
||||
name: "Jane Doe",
|
||||
id: 2,
|
||||
email: "janedoe@example.com",
|
||||
},
|
||||
],
|
||||
}),
|
||||
expect.objectContaining({
|
||||
product_user_alias: [
|
||||
{
|
||||
name: "Jane Doe",
|
||||
id: 2,
|
||||
email: "janedoe@example.com",
|
||||
},
|
||||
{
|
||||
name: "aaa bbb",
|
||||
id: 3,
|
||||
email: "aaa@example.com",
|
||||
},
|
||||
],
|
||||
}),
|
||||
])
|
||||
expect(data[0].products[0].product).toEqual({
|
||||
id: 101,
|
||||
user_id: 2,
|
||||
user: {
|
||||
name: "Jane Doe",
|
||||
id: 2,
|
||||
email: "janedoe@example.com",
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it("Should query multiple aliases and pass the arguments where defined on 'forwardArgumentsOnPath'", async () => {
|
||||
const query = RemoteJoiner.parseQuery(`
|
||||
query {
|
||||
order {
|
||||
id
|
||||
product_user_alias (arg: { random: 123 }) {
|
||||
name
|
||||
}
|
||||
products {
|
||||
variant {
|
||||
user_shortcut(arg: 123) {
|
||||
email
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`)
|
||||
const data = await joiner.query(query)
|
||||
|
||||
expect(callbacks.mock.calls).toEqual([
|
||||
[
|
||||
{
|
||||
service: "order",
|
||||
fieds: ["id", "product_user_alias", "products"],
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
service: "variantService",
|
||||
fieds: ["user_shortcut", "id", "product_id"],
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
service: "product",
|
||||
fieds: ["id", "user_id"],
|
||||
args: [
|
||||
{
|
||||
name: "arg",
|
||||
value: {
|
||||
random: 123,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
service: "user",
|
||||
fieds: ["name", "id"],
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
service: "product",
|
||||
fieds: ["id", "user_id"],
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
service: "user",
|
||||
fieds: ["email", "id"],
|
||||
},
|
||||
],
|
||||
])
|
||||
|
||||
expect(data[1]).toEqual(
|
||||
expect.objectContaining({
|
||||
product_user_alias: [
|
||||
{
|
||||
id: 2,
|
||||
name: "Jane Doe",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: "aaa bbb",
|
||||
},
|
||||
],
|
||||
})
|
||||
)
|
||||
|
||||
expect(data[0].products[0]).toEqual({
|
||||
variant_id: 991,
|
||||
product_id: 101,
|
||||
variant: {
|
||||
id: 991,
|
||||
product_id: 101,
|
||||
user_shortcut: {
|
||||
email: "janedoe@example.com",
|
||||
id: 2,
|
||||
},
|
||||
},
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
RemoteNestedExpands,
|
||||
} from "@medusajs/types"
|
||||
|
||||
import { isDefined } from "@medusajs/utils"
|
||||
import { isDefined, isString } from "@medusajs/utils"
|
||||
import GraphQLParser from "./graphql-ast"
|
||||
|
||||
const BASE_PATH = "_root"
|
||||
@@ -26,6 +26,12 @@ export type RemoteFetchDataCallback = (
|
||||
export class RemoteJoiner {
|
||||
private serviceConfigCache: Map<string, JoinerServiceConfig> = new Map()
|
||||
|
||||
private implodeMapping: {
|
||||
location: string[]
|
||||
property: string
|
||||
path: string[]
|
||||
}[] = []
|
||||
|
||||
private static filterFields(
|
||||
data: any,
|
||||
fields: string[],
|
||||
@@ -76,9 +82,7 @@ export class RemoteJoiner {
|
||||
}
|
||||
|
||||
private static getNestedItems(items: any[], property: string): any[] {
|
||||
return items
|
||||
.flatMap((item) => item[property])
|
||||
.filter((item) => item !== undefined)
|
||||
return items.flatMap((item) => item?.[property])
|
||||
}
|
||||
|
||||
private static createRelatedDataMap(
|
||||
@@ -265,6 +269,8 @@ export class RemoteJoiner {
|
||||
} else {
|
||||
uniqueIds = Array.from(new Set(uniqueIds.flat()))
|
||||
}
|
||||
|
||||
uniqueIds = uniqueIds.filter((id) => id !== undefined)
|
||||
}
|
||||
|
||||
if (relationship) {
|
||||
@@ -295,57 +301,110 @@ export class RemoteJoiner {
|
||||
return response
|
||||
}
|
||||
|
||||
private handleFieldAliases(
|
||||
items: any[],
|
||||
parsedExpands: Map<string, RemoteExpandProperty>
|
||||
) {
|
||||
const getChildren = (item: any, prop: string) => {
|
||||
if (Array.isArray(item)) {
|
||||
return item.flatMap((currentItem) => currentItem[prop])
|
||||
} else {
|
||||
return item[prop]
|
||||
}
|
||||
}
|
||||
const removeChildren = (item: any, prop: string) => {
|
||||
if (Array.isArray(item)) {
|
||||
item.forEach((currentItem) => delete currentItem[prop])
|
||||
} else {
|
||||
delete item[prop]
|
||||
}
|
||||
}
|
||||
|
||||
const cleanup: [any, string][] = []
|
||||
for (const alias of this.implodeMapping) {
|
||||
const propPath = alias.path
|
||||
|
||||
let itemsLocation = items
|
||||
for (const locationProp of alias.location) {
|
||||
propPath.shift()
|
||||
itemsLocation = RemoteJoiner.getNestedItems(itemsLocation, locationProp)
|
||||
}
|
||||
|
||||
itemsLocation.forEach((locationItem) => {
|
||||
if (!locationItem) {
|
||||
return
|
||||
}
|
||||
|
||||
let currentItems = locationItem
|
||||
let parentRemoveItems: any = null
|
||||
|
||||
const curPath: string[] = [BASE_PATH].concat(alias.location)
|
||||
for (const prop of propPath) {
|
||||
if (currentItems === undefined) {
|
||||
break
|
||||
}
|
||||
|
||||
curPath.push(prop)
|
||||
|
||||
const config = parsedExpands.get(curPath.join(".")) as any
|
||||
if (config?.isAliasMapping && parentRemoveItems === null) {
|
||||
parentRemoveItems = [currentItems, prop]
|
||||
}
|
||||
|
||||
currentItems = getChildren(currentItems, prop)
|
||||
}
|
||||
|
||||
if (Array.isArray(currentItems)) {
|
||||
if (currentItems.length < 2) {
|
||||
locationItem[alias.property] = currentItems.shift()
|
||||
} else {
|
||||
locationItem[alias.property] = currentItems
|
||||
}
|
||||
} else {
|
||||
locationItem[alias.property] = currentItems
|
||||
}
|
||||
|
||||
if (parentRemoveItems !== null) {
|
||||
cleanup.push(parentRemoveItems)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
for (const parentRemoveItems of cleanup) {
|
||||
const [remItems, path] = parentRemoveItems
|
||||
removeChildren(remItems, path)
|
||||
}
|
||||
}
|
||||
|
||||
private async handleExpands(
|
||||
items: any[],
|
||||
query: RemoteJoinerQuery,
|
||||
parsedExpands: Map<string, RemoteExpandProperty>
|
||||
): Promise<void> {
|
||||
if (!parsedExpands) {
|
||||
return
|
||||
}
|
||||
const resolvedPaths = new Set<string>()
|
||||
const stack: [any[], Partial<RemoteJoinerQuery>, string][] = [
|
||||
[items, query, BASE_PATH],
|
||||
]
|
||||
|
||||
while (stack.length > 0) {
|
||||
const [currentItems, currentQuery, basePath] = stack.pop()!
|
||||
for (const [expandedPath, expand] of parsedExpands.entries()) {
|
||||
if (expandedPath === BASE_PATH) {
|
||||
continue
|
||||
}
|
||||
|
||||
for (const [expandedPath, expand] of parsedExpands.entries()) {
|
||||
const isParentPath = expandedPath.startsWith(basePath)
|
||||
let nestedItems = items
|
||||
const expandedPathLevels = expandedPath.split(".")
|
||||
|
||||
if (!isParentPath || resolvedPaths.has(expandedPath)) {
|
||||
continue
|
||||
}
|
||||
for (let idx = 1; idx < expandedPathLevels.length - 1; idx++) {
|
||||
nestedItems = RemoteJoiner.getNestedItems(
|
||||
nestedItems,
|
||||
expandedPathLevels[idx]
|
||||
)
|
||||
}
|
||||
|
||||
resolvedPaths.add(expandedPath)
|
||||
const property = expand.property || ""
|
||||
|
||||
let curItems = currentItems
|
||||
const expandedPathLevels = expandedPath.split(".")
|
||||
for (let idx = 1; idx < expandedPathLevels.length - 1; idx++) {
|
||||
curItems = RemoteJoiner.getNestedItems(
|
||||
curItems,
|
||||
expandedPathLevels[idx]
|
||||
)
|
||||
}
|
||||
|
||||
await this.expandProperty(curItems, expand.parentConfig!, expand)
|
||||
const nestedItems = RemoteJoiner.getNestedItems(currentItems, property)
|
||||
|
||||
if (nestedItems.length > 0) {
|
||||
const relationship = expand.serviceConfig
|
||||
let nextProp = currentQuery
|
||||
if (relationship) {
|
||||
const relQuery = {
|
||||
service: relationship.serviceName,
|
||||
}
|
||||
nextProp = relQuery
|
||||
}
|
||||
stack.push([nestedItems, nextProp, expandedPath])
|
||||
}
|
||||
if (nestedItems.length > 0) {
|
||||
await this.expandProperty(nestedItems, expand.parentConfig!, expand)
|
||||
}
|
||||
}
|
||||
|
||||
this.handleFieldAliases(items, parsedExpands)
|
||||
}
|
||||
|
||||
private async expandProperty(
|
||||
@@ -379,11 +438,9 @@ export class RemoteJoiner {
|
||||
const idsToFetch: any[] = []
|
||||
|
||||
items.forEach((item) => {
|
||||
const values = fieldsArray
|
||||
.map((field) => item[field])
|
||||
.filter((value) => value !== undefined)
|
||||
const values = fieldsArray.map((field) => item?.[field])
|
||||
|
||||
if (values.length === fieldsArray.length && !item[relationship.alias]) {
|
||||
if (values.length === fieldsArray.length && !item?.[relationship.alias]) {
|
||||
if (fieldsArray.length === 1) {
|
||||
if (!idsToFetch.includes(values[0])) {
|
||||
idsToFetch.push(values[0])
|
||||
@@ -424,30 +481,30 @@ export class RemoteJoiner {
|
||||
)
|
||||
|
||||
items.forEach((item) => {
|
||||
if (!item[relationship.alias]) {
|
||||
const itemKey = fieldsArray.map((field) => item[field]).join(",")
|
||||
if (!item || item[relationship.alias]) {
|
||||
return
|
||||
}
|
||||
|
||||
if (Array.isArray(item[field])) {
|
||||
item[relationship.alias] = item[field]
|
||||
.map((id) => {
|
||||
if (relationship.isList && !Array.isArray(relatedDataMap[id])) {
|
||||
relatedDataMap[id] =
|
||||
relatedDataMap[id] !== undefined ? [relatedDataMap[id]] : []
|
||||
}
|
||||
const itemKey = fieldsArray.map((field) => item[field]).join(",")
|
||||
|
||||
return relatedDataMap[id]
|
||||
})
|
||||
.filter((relatedItem) => relatedItem !== undefined)
|
||||
} else {
|
||||
if (relationship.isList && !Array.isArray(relatedDataMap[itemKey])) {
|
||||
relatedDataMap[itemKey] =
|
||||
relatedDataMap[itemKey] !== undefined
|
||||
? [relatedDataMap[itemKey]]
|
||||
: []
|
||||
if (Array.isArray(item[field])) {
|
||||
item[relationship.alias] = item[field].map((id) => {
|
||||
if (relationship.isList && !Array.isArray(relatedDataMap[id])) {
|
||||
relatedDataMap[id] =
|
||||
relatedDataMap[id] !== undefined ? [relatedDataMap[id]] : []
|
||||
}
|
||||
|
||||
item[relationship.alias] = relatedDataMap[itemKey]
|
||||
return relatedDataMap[id]
|
||||
})
|
||||
} else {
|
||||
if (relationship.isList && !Array.isArray(relatedDataMap[itemKey])) {
|
||||
relatedDataMap[itemKey] =
|
||||
relatedDataMap[itemKey] !== undefined
|
||||
? [relatedDataMap[itemKey]]
|
||||
: []
|
||||
}
|
||||
|
||||
item[relationship.alias] = relatedDataMap[itemKey]
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -479,21 +536,65 @@ export class RemoteJoiner {
|
||||
const parsedExpands = new Map<string, any>()
|
||||
parsedExpands.set(BASE_PATH, initialService)
|
||||
|
||||
let forwardArgumentsOnPath: string[] = []
|
||||
for (const expand of expands || []) {
|
||||
const properties = expand.property.split(".")
|
||||
let currentServiceConfig = serviceConfig as any
|
||||
let currentServiceConfig = serviceConfig
|
||||
const currentPath: string[] = []
|
||||
|
||||
for (const prop of properties) {
|
||||
const fieldAlias = currentServiceConfig.fieldAlias ?? {}
|
||||
|
||||
if (fieldAlias[prop]) {
|
||||
const alias = fieldAlias[prop] as any
|
||||
|
||||
const path = isString(alias) ? alias : alias.path
|
||||
const fullPath = currentPath.concat(path.split("."))
|
||||
|
||||
forwardArgumentsOnPath = forwardArgumentsOnPath.concat(
|
||||
(alias?.forwardArgumentsOnPath || []).map(
|
||||
(forPath) =>
|
||||
BASE_PATH + "." + currentPath.concat(forPath).join(".")
|
||||
)
|
||||
)
|
||||
|
||||
this.implodeMapping.push({
|
||||
location: currentPath,
|
||||
property: prop,
|
||||
path: fullPath,
|
||||
})
|
||||
|
||||
const extMapping = expands as unknown[]
|
||||
|
||||
const middlePath = path.split(".").slice(0, -1)
|
||||
let curMiddlePath = currentPath
|
||||
for (const path of middlePath) {
|
||||
curMiddlePath = curMiddlePath.concat(path)
|
||||
extMapping.push({
|
||||
args: expand.args,
|
||||
property: curMiddlePath.join("."),
|
||||
isAliasMapping: true,
|
||||
})
|
||||
}
|
||||
|
||||
extMapping.push({
|
||||
...expand,
|
||||
property: fullPath.join("."),
|
||||
isAliasMapping: true,
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
const fullPath = [BASE_PATH, ...currentPath, prop].join(".")
|
||||
const relationship = currentServiceConfig.relationships.find(
|
||||
const relationship = currentServiceConfig.relationships?.find(
|
||||
(relation) => relation.alias === prop
|
||||
)
|
||||
|
||||
let fields: string[] | undefined =
|
||||
fullPath === BASE_PATH + "." + expand.property
|
||||
? expand.fields
|
||||
? expand.fields ?? []
|
||||
: undefined
|
||||
|
||||
const args =
|
||||
fullPath === BASE_PATH + "." + expand.property
|
||||
? expand.args
|
||||
@@ -504,29 +605,29 @@ export class RemoteJoiner {
|
||||
parsedExpands.get([BASE_PATH, ...currentPath].join(".")) || query
|
||||
|
||||
if (parentExpand) {
|
||||
if (parentExpand.fields) {
|
||||
const relField = relationship.inverse
|
||||
? relationship.primaryKey
|
||||
: relationship.foreignKey.split(".").pop()!
|
||||
const relField = relationship.inverse
|
||||
? relationship.primaryKey
|
||||
: relationship.foreignKey.split(".").pop()!
|
||||
|
||||
parentExpand.fields = parentExpand.fields
|
||||
.concat(relField.split(","))
|
||||
.filter((field) => field !== relationship.alias)
|
||||
parentExpand.fields ??= []
|
||||
|
||||
parentExpand.fields = [...new Set(parentExpand.fields)]
|
||||
}
|
||||
parentExpand.fields = parentExpand.fields
|
||||
.concat(relField.split(","))
|
||||
.filter((field) => field !== relationship.alias)
|
||||
|
||||
parentExpand.fields = [...new Set(parentExpand.fields)]
|
||||
|
||||
if (fields) {
|
||||
const relField = relationship.inverse
|
||||
? relationship.foreignKey.split(".").pop()!
|
||||
: relationship.primaryKey
|
||||
fields = fields.concat(relField.split(","))
|
||||
|
||||
fields = [...new Set(fields)]
|
||||
}
|
||||
}
|
||||
|
||||
currentServiceConfig = this.getServiceConfig(relationship.serviceName)
|
||||
currentServiceConfig = this.getServiceConfig(
|
||||
relationship.serviceName
|
||||
)!
|
||||
|
||||
if (!currentServiceConfig) {
|
||||
throw new Error(
|
||||
@@ -535,16 +636,33 @@ export class RemoteJoiner {
|
||||
}
|
||||
}
|
||||
|
||||
const isAliasMapping = (expand as any).isAliasMapping
|
||||
if (!parsedExpands.has(fullPath)) {
|
||||
const parentPath = [BASE_PATH, ...currentPath].join(".")
|
||||
|
||||
parsedExpands.set(fullPath, {
|
||||
property: prop,
|
||||
serviceConfig: currentServiceConfig,
|
||||
fields,
|
||||
args,
|
||||
args: isAliasMapping
|
||||
? forwardArgumentsOnPath.includes(fullPath)
|
||||
? args
|
||||
: undefined
|
||||
: args,
|
||||
isAliasMapping: isAliasMapping,
|
||||
parent: parentPath,
|
||||
parentConfig: parsedExpands.get(parentPath).serviceConfig,
|
||||
})
|
||||
} else {
|
||||
const exp = parsedExpands.get(fullPath)
|
||||
|
||||
if (forwardArgumentsOnPath.includes(fullPath) && args) {
|
||||
exp.args = (exp.args || []).concat(args)
|
||||
}
|
||||
|
||||
if (fields) {
|
||||
exp.fields = (exp.fields || []).concat(fields)
|
||||
}
|
||||
}
|
||||
|
||||
currentPath.push(prop)
|
||||
@@ -585,7 +703,7 @@ export class RemoteJoiner {
|
||||
targetExpand = targetExpand.expands[key] ??= {}
|
||||
}
|
||||
|
||||
targetExpand.fields = expand.fields
|
||||
targetExpand.fields = [...new Set(expand.fields)]
|
||||
targetExpand.args = expand.args
|
||||
|
||||
mergedExpands.delete(path)
|
||||
@@ -648,11 +766,7 @@ export class RemoteJoiner {
|
||||
|
||||
const data = response.path ? response.data[response.path!] : response.data
|
||||
|
||||
await this.handleExpands(
|
||||
Array.isArray(data) ? data : [data],
|
||||
queryObj,
|
||||
parsedExpands
|
||||
)
|
||||
await this.handleExpands(Array.isArray(data) ? data : [data], parsedExpands)
|
||||
|
||||
return response.data
|
||||
}
|
||||
|
||||
@@ -16,6 +16,14 @@ export interface JoinerServiceConfigAlias {
|
||||
export interface JoinerServiceConfig {
|
||||
serviceName: string
|
||||
alias?: JoinerServiceConfigAlias | JoinerServiceConfigAlias[] // Property name to use as entrypoint to the service
|
||||
fieldAlias?: Record<
|
||||
string,
|
||||
| string
|
||||
| {
|
||||
path: string
|
||||
forwardArgumentsOnPath: string[]
|
||||
}
|
||||
> // alias for deeper nested relationships (e.g. { 'price': 'prices.calculated_price_set.amount' })
|
||||
primaryKeys: string[]
|
||||
relationships?: JoinerRelationship[]
|
||||
extends?: {
|
||||
|
||||
Reference in New Issue
Block a user