Skip to content

Commit

Permalink
feat(templates): Adding connection create UI in list grid (#5022)
Browse files Browse the repository at this point in the history
Co-authored-by: Priti Sambandam <psamband@microsoft.com>
  • Loading branch information
preetriti1 and Priti Sambandam committed Jun 25, 2024
1 parent 81cc703 commit ea3d271
Show file tree
Hide file tree
Showing 10 changed files with 219 additions and 94 deletions.
4 changes: 4 additions & 0 deletions Localize/lang/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -599,6 +599,7 @@
"MnThTq": "Insert function",
"MsCHhQ": "Timed Out",
"MtWzvX": "Enter a positive integer between {min} and {max}",
"N+qRUv": "Fill out the fields below to create a connection for {connectorName}",
"N0pS6Y": "Target schema",
"N2CF0J": "Required. The key name of the form data values to return.",
"N4dEVo": "Headers",
Expand Down Expand Up @@ -1542,6 +1543,7 @@
"_MnThTq.comment": "Message to insert function",
"_MsCHhQ.comment": "The status message to show in monitoring view.",
"_MtWzvX.comment": "descriptio of maximum waiting runs setting",
"_N+qRUv.comment": "Message to show in title for connection creation",
"_N0pS6Y.comment": "Target schema",
"_N2CF0J.comment": "Required string parameter to be used as key for triggerFormDataMultiValues function",
"_N4dEVo.comment": "Display name for headers in outputs",
Expand Down Expand Up @@ -2509,6 +2511,7 @@
"_yF2R//.comment": "Label for description of custom substring Function",
"_yKOsmK.comment": "Label for description of custom sort Function",
"_yNtBUV.comment": "Warning message for when input node type does not match one of the function node input's allowed types",
"_yQ6+nV.comment": "Link to create a connection",
"_yRDuqj.comment": "Button text to add all advanced parameters",
"_yVFIAQ.comment": "Time zone value ",
"_yVh9kr.comment": "Hour of the day",
Expand Down Expand Up @@ -3173,6 +3176,7 @@
"yF2R//": "Returns a subset of characters from a string.",
"yKOsmK": "Returns an array sorted in ascending order",
"yNtBUV": "Warning: input node type does not match one of the allowed types for this input.",
"yQ6+nV": "Connect",
"yRDuqj": "Show all",
"yVFIAQ": "(UTC-01:00) Cabo Verde Is.",
"yVh9kr": "8",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { ConnectionsData, ParametersData } from '../Models/Workflow';
import type { ConnectionAndAppSetting, ConnectionsData, ParametersData } from '../Models/Workflow';
import type { ConnectionReferences } from '@microsoft/logic-apps-designer';

export class WorkflowUtility {
Expand Down Expand Up @@ -122,3 +122,49 @@ function replaceIfFoundAndVerifyJson(stringifiedJson: string, searchValue: strin
return undefined;
}
}

export async function addConnectionData(
connectionAndSetting: ConnectionAndAppSetting,
connectionsData: ConnectionsData,
settings: any
): Promise<void> {
addConnectionInJson(connectionAndSetting, connectionsData ?? {});
addOrUpdateAppSettings(connectionAndSetting.settings, settings?.properties ?? {});
}

function addConnectionInJson(connectionAndSetting: ConnectionAndAppSetting, connectionsJson: ConnectionsData): void {
const { connectionData, connectionKey, pathLocation } = connectionAndSetting;

let pathToSetConnectionsData: any = connectionsJson;

for (const path of pathLocation) {
if (!pathToSetConnectionsData[path]) {
pathToSetConnectionsData[path] = {};
}

pathToSetConnectionsData = pathToSetConnectionsData[path];
}

if (pathToSetConnectionsData && pathToSetConnectionsData[connectionKey]) {
// TODO: To show this in a notification of info bar on the blade.
// const message = 'ConnectionKeyAlreadyExist - Connection key \'{0}\' already exists.'.format(connectionKey);
return;
}

pathToSetConnectionsData[connectionKey] = connectionData;
}

function addOrUpdateAppSettings(settings: Record<string, string>, originalSettings: Record<string, string>): Record<string, string> {
const settingsToAdd = Object.keys(settings);

for (const settingKey of settingsToAdd) {
if (originalSettings[settingKey]) {
// TODO: To show this in a notification of info bar on the blade that key will be overriden.
}

// eslint-disable-next-line no-param-reassign
originalSettings[settingKey] = settings[settingKey];
}

return originalSettings;
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import {
useWorkflowApp,
} from './Services/WorkflowAndArtifacts';
import { ArmParser } from './Utilities/ArmParser';
import { WorkflowUtility } from './Utilities/Workflow';
import { WorkflowUtility, addConnectionData } from './Utilities/Workflow';
import { Chatbot, chatbotPanelWidth } from '@microsoft/logic-apps-chatbot';
import {
BaseApiManagementService,
Expand Down Expand Up @@ -123,9 +123,8 @@ const DesignerEditor = () => {
[originalConnectionsData, parameters, settingsData?.properties]
);

const addConnectionData = async (connectionAndSetting: ConnectionAndAppSetting): Promise<void> => {
addConnectionInJson(connectionAndSetting, connectionsData ?? {});
addOrUpdateAppSettings(connectionAndSetting.settings, settingsData?.properties ?? {});
const addConnectionDataInternal = async (connectionAndSetting: ConnectionAndAppSetting): Promise<void> => {
addConnectionData(connectionAndSetting, connectionsData ?? {}, settingsData ?? {});
};

const getConnectionConfiguration = async (connectionId: string): Promise<any> => {
Expand Down Expand Up @@ -168,7 +167,7 @@ const DesignerEditor = () => {
equals(workflow?.kind, 'stateful'),
connectionsData ?? {},
workflowAppData as WorkflowApp,
addConnectionData,
addConnectionDataInternal,
getConnectionConfiguration,
tenantId,
objectId,
Expand Down Expand Up @@ -716,43 +715,6 @@ const getDesignerServices = (
};
};

const addConnectionInJson = (connectionAndSetting: ConnectionAndAppSetting, connectionsJson: ConnectionsData): void => {
const { connectionData, connectionKey, pathLocation } = connectionAndSetting;

let pathToSetConnectionsData: any = connectionsJson;

for (const path of pathLocation) {
if (!pathToSetConnectionsData[path]) {
pathToSetConnectionsData[path] = {};
}

pathToSetConnectionsData = pathToSetConnectionsData[path];
}

if (pathToSetConnectionsData && pathToSetConnectionsData[connectionKey]) {
// TODO: To show this in a notification of info bar on the blade.
// const message = 'ConnectionKeyAlreadyExist - Connection key \'{0}\' already exists.'.format(connectionKey);
return;
}

pathToSetConnectionsData[connectionKey] = connectionData;
};

const addOrUpdateAppSettings = (settings: Record<string, string>, originalSettings: Record<string, string>): Record<string, string> => {
const settingsToAdd = Object.keys(settings);

for (const settingKey of settingsToAdd) {
if (originalSettings[settingKey]) {
// TODO: To show this in a notification of info bar on the blade that key will be overriden.
}

// eslint-disable-next-line no-param-reassign
originalSettings[settingKey] = settings[settingKey];
}

return originalSettings;
};

const hasNewKeys = (original: Record<string, any> = {}, updated: Record<string, any> = {}) => {
return !Object.keys(updated).some((key) => !Object.keys(original).includes(key));
};
Expand Down
13 changes: 10 additions & 3 deletions apps/Standalone/src/templates/app/TemplatesStandaloneDesigner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@ import {
useCurrentTenantId,
useWorkflowApp,
} from '../../designer/app/AzureLogicAppsDesigner/Services/WorkflowAndArtifacts';
import type { ConnectionsData } from '../../designer/app/AzureLogicAppsDesigner/Models/Workflow';
import type { ConnectionAndAppSetting, ConnectionsData } from '../../designer/app/AzureLogicAppsDesigner/Models/Workflow';
import type { WorkflowApp } from '../../designer/app/AzureLogicAppsDesigner/Models/WorkflowApp';
import { ArmParser } from '../../designer/app/AzureLogicAppsDesigner/Utilities/ArmParser';
import { StandaloneOAuthService } from '../../designer/app/AzureLogicAppsDesigner/Services/OAuthService';
import { WorkflowUtility } from '../../designer/app/AzureLogicAppsDesigner/Utilities/Workflow';
import { WorkflowUtility, addConnectionData } from '../../designer/app/AzureLogicAppsDesigner/Utilities/Workflow';
import { HttpClient } from '../../designer/app/AzureLogicAppsDesigner/Services/HttpClient';
// import { useNavigate } from 'react-router-dom';
// import type { Template, LogicAppsV2 } from '@microsoft/logic-apps-shared';
Expand Down Expand Up @@ -154,8 +154,13 @@ export const TemplatesStandaloneDesigner = () => {
}
};

const addConnectionDataInternal = async (connectionAndSetting: ConnectionAndAppSetting): Promise<void> => {
addConnectionData(connectionAndSetting, connectionsData ?? {}, settingsData ?? {});
};

const services = useMemo(
() => getServices(connectionsData ?? {}, workflowAppData as WorkflowApp, tenantId, objectId, canonicalLocation),
() =>
getServices(connectionsData ?? {}, workflowAppData as WorkflowApp, addConnectionDataInternal, tenantId, objectId, canonicalLocation),
// eslint-disable-next-line react-hooks/exhaustive-deps
[connectionsData, settingsData, workflowAppData, tenantId, canonicalLocation]
);
Expand Down Expand Up @@ -190,6 +195,7 @@ const httpClient = new HttpClient();
const getServices = (
connectionsData: ConnectionsData,
workflowApp: WorkflowApp | undefined,
addConnection: (data: ConnectionAndAppSetting) => Promise<void>,
tenantId: string | undefined,
objectId: string | undefined,
location: string
Expand All @@ -215,6 +221,7 @@ const getServices = (
},
workflowAppDetails: { appName, identity: workflowApp?.identity as any },
readConnections: () => Promise.resolve(connectionsData),
writeConnection: addConnection as any,
});
const gatewayService = new BaseGatewayService({
baseUrl: armUrl,
Expand Down
13 changes: 13 additions & 0 deletions libs/designer/src/lib/core/state/templates/templateSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ import {
type LogicAppsV2,
type Template,
InitWorkflowService,
type ILoggerService,
DevLogger,
InitLoggerService,
} from '@microsoft/logic-apps-shared';
import type { PayloadAction } from '@reduxjs/toolkit';
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';
Expand Down Expand Up @@ -64,11 +67,21 @@ export const initializeTemplateServices = createAsyncThunk(
functionService,
appServiceService,
connectionParameterEditorService,
loggerService,
}: TemplateServiceOptions) => {
InitConnectionService(connectionService);
InitOAuthService(oAuthService);
InitWorkflowService(workflowService);

const loggerServices: ILoggerService[] = [];
if (loggerService) {
loggerServices.push(loggerService);
}
if (process.env.NODE_ENV !== 'production') {
loggerServices.push(new DevLogger());
}
InitLoggerService(loggerServices);

if (gatewayService) {
InitGatewayService(gatewayService);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import LegacyManagedIdentityDropdown from './formInputs/legacyManagedIdentityPic
import LegacyMultiAuth, { LegacyMultiAuthOptions } from './formInputs/legacyMultiAuth';
import type { ConnectionParameterProps } from './formInputs/universalConnectionParameter';
import { UniversalConnectionParameter } from './formInputs/universalConnectionParameter';
import type { IDropdownOption } from '@fluentui/react';
import { css, type IDropdownOption } from '@fluentui/react';
import { Body1Strong, Button, Divider, MessageBar, MessageBarActions, MessageBarBody } from '@fluentui/react-components';
import {
ConnectionParameterEditorService,
Expand Down Expand Up @@ -47,10 +47,12 @@ import TenantPicker from './formInputs/tenantPicker';
type ParamType = ConnectionParameter | ConnectionParameterSetParameter;

export interface CreateConnectionProps {
classes?: Record<string, string>;
nodeIds?: string[];
iconUri?: string;
connector: Connector;
connectionParameterSets?: ConnectionParameterSets;
description?: string;
identity?: ManagedIdentity;
isLoading?: boolean;
createConnectionCallback?: (
Expand Down Expand Up @@ -78,11 +80,13 @@ export interface CreateConnectionProps {

export const CreateConnection = (props: CreateConnectionProps) => {
const {
classes,
nodeIds = [],
showActionBar = true,
iconUri = '',
connector,
connectionParameterSets: _connectionParameterSets,
description,
identity,
isLoading = false,
createConnectionCallback,
Expand Down Expand Up @@ -573,13 +577,17 @@ export const CreateConnection = (props: CreateConnectionProps) => {
// RENDER

return (
<div className="msla-edit-connection-container">
<div className={classes?.['root'] ? css('msla-edit-connection-container', classes?.['root']) : 'msla-edit-connection-container'}>
{showActionBar ? <ActionList nodeIds={nodeIds} iconUri={iconUri} /> : null}
<Divider />
{showActionBar ? <Divider /> : null}

<Body1Strong>{componentDescription}</Body1Strong>
<Body1Strong>{description ?? componentDescription}</Body1Strong>

<div className="msla-create-connection-container">
<div
className={
classes?.['content'] ? css('msla-create-connection-container', classes?.['content']) : 'msla-create-connection-container'
}
>
{/* Error Bar */}
{errorMessage && (
<MessageBar intent={'error'} style={{ width: '100%' }}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,19 +108,24 @@ export interface CreatedConnectionPayload {
}

export const CreateConnectionInternal = (props: {
classes?: Record<string, string>;
connectorId: string;
operationType: string;
existingReferences: string[];
hideCancelButton: boolean;
showActionBar: boolean;
updateConnectionInState: (payload: CreatedConnectionPayload) => void;
onConnectionCreated: (connection: Connection) => void;
onConnectionCancelled?: () => void;
description?: string;
nodeIds?: string[];
assistedConnectionProps?: AssistedConnectionProps;
connectionMetadata?: ConnectionMetadata;
}) => {
const {
classes,
connectorId,
description,
operationType,
assistedConnectionProps,
existingReferences,
Expand All @@ -130,6 +135,7 @@ export const CreateConnectionInternal = (props: {
showActionBar,
updateConnectionInState,
onConnectionCreated,
onConnectionCancelled,
} = props;
const dispatch = useDispatch<AppDispatch>();

Expand Down Expand Up @@ -318,7 +324,10 @@ export const CreateConnectionInternal = (props: {

const cancelCallback = useCallback(() => {
dispatch(setIsCreatingConnection(false));
}, [dispatch]);
if (onConnectionCancelled) {
onConnectionCancelled();
}
}, [dispatch, onConnectionCancelled]);

const loadingText = intl.formatMessage({
defaultMessage: 'Loading connection data...',
Expand All @@ -339,12 +348,14 @@ export const CreateConnectionInternal = (props: {
nodeIds={nodeIds}
iconUri={iconUri}
showActionBar={showActionBar}
classes={classes}
connector={connector}
connectionParameterSets={getSupportedParameterSets(
connector.properties.connectionParameterSets,
operationType,
connector.properties.capabilities
)}
description={description}
identity={identity}
createConnectionCallback={createConnectionCallback}
isLoading={isCreating}
Expand Down
Loading

0 comments on commit ea3d271

Please sign in to comment.