From f3c26b701376f37266539de699d40d089d3dbd33 Mon Sep 17 00:00:00 2001 From: Michael Charfadi Date: Tue, 18 Jun 2024 11:04:51 +0200 Subject: [PATCH 1/2] [3634] Simplifying the contribution to the GraphQL subscription of the diagram for custom nodes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bug: https://github.com/eclipse-sirius/sirius-web/issues/3634 Signed-off-by: Michaël Charfadi --- CHANGELOG.adoc | 5 + .../src/contexts/NodeContext.ts | 1 - .../src/contexts/NodeContext.types.ts | 2 - .../subscription/diagramEventSubscription.ts | 5 +- .../graphql/subscription/diagramFragment.ts | 5 +- .../src/graphql/subscription/nodeFragment.ts | 11 +- .../subscription/nodeFragment.types.ts | 5 - .../sirius-components-diagrams/src/index.ts | 2 +- .../representation/DiagramRepresentation.tsx | 11 +- .../DiagramRepresentationConfiguration.tsx | 1 - ...iagramRepresentationConfiguration.types.ts | 2 - .../frontend/sirius-web/src/index.tsx | 31 +++-- .../src/nodes/ElipseNodeDocumentTransform.ts | 107 ++++++++++++++++++ 13 files changed, 145 insertions(+), 43 deletions(-) create mode 100644 packages/sirius-web/frontend/sirius-web/src/nodes/ElipseNodeDocumentTransform.ts diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index c2808de359..107cb55c07 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -13,6 +13,9 @@ === Deprecation warning +- https://github.com/eclipse-sirius/sirius-web/issues/3634[#3634] All the existing parts of the API used to contribute custom nodes with `nodeLayoutHandlers`, `nodeConverters` and `nodeTypeContributions` will be migrated soon to leverage the extension registry. +The signature of the types to implement should not change but the way they are contributed will. + === Breaking changes - https://github.com/eclipse-sirius/sirius-web/issues/3562[#3562] [sirius-web] Update displayed default model creation action. @@ -26,6 +29,7 @@ More existing APIs will be migrated to this new common pattern. * `public Optional acquireRepresentationEventProcessor(IRepresentationConfiguration configuration, IInput input)`; * `Flux getSubscription(String editingContextId, IRepresentationConfiguration representationConfiguration, IInput input);` - https://github.com/eclipse-sirius/sirius-web/issues/3623[#3623] [form] Remove `IWidgetSubscriptionManagerFactory` + - https://github.com/eclipse-sirius/sirius-web/issues/3634[#3634] [sirius-web] Remove `GraphQLNodeStyleFragment` from `NodeTypeRegistry`, you can specify additional fields to be retreived by the subscription using the `DocumentTransform` GraphQL API and the `apolloClientOptionsConfigurersExtensionPoint` extension point === Dependency update @@ -104,6 +108,7 @@ image:doc/screenshots/insideLabelPositions.png[Inside label positions, 70%] - https://github.com/eclipse-sirius/sirius-web/issues/3561[#3561] [diagram] Add support for background and border on diagram labels - https://github.com/eclipse-sirius/sirius-web/issues/3604[#3604] [diagram] Make node overlap resolution faster during "Arrange All" - https://github.com/eclipse-sirius/sirius-web/issues/3653[#3653] [gantt] Add documentation for gantt representation +- https://github.com/eclipse-sirius/sirius-web/issues/3634[#3634] [sirius-web] Simplifying the contribution to the GraphQL subscription of the diagram for custom nodes == v2024.5.0 diff --git a/packages/diagrams/frontend/sirius-components-diagrams/src/contexts/NodeContext.ts b/packages/diagrams/frontend/sirius-components-diagrams/src/contexts/NodeContext.ts index 271738f49f..c89fddd2c7 100644 --- a/packages/diagrams/frontend/sirius-components-diagrams/src/contexts/NodeContext.ts +++ b/packages/diagrams/frontend/sirius-components-diagrams/src/contexts/NodeContext.ts @@ -16,7 +16,6 @@ import { NodeTypeContextValue } from './NodeContext.types'; const value: NodeTypeContextValue = { nodeConverters: [], nodeLayoutHandlers: [], - graphQLNodeStyleFragments: [], nodeTypeContributions: [], }; diff --git a/packages/diagrams/frontend/sirius-components-diagrams/src/contexts/NodeContext.types.ts b/packages/diagrams/frontend/sirius-components-diagrams/src/contexts/NodeContext.types.ts index 3a1754d8c2..9ae863ed99 100644 --- a/packages/diagrams/frontend/sirius-components-diagrams/src/contexts/NodeContext.types.ts +++ b/packages/diagrams/frontend/sirius-components-diagrams/src/contexts/NodeContext.types.ts @@ -12,7 +12,6 @@ *******************************************************************************/ import { NodeProps } from 'reactflow'; import { INodeConverter } from '../converter/ConvertEngine.types'; -import { GraphQLNodeStyleFragment } from '../graphql/subscription/nodeFragment.types'; import { NodeData } from '../renderer/DiagramRenderer.types'; import { INodeLayoutHandler } from '../renderer/layout/LayoutEngine.types'; @@ -24,7 +23,6 @@ export interface NodeTypeContributionProps { export type NodeTypeContributionElement = React.ReactElement; export interface NodeTypeContextValue { - graphQLNodeStyleFragments: GraphQLNodeStyleFragment[]; nodeLayoutHandlers: INodeLayoutHandler[]; nodeConverters: INodeConverter[]; nodeTypeContributions: NodeTypeContributionElement[]; diff --git a/packages/diagrams/frontend/sirius-components-diagrams/src/graphql/subscription/diagramEventSubscription.ts b/packages/diagrams/frontend/sirius-components-diagrams/src/graphql/subscription/diagramEventSubscription.ts index e214eeb1d1..c58ec8a9f6 100644 --- a/packages/diagrams/frontend/sirius-components-diagrams/src/graphql/subscription/diagramEventSubscription.ts +++ b/packages/diagrams/frontend/sirius-components-diagrams/src/graphql/subscription/diagramEventSubscription.ts @@ -12,9 +12,8 @@ *******************************************************************************/ import { diagramFragment } from './diagramFragment'; -import { GraphQLNodeStyleFragment } from './nodeFragment.types'; -export const diagramEventSubscription = (contributions: GraphQLNodeStyleFragment[]) => ` +export const diagramEventSubscription = ` subscription diagramEvent($input: DiagramEventInput!) { diagramEvent(input: $input) { ... on ErrorPayload { @@ -35,5 +34,5 @@ subscription diagramEvent($input: DiagramEventInput!) { } } -${diagramFragment(contributions)} +${diagramFragment} `; diff --git a/packages/diagrams/frontend/sirius-components-diagrams/src/graphql/subscription/diagramFragment.ts b/packages/diagrams/frontend/sirius-components-diagrams/src/graphql/subscription/diagramFragment.ts index 677c53529e..31930ef6fc 100644 --- a/packages/diagrams/frontend/sirius-components-diagrams/src/graphql/subscription/diagramFragment.ts +++ b/packages/diagrams/frontend/sirius-components-diagrams/src/graphql/subscription/diagramFragment.ts @@ -14,9 +14,8 @@ import { edgeFragment } from './edgeFragment'; import { insideLabelFragment, labelFragment, outsideLabelFragment } from './labelFragment'; import { nodeFragment } from './nodeFragment'; -import { GraphQLNodeStyleFragment } from './nodeFragment.types'; -export const diagramFragment = (contributions: GraphQLNodeStyleFragment[]) => ` +export const diagramFragment = ` fragment diagramFragment on Diagram { id targetObjectId @@ -73,7 +72,7 @@ fragment diagramFragment on Diagram { } } -${nodeFragment(contributions)} +${nodeFragment} ${edgeFragment} ${labelFragment} ${insideLabelFragment} diff --git a/packages/diagrams/frontend/sirius-components-diagrams/src/graphql/subscription/nodeFragment.ts b/packages/diagrams/frontend/sirius-components-diagrams/src/graphql/subscription/nodeFragment.ts index 8d0830bdb0..5d753b9237 100644 --- a/packages/diagrams/frontend/sirius-components-diagrams/src/graphql/subscription/nodeFragment.ts +++ b/packages/diagrams/frontend/sirius-components-diagrams/src/graphql/subscription/nodeFragment.ts @@ -10,9 +10,8 @@ * Contributors: * Obeo - initial API and implementation *******************************************************************************/ -import { GraphQLNodeStyleFragment } from './nodeFragment.types'; -export const nodeFragment = (contributions: GraphQLNodeStyleFragment[]) => ` +export const nodeFragment = ` fragment nodeFragment on Node { id type @@ -48,14 +47,6 @@ fragment nodeFragment on Node { ... on IconLabelNodeStyle { background } - ${contributions.map( - (nodeStyle) => - ` - ... on ${nodeStyle.type} { - ${nodeStyle.fields} - } - ` - )} } childrenLayoutStrategy { __typename diff --git a/packages/diagrams/frontend/sirius-components-diagrams/src/graphql/subscription/nodeFragment.types.ts b/packages/diagrams/frontend/sirius-components-diagrams/src/graphql/subscription/nodeFragment.types.ts index 985f33ec47..2a0dca751f 100644 --- a/packages/diagrams/frontend/sirius-components-diagrams/src/graphql/subscription/nodeFragment.types.ts +++ b/packages/diagrams/frontend/sirius-components-diagrams/src/graphql/subscription/nodeFragment.types.ts @@ -91,8 +91,3 @@ export interface GQLImageNodeStyle extends GQLNodeStyle { export interface GQLIconLabelNodeStyle extends GQLNodeStyle { background: string; } - -export interface GraphQLNodeStyleFragment { - type: string; - fields: string; -} diff --git a/packages/diagrams/frontend/sirius-components-diagrams/src/index.ts b/packages/diagrams/frontend/sirius-components-diagrams/src/index.ts index 592e31e8ed..d653a02a19 100644 --- a/packages/diagrams/frontend/sirius-components-diagrams/src/index.ts +++ b/packages/diagrams/frontend/sirius-components-diagrams/src/index.ts @@ -24,7 +24,7 @@ export type { GQLNodeDescription } from './graphql/query/nodeDescriptionFragment export type { GQLDiagram, GQLNodeLayoutData } from './graphql/subscription/diagramFragment.types'; export type { GQLEdge } from './graphql/subscription/edgeFragment.types'; export { GQLViewModifier } from './graphql/subscription/nodeFragment.types'; -export type { GQLNode, GQLNodeStyle, GraphQLNodeStyleFragment } from './graphql/subscription/nodeFragment.types'; +export type { GQLNode, GQLNodeStyle } from './graphql/subscription/nodeFragment.types'; export { BorderNodePosition as BorderNodePosition } from './renderer/DiagramRenderer.types'; export type { Diagram, NodeData } from './renderer/DiagramRenderer.types'; export { Label } from './renderer/Label'; diff --git a/packages/diagrams/frontend/sirius-components-diagrams/src/representation/DiagramRepresentation.tsx b/packages/diagrams/frontend/sirius-components-diagrams/src/representation/DiagramRepresentation.tsx index 7dcbec8c96..c1596aaaba 100644 --- a/packages/diagrams/frontend/sirius-components-diagrams/src/representation/DiagramRepresentation.tsx +++ b/packages/diagrams/frontend/sirius-components-diagrams/src/representation/DiagramRepresentation.tsx @@ -13,18 +13,15 @@ import { gql, OnDataOptions, useQuery, useSubscription } from '@apollo/client'; import { RepresentationComponentProps, useMultiToast } from '@eclipse-sirius/sirius-components-core'; -import { useContext, useEffect, useState } from 'react'; +import { useEffect, useState } from 'react'; import { ReactFlowProvider } from 'reactflow'; import { DiagramContext } from '../contexts/DiagramContext'; import { DiagramDescriptionContext } from '../contexts/DiagramDescriptionContext'; -import { NodeTypeContext } from '../contexts/NodeContext'; -import { NodeTypeContextValue } from '../contexts/NodeContext.types'; import { diagramEventSubscription } from '../graphql/subscription/diagramEventSubscription'; import { GQLDiagramEventPayload, GQLDiagramRefreshedEventPayload, } from '../graphql/subscription/diagramEventSubscription.types'; -import { GraphQLNodeStyleFragment } from '../graphql/subscription/nodeFragment.types'; import { ConnectorContextProvider } from '../renderer/connector/ConnectorContext'; import { DiagramRenderer } from '../renderer/DiagramRenderer'; import { DiagramDirectEditContextProvider } from '../renderer/direct-edit/DiagramDirectEditContext'; @@ -44,7 +41,7 @@ import { } from './DiagramRepresentation.types'; import { StoreContextProvider } from './StoreContext'; -const subscription = (contributions: GraphQLNodeStyleFragment[]) => gql(diagramEventSubscription(contributions)); +const subscription = gql(diagramEventSubscription); export const getDiagramDescription = gql` query getDiagramDescription($editingContextId: ID!, $representationId: ID!) { @@ -139,9 +136,7 @@ export const DiagramRepresentation = ({ setState((prevState) => ({ ...prevState, diagramRefreshedEventPayload: null, complete: true })); }; - const { graphQLNodeStyleFragments } = useContext(NodeTypeContext); - - const { error } = useSubscription(subscription(graphQLNodeStyleFragments), { + const { error } = useSubscription(subscription, { variables, fetchPolicy: 'no-cache', onData, diff --git a/packages/sirius-web/frontend/sirius-web-application/src/diagrams/DiagramRepresentationConfiguration.tsx b/packages/sirius-web/frontend/sirius-web-application/src/diagrams/DiagramRepresentationConfiguration.tsx index fd7380a419..2292a781d8 100644 --- a/packages/sirius-web/frontend/sirius-web-application/src/diagrams/DiagramRepresentationConfiguration.tsx +++ b/packages/sirius-web/frontend/sirius-web-application/src/diagrams/DiagramRepresentationConfiguration.tsx @@ -17,7 +17,6 @@ import { DiagramRepresentationConfigurationProps } from './DiagramRepresentation export const defaultNodeTypeRegistry: NodeTypeContextValue = { nodeConverters: [], nodeLayoutHandlers: [], - graphQLNodeStyleFragments: [], nodeTypeContributions: [], }; diff --git a/packages/sirius-web/frontend/sirius-web-application/src/diagrams/DiagramRepresentationConfiguration.types.ts b/packages/sirius-web/frontend/sirius-web-application/src/diagrams/DiagramRepresentationConfiguration.types.ts index 6c93cc7555..038f2cb9ea 100644 --- a/packages/sirius-web/frontend/sirius-web-application/src/diagrams/DiagramRepresentationConfiguration.types.ts +++ b/packages/sirius-web/frontend/sirius-web-application/src/diagrams/DiagramRepresentationConfiguration.types.ts @@ -12,7 +12,6 @@ *******************************************************************************/ import { - GraphQLNodeStyleFragment, INodeConverter, INodeLayoutHandler, NodeData, @@ -24,7 +23,6 @@ export interface DiagramRepresentationConfigurationProps { } export interface NodeTypeRegistry { - graphQLNodeStyleFragments: GraphQLNodeStyleFragment[]; nodeLayoutHandlers: INodeLayoutHandler[]; nodeConverters: INodeConverter[]; nodeTypeContributions: NodeTypeContributionElement[]; diff --git a/packages/sirius-web/frontend/sirius-web/src/index.tsx b/packages/sirius-web/frontend/sirius-web/src/index.tsx index c4861854d9..d6a536db7a 100644 --- a/packages/sirius-web/frontend/sirius-web/src/index.tsx +++ b/packages/sirius-web/frontend/sirius-web/src/index.tsx @@ -11,14 +11,18 @@ * Obeo - initial API and implementation *******************************************************************************/ import { loadDevMessages, loadErrorMessages } from '@apollo/client/dev'; +import { ExtensionRegistry } from '@eclipse-sirius/sirius-components-core'; import { NodeTypeContribution } from '@eclipse-sirius/sirius-components-diagrams'; import { + ApolloClientOptionsConfigurer, DiagramRepresentationConfiguration, NodeTypeRegistry, SiriusWebApplication, + apolloClientOptionsConfigurersExtensionPoint, } from '@eclipse-sirius/sirius-web-application'; import ReactDOM from 'react-dom'; import { httpOrigin, wsOrigin } from './core/URL'; +import { ellipseNodeStyleDocumentTransform } from './nodes/ElipseNodeDocumentTransform'; import { EllipseNode } from './nodes/EllipseNode'; import { EllipseNodeConverter } from './nodes/EllipseNodeConverter'; import { EllipseNodeLayoutHandler } from './nodes/EllipseNodeLayoutHandler'; @@ -34,20 +38,33 @@ if (process.env.NODE_ENV !== 'production') { loadErrorMessages(); } +const registry = new ExtensionRegistry(); + +const apolloClientOptionsConfigurer: ApolloClientOptionsConfigurer = (currentOptions) => { + const { documentTransform } = currentOptions; + + const newDocumentTransform = documentTransform + ? documentTransform.concat(ellipseNodeStyleDocumentTransform) + : ellipseNodeStyleDocumentTransform; + return { + ...currentOptions, + documentTransform: newDocumentTransform, + }; +}; + +registry.putData(apolloClientOptionsConfigurersExtensionPoint, { + identifier: `siriusWeb_${apolloClientOptionsConfigurersExtensionPoint.identifier}`, + data: [apolloClientOptionsConfigurer], +}); + const nodeTypeRegistry: NodeTypeRegistry = { - graphQLNodeStyleFragments: [ - { - type: 'EllipseNodeStyle', - fields: `borderColor borderSize borderStyle background`, - }, - ], nodeLayoutHandlers: [new EllipseNodeLayoutHandler()], nodeConverters: [new EllipseNodeConverter()], nodeTypeContributions: [], }; ReactDOM.render( - + , document.getElementById('root') diff --git a/packages/sirius-web/frontend/sirius-web/src/nodes/ElipseNodeDocumentTransform.ts b/packages/sirius-web/frontend/sirius-web/src/nodes/ElipseNodeDocumentTransform.ts new file mode 100644 index 0000000000..4ddc678adc --- /dev/null +++ b/packages/sirius-web/frontend/sirius-web/src/nodes/ElipseNodeDocumentTransform.ts @@ -0,0 +1,107 @@ +/******************************************************************************* + * Copyright (c) 2024 Obeo. + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Obeo - initial API and implementation + *******************************************************************************/ + +import { DocumentTransform } from '@apollo/client'; +import { DocumentNode, FieldNode, InlineFragmentNode, Kind, SelectionNode, visit } from 'graphql'; + +const shouldTransform = (document: DocumentNode) => { + return ( + document.definitions[0] && + document.definitions[0].kind === Kind.OPERATION_DEFINITION && + document.definitions[0].name?.value === 'diagramEvent' + ); +}; + +const isNodeStyleFragment = (field: FieldNode) => { + if (field.name.value === 'style') { + const inLinesFragment = field.selectionSet.selections + .filter((selection) => selection.kind === Kind.INLINE_FRAGMENT) + .map((inlineFragment: InlineFragmentNode) => inlineFragment.typeCondition.name.value); + if (inLinesFragment.includes('RectangularNodeStyle') && inLinesFragment.includes('ImageNodeStyle')) { + return true; + } + } + return false; +}; + +const borderColorField: SelectionNode = { + kind: Kind.FIELD, + name: { + kind: Kind.NAME, + value: 'borderColor', + }, +}; + +const borderSizeField: SelectionNode = { + kind: Kind.FIELD, + name: { + kind: Kind.NAME, + value: 'borderSize', + }, +}; + +const borderStyleField: SelectionNode = { + kind: Kind.FIELD, + name: { + kind: Kind.NAME, + value: 'borderStyle', + }, +}; + +const backgroundField: SelectionNode = { + kind: Kind.FIELD, + name: { + kind: Kind.NAME, + value: 'background', + }, +}; + +export const ellipseNodeStyleDocumentTransform = new DocumentTransform((document) => { + if (shouldTransform(document)) { + const transformedDocument = visit(document, { + Field(field) { + if (!isNodeStyleFragment(field)) { + return undefined; + } + + const selections = field.selectionSet?.selections ?? []; + + const ellipseNodeStyleInlineFragment: InlineFragmentNode = { + kind: Kind.INLINE_FRAGMENT, + selectionSet: { + kind: Kind.SELECTION_SET, + selections: [borderColorField, borderSizeField, borderStyleField, backgroundField], + }, + typeCondition: { + kind: Kind.NAMED_TYPE, + name: { + kind: Kind.NAME, + value: 'EllipseNodeStyle', + }, + }, + }; + + return { + ...field, + selectionSet: { + ...field.selectionSet, + selections: [...selections, ellipseNodeStyleInlineFragment], + }, + }; + }, + }); + + return transformedDocument; + } + return document; +}); From a99e0cd6c5ae340c2ea7d9c8979e5a76a2dc3beb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20B=C3=A9gaudeau?= Date: Thu, 20 Jun 2024 15:15:13 +0200 Subject: [PATCH 2/2] [3656] Add the ability to customize the GraphQL type resolver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bug: https://github.com/eclipse-sirius/sirius-web/issues/3656 Signed-off-by: Stéphane Bégaudeau --- CHANGELOG.adoc | 2 + .../graphql/api/ITypeResolverDelegate.java | 25 +++++++++ .../graphql/api/ReflectiveTypeResolver.java | 23 +++++++- .../configuration/GraphQLWiringFactory.java | 16 +++++- .../graphql/GraphQLWiringFactory.java | 14 ++++- .../services/TypeResolverTests.java | 56 +++++++++++++++++++ .../services/TestTypeResolverDelegate.java | 32 +++++++++++ 7 files changed, 161 insertions(+), 7 deletions(-) create mode 100644 packages/core/backend/sirius-components-graphql-api/src/main/java/org/eclipse/sirius/components/graphql/api/ITypeResolverDelegate.java create mode 100644 packages/sirius-web/backend/sirius-web/src/test/java/org/eclipse/sirius/web/application/services/TypeResolverTests.java create mode 100644 packages/sirius-web/backend/sirius-web/src/test/java/org/eclipse/sirius/web/services/TestTypeResolverDelegate.java diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index 107cb55c07..d7af0253a7 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -109,6 +109,8 @@ image:doc/screenshots/insideLabelPositions.png[Inside label positions, 70%] - https://github.com/eclipse-sirius/sirius-web/issues/3604[#3604] [diagram] Make node overlap resolution faster during "Arrange All" - https://github.com/eclipse-sirius/sirius-web/issues/3653[#3653] [gantt] Add documentation for gantt representation - https://github.com/eclipse-sirius/sirius-web/issues/3634[#3634] [sirius-web] Simplifying the contribution to the GraphQL subscription of the diagram for custom nodes +- https://github.com/eclipse-sirius/sirius-web/issues/3656[#3656] [core] Add the ability to customize the GraphQL type resolver + == v2024.5.0 diff --git a/packages/core/backend/sirius-components-graphql-api/src/main/java/org/eclipse/sirius/components/graphql/api/ITypeResolverDelegate.java b/packages/core/backend/sirius-components-graphql-api/src/main/java/org/eclipse/sirius/components/graphql/api/ITypeResolverDelegate.java new file mode 100644 index 0000000000..ef9fea57da --- /dev/null +++ b/packages/core/backend/sirius-components-graphql-api/src/main/java/org/eclipse/sirius/components/graphql/api/ITypeResolverDelegate.java @@ -0,0 +1,25 @@ +/******************************************************************************* + * Copyright (c) 2024 Obeo. + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.components.graphql.api; + +import graphql.TypeResolutionEnvironment; +import graphql.schema.GraphQLObjectType; + +/** + * Used to find GraphQL object type not found by the reflective type resolver. + * + * @author sbegaudeau + */ +public interface ITypeResolverDelegate { + GraphQLObjectType getType(TypeResolutionEnvironment environment); +} diff --git a/packages/core/backend/sirius-components-graphql-api/src/main/java/org/eclipse/sirius/components/graphql/api/ReflectiveTypeResolver.java b/packages/core/backend/sirius-components-graphql-api/src/main/java/org/eclipse/sirius/components/graphql/api/ReflectiveTypeResolver.java index cb0c685738..5e19249428 100644 --- a/packages/core/backend/sirius-components-graphql-api/src/main/java/org/eclipse/sirius/components/graphql/api/ReflectiveTypeResolver.java +++ b/packages/core/backend/sirius-components-graphql-api/src/main/java/org/eclipse/sirius/components/graphql/api/ReflectiveTypeResolver.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2019, 2022 Obeo. + * Copyright (c) 2019, 2024 Obeo. * This program and the accompanying materials * are made available under the terms of the Eclipse Public License v2.0 * which accompanies this distribution, and is available at @@ -12,6 +12,9 @@ *******************************************************************************/ package org.eclipse.sirius.components.graphql.api; +import java.util.List; +import java.util.Objects; + import graphql.TypeResolutionEnvironment; import graphql.schema.GraphQLObjectType; import graphql.schema.GraphQLSchema; @@ -24,11 +27,27 @@ */ public class ReflectiveTypeResolver implements TypeResolver { + private final List typeResolverDelegates; + + public ReflectiveTypeResolver(List typeResolverDelegates) { + this.typeResolverDelegates = Objects.requireNonNull(typeResolverDelegates); + } + @Override public GraphQLObjectType getType(TypeResolutionEnvironment environment) { GraphQLSchema graphQLSchema = environment.getSchema(); Object object = environment.getObject(); - return graphQLSchema.getObjectType(object.getClass().getSimpleName()); + var graphQLObjectType = graphQLSchema.getObjectType(object.getClass().getSimpleName()); + + if (graphQLObjectType == null) { + graphQLObjectType = this.typeResolverDelegates.stream() + .map(typeResolverDelegate -> typeResolverDelegate.getType(environment)) + .filter(Objects::nonNull) + .findFirst() + .orElse(null); + } + + return graphQLObjectType; } } diff --git a/packages/sirius-web/backend/sirius-web-graphql/src/main/java/org/eclipse/sirius/web/graphql/configuration/GraphQLWiringFactory.java b/packages/sirius-web/backend/sirius-web-graphql/src/main/java/org/eclipse/sirius/web/graphql/configuration/GraphQLWiringFactory.java index 099b308267..aeb557b4a6 100644 --- a/packages/sirius-web/backend/sirius-web-graphql/src/main/java/org/eclipse/sirius/web/graphql/configuration/GraphQLWiringFactory.java +++ b/packages/sirius-web/backend/sirius-web-graphql/src/main/java/org/eclipse/sirius/web/graphql/configuration/GraphQLWiringFactory.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2021, 2022 Obeo. + * Copyright (c) 2021, 2024 Obeo. * This program and the accompanying materials * are made available under the terms of the Eclipse Public License v2.0 * which accompanies this distribution, and is available at @@ -12,6 +12,10 @@ *******************************************************************************/ package org.eclipse.sirius.web.graphql.configuration; +import java.util.List; +import java.util.Objects; + +import org.eclipse.sirius.components.graphql.api.ITypeResolverDelegate; import org.eclipse.sirius.components.graphql.api.ReflectiveTypeResolver; import org.springframework.stereotype.Service; @@ -28,6 +32,12 @@ @Service public class GraphQLWiringFactory implements WiringFactory { + private final List typeResolverDelegates; + + public GraphQLWiringFactory(List typeResolverDelegates) { + this.typeResolverDelegates = Objects.requireNonNull(typeResolverDelegates); + } + @Override public boolean providesTypeResolver(InterfaceWiringEnvironment environment) { return true; @@ -35,7 +45,7 @@ public boolean providesTypeResolver(InterfaceWiringEnvironment environment) { @Override public TypeResolver getTypeResolver(InterfaceWiringEnvironment environment) { - return new ReflectiveTypeResolver(); + return new ReflectiveTypeResolver(this.typeResolverDelegates); } @Override @@ -45,6 +55,6 @@ public boolean providesTypeResolver(UnionWiringEnvironment environment) { @Override public TypeResolver getTypeResolver(UnionWiringEnvironment environment) { - return new ReflectiveTypeResolver(); + return new ReflectiveTypeResolver(this.typeResolverDelegates); } } diff --git a/packages/sirius-web/backend/sirius-web-infrastructure/src/main/java/org/eclipse/sirius/web/infrastructure/graphql/GraphQLWiringFactory.java b/packages/sirius-web/backend/sirius-web-infrastructure/src/main/java/org/eclipse/sirius/web/infrastructure/graphql/GraphQLWiringFactory.java index edad5f16e6..748965b5be 100644 --- a/packages/sirius-web/backend/sirius-web-infrastructure/src/main/java/org/eclipse/sirius/web/infrastructure/graphql/GraphQLWiringFactory.java +++ b/packages/sirius-web/backend/sirius-web-infrastructure/src/main/java/org/eclipse/sirius/web/infrastructure/graphql/GraphQLWiringFactory.java @@ -12,6 +12,10 @@ *******************************************************************************/ package org.eclipse.sirius.web.infrastructure.graphql; +import java.util.List; +import java.util.Objects; + +import org.eclipse.sirius.components.graphql.api.ITypeResolverDelegate; import org.eclipse.sirius.components.graphql.api.ReflectiveTypeResolver; import org.springframework.stereotype.Service; @@ -28,6 +32,12 @@ @Service public class GraphQLWiringFactory implements WiringFactory { + private final List typeResolverDelegates; + + public GraphQLWiringFactory(List typeResolverDelegates) { + this.typeResolverDelegates = Objects.requireNonNull(typeResolverDelegates); + } + @Override public boolean providesTypeResolver(InterfaceWiringEnvironment environment) { return true; @@ -35,7 +45,7 @@ public boolean providesTypeResolver(InterfaceWiringEnvironment environment) { @Override public TypeResolver getTypeResolver(InterfaceWiringEnvironment environment) { - return new ReflectiveTypeResolver(); + return new ReflectiveTypeResolver(this.typeResolverDelegates); } @Override @@ -45,6 +55,6 @@ public boolean providesTypeResolver(UnionWiringEnvironment environment) { @Override public TypeResolver getTypeResolver(UnionWiringEnvironment environment) { - return new ReflectiveTypeResolver(); + return new ReflectiveTypeResolver(this.typeResolverDelegates); } } \ No newline at end of file diff --git a/packages/sirius-web/backend/sirius-web/src/test/java/org/eclipse/sirius/web/application/services/TypeResolverTests.java b/packages/sirius-web/backend/sirius-web/src/test/java/org/eclipse/sirius/web/application/services/TypeResolverTests.java new file mode 100644 index 0000000000..45e719534c --- /dev/null +++ b/packages/sirius-web/backend/sirius-web/src/test/java/org/eclipse/sirius/web/application/services/TypeResolverTests.java @@ -0,0 +1,56 @@ +/******************************************************************************* + * Copyright (c) 2024 Obeo. + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.web.application.services; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.eclipse.sirius.web.AbstractIntegrationTests; +import org.eclipse.sirius.web.infrastructure.graphql.GraphQLWiringFactory; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.transaction.annotation.Transactional; + +import graphql.GraphQL; +import graphql.execution.TypeResolutionParameters; +import graphql.schema.idl.InterfaceWiringEnvironment; + +/** + * Used to test the GraphQL type resolution. + * + * @author sbegaudeau + */ +@Transactional +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +public class TypeResolverTests extends AbstractIntegrationTests { + + @Autowired + private GraphQLWiringFactory graphQLWiringFactory; + + @Autowired + private GraphQL graphQL; + + @Test + @DisplayName("Given the type resolver, when asked for a unknown type, then the expected type is returned") + public void givenTheTypeResolverWhenAskedForUnknownTypeThenTheExpectedTypeIsReturned() { + var environment = TypeResolutionParameters.newParameters() + .schema(this.graphQL.getGraphQLSchema()) + .value(this) + .build(); + + var typeResolver = this.graphQLWiringFactory.getTypeResolver((InterfaceWiringEnvironment) null); + var graphQLObjectType = typeResolver.getType(environment); + assertThat(graphQLObjectType.getName()).isEqualTo("Diagram"); + } +} diff --git a/packages/sirius-web/backend/sirius-web/src/test/java/org/eclipse/sirius/web/services/TestTypeResolverDelegate.java b/packages/sirius-web/backend/sirius-web/src/test/java/org/eclipse/sirius/web/services/TestTypeResolverDelegate.java new file mode 100644 index 0000000000..105afddc11 --- /dev/null +++ b/packages/sirius-web/backend/sirius-web/src/test/java/org/eclipse/sirius/web/services/TestTypeResolverDelegate.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2024 Obeo. + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.web.services; + +import org.eclipse.sirius.components.graphql.api.ITypeResolverDelegate; +import org.springframework.stereotype.Service; + +import graphql.TypeResolutionEnvironment; +import graphql.schema.GraphQLObjectType; + +/** + * Used during tests to return a GraphQL object type for unknown objects. + * + * @author sbegaudeau + */ +@Service +public class TestTypeResolverDelegate implements ITypeResolverDelegate { + @Override + public GraphQLObjectType getType(TypeResolutionEnvironment environment) { + return environment.getSchema().getObjectType("Diagram"); + } +}