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:
committed by
GitHub
parent
cb569c2dfe
commit
eeceec791c
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
])
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -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",
|
||||
])
|
||||
)
|
||||
})
|
||||
})
|
||||
37
packages/modules-sdk/src/utils/get-fields-and-relations.ts
Normal file
37
packages/modules-sdk/src/utils/get-fields-and-relations.ts
Normal 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
|
||||
}
|
||||
93
packages/modules-sdk/src/utils/graphql-schema-to-fields.ts
Normal file
93
packages/modules-sdk/src/utils/graphql-schema-to-fields.ts
Normal 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
|
||||
}
|
||||
3
packages/modules-sdk/src/utils/index.ts
Normal file
3
packages/modules-sdk/src/utils/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export * from "./get-fields-and-relations"
|
||||
export * from "./graphql-schema-to-fields"
|
||||
|
||||
Reference in New Issue
Block a user