From 1cefa7eea43ba55caf1ff76b611aa4c55dbfca8b Mon Sep 17 00:00:00 2001 From: Michael Charfadi Date: Tue, 18 Jun 2024 11:04:51 +0200 Subject: [PATCH] [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 | 2 + .../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 | 26 +++-- .../src/nodes/ElipseNodeDocumentTransform.ts | 107 ++++++++++++++++++ 13 files changed, 137 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 8322c5a90bc..fcea4cf70c2 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -25,6 +25,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 @@ -100,6 +101,7 @@ image:doc/screenshots/insideLabelPositions.png[Inside label positions, 70%] - https://github.com/eclipse-sirius/sirius-web/issues/3606[#3606] [test] Improve error handling in ExecuteEditingContextFunctionRunner and ExecuteEditingContextFunctionEventHandler - 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/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 271738f49f8..c89fddd2c72 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 3a1754d8c22..9ae863ed992 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 e214eeb1d1e..c58ec8a9f65 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 677c53529ea..31930ef6fcc 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 8d0830bdb06..5d753b92374 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 985f33ec475..2a0dca751ff 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 592e31e8edf..d653a02a194 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 7dcbec8c969..c1596aaaba8 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 fd7380a4196..2292a781d8b 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 6c93cc75551..038f2cb9ea1 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 c4861854d9a..4f37c99c32b 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,28 @@ if (process.env.NODE_ENV !== 'production') { loadErrorMessages(); } +const registry = new ExtensionRegistry(); + +const apolloClientOptionsConfigurer: ApolloClientOptionsConfigurer = (currentOptions) => { + return { + ...currentOptions, + documentTransform: ellipseNodeStyleDocumentTransform, + }; +}; + +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 00000000000..4ddc678adcf --- /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; +});