Skip to content

Commit

Permalink
EPMRPP-91756 || Rename metadata to manifest. Adjust extensions select…
Browse files Browse the repository at this point in the history
…ors and sagas
  • Loading branch information
AmsterGet committed Jul 19, 2024
1 parent e1ed73f commit 47ca328
Show file tree
Hide file tree
Showing 19 changed files with 139 additions and 138 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import { userInfoSelector } from 'controllers/user';
import { projectInfoSelector } from 'controllers/project';
import { extensionType } from '../extensionTypes';

// http://localhost:3000/#superadmin_personal/plugin/BrowserKube
// TODO: add loader while loading the iframe
// TODO: configure sandbox for iframe
function StandaloneExtensionLoader({ extension, userInfo, projectInfo }) {
Expand Down
3 changes: 2 additions & 1 deletion app/src/components/integrations/constants.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2019 EPAM Systems
* Copyright 2024 EPAM Systems
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -24,6 +24,7 @@ import LdapIcon from 'common/img/plugins/ldap.png';
import DefaultPluginIcon from 'common/img/plugins/default-plugin-icon.svg';
import ActiveDirectoryIcon from 'common/img/plugins/activeDirectory.png';

// Constants for plugins that do not provide UI extensions in their bundles
export const PLUGIN_NAME_TITLES = {
[JIRA]: 'JIRA Server',
[RALLY]: 'RALLY',
Expand Down
58 changes: 9 additions & 49 deletions app/src/components/integrations/elements/pluginIcon/pluginIcon.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2021 EPAM Systems
* Copyright 2024 EPAM Systems
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -15,79 +15,39 @@
*/

import React from 'react';
import { useSelector } from 'react-redux';
import PropTypes from 'prop-types';
import { URLS } from 'common/urls';
import { activeProjectSelector } from 'controllers/user';
import { COMMAND_GET_FILE } from 'controllers/plugins/uiExtensions/constants';
import { globalIntegrationsSelector } from 'controllers/plugins/selectors';
import {
filterIntegrationsByName,
isPluginSupportsAllowedCommand,
isPluginSupportsCommonCommand,
} from 'controllers/plugins/utils';
import { ICON_FILE_KEY } from 'controllers/plugins/uiExtensions/constants';
import { PLUGIN_DEFAULT_IMAGE, PLUGIN_IMAGES_MAP } from 'components/integrations/constants';
import { Image } from 'components/main/image';

// TODO: remove COMMAND_GET_FILE usage
export const PluginIcon = ({ pluginData, className, ...rest }) => {
const { details, name, enabled } = pluginData;
const isDynamicIconAvailable = details?.binaryData?.icon;
const projectId = useSelector(activeProjectSelector);
const globalIntegrations = useSelector(globalIntegrationsSelector);
const { details, name } = pluginData;
const isDynamicIconAvailable = details?.binaryData?.[ICON_FILE_KEY];

const calculateIconParams = () => {
const commandParams = { method: 'PUT', data: { fileKey: 'icon' } };

if (isDynamicIconAvailable && enabled) {
const isCommonCommandSupported = isPluginSupportsCommonCommand(pluginData, COMMAND_GET_FILE);
const isAllowedCommandSupported = isPluginSupportsAllowedCommand(
pluginData,
COMMAND_GET_FILE,
);

if (isCommonCommandSupported) {
return {
url: URLS.pluginCommandCommon(projectId, name, COMMAND_GET_FILE),
requestParams: commandParams,
};
}

const integration = filterIntegrationsByName(globalIntegrations, name)[0];
if (integration && isAllowedCommandSupported) {
return {
url: URLS.projectIntegrationByIdCommand(projectId, integration.id, COMMAND_GET_FILE),
requestParams: commandParams,
};
}

return {
url: URLS.pluginPublicFile(name, details.binaryData.icon),
};
const calculateIconUrl = () => {
if (isDynamicIconAvailable) {
return URLS.pluginPublicFile(name, details.binaryData[ICON_FILE_KEY]);
}

return {
url: PLUGIN_IMAGES_MAP[name] || PLUGIN_DEFAULT_IMAGE,
};
return PLUGIN_IMAGES_MAP[name] || PLUGIN_DEFAULT_IMAGE;
};

const { url, requestParams } = calculateIconParams();
const url = calculateIconUrl();

return (
<div className={className}>
<Image
src={url}
fallback={PLUGIN_DEFAULT_IMAGE}
isStatic={!isDynamicIconAvailable}
requestParams={requestParams}
preloaderColor="charcoal"
className={className}
{...rest}
/>
</div>
);
};

PluginIcon.propTypes = {
pluginData: PropTypes.object,
className: PropTypes.string,
Expand Down
3 changes: 1 addition & 2 deletions app/src/controllers/plugins/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ export {
isPostIssueActionAvailable,
isAuthorizationPlugin,
isPluginSwitchable,
isPluginSupportsCommonCommand,
} from './utils';
export { isPluginSupportsCommonCommand } from './uiExtensions/utils';
export {
pluginsSelector,
pluginByNameSelector,
Expand All @@ -62,7 +62,6 @@ export { pluginSagas } from './sagas';
export {
uiExtensionSettingsTabsSelector,
uiExtensionAdminPagesSelector,
extensionsLoadedSelector,
uiExtensionSidebarComponentsSelector,
uiExtensionLaunchItemComponentsSelector,
uiExtensionIntegrationSettingsSelector,
Expand Down
1 change: 0 additions & 1 deletion app/src/controllers/plugins/reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,6 @@ export const integrationsReducer = (state = {}, { type = '', payload = {} }) =>
// TODO: store remote plugins separately
export const pluginsReducer = combineReducers({
plugins: queueReducers(fetchReducer(NAMESPACE), updatePluginLocallyReducer),
// TODO: recheck public plugins reducer usage
publicPlugins: queueReducers(fetchReducer(PUBLIC_PLUGINS), updatePluginLocallyReducer),
integrations: integrationsReducer,
uiExtensions: uiExtensionsReducer,
Expand Down
18 changes: 7 additions & 11 deletions app/src/controllers/plugins/sagas.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ import {
fetchGlobalIntegrationsSuccessAction,
removeGlobalIntegrationsByTypeSuccessAction,
} from './actionCreators';
import { fetchExtensionsMetadata } from './uiExtensions';
import { fetchExtensionManifests, fetchExtensionManifest } from './uiExtensions';

function* addIntegration({ payload: { data, isGlobal, pluginName, callback }, meta }) {
yield put(showScreenLockAction());
Expand Down Expand Up @@ -254,19 +254,15 @@ function* watchRemovePlugin() {
yield takeEvery(REMOVE_PLUGIN, removePlugin);
}

// TODO: fetch single metadata in case of one plugin change
function* watchPluginChange() {
function* watchPluginListFetch() {
yield takeEvery(
[createFetchPredicate(NAMESPACE), UPDATE_PLUGIN_SUCCESS],
fetchExtensionsMetadata,
[createFetchPredicate(NAMESPACE), createFetchPredicate(PUBLIC_PLUGINS)],
fetchExtensionManifests,
);
}

function* watchPublicPluginChange() {
yield takeEvery(
[createFetchPredicate(PUBLIC_PLUGINS), UPDATE_PLUGIN_SUCCESS],
fetchExtensionsMetadata,
);
function* watchPluginChange() {
yield takeEvery([UPDATE_PLUGIN_SUCCESS], fetchExtensionManifest);
}

export function* pluginSagas() {
Expand All @@ -280,6 +276,6 @@ export function* pluginSagas() {
watchFetchPublicPlugins(),
watchRemovePlugin(),
watchPluginChange(),
watchPublicPluginChange(),
watchPluginListFetch(),
]);
}
14 changes: 7 additions & 7 deletions app/src/controllers/plugins/uiExtensions/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@
* limitations under the License.
*/

import { FETCH_EXTENSIONS_METADATA_SUCCESS, UPDATE_EXTENSION_METADATA } from './constants';
import { FETCH_EXTENSION_MANIFESTS_SUCCESS, UPDATE_EXTENSION_MANIFEST } from './constants';

export const fetchExtensionsMetadataSuccessAction = (extensionsMetadata) => ({
type: FETCH_EXTENSIONS_METADATA_SUCCESS,
payload: extensionsMetadata,
export const fetchExtensionManifestsSuccessAction = (extensionManifests) => ({
type: FETCH_EXTENSION_MANIFESTS_SUCCESS,
payload: extensionManifests,
});
export const updateExtensionMetadataAction = (extensionMetadata) => ({
type: UPDATE_EXTENSION_METADATA,
payload: extensionMetadata,
export const updateExtensionManifestAction = (extensionManifest) => ({
type: UPDATE_EXTENSION_MANIFEST,
payload: extensionManifest,
});
14 changes: 8 additions & 6 deletions app/src/controllers/plugins/uiExtensions/constants.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// extension types
export const EXTENSION_TYPE_SETTINGS_TAB = 'uiExtension:settingsTab';
export const EXTENSION_TYPE_ADMIN_SIDEBAR_COMPONENT = 'uiExtension:adminSidebarComponent';
export const EXTENSION_TYPE_ADMIN_PAGE = 'uiExtension:adminPage';
Expand All @@ -21,19 +22,20 @@ export const EXTENSION_TYPE_LOG_STACKTRACE_ADDON = 'uiExtension:logStacktraceAdd
export const EXTENSION_TYPE_TEST_ITEM_DETAILS_ADDON = 'uiExtension:testItemDetailsAddon';
export const EXTENSION_TYPE_PROJECT_PAGE = 'uiExtension:projectPage';

export const COMMAND_GET_FILE = 'getFile';
// plugin commands
export const COMMAND_GET_ISSUE_TYPES = 'getIssueTypes';
export const COMMAND_GET_ISSUE_FIELDS = 'getIssueFields';
export const COMMAND_POST_ISSUE = 'postTicket';
export const COMMAND_GET_ISSUE = 'getIssue';
export const COMMAND_GET_CLUSTERS = 'getClusters';

/* New plugins mechanism related code below */

export const METADATA_FILE_KEY = 'metadata';
// core files keys
export const MANIFEST_FILE_KEY = 'metadata';
export const MAIN_FILE_KEY = 'main';
export const ICON_FILE_KEY = 'icon';

export const FETCH_EXTENSIONS_METADATA_SUCCESS = 'fetchExtensionsMetadataSuccess';
export const UPDATE_EXTENSION_METADATA = 'updateExtensionMetadata';
// redux actions
export const FETCH_EXTENSION_MANIFESTS_SUCCESS = 'fetchExtensionManifestsSuccess';
export const UPDATE_EXTENSION_MANIFEST = 'updateExtensionManifest';

export const PLUGIN_TYPE_REMOTE = 'remote';
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ const INPUTS = {
WithAsyncLoading,
};

// TODO: share these components and other stuff will be shared via WMF and ui-kit library
// TODO: share these components and other stuff via WMF and ui-kit library
export const createImportProps = (pluginName) => ({
lib: {
React,
Expand Down
5 changes: 4 additions & 1 deletion app/src/controllers/plugins/uiExtensions/hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ export const useActivePluginPageExtension = (extensionsSelector) => {
const activePluginPage = useSelector(pluginPageSelector);

const extension = React.useMemo(
() => extensions.find((ex) => ex.internalRoute === activePluginPage),
() =>
extensions.find(
(ex) => activePluginPage === ex.internalRoute || activePluginPage === ex.name,
),
[extensions, activePluginPage],
);

Expand Down
2 changes: 1 addition & 1 deletion app/src/controllers/plugins/uiExtensions/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export { fetchExtensionsMetadata } from './sagas';
export { fetchExtensionManifests, fetchExtensionManifest } from './sagas';
export {
uiExtensionSettingsTabsSelector,
uiExtensionAdminPagesSelector,
Expand Down
19 changes: 12 additions & 7 deletions app/src/controllers/plugins/uiExtensions/overrideExtension.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
import { updateExtensionMetadataAction } from './actions';
import { METADATA_FILE_KEY } from './constants';
import { pluginByNameSelector } from 'controllers/plugins';
import { updateExtensionManifestAction } from './actions';
import { MANIFEST_FILE_KEY } from './constants';

// TODO: restrict access to this function (f.e. only for admins)
// TODO: replace metadata with manifest
export const createExtensionOverrider = (store) => async (pluginName, url) => {
const response = await fetch(`${url}/${METADATA_FILE_KEY}.json`, {
const plugin = pluginByNameSelector(store);

const manifestFileName =
plugin.details?.binaryData?.[MANIFEST_FILE_KEY] || `${MANIFEST_FILE_KEY}.json`;

const response = await fetch(`${url}/${manifestFileName}`, {
contentType: 'application/json',
});
const metadata = await response.json();
const manifest = await response.json();

store.dispatch(updateExtensionMetadataAction({ ...metadata, pluginName, url }));
store.dispatch(updateExtensionManifestAction({ ...manifest, pluginName, url }));

return metadata;
return manifest;
};
10 changes: 5 additions & 5 deletions app/src/controllers/plugins/uiExtensions/reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@
*/

import { combineReducers } from 'redux';
import { FETCH_EXTENSIONS_METADATA_SUCCESS, UPDATE_EXTENSION_METADATA } from './constants';
import { FETCH_EXTENSION_MANIFESTS_SUCCESS, UPDATE_EXTENSION_MANIFEST } from './constants';

const extensionsMetadataReducer = (state = [], { type = '', payload = {} }) => {
const extensionManifestsReducer = (state = [], { type = '', payload = {} }) => {
switch (type) {
case FETCH_EXTENSIONS_METADATA_SUCCESS:
case FETCH_EXTENSION_MANIFESTS_SUCCESS:
return payload;
case UPDATE_EXTENSION_METADATA:
case UPDATE_EXTENSION_MANIFEST:
return state.map((item) => {
if (item.pluginName === payload.pluginName) {
return payload;
Expand All @@ -34,5 +34,5 @@ const extensionsMetadataReducer = (state = [], { type = '', payload = {} }) => {
};

export const uiExtensionsReducer = combineReducers({
extensionsMetadata: extensionsMetadataReducer,
extensionManifests: extensionManifestsReducer,
});
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { createExtensionOverrider } from './overrideExtension';
window.RP = {};

export const initPluginRegistration = (store) => {
// allows overriding plugin UI extensions in favor of separately hosted files (e.g. locally hosted plugin files)
const overrideExtension = createExtensionOverrider(store);
window.RP = {
overrideExtension,
Expand Down
Loading

0 comments on commit 47ca328

Please sign in to comment.