Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[3634] Simplifying the contribution to the GraphQL subscription of the diagram for custom nodes #3639

Merged
merged 2 commits into from
Jun 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 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
sbegaudeau marked this conversation as resolved.
Show resolved Hide resolved

=== Dependency update

Expand Down Expand Up @@ -104,6 +108,9 @@ 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
- https://github.com/eclipse-sirius/sirius-web/issues/3656[#3656] [core] Add the ability to customize the GraphQL type resolver


== v2024.5.0

Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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;
Expand All @@ -24,11 +27,27 @@
*/
public class ReflectiveTypeResolver implements TypeResolver {

private final List<ITypeResolverDelegate> typeResolverDelegates;

public ReflectiveTypeResolver(List<ITypeResolverDelegate> 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;
}

}
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
@@ -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
Expand All @@ -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;

Expand All @@ -28,14 +32,20 @@
@Service
public class GraphQLWiringFactory implements WiringFactory {

private final List<ITypeResolverDelegate> typeResolverDelegates;

public GraphQLWiringFactory(List<ITypeResolverDelegate> typeResolverDelegates) {
this.typeResolverDelegates = Objects.requireNonNull(typeResolverDelegates);
}

@Override
public boolean providesTypeResolver(InterfaceWiringEnvironment environment) {
return true;
}

@Override
public TypeResolver getTypeResolver(InterfaceWiringEnvironment environment) {
return new ReflectiveTypeResolver();
return new ReflectiveTypeResolver(this.typeResolverDelegates);
}

@Override
Expand All @@ -45,6 +55,6 @@ public boolean providesTypeResolver(UnionWiringEnvironment environment) {

@Override
public TypeResolver getTypeResolver(UnionWiringEnvironment environment) {
return new ReflectiveTypeResolver();
return new ReflectiveTypeResolver(this.typeResolverDelegates);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -28,14 +32,20 @@
@Service
public class GraphQLWiringFactory implements WiringFactory {

private final List<ITypeResolverDelegate> typeResolverDelegates;

public GraphQLWiringFactory(List<ITypeResolverDelegate> typeResolverDelegates) {
this.typeResolverDelegates = Objects.requireNonNull(typeResolverDelegates);
}

@Override
public boolean providesTypeResolver(InterfaceWiringEnvironment environment) {
return true;
}

@Override
public TypeResolver getTypeResolver(InterfaceWiringEnvironment environment) {
return new ReflectiveTypeResolver();
return new ReflectiveTypeResolver(this.typeResolverDelegates);
}

@Override
Expand All @@ -45,6 +55,6 @@ public boolean providesTypeResolver(UnionWiringEnvironment environment) {

@Override
public TypeResolver getTypeResolver(UnionWiringEnvironment environment) {
return new ReflectiveTypeResolver();
return new ReflectiveTypeResolver(this.typeResolverDelegates);
}
}
Original file line number Diff line number Diff line change
@@ -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");
}
}
Loading
Loading