chore: Move graphl to a single place (#9303)
* chore: Move graphl to a single place * add new line
This commit is contained in:
@@ -1,358 +0,0 @@
|
||||
import GraphQLParser from "../../joiner/graphql-ast"
|
||||
|
||||
describe("RemoteJoiner.parseQuery", () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
it("Simple query with fields", async () => {
|
||||
const graphqlQuery = `
|
||||
query {
|
||||
order {
|
||||
id
|
||||
number
|
||||
date
|
||||
}
|
||||
}
|
||||
`
|
||||
const parser = new GraphQLParser(graphqlQuery)
|
||||
const rjQuery = parser.parseQuery()
|
||||
|
||||
expect(rjQuery).toEqual({
|
||||
alias: "order",
|
||||
fields: ["id", "number", "date"],
|
||||
expands: [],
|
||||
})
|
||||
})
|
||||
|
||||
it("Simple query with fields and arguments", async () => {
|
||||
const graphqlQuery = `
|
||||
query {
|
||||
order(
|
||||
id: "ord_123",
|
||||
another_arg: 987,
|
||||
complexArg: {
|
||||
id: "123",
|
||||
name: "test",
|
||||
nestedArg: {
|
||||
nest_id: "abc",
|
||||
num: 123
|
||||
}
|
||||
}
|
||||
) {
|
||||
id
|
||||
number
|
||||
date
|
||||
}
|
||||
}
|
||||
`
|
||||
const parser = new GraphQLParser(graphqlQuery)
|
||||
const rjQuery = parser.parseQuery()
|
||||
|
||||
expect(rjQuery).toEqual({
|
||||
alias: "order",
|
||||
fields: ["id", "number", "date"],
|
||||
expands: [],
|
||||
args: [
|
||||
{
|
||||
name: "id",
|
||||
value: "ord_123",
|
||||
},
|
||||
{
|
||||
name: "another_arg",
|
||||
value: 987,
|
||||
},
|
||||
{
|
||||
name: "complexArg",
|
||||
value: {
|
||||
id: "123",
|
||||
name: "test",
|
||||
nestedArg: {
|
||||
nest_id: "abc",
|
||||
num: 123,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
})
|
||||
|
||||
it("Simple query with mapping fields to services", async () => {
|
||||
const graphqlQuery = `
|
||||
query {
|
||||
order {
|
||||
id
|
||||
number
|
||||
date
|
||||
products {
|
||||
product_id
|
||||
variant_id
|
||||
order
|
||||
variant {
|
||||
name
|
||||
sku
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
const parser = new GraphQLParser(graphqlQuery, {})
|
||||
const rjQuery = parser.parseQuery()
|
||||
|
||||
expect(rjQuery).toEqual({
|
||||
alias: "order",
|
||||
fields: ["id", "number", "date", "products"],
|
||||
expands: [
|
||||
{
|
||||
property: "products",
|
||||
fields: ["product_id", "variant_id", "order", "variant"],
|
||||
},
|
||||
{
|
||||
property: "products.variant",
|
||||
fields: ["name", "sku"],
|
||||
},
|
||||
],
|
||||
})
|
||||
})
|
||||
|
||||
it("Nested query with fields", async () => {
|
||||
const graphqlQuery = `
|
||||
query {
|
||||
order {
|
||||
id
|
||||
number
|
||||
date
|
||||
products {
|
||||
product_id
|
||||
variant_id
|
||||
order
|
||||
variant {
|
||||
name
|
||||
sku
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
const parser = new GraphQLParser(graphqlQuery)
|
||||
const rjQuery = parser.parseQuery()
|
||||
|
||||
expect(rjQuery).toEqual({
|
||||
alias: "order",
|
||||
fields: ["id", "number", "date", "products"],
|
||||
expands: [
|
||||
{
|
||||
property: "products",
|
||||
fields: ["product_id", "variant_id", "order", "variant"],
|
||||
},
|
||||
{
|
||||
property: "products.variant",
|
||||
fields: ["name", "sku"],
|
||||
},
|
||||
],
|
||||
})
|
||||
})
|
||||
|
||||
it("Nested query with fields and arguments", async () => {
|
||||
const graphqlQuery = `
|
||||
query {
|
||||
order (order_id: "ord_123") {
|
||||
id
|
||||
number
|
||||
date
|
||||
products (limit: 10) {
|
||||
product_id
|
||||
variant_id
|
||||
order
|
||||
variant (complexArg: { id: "123", name: "test", nestedArg: { nest_id: "abc", num: 123 } }, region_id: "reg_123") {
|
||||
name
|
||||
sku
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
const parser = new GraphQLParser(graphqlQuery)
|
||||
const rjQuery = parser.parseQuery()
|
||||
|
||||
expect(rjQuery).toEqual({
|
||||
alias: "order",
|
||||
fields: ["id", "number", "date", "products"],
|
||||
expands: [
|
||||
{
|
||||
property: "products",
|
||||
fields: ["product_id", "variant_id", "order", "variant"],
|
||||
args: [
|
||||
{
|
||||
name: "limit",
|
||||
value: 10,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
property: "products.variant",
|
||||
fields: ["name", "sku"],
|
||||
args: [
|
||||
{
|
||||
name: "complexArg",
|
||||
value: {
|
||||
id: "123",
|
||||
name: "test",
|
||||
nestedArg: {
|
||||
nest_id: "abc",
|
||||
num: 123,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "region_id",
|
||||
value: "reg_123",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
args: [
|
||||
{
|
||||
name: "order_id",
|
||||
value: "ord_123",
|
||||
},
|
||||
],
|
||||
})
|
||||
})
|
||||
|
||||
it("Nested query with fields and arguments using variables", async () => {
|
||||
const graphqlQuery = `
|
||||
query($orderId: ID, $anotherArg: String, $randomVariable: nonValidatedType) {
|
||||
order (order_id: $orderId, anotherArg: $anotherArg) {
|
||||
id
|
||||
number
|
||||
date
|
||||
products (randomValue: $randomVariable) {
|
||||
product_id
|
||||
variant_id
|
||||
order
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
const parser = new GraphQLParser(graphqlQuery, {
|
||||
orderId: 123,
|
||||
randomVariable: { complex: { num: 12343, str: "str_123" } },
|
||||
anotherArg: "any string",
|
||||
})
|
||||
const rjQuery = parser.parseQuery()
|
||||
|
||||
expect(rjQuery).toEqual({
|
||||
alias: "order",
|
||||
fields: ["id", "number", "date", "products"],
|
||||
expands: [
|
||||
{
|
||||
property: "products",
|
||||
fields: ["product_id", "variant_id", "order"],
|
||||
args: [
|
||||
{
|
||||
name: "randomValue",
|
||||
value: {
|
||||
complex: {
|
||||
num: 12343,
|
||||
str: "str_123",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
args: [
|
||||
{
|
||||
name: "order_id",
|
||||
value: 123,
|
||||
},
|
||||
{
|
||||
name: "anotherArg",
|
||||
value: "any string",
|
||||
},
|
||||
],
|
||||
})
|
||||
})
|
||||
|
||||
it("Nested query with fields and directives", async () => {
|
||||
const graphqlQuery = `
|
||||
query {
|
||||
order(regularArgs: 123) {
|
||||
id
|
||||
number @include(if: "date > '2020-01-01'")
|
||||
date
|
||||
products {
|
||||
product_id
|
||||
variant_id
|
||||
variant @count {
|
||||
name @lowerCase
|
||||
sku @include(if: "name == 'test'")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
const parser = new GraphQLParser(graphqlQuery)
|
||||
const rjQuery = parser.parseQuery()
|
||||
|
||||
expect(rjQuery).toEqual({
|
||||
alias: "order",
|
||||
fields: ["id", "number", "date", "products"],
|
||||
expands: [
|
||||
{
|
||||
property: "products",
|
||||
fields: ["product_id", "variant_id", "variant"],
|
||||
directives: {
|
||||
variant: [
|
||||
{
|
||||
name: "count",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
property: "products.variant",
|
||||
fields: ["name", "sku"],
|
||||
directives: {
|
||||
name: [
|
||||
{
|
||||
name: "lowerCase",
|
||||
},
|
||||
],
|
||||
sku: [
|
||||
{
|
||||
name: "include",
|
||||
args: [
|
||||
{
|
||||
name: "if",
|
||||
value: "name == 'test'",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
args: [
|
||||
{
|
||||
name: "regularArgs",
|
||||
value: 123,
|
||||
},
|
||||
],
|
||||
directives: {
|
||||
number: [
|
||||
{
|
||||
name: "include",
|
||||
args: [
|
||||
{
|
||||
name: "if",
|
||||
value: "date > '2020-01-01'",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,194 +0,0 @@
|
||||
import { RemoteJoinerQuery } from "@medusajs/types"
|
||||
import {
|
||||
ArgumentNode,
|
||||
DirectiveNode,
|
||||
DocumentNode,
|
||||
FieldNode,
|
||||
Kind,
|
||||
OperationDefinitionNode,
|
||||
SelectionSetNode,
|
||||
ValueNode,
|
||||
parse,
|
||||
} from "graphql"
|
||||
|
||||
interface Argument {
|
||||
name: string
|
||||
value?: unknown
|
||||
}
|
||||
|
||||
interface Directive {
|
||||
name: string
|
||||
args?: Argument[]
|
||||
}
|
||||
|
||||
interface Entity {
|
||||
property: string
|
||||
fields: string[]
|
||||
args?: Argument[]
|
||||
directives?: { [field: string]: Directive[] }
|
||||
}
|
||||
|
||||
class GraphQLParser {
|
||||
private ast: DocumentNode
|
||||
|
||||
constructor(input: string, private variables: Record<string, unknown> = {}) {
|
||||
this.ast = parse(input)
|
||||
this.variables = variables
|
||||
}
|
||||
|
||||
private parseValueNode(valueNode: ValueNode): unknown {
|
||||
const obj = {}
|
||||
|
||||
switch (valueNode.kind) {
|
||||
case Kind.VARIABLE:
|
||||
return this.variables ? this.variables[valueNode.name.value] : undefined
|
||||
case Kind.INT:
|
||||
return parseInt(valueNode.value, 10)
|
||||
case Kind.FLOAT:
|
||||
return parseFloat(valueNode.value)
|
||||
case Kind.BOOLEAN:
|
||||
return Boolean(valueNode.value)
|
||||
case Kind.STRING:
|
||||
case Kind.ENUM:
|
||||
return valueNode.value
|
||||
case Kind.NULL:
|
||||
return null
|
||||
case Kind.LIST:
|
||||
return valueNode.values.map((v) => this.parseValueNode(v))
|
||||
case Kind.OBJECT:
|
||||
for (const field of valueNode.fields) {
|
||||
obj[field.name.value] = this.parseValueNode(field.value)
|
||||
}
|
||||
return obj
|
||||
default:
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
private parseArguments(
|
||||
args: readonly ArgumentNode[]
|
||||
): Argument[] | undefined {
|
||||
if (!args.length) {
|
||||
return
|
||||
}
|
||||
|
||||
return args.map((arg) => {
|
||||
const value = this.parseValueNode(arg.value)
|
||||
|
||||
return {
|
||||
name: arg.name.value,
|
||||
value: value,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private parseDirectives(directives: readonly DirectiveNode[]): Directive[] {
|
||||
return directives.map((directive) => ({
|
||||
name: directive.name.value,
|
||||
args: this.parseArguments(directive.arguments || []),
|
||||
}))
|
||||
}
|
||||
|
||||
private createDirectivesMap(selectionSet: SelectionSetNode):
|
||||
| {
|
||||
[field: string]: Directive[]
|
||||
}
|
||||
| undefined {
|
||||
const directivesMap: { [field: string]: Directive[] } = {}
|
||||
let hasDirectives = false
|
||||
selectionSet.selections.forEach((field) => {
|
||||
const fieldName = (field as FieldNode).name.value
|
||||
const fieldDirectives = this.parseDirectives(
|
||||
(field as FieldNode).directives || []
|
||||
)
|
||||
if (fieldDirectives.length > 0) {
|
||||
hasDirectives = true
|
||||
directivesMap[fieldName] = fieldDirectives
|
||||
}
|
||||
})
|
||||
return hasDirectives ? directivesMap : undefined
|
||||
}
|
||||
|
||||
private extractEntities(
|
||||
node: SelectionSetNode,
|
||||
parentName = "",
|
||||
mainService = ""
|
||||
): Entity[] {
|
||||
const entities: Entity[] = []
|
||||
|
||||
node.selections.forEach((selection) => {
|
||||
if (selection.kind === "Field") {
|
||||
const fieldNode = selection as FieldNode
|
||||
|
||||
if (!fieldNode.selectionSet) {
|
||||
return
|
||||
}
|
||||
|
||||
const propName = fieldNode.name.value
|
||||
const entityName = parentName ? `${parentName}.${propName}` : propName
|
||||
|
||||
const nestedEntity: Entity = {
|
||||
property: entityName.replace(`${mainService}.`, ""),
|
||||
fields: fieldNode.selectionSet.selections.map(
|
||||
(field) => (field as FieldNode).name.value
|
||||
),
|
||||
args: this.parseArguments(fieldNode.arguments || []),
|
||||
directives: this.createDirectivesMap(fieldNode.selectionSet),
|
||||
}
|
||||
|
||||
entities.push(nestedEntity)
|
||||
entities.push(
|
||||
...this.extractEntities(
|
||||
fieldNode.selectionSet,
|
||||
entityName,
|
||||
mainService
|
||||
)
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
return entities
|
||||
}
|
||||
|
||||
public parseQuery(): RemoteJoinerQuery {
|
||||
const queryDefinition = this.ast.definitions.find(
|
||||
(definition) => definition.kind === "OperationDefinition"
|
||||
) as OperationDefinitionNode
|
||||
|
||||
if (!queryDefinition) {
|
||||
throw new Error("No query found")
|
||||
}
|
||||
|
||||
const rootFieldNode = queryDefinition.selectionSet
|
||||
.selections[0] as FieldNode
|
||||
const propName = rootFieldNode.name.value
|
||||
|
||||
const remoteJoinConfig: RemoteJoinerQuery = {
|
||||
alias: propName,
|
||||
fields: [],
|
||||
expands: [],
|
||||
}
|
||||
|
||||
if (rootFieldNode.arguments) {
|
||||
remoteJoinConfig.args = this.parseArguments(rootFieldNode.arguments)
|
||||
}
|
||||
|
||||
if (rootFieldNode.selectionSet) {
|
||||
remoteJoinConfig.fields = rootFieldNode.selectionSet.selections.map(
|
||||
(field) => (field as FieldNode).name.value
|
||||
)
|
||||
remoteJoinConfig.directives = this.createDirectivesMap(
|
||||
rootFieldNode.selectionSet
|
||||
)
|
||||
remoteJoinConfig.expands = this.extractEntities(
|
||||
rootFieldNode.selectionSet,
|
||||
propName,
|
||||
propName
|
||||
)
|
||||
}
|
||||
|
||||
return remoteJoinConfig
|
||||
}
|
||||
}
|
||||
|
||||
export default GraphQLParser
|
||||
@@ -10,12 +10,11 @@ import {
|
||||
} from "@medusajs/types"
|
||||
import {
|
||||
deduplicate,
|
||||
extractRelationsFromGQL,
|
||||
GraphQLUtils,
|
||||
isDefined,
|
||||
isString,
|
||||
MedusaError,
|
||||
} from "@medusajs/utils"
|
||||
import GraphQLParser from "./graphql-ast"
|
||||
|
||||
const BASE_PATH = "_root"
|
||||
|
||||
@@ -140,7 +139,7 @@ export class RemoteJoiner {
|
||||
graphqlQuery: string,
|
||||
variables?: Record<string, unknown>
|
||||
): RemoteJoinerQuery {
|
||||
const parser = new GraphQLParser(graphqlQuery, variables)
|
||||
const parser = new GraphQLUtils.GraphQLParser(graphqlQuery, variables)
|
||||
return parser.parseQuery()
|
||||
}
|
||||
|
||||
@@ -154,7 +153,9 @@ export class RemoteJoiner {
|
||||
) {
|
||||
this.options.autoCreateServiceNameAlias ??= true
|
||||
if (this.options.entitiesMap) {
|
||||
this.entityMap = extractRelationsFromGQL(this.options.entitiesMap)
|
||||
this.entityMap = GraphQLUtils.extractRelationsFromGQL(
|
||||
this.options.entitiesMap
|
||||
)
|
||||
}
|
||||
|
||||
this.buildReferences(
|
||||
|
||||
Reference in New Issue
Block a user