diff --git a/integration-tests/modules/__tests__/modules/remote-query.spec.ts b/integration-tests/modules/__tests__/modules/remote-query.spec.ts index 8208485756..9a2fad5744 100644 --- a/integration-tests/modules/__tests__/modules/remote-query.spec.ts +++ b/integration-tests/modules/__tests__/modules/remote-query.spec.ts @@ -231,6 +231,40 @@ medusaIntegrationTestRunner({ }) }) + it(`should throw if not exists`, async () => { + const err = await query + .graph( + { + entity: "product", + fields: ["id", "title", "variants.*", "variants.prices.amount"], + filters: { + id: "non-existing-id", + variants: { + prices: { + amount: { + $gt: 100, + }, + }, + }, + }, + }, + { + throwIfKeyNotFound: true, + } + ) + .catch((err) => { + return err + }) + + expect(err).toEqual( + expect.objectContaining({ + message: expect.stringContaining( + "Product id not found: non-existing-id" + ), + }) + ) + }) + it(`should perform cross module query and apply filters correctly to the correct modules [1]`, async () => { const { data } = await query.graph({ entity: "product", diff --git a/packages/core/orchestration/src/__tests__/joiner/remote-joiner.ts b/packages/core/orchestration/src/__tests__/joiner/remote-joiner.ts index 35193a22fd..07bd04ef9b 100644 --- a/packages/core/orchestration/src/__tests__/joiner/remote-joiner.ts +++ b/packages/core/orchestration/src/__tests__/joiner/remote-joiner.ts @@ -357,7 +357,6 @@ describe("RemoteJoiner", () => { expect(serviceMock.userService).toHaveBeenCalledTimes(1) expect(serviceMock.userService).toHaveBeenCalledWith({ - args: [], fields: ["id", "name", "email"], options: { id: ["1"] }, }) @@ -379,7 +378,6 @@ describe("RemoteJoiner", () => { expect(serviceMock.userService).toHaveBeenCalledTimes(1) expect(serviceMock.userService).toHaveBeenCalledWith({ - args: [], fields: ["id"], options: { id: ["1"] }, }) @@ -432,7 +430,6 @@ describe("RemoteJoiner", () => { expect(serviceMock.userService).toHaveBeenCalledTimes(1) expect(serviceMock.userService).toHaveBeenCalledWith({ - args: [], fields: ["username", "email"], options: { id: ["1"] }, }) @@ -464,7 +461,6 @@ describe("RemoteJoiner", () => { expect(serviceMock.userService).toHaveBeenCalledTimes(1) expect(serviceMock.userService).toHaveBeenCalledWith({ - args: [], fields: ["username", "email", "products"], expands: { products: { diff --git a/packages/core/orchestration/src/joiner/remote-joiner.ts b/packages/core/orchestration/src/joiner/remote-joiner.ts index b7e15e02cb..63e610fa28 100644 --- a/packages/core/orchestration/src/joiner/remote-joiner.ts +++ b/packages/core/orchestration/src/joiner/remote-joiner.ts @@ -1189,34 +1189,32 @@ export class RemoteJoiner { throw new Error(`Service "${queryObj.service}" was not found.`) } - let pkName = serviceConfig.primaryKeys[0] - const primaryKeyArg = queryObj.args?.find((arg) => { - const inc = serviceConfig.primaryKeys.includes(arg.name) - if (inc) { - pkName = arg.name - } - return inc + const { primaryKeyArg, otherArgs, pkName } = gerPrimaryKeysAndOtherFilters({ + serviceConfig, + queryObj, }) - const otherArgs = queryObj.args?.filter( - (arg) => !serviceConfig.primaryKeys.includes(arg.name) - ) const implodeMapping: InternalImplodeMapping[] = [] - const parsedExpands = this.parseExpands({ + const parseExpandsConfig: Parameters[0] = { initialService: { property: "", parent: "", serviceConfig, entity: serviceConfig.entity, fields: queryObj.fields, - args: otherArgs, }, query: queryObj, serviceConfig, expands: queryObj.expands!, implodeMapping, options, - }) + } + + if (otherArgs) { + parseExpandsConfig.initialService.args = otherArgs + } + + const parsedExpands = this.parseExpands(parseExpandsConfig) const root = parsedExpands.get(BASE_PATH)! @@ -1239,3 +1237,49 @@ export class RemoteJoiner { return response.data } } + +function gerPrimaryKeysAndOtherFilters({ serviceConfig, queryObj }): { + primaryKeyArg: { name: string; value: any } | undefined + otherArgs: { name: string; value: any }[] | undefined + pkName: string +} { + let pkName = serviceConfig.primaryKeys[0] + let primaryKeyArg = queryObj.args?.find((arg) => { + const include = serviceConfig.primaryKeys.includes(arg.name) + if (include) { + pkName = arg.name + } + return include + }) + + let otherArgs = queryObj.args?.filter( + (arg) => !serviceConfig.primaryKeys.includes(arg.name) + ) + + const filters = + queryObj.args?.find((arg) => arg.name === "filters")?.value ?? {} + + if (!primaryKeyArg) { + const primaryKeyFilter = Object.keys(filters).find((key) => { + return serviceConfig.primaryKeys.includes(key) + }) + + if (primaryKeyFilter) { + pkName = primaryKeyFilter + primaryKeyArg = { + name: primaryKeyFilter, + value: filters[primaryKeyFilter], + } + + delete filters[primaryKeyFilter] + } + } + + otherArgs = otherArgs?.length ? otherArgs : undefined + + return { + primaryKeyArg, + otherArgs, + pkName, + } +} diff --git a/packages/core/utils/src/modules-sdk/joiner-config-builder.ts b/packages/core/utils/src/modules-sdk/joiner-config-builder.ts index 9e1ebaa0dc..ba80eb9033 100644 --- a/packages/core/utils/src/modules-sdk/joiner-config-builder.ts +++ b/packages/core/utils/src/modules-sdk/joiner-config-builder.ts @@ -8,12 +8,12 @@ import { accessSync } from "fs" import * as path from "path" import { dirname, join, normalize } from "path" import { - MapToConfig, camelToSnakeCase, deduplicate, getCallerFilePath, isObject, lowerCaseFirst, + MapToConfig, pluralize, toCamelCase, upperCaseFirst,