diff --git a/.changeset/bright-colts-rescue.md b/.changeset/bright-colts-rescue.md new file mode 100644 index 0000000000..4ab90ecd83 --- /dev/null +++ b/.changeset/bright-colts-rescue.md @@ -0,0 +1,6 @@ +--- +"@medusajs/orchestration": minor +"@medusajs/modules-sdk": minor +--- + +object to remote joiner query diff --git a/packages/modules-sdk/src/remote-query.ts b/packages/modules-sdk/src/remote-query.ts index b74df21c99..3a80ed26b6 100644 --- a/packages/modules-sdk/src/remote-query.ts +++ b/packages/modules-sdk/src/remote-query.ts @@ -1,3 +1,8 @@ +import { + RemoteFetchDataCallback, + RemoteJoiner, + toRemoteJoinerQuery, +} from "@medusajs/orchestration" import { JoinerRelationship, JoinerServiceConfig, @@ -6,8 +11,6 @@ import { RemoteExpandProperty, RemoteJoinerQuery, } from "@medusajs/types" - -import { RemoteFetchDataCallback, RemoteJoiner } from "@medusajs/orchestration" import { isString, toPascalCase } from "@medusajs/utils" import { MedusaModule } from "./medusa-module" @@ -213,12 +216,16 @@ export class RemoteQuery { } public async query( - query: string | RemoteJoinerQuery, + query: string | RemoteJoinerQuery | object, variables?: Record ): Promise { - const finalQuery = isString(query) - ? RemoteJoiner.parseQuery(query, variables) - : query + let finalQuery: RemoteJoinerQuery = query as RemoteJoinerQuery + + if (isString(query)) { + finalQuery = RemoteJoiner.parseQuery(query, variables) + } else if (!isString(finalQuery?.service) && !isString(finalQuery?.alias)) { + finalQuery = toRemoteJoinerQuery(query) + } return await this.remoteJoiner.query(finalQuery) } diff --git a/packages/orchestration/src/__tests__/joiner/helpers.ts b/packages/orchestration/src/__tests__/joiner/helpers.ts new file mode 100644 index 0000000000..94c78eca8a --- /dev/null +++ b/packages/orchestration/src/__tests__/joiner/helpers.ts @@ -0,0 +1,147 @@ +import { toRemoteJoinerQuery } from "../../joiner/helpers" + +describe("toRemoteJoinerQuery", () => { + beforeEach(() => { + jest.clearAllMocks() + }) + + it("should transform a simple object to a Remote Joiner Query format", async () => { + const obj = { + product: { + fields: ["id", "title", "handle"], + }, + } + + const rjQuery = toRemoteJoinerQuery(obj) + + expect(rjQuery).toEqual({ + alias: "product", + fields: ["id", "title", "handle"], + expands: [], + }) + }) + + it("should transform a nested object to a Remote Joiner Query format", async () => { + const obj = { + product: { + fields: ["id", "title", "handle"], + variants: { + fields: ["sku"], + shipping_profiles: { + profile: { + fields: ["id", "name"], + }, + }, + options: { + fields: ["value"], + }, + }, + options: { + fields: ["value", "name"], + }, + }, + } + + const rjQuery = toRemoteJoinerQuery(obj) + + expect(rjQuery).toEqual({ + alias: "product", + fields: ["id", "title", "handle"], + expands: [ + { + property: "product.variants", + fields: ["sku"], + }, + { + property: "product.variants.shipping_profiles", + }, + { + property: "product.variants.shipping_profiles.profile", + fields: ["id", "name"], + }, + { + property: "product.variants.options", + fields: ["value"], + }, + { + property: "product.options", + fields: ["value", "name"], + }, + ], + }) + }) + + it("should transform a nested object with arguments and directives to a Remote Joiner Query format", async () => { + const obj = { + product: { + fields: ["id", "title", "handle"], + __args: { + limit: 10, + offset: 0, + }, + variants: { + fields: ["sku"], + __directives: { + directiveName: "value", + }, + shipping_profiles: { + profile: { + fields: ["id", "name"], + __args: { + context: { + customer_group: "cg_123", + region_id: "US", + }, + }, + }, + }, + }, + }, + } + + const rjQuery = toRemoteJoinerQuery(obj) + + expect(rjQuery).toEqual({ + alias: "product", + fields: ["id", "title", "handle"], + expands: [ + { + property: "product.variants", + directives: [ + { + name: "directiveName", + value: "value", + }, + ], + fields: ["sku"], + }, + { + property: "product.variants.shipping_profiles", + }, + { + property: "product.variants.shipping_profiles.profile", + args: [ + { + name: "context", + value: { + customer_group: "cg_123", + region_id: "US", + }, + }, + ], + fields: ["id", "name"], + }, + ], + args: [ + { + name: "limit", + value: 10, + }, + { + name: "offset", + value: 0, + }, + ], + }) + }) +}) diff --git a/packages/orchestration/src/joiner/helpers.ts b/packages/orchestration/src/joiner/helpers.ts new file mode 100644 index 0000000000..e845df8e33 --- /dev/null +++ b/packages/orchestration/src/joiner/helpers.ts @@ -0,0 +1,58 @@ +import { RemoteJoinerQuery } from "@medusajs/types" + +export function toRemoteJoinerQuery(obj: any): RemoteJoinerQuery { + const remoteJoinerQuery: RemoteJoinerQuery = { + alias: "", + fields: [], + expands: [], + } + + function extractRecursive(obj, parentName = "") { + for (const key in obj) { + const value = obj[key] + + const canExpand = + typeof value === "object" && + !["fields", "__args", "__directives"].includes(key) + + if (canExpand) { + const entityName = parentName ? `${parentName}.${key}` : key + const expandObj: any = { + property: entityName, + } + + const isEntryPoint = parentName === "" + const reference = isEntryPoint ? remoteJoinerQuery : expandObj + + if (value.__args) { + reference.args = Object.entries(value.__args).map( + ([name, value]) => ({ + name, + value, + }) + ) + } + + if (value.__directives) { + reference.directives = Object.entries(value.__directives).map( + ([name, value]) => ({ name, value }) + ) + } + + reference.fields = value.fields + + if (isEntryPoint) { + remoteJoinerQuery.alias = key + } else { + remoteJoinerQuery.expands!.push(expandObj) + } + + extractRecursive(value, entityName) + } + } + + return remoteJoinerQuery + } + + return extractRecursive(obj) +} diff --git a/packages/orchestration/src/joiner/index.ts b/packages/orchestration/src/joiner/index.ts index ba55313b31..13294b3675 100644 --- a/packages/orchestration/src/joiner/index.ts +++ b/packages/orchestration/src/joiner/index.ts @@ -1 +1,2 @@ +export * from "./helpers" export * from "./remote-joiner"