Skip to content

Commit

Permalink
[3634] Simplifying the contribution to the GraphQL subscription of th…
Browse files Browse the repository at this point in the history
…e diagram for custom nodes

Bug: #3634
Signed-off-by: Michaël Charfadi <[email protected]>
  • Loading branch information
mcharfadi authored and sbegaudeau committed Jun 20, 2024
1 parent 11e5b3b commit f3c26b7
Show file tree
Hide file tree
Showing 13 changed files with 145 additions and 43 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -26,6 +29,7 @@ More existing APIs will be migrated to this new common pattern.
* `public Optional<IRepresentationEventProcessor> acquireRepresentationEventProcessor(IRepresentationConfiguration configuration, IInput input)`;
* `Flux<IPayload> 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

Expand Down Expand Up @@ -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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import { NodeTypeContextValue } from './NodeContext.types';
const value: NodeTypeContextValue = {
nodeConverters: [],
nodeLayoutHandlers: [],
graphQLNodeStyleFragments: [],
nodeTypeContributions: [],
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -24,7 +23,6 @@ export interface NodeTypeContributionProps {
export type NodeTypeContributionElement = React.ReactElement<NodeTypeContributionProps>;

export interface NodeTypeContextValue {
graphQLNodeStyleFragments: GraphQLNodeStyleFragment[];
nodeLayoutHandlers: INodeLayoutHandler<NodeData>[];
nodeConverters: INodeConverter[];
nodeTypeContributions: NodeTypeContributionElement[];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -35,5 +34,5 @@ subscription diagramEvent($input: DiagramEventInput!) {
}
}
${diagramFragment(contributions)}
${diagramFragment}
`;
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -73,7 +72,7 @@ fragment diagramFragment on Diagram {
}
}
${nodeFragment(contributions)}
${nodeFragment}
${edgeFragment}
${labelFragment}
${insideLabelFragment}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -48,14 +47,6 @@ fragment nodeFragment on Node {
... on IconLabelNodeStyle {
background
}
${contributions.map(
(nodeStyle) =>
`
... on ${nodeStyle.type} {
${nodeStyle.fields}
}
`
)}
}
childrenLayoutStrategy {
__typename
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,3 @@ export interface GQLImageNodeStyle extends GQLNodeStyle {
export interface GQLIconLabelNodeStyle extends GQLNodeStyle {
background: string;
}

export interface GraphQLNodeStyleFragment {
type: string;
fields: string;
}
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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!) {
Expand Down Expand Up @@ -139,9 +136,7 @@ export const DiagramRepresentation = ({
setState((prevState) => ({ ...prevState, diagramRefreshedEventPayload: null, complete: true }));
};

const { graphQLNodeStyleFragments } = useContext<NodeTypeContextValue>(NodeTypeContext);

const { error } = useSubscription<GQLDiagramEventData>(subscription(graphQLNodeStyleFragments), {
const { error } = useSubscription<GQLDiagramEventData>(subscription, {
variables,
fetchPolicy: 'no-cache',
onData,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import { DiagramRepresentationConfigurationProps } from './DiagramRepresentation
export const defaultNodeTypeRegistry: NodeTypeContextValue = {
nodeConverters: [],
nodeLayoutHandlers: [],
graphQLNodeStyleFragments: [],
nodeTypeContributions: [],
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
*******************************************************************************/

import {
GraphQLNodeStyleFragment,
INodeConverter,
INodeLayoutHandler,
NodeData,
Expand All @@ -24,7 +23,6 @@ export interface DiagramRepresentationConfigurationProps {
}

export interface NodeTypeRegistry {
graphQLNodeStyleFragments: GraphQLNodeStyleFragment[];
nodeLayoutHandlers: INodeLayoutHandler<NodeData>[];
nodeConverters: INodeConverter[];
nodeTypeContributions: NodeTypeContributionElement[];
Expand Down
31 changes: 24 additions & 7 deletions packages/sirius-web/frontend/sirius-web/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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: [<NodeTypeContribution component={EllipseNode} type={'ellipseNode'} />],
};

ReactDOM.render(
<SiriusWebApplication httpOrigin={httpOrigin} wsOrigin={wsOrigin}>
<SiriusWebApplication httpOrigin={httpOrigin} wsOrigin={wsOrigin} extensionRegistry={registry}>
<DiagramRepresentationConfiguration nodeTypeRegistry={nodeTypeRegistry} />
</SiriusWebApplication>,
document.getElementById('root')
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
});

0 comments on commit f3c26b7

Please sign in to comment.