Skip to content

Commit

Permalink
[4597] Add a command to publish libraries from studios
Browse files Browse the repository at this point in the history
Bug: eclipse-sirius#4597
Signed-off-by: Gwendal Daniel <[email protected]>
  • Loading branch information
gdaniel committed Feb 28, 2025
1 parent 22233f0 commit 8f1d319
Show file tree
Hide file tree
Showing 34 changed files with 1,385 additions and 99 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ You have to add `tableWidgetDocumentTransform` to your extensionRegistry to use
- https://github.com/eclipse-sirius/sirius-web/issues/4588[#4588] [core] Added support for custom ordering in representation creation modal.
See the new interface `org.eclipse.sirius.components.emf.services.api.IRepresentationDescriptionMetadataSorter`.
- https://github.com/eclipse-sirius/sirius-web/issues/4616[#4616] [sirius-web] Allow end users to see the content of a library in a workbench, the path of the new page is `/libraries/:namespace/:name/:version`
- https://github.com/eclipse-sirius/sirius-web/issues/4597[#4597] [sirius-web] Add a command to publish libraries from studios
The command creates a library for each _RepresentationDescription_ and _Domain_ in the studio, and creates the dependencies between them.
A _shared components_ library can be created in the process to store elements that are needed by other libraries but are not stored in libraries themselves.


=== Improvements
Expand Down
53 changes: 14 additions & 39 deletions packages/core/frontend/sirius-components-omnibox/src/Omnibox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
* Contributors:
* Obeo - initial API and implementation
*******************************************************************************/
import { useData, useSelection } from '@eclipse-sirius/sirius-components-core';
import { useSelection } from '@eclipse-sirius/sirius-components-core';
import ChevronRightIcon from '@mui/icons-material/ChevronRight';
import SubdirectoryArrowLeftIcon from '@mui/icons-material/SubdirectoryArrowLeft';
import CircularProgress from '@mui/material/CircularProgress';
Expand All @@ -22,12 +22,9 @@ import IconButton from '@mui/material/IconButton';
import Input from '@mui/material/Input';
import { useEffect, useRef, useState } from 'react';
import { makeStyles } from 'tss-react/mui';
import { OmniboxAction, OmniboxProps, OmniboxState } from './Omnibox.types';
import { OmniboxMode, OmniboxProps, OmniboxState } from './Omnibox.types';
import { OmniboxCommandList } from './OmniboxCommandList';
import { omniboxCommandOverrideContributionExtensionPoint } from './OmniboxExtensionPoints';
import { OmniboxCommandOverrideContribution } from './OmniboxExtensionPoints.types';
import { OmniboxObjectList } from './OmniboxObjectList';
import { isExecuteOmniboxCommandSuccessPayload, useExecuteOmniboxCommand } from './useExecuteOmniboxCommand';
import { useOmniboxCommands } from './useOmniboxCommands';
import { GQLGetOmniboxCommandsQueryVariables } from './useOmniboxCommands.types';
import { useOmniboxSearch } from './useOmniboxSearch';
Expand Down Expand Up @@ -76,7 +73,6 @@ export const Omnibox = ({ open, editingContextId, onClose }: OmniboxProps) => {

const { getOmniboxCommands, loading: commandLoading, data: commandData } = useOmniboxCommands();
const { getOmniboxSearchResults, loading: searchResultsLoading, data: searchResultsData } = useOmniboxSearch();
const { executeOmniboxCommand, data: executeOmniboxCommandData } = useExecuteOmniboxCommand();

const { selection } = useSelection();
const selectedObjectIds: string[] = selection.entries.map((entry) => entry.id);
Expand All @@ -90,10 +86,6 @@ export const Omnibox = ({ open, editingContextId, onClose }: OmniboxProps) => {
getOmniboxCommands({ variables });
}, []);

const { data: omniboxCommandOverrideContributions } = useData<OmniboxCommandOverrideContribution[]>(
omniboxCommandOverrideContributionExtensionPoint
);

const inputRef = useRef<HTMLInputElement>(null);
const listRef = useRef<HTMLUListElement>(null);

Expand Down Expand Up @@ -142,44 +134,27 @@ export const Omnibox = ({ open, editingContextId, onClose }: OmniboxProps) => {
}
};

const handleOnActionClick = (action: OmniboxAction) => {
const commandOverride: OmniboxCommandOverrideContribution | undefined = omniboxCommandOverrideContributions.filter(
(contribution) => contribution.canHandle(action)
)[0];
if (commandOverride) {
commandOverride.handle(action);
onClose();
} else if (action.id === 'search') {
setState((prevState) => ({
...prevState,
mode: 'Search',
queryHasChanged: true,
}));
if (inputRef.current) {
inputRef.current.value = '';
}
inputRef.current?.focus();
} else {
executeOmniboxCommand(editingContextId, selectedObjectIds, action.id);
const onModeChanged = (mode: OmniboxMode) => {
setState((prevState) => ({
...prevState,
mode,
queryHasChanged: true,
}));
if (inputRef.current) {
inputRef.current.value = '';
}
inputRef.current?.focus();
};

useEffect(() => {
if (
executeOmniboxCommandData &&
isExecuteOmniboxCommandSuccessPayload(executeOmniboxCommandData.executeOmniboxCommand)
) {
onClose();
}
}, [executeOmniboxCommandData]);

let omniboxResult: JSX.Element | null = null;
if (state.mode === 'Command') {
omniboxResult = (
<OmniboxCommandList
loading={commandLoading}
data={commandData}
onActionClick={handleOnActionClick}
editingContextId={editingContextId}
onClose={onClose}
onModeChanged={onModeChanged}
ref={listRef}
/>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,3 @@ export interface OmniboxState {
}

export type OmniboxMode = 'Command' | 'Search';

export interface OmniboxAction {
id: string;
icon: JSX.Element;
label: string;
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,35 @@
* Contributors:
* Obeo - initial API and implementation
*******************************************************************************/
import { IconOverlay } from '@eclipse-sirius/sirius-components-core';
import { IconOverlay, useData, useSelection } from '@eclipse-sirius/sirius-components-core';
import List from '@mui/material/List';
import ListItem from '@mui/material/ListItem';
import ListItemButton from '@mui/material/ListItemButton';
import ListItemIcon from '@mui/material/ListItemIcon';
import ListItemText from '@mui/material/ListItemText';
import { forwardRef } from 'react';
import { forwardRef, useEffect } from 'react';
import { OmniboxCommandListProps } from './OmniboxCommandList.types';
import { isExecuteOmniboxCommandSuccessPayload, useExecuteOmniboxCommand } from './useExecuteOmniboxCommand';
import { OmniboxCommandOverrideContribution } from './OmniboxExtensionPoints.types';
import { omniboxCommandOverrideContributionExtensionPoint } from './OmniboxExtensionPoints';
import { GQLOmniboxCommand } from './useOmniboxCommands.types';

export const OmniboxCommandList = forwardRef(
({ loading, data, onActionClick }: OmniboxCommandListProps, ref: React.ForwardedRef<HTMLUListElement>) => {
(
{ loading, data, editingContextId, onClose, onModeChanged }: OmniboxCommandListProps,
ref: React.ForwardedRef<HTMLUListElement>
) => {
const { executeOmniboxCommand, data: executeOmniboxCommandData } = useExecuteOmniboxCommand();

useEffect(() => {
if (
executeOmniboxCommandData &&
isExecuteOmniboxCommandSuccessPayload(executeOmniboxCommandData.executeOmniboxCommand)
) {
onClose();
}
}, [executeOmniboxCommandData]);

const handleListItemKeyDown: React.KeyboardEventHandler<HTMLDivElement> = (event) => {
if (event.key === 'ArrowDown') {
const nextListItemButton = event.currentTarget.nextSibling;
Expand All @@ -35,6 +53,17 @@ export const OmniboxCommandList = forwardRef(
}
};

const { selection } = useSelection();
const selectedObjectIds: string[] = selection.entries.map((entry) => entry.id);

const handleOnActionClick = (command: GQLOmniboxCommand) => {
if (command.id === 'search') {
onModeChanged('Search');
} else {
executeOmniboxCommand(editingContextId, selectedObjectIds, command.id);
}
};

let listItems: JSX.Element[] = [];
if (loading) {
listItems = [
Expand All @@ -46,29 +75,36 @@ export const OmniboxCommandList = forwardRef(
];
}

const { data: omniboxCommandOverrideContributions } = useData<OmniboxCommandOverrideContribution[]>(
omniboxCommandOverrideContributionExtensionPoint
);

if (!loading && data) {
const commands = data.viewer.omniboxCommands.edges.map((edge) => edge.node);
if (commands.length > 0) {
listItems = commands
.map((node) => {
return {
id: node.id,
icon: <IconOverlay iconURL={node.iconURLs} alt={node.label} />,
label: node.label,
};
})
.map((action) => {
listItems = commands.map((command) => {
const CommandOverride = omniboxCommandOverrideContributions
.filter((contribution) => contribution.canHandle(command))
.map((contribution) => contribution.component)[0];
if (CommandOverride) {
return (
<CommandOverride key={command.id} command={command} onClose={onClose} onKeyDown={handleListItemKeyDown} />
);
} else {
return (
<ListItemButton
key={action.id}
data-testid={action.label}
onClick={() => onActionClick(action)}
key={command.id}
data-testid={command.label}
onClick={() => handleOnActionClick(command)}
onKeyDown={handleListItemKeyDown}>
<ListItemIcon>{action.icon}</ListItemIcon>
<ListItemText sx={{ whiteSpace: 'nowrap', textOverflow: 'ellipsis' }}>{action.label}</ListItemText>
<ListItemIcon>
<IconOverlay iconURL={command.iconURLs} alt={command.label} />
</ListItemIcon>
<ListItemText sx={{ whiteSpace: 'nowrap', textOverflow: 'ellipsis' }}>{command.label}</ListItemText>
</ListItemButton>
);
});
}
});
} else {
listItems = [
<ListItem key={'no-result-key'}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@
* Obeo - initial API and implementation
*******************************************************************************/

import { OmniboxAction } from './Omnibox.types';
import { OmniboxMode } from './Omnibox.types';
import { GQLGetOmniboxCommandsQueryData } from './useOmniboxCommands.types';

export interface OmniboxCommandListProps {
data: GQLGetOmniboxCommandsQueryData | null;
loading: boolean;
onActionClick: (action: OmniboxAction) => void;
editingContextId: string;
onClose: () => void;
onModeChanged: (mode: OmniboxMode) => void;
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,15 @@
* Contributors:
* Obeo - initial API and implementation
*******************************************************************************/
import { OmniboxAction } from './Omnibox.types';
import { GQLOmniboxCommand } from './useOmniboxCommands.types';

export interface OmniboxCommandOverrideContribution {
canHandle: (action: OmniboxAction) => boolean;
handle: (action: OmniboxAction) => void;
canHandle: (action: GQLOmniboxCommand) => boolean;
component: React.ComponentType<OmniboxCommandComponentProps>;
}

export interface OmniboxCommandComponentProps {
command: GQLOmniboxCommand;
onKeyDown: React.KeyboardEventHandler<HTMLDivElement>;
onClose: () => void;
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
* Obeo - initial API and implementation
*******************************************************************************/

export { type OmniboxAction } from './Omnibox.types';
export * from './OmniboxButton';
export * from './OmniboxExtensionPoints';
export * from './OmniboxExtensionPoints.types';
export * from './OmniboxProvider';
export { type GQLOmniboxCommand } from './useOmniboxCommands.types';
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,10 @@ export interface GQLViewerOmniboxCommandsConnection {
}

export interface GQLViewerOmniboxCommandsEdge {
node: GQLViewerOmniboxCommand;
node: GQLOmniboxCommand;
}

export interface GQLViewerOmniboxCommand {
export interface GQLOmniboxCommand {
id: string;
label: string;
iconURLs: string[];
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*******************************************************************************
* Copyright (c) 2025 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.library.controllers;

import com.fasterxml.jackson.databind.ObjectMapper;

import java.util.Objects;
import java.util.concurrent.CompletableFuture;

import org.eclipse.sirius.components.annotations.spring.graphql.QueryDataFetcher;
import org.eclipse.sirius.components.core.api.IPayload;
import org.eclipse.sirius.components.graphql.api.IDataFetcherWithFieldCoordinates;
import org.eclipse.sirius.web.application.library.dto.PublishLibrariesInput;
import org.eclipse.sirius.web.application.library.services.api.ILibraryApplicationService;

import graphql.schema.DataFetchingEnvironment;

/**
* Data fetcher for the field Mutation#publishLibraries.
*
* @author gdaniel
*/
@QueryDataFetcher(type = "Mutation", field = "publishLibraries")
public class MutationPublishLibrariesDataFetcher implements IDataFetcherWithFieldCoordinates<CompletableFuture<IPayload>> {

private static final String INPUT_ARGUMENT = "input";

private final ObjectMapper objectMapper;

private final ILibraryApplicationService libraryApplicationService;

public MutationPublishLibrariesDataFetcher(ObjectMapper objectMapper, ILibraryApplicationService libraryApplicationService) {
this.objectMapper = Objects.requireNonNull(objectMapper);
this.libraryApplicationService = Objects.requireNonNull(libraryApplicationService);
}

@Override
public CompletableFuture<IPayload> get(DataFetchingEnvironment environment) throws Exception {
Object argument = environment.getArgument(INPUT_ARGUMENT);
var input = this.objectMapper.convertValue(argument, PublishLibrariesInput.class);

return CompletableFuture.supplyAsync(() -> this.libraryApplicationService.publishLibraries(input));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*******************************************************************************
* Copyright (c) 2025 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.library.dto;

import java.util.UUID;

import org.eclipse.sirius.components.core.api.IInput;

import jakarta.validation.constraints.NotNull;

/**
* Input used to publish libraries.
*
* @author gdaniel
*/
public record PublishLibrariesInput(@NotNull UUID id, @NotNull String projectId, @NotNull String publicationKind, @NotNull String version, @NotNull String description) implements IInput {

}
Loading

0 comments on commit 8f1d319

Please sign in to comment.