-
Notifications
You must be signed in to change notification settings - Fork 354
Description
Issue workflow progress
Progress of the issue based on the
Contributor Workflow
- 1. The issue provides a reproduction available on
Github,
Stackblitz
or
CodeSandbox
Make sure to fork this template and run
yarn generatein the terminal.Please make sure Mesh package versions under
package.jsonmatches yours.
- 2. A failing test has been provided
- 3. A local solution has been provided
- 4. A pull request is pending review
Describe the bug
When I want to generate a schema from @omnigraph/json-schema, I have a type that is generated several times but with a different name, I can’t understand why and how the generation works.
Here is what my function loadJSONSchemaSubgraph looks like, nothing particular at this level:
loadJSONSchemaSubgraph('SuluRest', {
endpoint: 'MY_ENDPOINT',
operations: [
{
type: 'Query',
field: 'ambassadorList',
method: 'GET',
path: '/{context.headers.lang}/universe/ambassadors.json',
responseSchema:
'./api/rest/ambassador.json#/definitions/AmbassadorList',
},
],
})Here is the structure of the ambassador.json file. This file uses several references that use the $ref to utils.json#/definitions/singleMediaSelection.json more than once :
ambassador.json
{
"definitions": {
"AmbassadorList": {
"title": "AmbassadorList",
"type": "object",
"properties": {
"content": {
"title": "AmbassadorListContent",
"type": "object",
"properties": {
"title": {
"type": ["string", "null"]
},
"subtitle": {
"type": ["string", "null"]
},
"image": {
"$ref": "utils.json#/definitions/SingleMediaSelection"
},
"videoMobile": {
"$ref": "utils.json#/definitions/singleMediaSelection"
},
"videoDesktop": {
"$ref": "utils.json#/definitions/singleMediaSelection"
},
"autoplay": {
"type": "boolean"
},
"videoLoop": {
"type": "boolean"
},
"youtubeUrl": {
"type": ["string", "null"]
}
}
},
"extension": {
"$ref": "types/extension.json"
}
}
}
}
}The types/extension.json also use utils.json :
{
"type": "object",
"title": "Extension",
"properties": {
"excerpt": {
"$ref": "extension/excerpt.json"
}
}
}extension/excerpt.json
{
"title": "Excerpt",
"type": "object",
"properties": {
"title": {
"type": ["string", "null"]
},
"more": {
"type": ["string", "null"]
},
"defaultMore": {
"type": ["string", "null"]
},
"description": {
"type": ["string", "null"]
},
"categories": {
"$ref": "excerpt/categories.json"
},
"icon": {
"type": "array",
"items": {
"$ref": "../../utils.json#/definitions/SingleMediaSelection"
}
},
"images": {
"type": "array",
"items": {
"$ref": "../../utils.json#/definitions/SingleMediaSelection"
}
},
"tags": {
"type": "array",
"items": {
"type": "string"
}
}
}
}The utils.json file is very simple :
{
"definitions": {
"SingleMediaSelection": {
"$ref": "types/singleMediaSelection.json"
}
}
}And the types/singleMediaSelection.json file is also very simple :
types/singleMediaSelection.json
{
"title": "SingleMediaSelection",
"type": "object",
"properties": {
"id": {
"type": ["integer", "null"]
},
"locale": {
"type": ["string", "null"]
},
"collection": {
"type": ["integer", "null"]
},
"size": {
"type": ["integer", "null"]
},
"mimeType": {
"type": ["string", "null"]
},
"title": {
"type": ["string", "null"]
},
"version": {
"type": ["integer", "null"]
},
"subVersion": {
"type": ["integer", "null"]
},
"name": {
"type": ["string", "null"]
},
"description": {
"type": ["string", "null"]
},
"type": {
"type": ["object", "null"],
"properties": {
"name": {
"type": ["string", "null"]
},
"id": {
"type": ["integer", "null"]
}
}
},
"isImage": {
"type": "boolean"
},
"isVideo": {
"type": "boolean"
},
"isAudio": {
"type": "boolean"
},
"isDocument": {
"type": "boolean"
},
"publishLanguages": {
"type": "array"
},
"contentLanguages": {
"type": "array"
},
"tags": {
"type": "array"
},
"url": {
"type": ["string", "null"]
},
"changed": {
"type": ["string", "null"]
},
"changer": {
"type": ["string", "null"]
},
"created": {
"type": ["string", "null"]
},
"creator": {
"type": ["string", "null"]
},
"properties": {
"type": ["object", "null"],
"properties": {
"width": {
"type": ["integer", "null"]
},
"height": {
"type": ["integer", "null"]
}
}
},
"categories": {
"type": ["string", "null"]
},
"targetGroups": {
"type": ["string", "null"]
},
"formatUri": {
"type": "string"
},
"focusPointX": {
"type": ["integer", "null"]
},
"focusPointY": {
"type": ["integer", "null"]
}
}
}When I excuse the following command npx mesh-compose -o . mesh/schema.graphql, the schema is indeed generated but the SingleMediaSelection type is duplicated several times with a different suffix: SingleMediaSelection, SingleMediaSelection1, SingleMediaSelection2, ... Here is the generated schema.graphql:
schema.graphql
schema
@link(url: "https://specs.apollo.dev/link/v1.0")
@link(url: "https://specs.apollo.dev/join/v0.3", for: EXECUTION)
@link(
url: "https://the-guild.dev/graphql/mesh/spec/v1.0"
import: ["@httpOperation", "@transport", "@source", "@extraSchemaDefinitionDirective"]
)
{
query: Query
}
directive @join__enumValue(graph: join__Graph!) repeatable on ENUM_VALUE
directive @join__graph(name: String!, url: String!) on ENUM_VALUE
directive @join__field(
graph: join__Graph
requires: join__FieldSet
provides: join__FieldSet
type: String
external: Boolean
override: String
usedOverridden: Boolean
) repeatable on FIELD_DEFINITION | INPUT_FIELD_DEFINITION
directive @join__implements(
graph: join__Graph!
interface: String!
) repeatable on OBJECT | INTERFACE
directive @join__type(
graph: join__Graph!
key: join__FieldSet
extension: Boolean! = false
resolvable: Boolean! = true
isInterfaceObject: Boolean! = false
) repeatable on OBJECT | INTERFACE | UNION | ENUM | INPUT_OBJECT | SCALAR
directive @join__unionMember(
graph: join__Graph!
member: String!
) repeatable on UNION
scalar join__FieldSet
directive @link(
url: String
as: String
for: link__Purpose
import: [link__Import]
) repeatable on SCHEMA
scalar link__Import
enum link__Purpose {
"""
`SECURITY` features provide metadata necessary to securely resolve fields.
"""
SECURITY
"""
`EXECUTION` features provide metadata necessary for operation execution.
"""
EXECUTION
}
enum join__Graph {
SULU_REST @join__graph(
name: "SuluRest"
url: "MY_ENDPOINT"
)
}
directive @httpOperation(
subgraph: String
path: String
operationSpecificHeaders: [[String]]
httpMethod: HTTPMethod
isBinary: Boolean
requestBaseBody: ObjMap
queryParamArgMap: ObjMap
queryStringOptionsByParam: ObjMap
jsonApiFields: Boolean
queryStringOptions: ObjMap
) repeatable on FIELD_DEFINITION
directive @transport(
subgraph: String
kind: String
location: String
headers: [[String]]
queryStringOptions: ObjMap
queryParams: [[String]]
) repeatable on SCHEMA
directive @source(name: String!, type: String, subgraph: String!) repeatable on SCALAR | OBJECT | FIELD_DEFINITION | ARGUMENT_DEFINITION | INTERFACE | UNION | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION
directive @extraSchemaDefinitionDirective(directives: _DirectiveExtensions) repeatable on OBJECT
"""
The `JSON` scalar type represents JSON values as specified by [ECMA-404](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf).
"""
scalar JSON @join__type(graph: SULU_REST) @specifiedBy(
url: "http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf"
)
scalar ObjMap @join__type(graph: SULU_REST)
scalar _DirectiveExtensions @join__type(graph: SULU_REST)
type Query @extraSchemaDefinitionDirective(
directives: {transport: [{subgraph: "SuluRest", kind: "rest", location: "MY_ENDPOINT"}]}
) @join__type(graph: SULU_REST) {
ambassadorList: AmbassadorList @httpOperation(
subgraph: "SuluRest"
path: "/{context.headers.lang}/universe/ambassadors.json"
httpMethod: GET
)
}
type AmbassadorList @join__type(graph: SULU_REST) {
content: AmbassadorListContent
extension: Extension
}
type AmbassadorListContent @join__type(graph: SULU_REST) {
title: String
subtitle: String
image: SingleMediaSelection
videoMobile: JSON
videoDesktop: JSON
autoplay: Boolean
videoLoop: Boolean
youtubeUrl: String
}
type SingleMediaSelection @join__type(graph: SULU_REST) {
id: Int
locale: String
collection: Int
size: Int
mimeType: String
title: String
version: Int
subVersion: Int
name: String
description: String
type: query_ambassadorList_content_image_type
isImage: Boolean
isVideo: Boolean
isAudio: Boolean
isDocument: Boolean
publishLanguages: [JSON]
contentLanguages: [JSON]
tags: [JSON]
url: String
changed: String
changer: String
created: String
creator: String
properties: query_ambassadorList_content_image
categories: String
targetGroups: String
formatUri: String
focusPointX: Int
focusPointY: Int
}
type query_ambassadorList_content_image_type @join__type(graph: SULU_REST) {
name: String
id: Int
}
type query_ambassadorList_content_image @join__type(graph: SULU_REST) {
width: Int
height: Int
}
type Extension @join__type(graph: SULU_REST) {
seo: Seo
excerpt: Excerpt
}
type Seo @join__type(graph: SULU_REST) {
title: String
description: String
keywords: String
canonicalUrl: String
noIndex: Boolean
noFollow: Boolean
hideInSitemap: Boolean
published: [String]
}
type Excerpt @join__type(graph: SULU_REST) {
title: String
more: String
defaultMore: String
description: String
categories: [AmbassadorCategory]
icon: [SingleMediaSelection3]
images: [SingleMediaSelection3]
tags: [String]
}
type AmbassadorCategory @join__type(graph: SULU_REST) {
locale: String
id: Int
name: String
ghostLocale: String
parentId: Int
medias: [SingleMediaSelection2]
}
type SingleMediaSelection2 @join__type(graph: SULU_REST) {
id: Int
locale: String
collection: Int
size: Int
mimeType: String
title: String
version: Int
subVersion: Int
name: String
description: String
type: query_ambassadorList_extension_excerpt_categories_items_medias_items_type
isImage: Boolean
isVideo: Boolean
isAudio: Boolean
isDocument: Boolean
publishLanguages: [JSON]
contentLanguages: [JSON]
tags: [JSON]
url: String
changed: String
changer: String
created: String
creator: String
properties: query_ambassadorList_extension_excerpt_categories_items_medias_items
categories: String
targetGroups: String
formatUri: String
focusPointX: Int
focusPointY: Int
}
type query_ambassadorList_extension_excerpt_categories_items_medias_items_type @join__type(graph: SULU_REST) {
name: String
id: Int
}
type query_ambassadorList_extension_excerpt_categories_items_medias_items @join__type(graph: SULU_REST) {
width: Int
height: Int
}
type SingleMediaSelection3 @join__type(graph: SULU_REST) {
id: Int
locale: String
collection: Int
size: Int
mimeType: String
title: String
version: Int
subVersion: Int
name: String
description: String
type: query_ambassadorList_extension_excerpt_icon_items_type
isImage: Boolean
isVideo: Boolean
isAudio: Boolean
isDocument: Boolean
publishLanguages: [JSON]
contentLanguages: [JSON]
tags: [JSON]
url: String
changed: String
changer: String
created: String
creator: String
properties: query_ambassadorList_extension_excerpt_icon_items
categories: String
targetGroups: String
formatUri: String
focusPointX: Int
focusPointY: Int
}
type query_ambassadorList_extension_excerpt_icon_items_type @join__type(graph: SULU_REST) {
name: String
id: Int
}
type query_ambassadorList_extension_excerpt_icon_items @join__type(graph: SULU_REST) {
width: Int
height: Int
}
enum HTTPMethod @join__type(graph: SULU_REST) {
GET @join__enumValue(graph: SULU_REST)
HEAD @join__enumValue(graph: SULU_REST)
POST @join__enumValue(graph: SULU_REST)
PUT @join__enumValue(graph: SULU_REST)
DELETE @join__enumValue(graph: SULU_REST)
CONNECT @join__enumValue(graph: SULU_REST)
OPTIONS @join__enumValue(graph: SULU_REST)
TRACE @join__enumValue(graph: SULU_REST)
PATCH @join__enumValue(graph: SULU_REST)
}
To Reproduce Steps to reproduce the behavior:
Expected behavior
The SingleMediaSelection type must be generated only once since the $ref always refers to the same file the graphQL
Environment:
- OS: Ubuntu 24
@graphql-mesh/compose-cli: 1.5.3@omnigraph/json-schema: 0.109.17- NodeJS:
v22.21.1
Additional context
I am trying to migrate from mesh v0 to v1, with v0 there was no problem, the SingleMediaSelection type was generated only once. With v1, the type is generated several times ...