feat(utils,modules-sdk): Remote query object to string array (#5216)

**What**
- transform remote query object back to string array
- get all fields and fields from given relations from a GraphQL schema

Co-authored-by: Carlos R. L. Rodrigues <37986729+carlos-r-l-rodrigues@users.noreply.github.com>
This commit is contained in:
Adrien de Peretti
2023-10-03 19:54:41 +02:00
committed by GitHub
parent cb569c2dfe
commit eeceec791c
13 changed files with 664 additions and 7 deletions

View File

@@ -24,6 +24,8 @@
"typescript": "^5.1.6"
},
"dependencies": {
"@graphql-tools/merge": "^9.0.0",
"@graphql-tools/schema": "^10.0.0",
"@medusajs/orchestration": "^0.4.1",
"@medusajs/types": "^1.11.2",
"@medusajs/utils": "^1.10.2",

View File

@@ -0,0 +1,94 @@
import { mergeTypeDefs } from "@graphql-tools/merge"
import { makeExecutableSchema } from "@graphql-tools/schema"
import { getFieldsAndRelations } from "../../utils"
const userModule = `
type User {
id: ID!
name: String!
blabla: WHATEVER
}
type Post {
author: User!
}
`
const postModule = `
type Post {
id: ID!
title: String!
date: String
}
type User {
posts: [Post!]!
}
type WHATEVER {
random_field: String
post: Post
}
`
const mergedSchema = mergeTypeDefs([userModule, postModule])
const schema = makeExecutableSchema({
typeDefs: mergedSchema,
})
const types = schema.getTypeMap()
describe("getFieldsAndRelations", function () {
it("Should get all fields of a given entity", async function () {
const fields = getFieldsAndRelations(types, "User")
expect(fields).toEqual(expect.arrayContaining(["id", "name"]))
})
it("Should get all fields of a given entity and a relation", async function () {
const fields = getFieldsAndRelations(types, "User", ["posts"])
expect(fields).toEqual(
expect.arrayContaining([
"id",
"name",
"posts.id",
"posts.title",
"posts.date",
])
)
})
it("Should get all fields of a given entity and many relations", async function () {
const fields = getFieldsAndRelations(types, "User", [
"posts",
"blabla",
"blabla.post",
])
expect(fields).toEqual(
expect.arrayContaining([
"id",
"name",
"posts.id",
"posts.title",
"posts.date",
"blabla.random_field",
"blabla.post.id",
"blabla.post.title",
"blabla.post.date",
])
)
})
it("Should get all fields of a given entity and many relations limited to the relations given", async function () {
const fields = getFieldsAndRelations(types, "User", ["posts", "blabla"])
expect(fields).toEqual(
expect.arrayContaining([
"id",
"name",
"posts.id",
"posts.title",
"posts.date",
"blabla.random_field",
])
)
})
})

View File

@@ -0,0 +1,110 @@
import { mergeTypeDefs } from "@graphql-tools/merge"
import { makeExecutableSchema } from "@graphql-tools/schema"
import { graphqlSchemaToFields } from "../../utils"
const userModule = `
type User {
id: ID!
name: String!
blabla: WHATEVER
}
type Post {
author: User!
}
`
const postModule = `
type Post {
id: ID!
title: String!
date: String
}
type User {
posts: [Post!]!
}
type WHATEVER {
random_field: String
post: Post
}
`
const mergedSchema = mergeTypeDefs([userModule, postModule])
const schema = makeExecutableSchema({
typeDefs: mergedSchema,
})
const types = schema.getTypeMap()
describe("graphqlSchemaToFields", function () {
it("Should get all fields of a given entity", async function () {
const fields = graphqlSchemaToFields(types, "User")
expect(fields).toEqual(expect.arrayContaining(["id", "name"]))
})
it("Should get all fields of a given entity and a relation", async function () {
const fields = graphqlSchemaToFields(types, "User", ["posts"])
expect(fields).toEqual(
expect.arrayContaining([
"id",
"name",
"posts.id",
"posts.title",
"posts.date",
])
)
})
it("Should get all fields of a given entity and many relations", async function () {
const fields = graphqlSchemaToFields(types, "User", [
"posts",
"blabla",
"blabla.post",
])
expect(fields).toEqual(
expect.arrayContaining([
"id",
"name",
"posts.id",
"posts.title",
"posts.date",
"blabla.random_field",
"blabla.post.id",
"blabla.post.title",
"blabla.post.date",
])
)
})
it("Should get all fields of a given entity and many relations limited to the relations given", async function () {
const fields = graphqlSchemaToFields(types, "User", ["posts", "blabla"])
expect(fields).toEqual(
expect.arrayContaining([
"id",
"name",
"posts.id",
"posts.title",
"posts.date",
"blabla.random_field",
])
)
})
it("Should get all fields of a given entity and many relations limited to the relations given if they exists", async function () {
const fields = graphqlSchemaToFields(types, "User", [
"posts",
"doNotExists",
])
expect(fields).toEqual(
expect.arrayContaining([
"id",
"name",
"posts.id",
"posts.title",
"posts.date",
])
)
})
})

View File

@@ -0,0 +1,37 @@
import { GraphQLNamedType, GraphQLObjectType, isObjectType } from "graphql"
export function getFieldsAndRelations(
schemaTypeMap: { [key: string]: GraphQLNamedType },
typeName: string,
relations: string[] = []
) {
const result: string[] = []
function traverseFields(typeName, prefix) {
const type = schemaTypeMap[typeName]
if (!(type instanceof GraphQLObjectType)) {
return
}
const fields = type.getFields()
for (const fieldName in fields) {
const field = fields[fieldName]
let fieldType = field.type as any
while (fieldType.ofType) {
fieldType = fieldType.ofType
}
if (!isObjectType(fieldType)) {
result.push(`${prefix}${fieldName}`)
} else if (relations.includes(prefix + fieldName)) {
traverseFields(fieldType.name, `${prefix}${fieldName}.`)
}
}
}
traverseFields(typeName, "")
return result
}

View File

@@ -0,0 +1,93 @@
import { GraphQLNamedType, GraphQLObjectType, isObjectType } from "graphql"
/**
* From graphql schema get all the fields for the requested type and relations
*
* @param schemaTypeMap
* @param typeName
* @param relations
*
* @example
*
* const userModule = `
* type User {
* id: ID!
* name: String!
* blabla: WHATEVER
* }
*
* type Post {
* author: User!
* }
* `
*
* const postModule = `
* type Post {
* id: ID!
* title: String!
* date: String
* }
*
* type User {
* posts: [Post!]!
* }
*
* type WHATEVER {
* random_field: String
* post: Post
* }
* `
*
* const mergedSchema = mergeTypeDefs([userModule, postModule])
* const schema = makeExecutableSchema({
* typeDefs: mergedSchema,
* })
*
* const fields = graphqlSchemaToFields(types, "User", ["posts"])
*
* console.log(fields)
*
* // [
* // "id",
* // "name",
* // "posts.id",
* // "posts.title",
* // "posts.date",
* // ]
*/
export function graphqlSchemaToFields(
schemaTypeMap: { [key: string]: GraphQLNamedType },
typeName: string,
relations: string[] = []
) {
const result: string[] = []
function traverseFields(typeName, parent = "") {
const type = schemaTypeMap[typeName]
if (!(type instanceof GraphQLObjectType)) {
return
}
const fields = type.getFields()
for (const fieldName in fields) {
const field = fields[fieldName]
let fieldType = field.type as any
while (fieldType.ofType) {
fieldType = fieldType.ofType
}
const composedField = parent ? `${parent}.${fieldName}` : fieldName
if (!isObjectType(fieldType)) {
result.push(composedField)
} else if (relations.includes(composedField)) {
traverseFields(fieldType.name, composedField)
}
}
}
traverseFields(typeName)
return result
}

View File

@@ -0,0 +1,3 @@
export * from "./get-fields-and-relations"
export * from "./graphql-schema-to-fields"