Skip to content

Commit

Permalink
fix(Designer): Fixed issue where connection references would sometime…
Browse files Browse the repository at this point in the history
…s overlap (#4290)

* Fixed issue where connection references would sometimes overlap

* Undefined catch

* Fixed issue

* Another fix

* Fixed small ui issue
  • Loading branch information
rllyy97 committed Mar 1, 2024
1 parent e61a878 commit 01e8768
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 45 deletions.
100 changes: 61 additions & 39 deletions apps/designer-standalone/src/app/AzureLogicAppsDesigner/laDesigner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -182,20 +182,24 @@ const DesignerEditor = () => {
definition,
};

const referencesToAdd = { ...(connectionsData?.managedApiConnections ?? {}) };
if (Object.keys(connectionReferences ?? {}).length) {
const newManagedApiConnections = { ...(connectionsData?.managedApiConnections ?? {}) };
const newServiceProviderConnections: Record<string, any> = {};

const referenceKeys = Object.keys(connectionReferences ?? {});
if (referenceKeys.length) {
await Promise.all(
Object.keys(connectionReferences).map(async (referenceKey) => {
referenceKeys.map(async (referenceKey) => {
const reference = connectionReferences[referenceKey];
if (isArmResourceId(reference.connection.id) && !referencesToAdd[referenceKey]) {
if (isArmResourceId(reference?.connection?.id) && !newManagedApiConnections[referenceKey]) {
// Managed API Connection
const {
api: { id: apiId },
connection: { id: connectionId },
connectionProperties,
} = reference;
const connection = await getConnectionStandard(connectionId);
const userIdentity = connectionProperties?.authentication?.identity;
referencesToAdd[referenceKey] = {
const newConnectionObj = {
api: { id: apiId },
connection: { id: connectionId },
authentication: {
Expand All @@ -205,10 +209,22 @@ const DesignerEditor = () => {
connectionRuntimeUrl: connection?.properties?.connectionRuntimeUrl ?? '',
connectionProperties,
};
newManagedApiConnections[referenceKey] = newConnectionObj;
} else if (reference?.connection?.id.startsWith('/serviceProviders/')) {
// Service Provider Connection
const connectionKey = reference.connection.id.split('/').splice(-1)[0];
// We can't apply this directly in case there is a temporary key overlap
// We need to move the data out to a new object, delete the old data, then apply the new data at the end
newServiceProviderConnections[referenceKey] = connectionsData?.serviceProviderConnections?.[connectionKey];
delete connectionsData?.serviceProviderConnections?.[connectionKey];
}
})
);
(connectionsData as ConnectionsData).managedApiConnections = referencesToAdd;
(connectionsData as ConnectionsData).managedApiConnections = newManagedApiConnections;
(connectionsData as ConnectionsData).serviceProviderConnections = {
...connectionsData?.serviceProviderConnections,
...newServiceProviderConnections,
};
}

const connectionsToUpdate = getConnectionsToUpdate(originalConnectionsData, connectionsData ?? {});
Expand Down Expand Up @@ -556,57 +572,63 @@ const addOrUpdateAppSettings = (settings: Record<string, string>, originalSettin
return originalSettings;
};

const hasNewKeys = (original: Record<string, any> = {}, updated: Record<string, any> = {}) => {
return !Object.keys(updated).some((key) => !Object.keys(original).includes(key));
};

const getConnectionsToUpdate = (
originalConnectionsJson: ConnectionsData,
connectionsJson: ConnectionsData
): ConnectionsData | undefined => {
const originalKeys = Object.keys({
...(originalConnectionsJson.functionConnections ?? {}),
...(originalConnectionsJson.apiManagementConnections ?? {}),
...(originalConnectionsJson.managedApiConnections ?? {}),
...(originalConnectionsJson.serviceProviderConnections ?? {}),
});

const updatedKeys = Object.keys({
...(connectionsJson.functionConnections ?? {}),
...(connectionsJson.apiManagementConnections ?? {}),
...(connectionsJson.managedApiConnections ?? {}),
...(connectionsJson.serviceProviderConnections ?? {}),
});
const hasNewFunctionKeys = hasNewKeys(originalConnectionsJson.functionConnections, connectionsJson.functionConnections);
const hasNewApimKeys = hasNewKeys(originalConnectionsJson.apiManagementConnections, connectionsJson.apiManagementConnections);
const hasNewManagedApiKeys = hasNewKeys(originalConnectionsJson.managedApiConnections, connectionsJson.managedApiConnections);
const hasNewServiceProviderKeys = hasNewKeys(
originalConnectionsJson.serviceProviderConnections,
connectionsJson.serviceProviderConnections
);

// NOTE: We don't edit connections from the workflow, so existing connections should not be changed. If no new connections are added, there was no change.
if (!updatedKeys.some((conn) => !originalKeys.includes(conn))) {
if (!hasNewFunctionKeys && !hasNewApimKeys && !hasNewManagedApiKeys && !hasNewServiceProviderKeys) {
return undefined;
}

const connectionsToUpdate = { ...connectionsJson };
for (const functionConnectionName of Object.keys(connectionsJson.functionConnections ?? {})) {
if (originalConnectionsJson.functionConnections?.[functionConnectionName]) {
(connectionsToUpdate.functionConnections as any)[functionConnectionName] =
originalConnectionsJson.functionConnections[functionConnectionName];

if (hasNewFunctionKeys) {
for (const functionConnectionName of Object.keys(connectionsJson.functionConnections ?? {})) {
if (originalConnectionsJson.functionConnections?.[functionConnectionName]) {
(connectionsToUpdate.functionConnections as any)[functionConnectionName] =
originalConnectionsJson.functionConnections[functionConnectionName];
}
}
}

for (const apimConnectionName of Object.keys(connectionsJson.apiManagementConnections ?? {})) {
if (originalConnectionsJson.apiManagementConnections?.[apimConnectionName]) {
(connectionsToUpdate.apiManagementConnections as any)[apimConnectionName] =
originalConnectionsJson.apiManagementConnections[apimConnectionName];
if (hasNewApimKeys) {
for (const apimConnectionName of Object.keys(connectionsJson.apiManagementConnections ?? {})) {
if (originalConnectionsJson.apiManagementConnections?.[apimConnectionName]) {
(connectionsToUpdate.apiManagementConnections as any)[apimConnectionName] =
originalConnectionsJson.apiManagementConnections[apimConnectionName];
}
}
}

for (const managedApiConnectionName of Object.keys(connectionsJson.managedApiConnections ?? {})) {
if (originalConnectionsJson.managedApiConnections?.[managedApiConnectionName]) {
// eslint-disable-next-line no-param-reassign
(connectionsJson.managedApiConnections as any)[managedApiConnectionName] =
originalConnectionsJson.managedApiConnections[managedApiConnectionName];
if (hasNewManagedApiKeys) {
for (const managedApiConnectionName of Object.keys(connectionsJson.managedApiConnections ?? {})) {
if (originalConnectionsJson.managedApiConnections?.[managedApiConnectionName]) {
// eslint-disable-next-line no-param-reassign
(connectionsJson.managedApiConnections as any)[managedApiConnectionName] =
originalConnectionsJson.managedApiConnections[managedApiConnectionName];
}
}
}

for (const serviceProviderConnectionName of Object.keys(connectionsJson.serviceProviderConnections ?? {})) {
if (originalConnectionsJson.serviceProviderConnections?.[serviceProviderConnectionName]) {
// eslint-disable-next-line no-param-reassign
(connectionsJson.serviceProviderConnections as any)[serviceProviderConnectionName] =
originalConnectionsJson.serviceProviderConnections[serviceProviderConnectionName];
if (hasNewServiceProviderKeys) {
for (const serviceProviderConnectionName of Object.keys(connectionsJson.serviceProviderConnections ?? {})) {
if (originalConnectionsJson.serviceProviderConnections?.[serviceProviderConnectionName]) {
// eslint-disable-next-line no-param-reassign
(connectionsJson.serviceProviderConnections as any)[serviceProviderConnectionName] =
originalConnectionsJson.serviceProviderConnections[serviceProviderConnectionName];
}
}
}

Expand Down
4 changes: 2 additions & 2 deletions libs/designer/src/lib/core/queries/connections.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,10 +88,10 @@ export const getConnection = async (connectionId: string, connectorId: string, f
return !connection && fetchResourceIfNeeded ? getConnectionFromResource(connectionId) : connection;
};

export const getUniqueConnectionName = async (connectorId: string): Promise<string> => {
export const getUniqueConnectionName = async (connectorId: string, existingKeys: string[] = []): Promise<string> => {
const connectionNames = (await getConnectionsForConnector(connectorId)).map((connection) => connection.name);
const connectorName = connectorId.split('/').at(-1);
return ConnectionService().getUniqueConnectionName(connectorId, connectionNames, connectorName as string);
return ConnectionService().getUniqueConnectionName(connectorId, [...connectionNames, ...existingKeys], connectorName as string);
};

export const useConnectionResource = (connectionId: string) => {
Expand Down
2 changes: 1 addition & 1 deletion libs/designer/src/lib/ui/Controls.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ const CustomControls = () => {
return (
<Controls showInteractive={false}>
<ControlButton id={searchId} aria-label={searchAria} title={searchAria} onClick={searchToggleClick}>
<Icon iconName={'Search'} styles={iconStyles} />
<Icon iconName={'Search'} />
</ControlButton>
<ControlButton aria-label={minimapAria} title={minimapAria} onClick={minimapToggleClick}>
<Icon iconName={'Nav2DMapView'} styles={iconStyles} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ export const CreateConnectionWrapper = () => {
const availableGateways = useMemo(() => gatewaysQuery.data, [gatewaysQuery]);
const gatewayServiceConfig = useGatewayServiceConfig();

const existingReferences = useSelector((state: RootState) => Object.keys(state.connections.connectionReferences));

const identity = WorkflowService().getAppIdentity?.() as ManagedIdentity;

const [isLoading, setIsLoading] = useState(false);
Expand Down Expand Up @@ -200,7 +202,7 @@ export const CreateConnectionWrapper = () => {

let connection, err;

const newName = await getUniqueConnectionName(connector.id);
const newName = await getUniqueConnectionName(connector.id, existingReferences);
if (isOAuthConnection) {
await ConnectionService()
.createAndAuthorizeOAuthConnection(newName, connector?.id ?? '', connectionInfo, parametersMetadata)
Expand Down Expand Up @@ -245,6 +247,7 @@ export const CreateConnectionWrapper = () => {
closeConnectionsFlow,
nodeIds,
applyNewConnection,
existingReferences,
]
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -202,11 +202,11 @@ function _isConnectionParameterHidden(connectionParameter: ConnectionParameter):
}

export const getUniqueName = (keys: string[], prefix: string): { name: string; index: number } => {
const set = new Set(keys.map((name) => name.split('::')[0]));
const set = new Set(keys.map((name) => name.split('::')[0].toLowerCase()));

let index = 0;
let name = prefix;
while (set.has(name)) {
while (set.has(name.toLowerCase())) {
name = `${prefix}-${++index}`;
}

Expand Down

0 comments on commit 01e8768

Please sign in to comment.