1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-07-26 13:48:33 +02:00

Integrations - frontend adjustments (#4527)

## About the changes
  - [x] Create a feature flag
  - [x] Rename page title
  - [x] Rename menu item
  - [x] Update frontend URL (add redirect from old one)


https://linear.app/unleash/issue/1-1263/integrations-frontend-adjustments
This commit is contained in:
Tymoteusz Czech 2023-08-22 14:40:38 +02:00 committed by GitHub
parent 8a3889d570
commit 573518e48d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 225 additions and 94 deletions

View File

@ -1,19 +0,0 @@
import React from 'react';
import { ConfiguredAddons } from './ConfiguredAddons/ConfiguredAddons';
import { AvailableAddons } from './AvailableAddons/AvailableAddons';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import useAddons from 'hooks/api/getters/useAddons/useAddons';
export const AddonList = () => {
const { providers, addons, loading } = useAddons();
return (
<>
<ConditionallyRender
condition={addons.length > 0}
show={<ConfiguredAddons />}
/>
<AvailableAddons loading={loading} providers={providers} />
</>
);
};

View File

@ -0,0 +1,19 @@
import { Link, useLocation, useNavigate } from 'react-router-dom';
import { useEffect } from 'react';
import { PageContent } from 'component/common/PageContent/PageContent';
export const AddonRedirect = () => {
const location = useLocation();
const navigate = useNavigate();
useEffect(() => {
navigate(`/integrations${location.pathname.replace('/addons', '')}`);
}, [location.pathname, navigate]);
return (
<PageContent>
Addons where renamed to{' '}
<Link to="/integrations">/integrations</Link>
</PageContent>
);
};

View File

@ -1,5 +1,5 @@
import useAddons from 'hooks/api/getters/useAddons/useAddons';
import { AddonForm } from '../AddonForm/AddonForm';
import { IntegrationForm } from '../IntegrationForm/IntegrationForm';
import cloneDeep from 'lodash.clonedeep';
import { IAddon } from 'interfaces/addons';
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
@ -14,7 +14,7 @@ export const DEFAULT_DATA = {
environments: [],
} as unknown as IAddon; // TODO: improve type
export const CreateAddon = () => {
export const CreateIntegration = () => {
const providerId = useRequiredPathParam('providerId');
const { providers, refetchAddons } = useAddons();
@ -29,7 +29,7 @@ export const CreateAddon = () => {
};
return (
<AddonForm
<IntegrationForm
editMode={editMode}
provider={provider}
fetch={refetchAddons}

View File

@ -1,11 +1,11 @@
import useAddons from 'hooks/api/getters/useAddons/useAddons';
import { AddonForm } from '../AddonForm/AddonForm';
import { IntegrationForm } from '../IntegrationForm/IntegrationForm';
import cloneDeep from 'lodash.clonedeep';
import { IAddon } from 'interfaces/addons';
import { DEFAULT_DATA } from '../CreateAddon/CreateAddon';
import { DEFAULT_DATA } from '../CreateIntegration/CreateIntegration';
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
export const EditAddon = () => {
export const EditIntegration = () => {
const addonId = useRequiredPathParam('addonId');
const { providers, addons, refetchAddons } = useAddons();
@ -18,7 +18,7 @@ export const EditAddon = () => {
: undefined;
return (
<AddonForm
<IntegrationForm
editMode={editMode}
provider={provider}
fetch={refetchAddons}

View File

@ -16,8 +16,8 @@ import {
import produce from 'immer';
import { trim } from 'component/common/util';
import { IAddon, IAddonProvider } from 'interfaces/addons';
import { AddonParameters } from './AddonParameters/AddonParameters';
import { AddonInstall } from './AddonInstall/AddonInstall';
import { IntegrationParameters } from './IntegrationParameters/IntegrationParameters';
import { IntegrationInstall } from './IntegrationInstall/IntegrationInstall';
import cloneDeep from 'lodash.clonedeep';
import { useNavigate } from 'react-router-dom';
import useAddonsApi from 'hooks/api/actions/useAddonsApi/useAddonsApi';
@ -25,7 +25,7 @@ import useToast from 'hooks/useToast';
import { formatUnknownError } from 'utils/formatUnknownError';
import useProjects from 'hooks/api/getters/useProjects/useProjects';
import { useEnvironments } from 'hooks/api/getters/useEnvironments/useEnvironments';
import { AddonMultiSelector } from './AddonMultiSelector/AddonMultiSelector';
import { IntegrationMultiSelector } from './IntegrationMultiSelector/IntegrationMultiSelector';
import FormTemplate from 'component/common/FormTemplate/FormTemplate';
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
import PermissionButton from 'component/common/PermissionButton/PermissionButton';
@ -42,7 +42,7 @@ import {
StyledContainer,
StyledButtonContainer,
StyledButtonSection,
} from './AddonForm.styles';
} from './IntegrationForm.styles';
import { useTheme } from '@mui/system';
import { GO_BACK } from 'constants/navigate';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
@ -54,7 +54,7 @@ interface IAddonFormProps {
editMode: boolean;
}
export const AddonForm: VFC<IAddonFormProps> = ({
export const IntegrationForm: VFC<IAddonFormProps> = ({
editMode,
provider,
addon: initialValues,
@ -272,7 +272,7 @@ export const AddonForm: VFC<IAddonFormProps> = ({
<ConditionallyRender
condition={Boolean(installation)}
show={() => (
<AddonInstall
<IntegrationInstall
url={installation!.url}
title={installation!.title}
helpText={installation!.helpText}
@ -321,7 +321,7 @@ export const AddonForm: VFC<IAddonFormProps> = ({
</StyledFormSection>
<StyledFormSection>
<AddonMultiSelector
<IntegrationMultiSelector
options={selectableEvents || []}
selectedItems={formValues.events}
onChange={setEventValues}
@ -333,7 +333,7 @@ export const AddonForm: VFC<IAddonFormProps> = ({
/>
</StyledFormSection>
<StyledFormSection>
<AddonMultiSelector
<IntegrationMultiSelector
options={selectableProjects}
selectedItems={formValues.projects || []}
onChange={setProjects}
@ -342,7 +342,7 @@ export const AddonForm: VFC<IAddonFormProps> = ({
/>
</StyledFormSection>
<StyledFormSection>
<AddonMultiSelector
<IntegrationMultiSelector
options={selectableEnvironments}
selectedItems={formValues.environments || []}
onChange={setEnvironments}
@ -351,7 +351,7 @@ export const AddonForm: VFC<IAddonFormProps> = ({
/>
</StyledFormSection>
<StyledFormSection>
<AddonParameters
<IntegrationParameters
provider={provider}
config={formValues}
parametersErrors={errors.parameters}

View File

@ -3,7 +3,7 @@ import {
StyledFormSection,
StyledHelpText,
StyledTitle,
} from '../AddonForm.styles';
} from '../IntegrationForm.styles';
import { Button } from '@mui/material';
import { Link } from 'react-router-dom';
@ -13,7 +13,7 @@ export interface IAddonInstallProps {
helpText?: string;
}
export const AddonInstall = ({
export const IntegrationInstall = ({
url,
title = 'Install addon',
helpText = 'Click this button to install this addon.',

View File

@ -4,15 +4,15 @@ import { screen, waitFor, within } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { render } from 'utils/testRenderer';
import {
IAddonMultiSelectorProps,
AddonMultiSelector,
} from './AddonMultiSelector';
IIntegrationMultiSelectorProps,
IntegrationMultiSelector,
} from './IntegrationMultiSelector';
import { testServerRoute, testServerSetup } from 'utils/testServer';
const onChange = vi.fn();
const onFocus = vi.fn();
const mockProps: IAddonMultiSelectorProps = {
const mockProps: IIntegrationMultiSelectorProps = {
options: [
{ label: 'Project1', value: 'project1' },
{ label: 'Project2', value: 'project2' },
@ -35,7 +35,9 @@ describe('AddonMultiSelector', () => {
});
it('renders with default state', () => {
render(<AddonMultiSelector {...mockProps} selectedItems={['*']} />);
render(
<IntegrationMultiSelector {...mockProps} selectedItems={['*']} />
);
const checkbox = screen.getByLabelText(
/all current and future projects/i
@ -49,7 +51,9 @@ describe('AddonMultiSelector', () => {
it('can toggle "ALL" checkbox', async () => {
const user = userEvent.setup();
render(<AddonMultiSelector {...mockProps} selectedItems={['*']} />);
render(
<IntegrationMultiSelector {...mockProps} selectedItems={['*']} />
);
await user.click(screen.getByTestId('select-all-projects'));
@ -70,7 +74,10 @@ describe('AddonMultiSelector', () => {
it('renders with autocomplete enabled if default value is not a wildcard', () => {
render(
<AddonMultiSelector {...mockProps} selectedItems={['project1']} />
<IntegrationMultiSelector
{...mockProps}
selectedItems={['project1']}
/>
);
const checkbox = screen.getByLabelText(
@ -87,7 +94,7 @@ describe('AddonMultiSelector', () => {
it("doesn't show up for less than 3 options", async () => {
const user = userEvent.setup();
render(
<AddonMultiSelector
<IntegrationMultiSelector
{...mockProps}
selectedItems={[]}
options={[
@ -108,7 +115,7 @@ describe('AddonMultiSelector', () => {
it('can filter options', async () => {
const user = userEvent.setup();
render(
<AddonMultiSelector
<IntegrationMultiSelector
{...mockProps}
selectedItems={[]}
options={[

View File

@ -22,9 +22,9 @@ import {
StyledHelpText,
StyledSelectAllFormControlLabel,
StyledTitle,
} from '../AddonForm.styles';
} from '../IntegrationForm.styles';
export interface IAddonMultiSelectorProps {
export interface IIntegrationMultiSelectorProps {
options: IAutocompleteBoxOption[];
selectedItems: string[];
onChange: (value: string[]) => void;
@ -44,7 +44,7 @@ const StyledCheckbox = styled(Checkbox)(() => ({
const CustomPaper = ({ ...props }) => <Paper elevation={8} {...props} />;
export const AddonMultiSelector: VFC<IAddonMultiSelectorProps> = ({
export const IntegrationMultiSelector: VFC<IIntegrationMultiSelectorProps> = ({
options,
selectedItems,
onChange,
@ -138,9 +138,9 @@ export const AddonMultiSelector: VFC<IAddonMultiSelectorProps> = ({
const DefaultHelpText = () => (
<StyledHelpText>
Selecting {entityName}(s) here will filter events so that your addon
will only receive events that are tagged with one of your{' '}
{entityName}s.
Selecting {entityName}(s) here will filter events so that your
integration will only receive events that are tagged with one of
your {entityName}s.
</StyledHelpText>
);

View File

@ -1,7 +1,7 @@
import { TextField, Typography } from '@mui/material';
import { IAddonConfig, IAddonProviderParams } from 'interfaces/addons';
import { ChangeEventHandler } from 'react';
import { StyledAddonParameterContainer } from '../../AddonForm.styles';
import { StyledAddonParameterContainer } from '../../IntegrationForm.styles';
const resolveType = ({ type = 'text', sensitive = false }, value: string) => {
if (sensitive && value === MASKED_VALUE) {
@ -15,19 +15,19 @@ const resolveType = ({ type = 'text', sensitive = false }, value: string) => {
const MASKED_VALUE = '*****';
export interface IAddonParameterProps {
export interface IIntegrationParameterProps {
parametersErrors: Record<string, string>;
definition: IAddonProviderParams;
setParameterValue: (param: string) => ChangeEventHandler<HTMLInputElement>;
config: IAddonConfig;
}
export const AddonParameter = ({
export const IntegrationParameter = ({
definition,
config,
parametersErrors,
setParameterValue,
}: IAddonParameterProps) => {
}: IIntegrationParameterProps) => {
const value = config.parameters[definition.name] || '';
const type = resolveType(definition, value);
const error = parametersErrors[definition.name];

View File

@ -1,26 +1,26 @@
import React from 'react';
import { IAddonProvider } from 'interfaces/addons';
import {
AddonParameter,
IAddonParameterProps,
} from './AddonParameter/AddonParameter';
import { StyledTitle } from '../AddonForm.styles';
IntegrationParameter,
IIntegrationParameterProps,
} from './IntegrationParameter/IntegrationParameter';
import { StyledTitle } from '../IntegrationForm.styles';
interface IAddonParametersProps {
interface IIntegrationParametersProps {
provider?: IAddonProvider;
parametersErrors: IAddonParameterProps['parametersErrors'];
parametersErrors: IIntegrationParameterProps['parametersErrors'];
editMode: boolean;
setParameterValue: IAddonParameterProps['setParameterValue'];
config: IAddonParameterProps['config'];
setParameterValue: IIntegrationParameterProps['setParameterValue'];
config: IIntegrationParameterProps['config'];
}
export const AddonParameters = ({
export const IntegrationParameters = ({
provider,
config,
parametersErrors,
setParameterValue,
editMode,
}: IAddonParametersProps) => {
}: IIntegrationParametersProps) => {
if (!provider) return null;
return (
<React.Fragment>
@ -33,7 +33,7 @@ export const AddonParameters = ({
</p>
) : null}
{provider.parameters.map(parameter => (
<AddonParameter
<IntegrationParameter
key={parameter.name}
definition={parameter}
parametersErrors={parametersErrors}

View File

@ -16,10 +16,11 @@ import { PageHeader } from 'component/common/PageHeader/PageHeader';
import { sortTypes } from 'utils/sortTypes';
import { IconCell } from 'component/common/Table/cells/IconCell/IconCell';
import { ActionCell } from 'component/common/Table/cells/ActionCell/ActionCell';
import { ConfigureAddonButton } from './ConfigureAddonButton/ConfigureAddonButton';
import { AddonIcon } from '../AddonIcon/AddonIcon';
import { AddonNameCell } from '../AddonNameCell/AddonNameCell';
import { ConfigureAddonsButton } from './ConfigureAddonButton/ConfigureAddonsButton';
import { IntegrationIcon } from '../IntegrationIcon/IntegrationIcon';
import { IntegrationNameCell } from '../IntegrationNameCell/IntegrationNameCell';
import { IAddonInstallation } from 'interfaces/addons';
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
interface IProvider {
name: string;
@ -37,6 +38,9 @@ interface IAvailableAddonsProps {
loading: boolean;
}
/**
* @deprecated Remove when integrationsRework flag is removed
*/
export const AvailableAddons = ({
providers,
loading,
@ -70,7 +74,9 @@ export const AvailableAddons = ({
},
}: any) => {
return (
<IconCell icon={<AddonIcon name={name as string} />} />
<IconCell
icon={<IntegrationIcon name={name as string} />}
/>
);
},
},
@ -79,7 +85,7 @@ export const AvailableAddons = ({
accessor: 'name',
width: '90%',
Cell: ({ row: { original } }: any) => (
<AddonNameCell provider={original} />
<IntegrationNameCell provider={original} />
),
sortType: 'alphanumeric',
},
@ -88,7 +94,7 @@ export const AvailableAddons = ({
align: 'center',
Cell: ({ row: { original } }: any) => (
<ActionCell>
<ConfigureAddonButton provider={original} />
<ConfigureAddonsButton provider={original} />
</ActionCell>
),
width: 150,

View File

@ -3,13 +3,16 @@ import { CREATE_ADDON } from 'component/providers/AccessProvider/permissions';
import { IAddonProvider } from 'interfaces/addons';
import { useNavigate } from 'react-router-dom';
interface IConfigureAddonButtonProps {
interface IConfigureAddonsButtonProps {
provider: IAddonProvider;
}
export const ConfigureAddonButton = ({
/**
* @deprecated Remove when integrationsRework flag is removed
*/
export const ConfigureAddonsButton = ({
provider,
}: IConfigureAddonButtonProps) => {
}: IConfigureAddonsButtonProps) => {
const navigate = useNavigate();
return (

View File

@ -0,0 +1,8 @@
import { PageContent } from 'component/common/PageContent/PageContent';
import { VFC } from 'react';
interface IAvailableIntegrationsProps {}
export const AvailableIntegrations: VFC<IAvailableIntegrationsProps> = () => {
return <PageContent>Available integrations</PageContent>;
};

View File

@ -13,10 +13,13 @@ import { useTable, useSortBy } from 'react-table';
import { PageHeader } from 'component/common/PageHeader/PageHeader';
import { SortableTableHeader, TablePlaceholder } from 'component/common/Table';
import { IconCell } from 'component/common/Table/cells/IconCell/IconCell';
import { AddonIcon } from '../AddonIcon/AddonIcon';
import { IntegrationIcon } from '../IntegrationIcon/IntegrationIcon';
import { ConfiguredAddonsActionsCell } from './ConfiguredAddonsActionCell/ConfiguredAddonsActionsCell';
import { AddonNameCell } from '../AddonNameCell/AddonNameCell';
import { IntegrationNameCell } from '../IntegrationNameCell/IntegrationNameCell';
/**
* @deprecated Remove when integrationsRework flag is removed
*/
export const ConfiguredAddons = () => {
const { refetchAddons, addons, providers, loading } = useAddons();
const { updateAddon, removeAddon } = useAddonsApi();
@ -73,7 +76,9 @@ export const ConfiguredAddons = () => {
original: { provider },
},
}: any) => (
<IconCell icon={<AddonIcon name={provider as string} />} />
<IconCell
icon={<IntegrationIcon name={provider as string} />}
/>
),
disableSortBy: true,
},
@ -86,7 +91,7 @@ export const ConfiguredAddons = () => {
original: { provider, description },
},
}: any) => (
<AddonNameCell
<IntegrationNameCell
provider={{
...(providers.find(
({ name }) => name === provider

View File

@ -18,6 +18,9 @@ interface IConfiguredAddonsActionsCellProps {
setDeletedAddon: React.Dispatch<React.SetStateAction<IAddon>>;
}
/**
* @deprecated Remove when integrationsRework flag is removed
*/
export const ConfiguredAddonsActionsCell = ({
toggleAddon,
setShowDelete,

View File

@ -14,11 +14,11 @@ const style: React.CSSProperties = {
marginRight: '16px',
};
interface IAddonIconProps {
interface IIntegrationIconProps {
name: string;
}
export const AddonIcon = ({ name }: IAddonIconProps) => {
export const IntegrationIcon = ({ name }: IIntegrationIconProps) => {
switch (name) {
case 'slack':
case 'slack-app':

View File

@ -0,0 +1,28 @@
import { ConfiguredAddons } from './ConfiguredAddons/ConfiguredAddons';
import { AvailableAddons } from './AvailableAddons/AvailableAddons';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import useAddons from 'hooks/api/getters/useAddons/useAddons';
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
import { AvailableIntegrations } from './AvailableIntegrations/AvailableIntegrations';
export const IntegrationList = () => {
const { providers, addons, loading } = useAddons();
const { uiConfig } = useUiConfig();
const integrationsRework = uiConfig?.flags?.integrationsRework || false;
return (
<>
<ConditionallyRender
condition={addons.length > 0}
show={<ConfiguredAddons />}
/>
<ConditionallyRender
condition={integrationsRework}
show={<AvailableIntegrations />}
elseShow={
<AvailableAddons loading={loading} providers={providers} />
}
/>
</>
);
};

View File

@ -10,14 +10,19 @@ const StyledBadge = styled(Badge)(({ theme }) => ({
marginLeft: theme.spacing(1),
}));
interface IAddonNameCellProps {
interface IIntegrationNameCellProps {
provider: Pick<
IAddonProvider,
'displayName' | 'description' | 'deprecated'
>;
}
export const AddonNameCell = ({ provider }: IAddonNameCellProps) => (
/**
* @deprecated Remove when integrationsRework flag is removed
*/
export const IntegrationNameCell = ({
provider,
}: IIntegrationNameCellProps) => (
<HighlightCell
value={provider.displayName}
subtitle={provider.description}

View File

@ -317,6 +317,34 @@ exports[`returns all baseRoutes 1`] = `
"title": "Addons",
"type": "protected",
},
{
"component": [Function],
"menu": {},
"parent": "/integrations",
"path": "/integrations/create/:providerId",
"title": "Create",
"type": "protected",
},
{
"component": [Function],
"menu": {},
"parent": "/integrations",
"path": "/integrations/edit/:addonId",
"title": "Edit",
"type": "protected",
},
{
"component": [Function],
"flag": "integrationsRework",
"hidden": false,
"menu": {
"advanced": true,
"mobile": true,
},
"path": "/integrations",
"title": "Integrations",
"type": "protected",
},
{
"component": [Function],
"flag": "SE",

View File

@ -2,7 +2,7 @@ import { FeatureToggleListTable } from 'component/feature/FeatureToggleList/Feat
import { StrategyView } from 'component/strategies/StrategyView/StrategyView';
import { StrategiesList } from 'component/strategies/StrategiesList/StrategiesList';
import { TagTypeList } from 'component/tags/TagTypeList/TagTypeList';
import { AddonList } from 'component/addons/AddonList/AddonList';
import { IntegrationList } from 'component/integrations/IntegrationList/IntegrationList';
import Login from 'component/user/Login/Login';
import { EEA, P, SE } from 'component/common/flags';
import { NewUser } from 'component/user/NewUser/NewUser';
@ -21,8 +21,8 @@ import EditFeature from 'component/feature/EditFeature/EditFeature';
import { ApplicationEdit } from 'component/application/ApplicationEdit/ApplicationEdit';
import ContextList from 'component/context/ContextList/ContextList/ContextList';
import RedirectFeatureView from 'component/feature/RedirectFeatureView/RedirectFeatureView';
import { CreateAddon } from 'component/addons/CreateAddon/CreateAddon';
import { EditAddon } from 'component/addons/EditAddon/EditAddon';
import { CreateIntegration } from 'component/integrations/CreateIntegration/CreateIntegration';
import { EditIntegration } from 'component/integrations/EditIntegration/EditIntegration';
import { CopyFeatureToggle } from 'component/feature/CopyFeature/CopyFeature';
import { EventPage } from 'component/events/EventPage/EventPage';
import { CreateStrategy } from 'component/strategies/CreateStrategy/CreateStrategy';
@ -300,12 +300,13 @@ export const routes: IRoute[] = [
menu: { mobile: true, advanced: true },
},
// Addons
// Integrations
{
path: '/addons/create/:providerId',
parent: '/addons',
title: 'Create',
component: CreateAddon,
component: CreateIntegration,
// TODO: use AddonRedirect after removing `integrationsRework` menu flag
type: 'protected',
menu: {},
},
@ -313,17 +314,45 @@ export const routes: IRoute[] = [
path: '/addons/edit/:addonId',
parent: '/addons',
title: 'Edit',
component: EditAddon,
component: EditIntegration,
// TODO: use AddonRedirect after removing `integrationsRework` menu flag
type: 'protected',
menu: {},
},
{
path: '/addons',
title: 'Addons',
component: AddonList,
component: IntegrationList,
// TODO: use AddonRedirect after removing `integrationsRework` menu flag
hidden: false,
type: 'protected',
menu: { mobile: true, advanced: true },
// TODO: remove 'addons' from menu after removing `integrationsRework` menu flag
},
{
path: '/integrations/create/:providerId',
parent: '/integrations',
title: 'Create',
component: CreateIntegration,
type: 'protected',
menu: {},
},
{
path: '/integrations/edit/:addonId',
parent: '/integrations',
title: 'Edit',
component: EditIntegration,
type: 'protected',
menu: {},
},
{
path: '/integrations',
title: 'Integrations',
component: IntegrationList,
hidden: false,
type: 'protected',
menu: { mobile: true, advanced: true },
flag: 'integrationsRework',
},
// Segments

View File

@ -58,6 +58,7 @@ export interface IFlags {
changeRequestReject?: boolean;
lastSeenByEnvironment?: boolean;
newApplicationList?: boolean;
integrationsRework?: boolean;
}
export interface IVersionInfo {

View File

@ -82,6 +82,7 @@ exports[`should create default config 1`] = `
"filterInvalidClientMetrics": false,
"frontendNavigationUpdate": false,
"googleAuthEnabled": false,
"integrationsRework": false,
"lastSeenByEnvironment": false,
"maintenanceMode": false,
"messageBanner": {
@ -118,6 +119,7 @@ exports[`should create default config 1`] = `
"filterInvalidClientMetrics": false,
"frontendNavigationUpdate": false,
"googleAuthEnabled": false,
"integrationsRework": false,
"lastSeenByEnvironment": false,
"maintenanceMode": false,
"messageBanner": {

View File

@ -29,7 +29,8 @@ export type IFlagKey =
| 'segmentChangeRequests'
| 'changeRequestReject'
| 'customRootRolesKillSwitch'
| 'newApplicationList';
| 'newApplicationList'
| 'integrationsRework';
export type IFlags = Partial<{ [key in IFlagKey]: boolean | Variant }>;
@ -133,6 +134,10 @@ const flags: IFlags = {
process.env.UNLEASH_EXPERIMENTAL_CUSTOM_ROOT_ROLES_KILL_SWITCH,
false,
),
integrationsRework: parseEnvVarBoolean(
process.env.UNLEASH_INTEGRATIONS,
false,
),
};
export const defaultExperimentalOptions: IExperimentalOptions = {

View File

@ -13,6 +13,7 @@ yarn install
```console
yarn generate
```
Generate the Open API docs that live at Reference documentation > APIs > OpenAPI
## Local Development
@ -21,7 +22,7 @@ Generate the Open API docs that live at Reference documentation > APIs > OpenAPI
yarn start
```
Start a local development server and opens up a browser window. Most changes are reflected live without having to restart the server.
Start a local development server and opens up a browser window. Most changes are reflected live without having to restart the server.
## Build