Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(vscode): Introduce .NET 8 to custom code (#4953)
Browse files Browse the repository at this point in the history
* feat(vscode): Introduce .NET 8 to custom code and extension dependencies (#4947)

* Add validation for multiple dotnet versions

* Add comments and return in the await

* Fix command execution

* Add version to switch to nuget based

* Add code to differentiate .net7 and netfx

* Add .NET8 files and creation file decision

* Update to function

* Add selfContained

* Remove .NET 6

* Move target framework step after workspace type step

* Remove nuget config file

* Upgrade packages

* Add new dotnetMulti prop for manifest

* Update pack command to build all projects for extension

* Add comments and remove extra wizard context property

* fix(vscode): Update Workflows Webjobs sdk version (#4952)

Update Workflows Webjobs sdk version
ccastrotrejo authored Jun 10, 2024
1 parent b884d6f commit 74a5363
Showing 27 changed files with 450 additions and 191 deletions.
Original file line number Diff line number Diff line change
@@ -15,7 +15,7 @@ import { InitDataMapperApiService, defaultDataMapperApiServiceOptions, getFuncti
import { Theme as ThemeType } from '@microsoft/logic-apps-shared';
import { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { IFileSysTreeItem } from '@microsoft/logic-apps-data-mapper-v2/src/models/Tree';
import type { IFileSysTreeItem } from '@microsoft/logic-apps-data-mapper-v2/src/models/Tree';

const mockFileItems: IFileSysTreeItem[] = [
{
@@ -41,7 +41,6 @@ const mockFileItems: IFileSysTreeItem[] = [
},
];


const customXsltPath = ['folder/file.xslt', 'file2.xslt'];

export const DataMapperStandaloneDesignerV2 = () => {
@@ -118,7 +117,11 @@ export const DataMapperStandaloneDesignerV2 = () => {
fetchedFunctions={fetchedFunctions}
theme={theme}
>
<DataMapperDesignerV2 saveMapDefinitionCall={saveMapDefinitionCall} saveXsltCall={saveXsltCall} readCurrentSchemaOptions={() => null}/>
<DataMapperDesignerV2
saveMapDefinitionCall={saveMapDefinitionCall}
saveXsltCall={saveXsltCall}
readCurrentSchemaOptions={() => null}
/>
</DataMapDataProviderV2>
</DataMapperDesignerProviderV2>
</div>
Original file line number Diff line number Diff line change
@@ -89,12 +89,13 @@ export async function validateAndInstallBinaries(context: IActionContext) {
context.telemetry.properties.lastStep = 'validateDotNetIsLatest';
await runWithDurationTelemetry(context, 'azureLogicAppsStandard.validateDotNetIsLatest', async () => {
progress.report({ increment: 20, message: '.NET SDK' });
const dotnetDependencies = dependenciesVersions?.dotnetMulti ?? dependenciesVersions?.dotnet;
await timeout(
validateDotNetIsLatest,
'.NET SDK',
dependencyTimeout,
'https://dotnet.microsoft.com/en-us/download/dotnet/6.0',
dependenciesVersions?.dotnet
'https://dotnet.microsoft.com/en-us/download/dotnet',
dotnetDependencies
);
await setDotNetCommand(context);
});
Original file line number Diff line number Diff line change
@@ -5,7 +5,7 @@
import { FunctionConfigFile } from './FunctionConfigFile';
import { AzureWizardPromptStep } from '@microsoft/vscode-azext-utils';
import type { IProjectWizardContext } from '@microsoft/vscode-extension-logic-apps';
import { ProjectType } from '@microsoft/vscode-extension-logic-apps';
import { TargetFramework, ProjectType } from '@microsoft/vscode-extension-logic-apps';
import * as fs from 'fs-extra';
import * as path from 'path';

@@ -17,12 +17,14 @@ export class InvokeFunctionProjectSetup extends AzureWizardPromptStep<IProjectWi
public hideStepCount = true;

private csFileName = {
[ProjectType.customCode]: 'FunctionsFile',
[TargetFramework.NetFx]: 'FunctionsFileNetFx',
[TargetFramework.Net8]: 'FunctionsFileNet8',
[ProjectType.rulesEngine]: 'RulesFunctionsFile',
};

private templateFileName = {
[ProjectType.customCode]: 'FunctionsProj',
[TargetFramework.NetFx]: 'FunctionsProjNetFx',
[TargetFramework.Net8]: 'FunctionsProjNet8',
[ProjectType.rulesEngine]: 'RulesFunctionsProj',
};

@@ -39,6 +41,7 @@ export class InvokeFunctionProjectSetup extends AzureWizardPromptStep<IProjectWi
// Set the methodName and namespaceName properties from the context wizard
const methodName = context.methodName;
const namespace = context.namespaceName;
const targetFramework = context.targetFramework;

// Define the functions folder path using the context property of the wizard
const functionFolderPath = context.functionFolderPath;
@@ -47,13 +50,13 @@ export class InvokeFunctionProjectSetup extends AzureWizardPromptStep<IProjectWi
const projectType = context.projectType;

// Create the .cs file inside the functions folder
await this.createCsFile(functionFolderPath, methodName, namespace, projectType);
await this.createCsFile(functionFolderPath, methodName, namespace, projectType, targetFramework);

// Create the .cs files inside the functions folders for rule code projects
await this.createRulesFiles(functionFolderPath, projectType);

// Create the .csproj file inside the functions folder
await this.createCsprojFile(functionFolderPath, methodName, projectType);
await this.createCsprojFile(functionFolderPath, methodName, projectType, targetFramework);

// Generate the Visual Studio Code configuration files in the specified folder.
const createConfigFiles = new FunctionConfigFile();
@@ -74,14 +77,22 @@ export class InvokeFunctionProjectSetup extends AzureWizardPromptStep<IProjectWi
* @param methodName - The name of the method.
* @param namespace - The name of the namespace.
* @param projectType - The workspace projet type.
* @param targetFramework - The target framework.
*/
private async createCsFile(functionFolderPath: string, methodName: string, namespace: string, projectType: ProjectType): Promise<void> {
const templatePath = path.join(__dirname, 'assets', this.templateFolderName[projectType], this.csFileName[projectType]);
private async createCsFile(
functionFolderPath: string,
methodName: string,
namespace: string,
projectType: ProjectType,
targetFramework: TargetFramework
): Promise<void> {
const templateFile =
projectType === ProjectType.rulesEngine ? this.csFileName[ProjectType.rulesEngine] : this.csFileName[targetFramework];
const templatePath = path.join(__dirname, 'assets', this.templateFolderName[projectType], templateFile);
const templateContent = await fs.readFile(templatePath, 'utf-8');

const csFilePath = path.join(functionFolderPath, `${methodName}.cs`);
const csFileContent = templateContent.replace(/<%= methodName %>/g, methodName).replace(/<%= namespace %>/g, namespace);

await fs.writeFile(csFilePath, csFileContent);
}

@@ -90,9 +101,17 @@ export class InvokeFunctionProjectSetup extends AzureWizardPromptStep<IProjectWi
* @param functionFolderPath - The path to the folder where the .csproj file will be created.
* @param methodName - The name of the Azure Function.
* @param projectType - The workspace projet type.
* @param targetFramework - The target framework.
*/
private async createCsprojFile(functionFolderPath: string, methodName: string, projectType: ProjectType): Promise<void> {
const templatePath = path.join(__dirname, 'assets', this.templateFolderName[projectType], this.templateFileName[projectType]);
private async createCsprojFile(
functionFolderPath: string,
methodName: string,
projectType: ProjectType,
targetFramework: TargetFramework
): Promise<void> {
const templateFile =
projectType === ProjectType.rulesEngine ? this.templateFileName[ProjectType.rulesEngine] : this.templateFileName[targetFramework];
const templatePath = path.join(__dirname, 'assets', this.templateFolderName[projectType], templateFile);
const templateContent = await fs.readFile(templatePath, 'utf-8');

const csprojFilePath = path.join(functionFolderPath, `${methodName}.csproj`);
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { type IProjectWizardContext, TargetFramework, ProjectType } from '@microsoft/vscode-extension-logic-apps';
import { localize } from '../../../../../localize';
import { AzureWizardPromptStep, type IAzureQuickPickItem } from '@microsoft/vscode-azext-utils';
import { Platform } from '../../../../../constants';

/**
* Represents a step in the project creation wizard for selecting the target framework.
*/
export class TargetFrameworkStep extends AzureWizardPromptStep<IProjectWizardContext> {
public hideStepCount = true;

/**
* Prompts the user to select a target framework.
* @param {IProjectWizardContext} context - The project wizard context.
*/
public async prompt(context: IProjectWizardContext): Promise<void> {
const placeHolder: string = localize('selectTargetFramework', 'Select a target framework.');
const picks: IAzureQuickPickItem<TargetFramework>[] = [{ label: localize('Net8', '.NET 8'), data: TargetFramework.Net8 }];
if (process.platform === Platform.windows) {
picks.unshift({ label: localize('NetFx', '.NET Framework'), data: TargetFramework.NetFx });
}
context.targetFramework = (await context.ui.showQuickPick(picks, { placeHolder })).data;
}

/**
* Determines whether this step should be prompted based on the project wizard context.
* @param {IProjectWizardContext} context - The project wizard context.
* @returns True if this step should be prompted, false otherwise.
*/
public shouldPrompt(context: IProjectWizardContext): boolean {
return context.projectType === ProjectType.customCode;
}
}
Original file line number Diff line number Diff line change
@@ -34,6 +34,7 @@ import type {
import * as fse from 'fs-extra';
import * as path from 'path';
import { window } from 'vscode';
import { TargetFrameworkStep } from './createCodeProjectSteps/createFunction/TargetFrameworkStep';

export async function createNewCodeProjectFromCommand(
context: IActionContext,
@@ -86,6 +87,7 @@ export async function createNewCodeProjectInternal(context: IActionContext, opti
new FolderListStep(),
new setWorkspaceName(),
new SetLogicAppType(),
new TargetFrameworkStep(),
new SetLogicAppName(),
new NewCodeProjectTypeStep(options.templateId, options.functionSettings),
new OpenBehaviorStep(),
Original file line number Diff line number Diff line change
@@ -2,6 +2,7 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { isNullOrUndefined } from '@microsoft/logic-apps-shared';
import { dotnetDependencyName } from '../../../constants';
import { localize } from '../../../localize';
import { binariesExist, getLatestDotNetVersion } from '../../utils/binaries';
@@ -17,44 +18,52 @@ export async function validateDotNetIsLatest(majorVersion?: string): Promise<voi
await callWithTelemetryAndErrorHandling('azureLogicAppsStandard.validateDotNetIsLatest', async (context: IActionContext) => {
context.errorHandling.suppressDisplay = true;
context.telemetry.properties.isActivationEvent = 'true';
const majorVersions = majorVersion.split(',');

const showDotNetWarningKey = 'showDotNetWarning';
const showDotNetWarning = !!getWorkspaceSetting<boolean>(showDotNetWarningKey);
const binaries = binariesExist(dotnetDependencyName);
context.telemetry.properties.binariesExist = `${binaries}`;

if (!binaries) {
await installDotNet(context, majorVersion);
context.telemetry.properties.binaryCommand = `${getDotNetCommand()}`;
for (const version of majorVersions) {
await installDotNet(context, version);
}
} else if (showDotNetWarning) {
context.telemetry.properties.binaryCommand = `${getDotNetCommand()}`;
const localVersion: string | null = await getLocalDotNetVersionFromBinaries();
context.telemetry.properties.localVersion = localVersion;
const newestVersion: string | undefined = await getLatestDotNetVersion(context, majorVersion);
if (semver.major(newestVersion) === semver.major(localVersion) && semver.gt(newestVersion, localVersion)) {
context.telemetry.properties.outOfDateDotNet = 'true';
const message: string = localize(
'outdatedDotNetRuntime',
'Update your local .NET SDK version ({0}) to the latest version ({1}) for the best experience.',
localVersion,
newestVersion
);
const update: MessageItem = { title: 'Update' };
let result: MessageItem;
do {
result =
newestVersion !== undefined
? await context.ui.showWarningMessage(message, update, DialogResponses.learnMore, DialogResponses.dontWarnAgain)
: await context.ui.showWarningMessage(message, DialogResponses.learnMore, DialogResponses.dontWarnAgain);
if (result === DialogResponses.learnMore) {
await openUrl('https://dotnet.microsoft.com/en-us/download/dotnet/6.0');
} else if (result === update) {
await installDotNet(context, majorVersion);
} else if (result === DialogResponses.dontWarnAgain) {
await updateGlobalSetting(showDotNetWarningKey, false);
for (const version of majorVersions) {
const localVersion: string | null = await getLocalDotNetVersionFromBinaries(version);
if (isNullOrUndefined(localVersion)) {
await installDotNet(context, version);
} else {
context.telemetry.properties.localVersion = localVersion;
const newestVersion: string | undefined = await getLatestDotNetVersion(context, version);
if (semver.major(newestVersion) === semver.major(localVersion) && semver.gt(newestVersion, localVersion)) {
context.telemetry.properties.outOfDateDotNet = 'true';
const message: string = localize(
'outdatedDotNetRuntime',
'Update your local .NET SDK version ({0}) to the latest version ({1}) for the best experience.',
localVersion,
newestVersion
);
const update: MessageItem = { title: 'Update' };
let result: MessageItem;
do {
result =
newestVersion !== undefined
? await context.ui.showWarningMessage(message, update, DialogResponses.learnMore, DialogResponses.dontWarnAgain)
: await context.ui.showWarningMessage(message, DialogResponses.learnMore, DialogResponses.dontWarnAgain);
if (result === DialogResponses.learnMore) {
await openUrl(`https://dotnet.microsoft.com/en-us/download/dotnet/${version}`);
} else if (result === update) {
await installDotNet(context, version);
} else if (result === DialogResponses.dontWarnAgain) {
await updateGlobalSetting(showDotNetWarningKey, false);
}
} while (result === DialogResponses.learnMore);
}
} while (result === DialogResponses.learnMore);
}
}
}
context.telemetry.properties.binaryCommand = `${getDotNetCommand()}`;
});
}
Original file line number Diff line number Diff line change
@@ -112,7 +112,7 @@ async function setupWizardScriptContext(context: IActionContext, projectRoot: vs
scriptContext.projectPath = parentDirPath;
return scriptContext;
} catch (error) {
const errorMessage = localize('executeAzureWizardError', 'Error in setupWizardScriptContext', error.message ?? error);
const errorMessage = localize('setupWizardScriptContextError', 'Error in setupWizardScriptContext: {0}', error.message ?? error);
ext.outputChannel.appendLog(errorMessage);
throw new Error(errorMessage);
}
Original file line number Diff line number Diff line change
@@ -117,7 +117,7 @@ export async function switchToDotnetProject(context: IProjectWizardContext, targ
const projTemplateKey = await getTemplateKeyFromProjFile(context, projectPath, version, ProjectLanguage.CSharp);
const dotnetVersion = await getFramework(context, projectPath);
const useBinaries = useBinariesDependencies();
const dotnetLocalVersion = useBinaries ? await getLocalDotNetVersionFromBinaries() : '';
const dotnetLocalVersion = useBinaries ? await getLocalDotNetVersionFromBinaries('6') : '';

await deleteBundleProjectFiles(target);
await renameBundleProjectFiles(target);
29 changes: 24 additions & 5 deletions apps/vs-code-designer/src/app/utils/binaries.ts
Original file line number Diff line number Diff line change
@@ -34,6 +34,7 @@ import * as vscode from 'vscode';

import AdmZip = require('adm-zip');
import request = require('request');
import { isNullOrUndefined } from '@microsoft/logic-apps-shared';

/**
* Download and Extracts dependency zip.
@@ -154,20 +155,32 @@ export async function getLatestFunctionCoreToolsVersion(context: IActionContext,
return DependencyVersion.funcCoreTools;
}

/**
* Retrieves the latest version of .NET SDK.
* @param {IActionContext} context - The action context.
* @param {string} majorVersion - The major version of .NET SDK to retrieve. (optional)
* @returns A promise that resolves to the latest version of .NET SDK.
* @throws An error if there is an issue retrieving the latest .NET SDK version.
*/
export async function getLatestDotNetVersion(context: IActionContext, majorVersion?: string): Promise<string> {
context.telemetry.properties.dotNetMajorVersion = majorVersion;

if (majorVersion) {
await readJsonFromUrl('https://api.github.com/repos/dotnet/sdk/releases')
return await readJsonFromUrl('https://api.github.com/repos/dotnet/sdk/releases')
.then((response: IGitHubReleaseInfo[]) => {
context.telemetry.properties.latestVersionSource = 'github';
response.forEach((releaseInfo: IGitHubReleaseInfo) => {
let latestVersion: string | null;
for (const releaseInfo of response) {
const releaseVersion: string | null = semver.valid(semver.coerce(releaseInfo.tag_name));
context.telemetry.properties.latestGithubVersion = releaseInfo.tag_name;
if (checkMajorVersion(releaseVersion, majorVersion)) {
return releaseVersion;
if (
checkMajorVersion(releaseVersion, majorVersion) &&
(isNullOrUndefined(latestVersion) || semver.gt(releaseVersion, latestVersion))
) {
latestVersion = releaseVersion;
}
});
}
return latestVersion;
})
.catch((error) => {
throw Error(localize('errorNewestDotNetVersion', `Error getting latest .NET SDK version: ${error}`));
@@ -299,6 +312,12 @@ async function extractDependency(dependencyFilePath: string, targetFolder: strin
}
}

/**
* Checks if the major version of a given version string matches the specified major version.
* @param {string} version - The version string to check.
* @param {string} majorVersion - The major version to compare against.
* @returns A boolean indicating whether the major version matches.
*/
function checkMajorVersion(version: string, majorVersion: string): boolean {
return semver.major(version) === Number(majorVersion);
}
35 changes: 19 additions & 16 deletions apps/vs-code-designer/src/app/utils/dotnet/dotnet.ts
Original file line number Diff line number Diff line change
@@ -2,6 +2,7 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { isNullOrUndefined } from '@microsoft/logic-apps-shared';
import {
DotnetVersion,
Platform,
@@ -188,33 +189,35 @@ export function getTemplateKeyFromFeedEntry(runtimeInfo: IWorkerRuntime): string
return getProjectTemplateKey(runtimeInfo.targetFramework, isIsolated);
}

export async function getLocalDotNetVersionFromBinaries(): Promise<string> {
export async function getLocalDotNetVersionFromBinaries(majorVersion?: string): Promise<string> {
const binariesLocation = getGlobalSetting<string>(autoRuntimeDependenciesPathSettingKey);
const dotNetBinariesPath = path.join(binariesLocation, dotnetDependencyName);
const sdkVersionFolder = path.join(dotNetBinariesPath, 'sdk');
const sdkVersionFolder = path.join(binariesLocation, dotnetDependencyName, 'sdk');

if (isNullOrUndefined(majorVersion)) {
try {
const output: string = await executeCommand(ext.outputChannel, undefined, getDotNetCommand(), '--version');
const version: string | null = semver.clean(output);
if (version) {
return version;
}
} catch (error) {
return null;
}
}

// First try to get sdk from Binary installation folder
const files = fs.existsSync(sdkVersionFolder) ? fs.readdirSync(sdkVersionFolder, { withFileTypes: true }) : null;
if (Array.isArray(files)) {
for (const file of files) {
if (file.isDirectory()) {
const version = file.name;
await executeCommand(ext.outputChannel, undefined, 'echo', 'Local binary .NET SDK version', version);
return version;
if (semver.major(version).toString() === majorVersion) {
await executeCommand(ext.outputChannel, undefined, 'echo', 'Local binary .NET SDK version', version);
return version;
}
}
}
}

try {
const output: string = await executeCommand(ext.outputChannel, undefined, `${getDotNetCommand()}`, '--version');
const version: string | null = semver.clean(output);
if (version) {
return version;
}
} catch (error) {
return null;
}

return null;
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------

namespace <%= namespace %>
{
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.Azure.Functions.Extensions.Workflows;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Extensions.Logging;

/// <summary>
/// Represents the <%= methodName %> flow invoked function.
/// </summary>
public class <%= methodName %>
{
private readonly ILogger<<%= methodName %>> logger;

public <%= methodName %>(ILoggerFactory loggerFactory)
{
logger = loggerFactory.CreateLogger<<%= methodName %>>();
}

/// <summary>
/// Executes the logic app workflow.
/// </summary>
/// <param name="zipCode">The zip code.</param>
/// <param name="temperatureScale">The temperature scale (e.g., Celsius or Fahrenheit).</param>
[Function("<%= methodName %>")]
public Task<Weather> Run([WorkflowActionTrigger] int zipCode, string temperatureScale)
{
this.logger.LogInformation("Starting <%= methodName %> with Zip Code: " + zipCode + " and Scale: " + temperatureScale);

// Generate random temperature within a range based on the temperature scale
Random rnd = new Random();
var currentTemp = temperatureScale == "Celsius" ? rnd.Next(1, 30) : rnd.Next(40, 90);
var lowTemp = currentTemp - 10;
var highTemp = currentTemp + 10;

// Create a Weather object with the temperature information
var weather = new Weather()
{
ZipCode = zipCode,
CurrentWeather = $"The current weather is {currentTemp} {temperatureScale}",
DayLow = $"The low for the day is {lowTemp} {temperatureScale}",
DayHigh = $"The high for the day is {highTemp} {temperatureScale}"
};

return Task.FromResult(weather);
}

/// <summary>
/// Represents the weather information for <%= methodName %>.
/// </summary>
public class Weather
{
/// <summary>
/// Gets or sets the zip code.
/// </summary>
public int ZipCode { get; set; }

/// <summary>
/// Gets or sets the current weather.
/// </summary>
public string CurrentWeather { get; set; }

/// <summary>
/// Gets or sets the low temperature for the day.
/// </summary>
public string DayLow { get; set; }

/// <summary>
/// Gets or sets the high temperature for the day.
/// </summary>
public string DayHigh { get; set; }
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<IsPackable>false</IsPackable>
<TargetFramework>net8</TargetFramework>
<AzureFunctionsVersion>v4</AzureFunctionsVersion>
<OutputType>Library</OutputType>
<PlatformTarget>AnyCPU</PlatformTarget>
<!-- Please replace 'LogicAppFolder' with the name of your folder that contains your logic app project. -->
<LogicAppFolder>LogicApp</LogicAppFolder>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<SelfContained>false</SelfContained>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Abstractions" Version="1.3.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Sdk" Version="1.15.1" />
<PackageReference Include="Microsoft.Azure.Workflows.Webjobs.Sdk" Version="1.1.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="5.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="5.0.0" />
</ItemGroup>

<Target Name="Task" AfterTargets="Compile">
<ItemGroup>
<DirsToClean2 Include="..\$(LogicAppFolder)\lib\custom" />
</ItemGroup>
<RemoveDir Directories="@(DirsToClean2)" />
</Target>

<Target Name="CopyExtensionFiles" AfterTargets="ParameterizedFunctionJsonGeneratorNetCore">
<ItemGroup>
<CopyFiles Include="$(MSBuildProjectDirectory)\bin\$(Configuration)\net8\**\*.*" CopyToOutputDirectory="PreserveNewest" Exclude="$(MSBuildProjectDirectory)\bin\$(Configuration)\net8\*.*" />
<CopyFiles2 Include="$(MSBuildProjectDirectory)\bin\$(Configuration)\net8\*.*" />
</ItemGroup>
<Copy SourceFiles="@(CopyFiles)" DestinationFolder="..\$(LogicAppFolder)\lib\custom\%(RecursiveDir)" SkipUnchangedFiles="true" />
<Copy SourceFiles="@(CopyFiles2)" DestinationFolder="..\$(LogicAppFolder)\lib\custom\net8\" SkipUnchangedFiles="true" />
<ItemGroup>
<MoveFiles Include="..\$(LogicAppFolder)\lib\custom\bin\*.*" />
</ItemGroup>

<Move SourceFiles="@(MoveFiles)" DestinationFolder="..\$(LogicAppFolder)\lib\custom\net8" />
<ItemGroup>
<DirsToClean Include="..\$(LogicAppFolder)\lib\custom\bin" />
</ItemGroup>
<RemoveDir Directories="@(DirsToClean)" />
</Target>

<ItemGroup>
<Reference Include="Microsoft.CSharp" />
</ItemGroup>
<ItemGroup>
<Folder Include="bin\$(Configuration)\net8\" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -11,11 +11,11 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Azure.WebJobs.Core" Version="3.0.33" />
<PackageReference Include="Microsoft.Azure.Workflows.WebJobs.Sdk" Version="1.0.0" />
<PackageReference Include="Microsoft.Azure.WebJobs.Core" Version="3.0.39" />
<PackageReference Include="Microsoft.Azure.Workflows.WebJobs.Sdk" Version="1.1.0" />
<PackageReference Include="Microsoft.NET.Sdk.Functions" Version="4.2.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="2.1.1" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.1.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="2.1.1" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.1.1" />
</ItemGroup>

<Target Name="Task" AfterTargets="Compile">
Original file line number Diff line number Diff line change
@@ -12,7 +12,7 @@

<ItemGroup>
<PackageReference Include="Microsoft.Azure.WebJobs.Core" Version="3.0.39" />
<PackageReference Include="Microsoft.Azure.Workflows.WebJobs.Sdk" Version="1.0.1" />
<PackageReference Include="Microsoft.Azure.Workflows.WebJobs.Sdk" Version="1.1.0" />
<PackageReference Include="Microsoft.NET.Sdk.Functions" Version="4.2.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="2.1.1" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.1.1" />
2 changes: 1 addition & 1 deletion apps/vs-code-react/package.json
Original file line number Diff line number Diff line change
@@ -41,4 +41,4 @@
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0"
},
"type": "module"
}
}
2 changes: 1 addition & 1 deletion libs/data-mapper-v2/src/components/addSchema/styles.ts
Original file line number Diff line number Diff line change
@@ -51,5 +51,5 @@ export const useStyles = makeStyles({
},
rightDrawer: {
...shorthands.borderLeft('1px', 'solid', '#ddd'),
}
},
});
6 changes: 4 additions & 2 deletions libs/data-mapper-v2/src/components/common/DropdownTree.tsx
Original file line number Diff line number Diff line change
@@ -23,7 +23,7 @@ export const DropdownTree = ({ items, onItemSelect, onDropdownOpenClose }: Dropd
useEffect(() => {
// update items when the tree is closed and reopened
onDropdownOpenClose();
}, [showDropdownTree, onDropdownOpenClose])
}, [showDropdownTree, onDropdownOpenClose]);

const selectSchema = intl.formatMessage({
defaultMessage: 'Select schema',
@@ -49,7 +49,9 @@ export const DropdownTree = ({ items, onItemSelect, onDropdownOpenClose }: Dropd
}

if (item.type === 'directory') {
const children = item.children.map((child) => filterDropdownItem(child, value)).filter((child) => child !== undefined) as IFileSysTreeItem[];
const children = item.children
.map((child) => filterDropdownItem(child, value))
.filter((child) => child !== undefined) as IFileSysTreeItem[];

if (children.length === 0) {
return undefined;
4 changes: 2 additions & 2 deletions libs/data-mapper-v2/src/components/schemaView/styles.ts
Original file line number Diff line number Diff line change
@@ -10,5 +10,5 @@ export const useStyles = makeStyles({
searchBox: {
width: '85%',
alignSelf: 'center',
}
});
},
});
Original file line number Diff line number Diff line change
@@ -23,7 +23,7 @@ export const mapNodeParams = {
for: '$for',
if: '$if',
value: '$value',
backout: '../'
backout: '../',
};

export const reservedMapNodeParamsArray: string[] = [mapNodeParams.for, mapNodeParams.if, mapNodeParams.value];
6 changes: 5 additions & 1 deletion libs/data-mapper-v2/src/ui/DataMapperDesigner.tsx
Original file line number Diff line number Diff line change
@@ -25,7 +25,11 @@ interface DataMapperDesignerProps {
setIsMapStateDirty?: (isMapStateDirty: boolean) => void;
}

export const DataMapperDesigner = ({ readCurrentCustomXsltPathOptions, setIsMapStateDirty, readCurrentSchemaOptions }: DataMapperDesignerProps) => {
export const DataMapperDesigner = ({
readCurrentCustomXsltPathOptions,
setIsMapStateDirty,
readCurrentSchemaOptions,
}: DataMapperDesignerProps) => {
useStaticStyles();
const styles = useStyles();
const ref = useRef<HTMLDivElement | null>(null);
92 changes: 56 additions & 36 deletions libs/logic-apps-shared/src/intl/src/__test__/IntlProvider.spec.tsx
Original file line number Diff line number Diff line change
@@ -2,42 +2,62 @@ import { describe, test, expect } from 'vitest';
import { loadLocaleData } from '../IntlProvider';

describe('loadLocaleData', () => {
test('returns correct messages for each non english locale', async () => {
const locales = ['nl', 'pl', 'pt', 'pt-BR', 'zh-Hans', 'en-XA', 'ru', 'sv', 'tr', 'zh', 'fr', 'cs', 'de', 'es', 'hu', 'id', 'it', 'ja', 'ko'];
const englishMessages = await loadLocaleData('en-US');
for (const locale of locales) {
const messages = await loadLocaleData(locale);
expect(messages).toBeDefined();
expect(messages).not.toEqual(englishMessages);
// Add more specific checks here based on the expected structure of your messages
}
});
test('returns correct messages for english locale, same as default messages', async () => {
const messages = await loadLocaleData('en-US');
const defaultMessages = await loadLocaleData('');
expect(messages).toBeDefined();
expect(messages).toEqual(defaultMessages);
// Add more specific checks here based on the expected structure of your messages
});
test('returns correct messages for each non english locale', async () => {
const locales = [
'nl',
'pl',
'pt',
'pt-BR',
'zh-Hans',
'en-XA',
'ru',
'sv',
'tr',
'zh',
'fr',
'cs',
'de',
'es',
'hu',
'id',
'it',
'ja',
'ko',
];
const englishMessages = await loadLocaleData('en-US');
for (const locale of locales) {
const messages = await loadLocaleData(locale);
expect(messages).toBeDefined();
expect(messages).not.toEqual(englishMessages);
// Add more specific checks here based on the expected structure of your messages
}
});
test('returns correct messages for english locale, same as default messages', async () => {
const messages = await loadLocaleData('en-US');
const defaultMessages = await loadLocaleData('');
expect(messages).toBeDefined();
expect(messages).toEqual(defaultMessages);
// Add more specific checks here based on the expected structure of your messages
});

test('returns english messages for unsupported locale', async () => {
const messages = await loadLocaleData('unsupported-locale');
const englishMessages = await loadLocaleData('en-US');
expect(messages).toBeDefined();
expect(messages).toEqual(englishMessages);
// Add more specific checks here based on the expected structure of your default messages
});
test('returns english messages for unsupported locale', async () => {
const messages = await loadLocaleData('unsupported-locale');
const englishMessages = await loadLocaleData('en-US');
expect(messages).toBeDefined();
expect(messages).toEqual(englishMessages);
// Add more specific checks here based on the expected structure of your default messages
});

test('merges string overrides into messages', async () => {
const stringOverrides = {
g5A6Bn: 'Connector Type',
TRpSCQ: 'Action or Trigger',
'/VcZ9g': 'Test String Override',
};
const messages = await loadLocaleData('en', stringOverrides);
expect(messages).toBeDefined();
expect(messages.g5A6Bn).toEqual('Connector Type');
expect(messages.TRpSCQ).toEqual('Action or Trigger');
expect(messages['/VcZ9g']).toEqual('Test String Override');
});
test('merges string overrides into messages', async () => {
const stringOverrides = {
g5A6Bn: 'Connector Type',
TRpSCQ: 'Action or Trigger',
'/VcZ9g': 'Test String Override',
};
const messages = await loadLocaleData('en', stringOverrides);
expect(messages).toBeDefined();
expect(messages.g5A6Bn).toEqual('Connector Type');
expect(messages.TRpSCQ).toEqual('Action or Trigger');
expect(messages['/VcZ9g']).toEqual('Test String Override');
});
});
146 changes: 73 additions & 73 deletions libs/logic-apps-shared/src/intl/src/__test__/intl.spec.tsx
Original file line number Diff line number Diff line change
@@ -7,83 +7,83 @@ import { IntlProvider } from '../IntlProvider';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';

describe('IntlGlobalProvider', () => {
beforeEach(() => {
resetIntl();
});
test('renders children correctly', () => {
render(
<QueryClientProvider
client={
new QueryClient({
defaultOptions: {
queries: {
refetchInterval: false,
refetchIntervalInBackground: false,
refetchOnWindowFocus: false,
refetchOnReconnect: false,
refetchOnMount: false,
staleTime: 1000 * 60 * 60 * 24, // 24 hours
},
},
})
}
>
<IntlProvider locale="fr" defaultLocale="en" onError={() => { }}>
<div>Test Child</div>
</IntlProvider>
</QueryClientProvider>
);
expect(screen.getByText('Test Child')).toBeInTheDocument();
});
beforeEach(() => {
resetIntl();
});
test('renders children correctly', () => {
render(
<QueryClientProvider
client={
new QueryClient({
defaultOptions: {
queries: {
refetchInterval: false,
refetchIntervalInBackground: false,
refetchOnWindowFocus: false,
refetchOnReconnect: false,
refetchOnMount: false,
staleTime: 1000 * 60 * 60 * 24, // 24 hours
},
},
})
}
>
<IntlProvider locale="fr" defaultLocale="en" onError={() => {}}>
<div>Test Child</div>
</IntlProvider>
</QueryClientProvider>
);
expect(screen.getByText('Test Child')).toBeInTheDocument();
});
});

describe('getIntl', () => {
beforeEach(() => {
resetIntl();
});
test('returns default IntlShape when INTL is undefined', () => {
const intl = getIntl();
expect(intl.locale).toBe('en');
expect(intl.messages).toEqual({});
expect(intl.defaultLocale).toBe('en');
});
beforeEach(() => {
resetIntl();
});
test('returns default IntlShape when INTL is undefined', () => {
const intl = getIntl();
expect(intl.locale).toBe('en');
expect(intl.messages).toEqual({});
expect(intl.defaultLocale).toBe('en');
});

test('returns defined INTL when INTL is defined', async () => {
render(
<QueryClientProvider
client={
new QueryClient({
defaultOptions: {
queries: {
refetchInterval: false,
refetchIntervalInBackground: false,
refetchOnWindowFocus: false,
refetchOnReconnect: false,
refetchOnMount: false,
staleTime: 1000 * 60 * 60 * 24, // 24 hours
},
},
})
}
>
<IntlProvider locale="fr" defaultLocale="en" onError={() => { }} />
</QueryClientProvider>
);
test('returns defined INTL when INTL is defined', async () => {
render(
<QueryClientProvider
client={
new QueryClient({
defaultOptions: {
queries: {
refetchInterval: false,
refetchIntervalInBackground: false,
refetchOnWindowFocus: false,
refetchOnReconnect: false,
refetchOnMount: false,
staleTime: 1000 * 60 * 60 * 24, // 24 hours
},
},
})
}
>
<IntlProvider locale="fr" defaultLocale="en" onError={() => {}} />
</QueryClientProvider>
);

await waitFor(() => {
const intl = getIntl();
expect(intl.locale).toBe('fr');
expect(intl.defaultLocale).toBe('en');
expect(intl.messages).toEqual(
expect.objectContaining({
'00xlpa': [
{
type: 0,
value: 'Partagé',
},
],
})
);
});
await waitFor(() => {
const intl = getIntl();
expect(intl.locale).toBe('fr');
expect(intl.defaultLocale).toBe('en');
expect(intl.messages).toEqual(
expect.objectContaining({
'00xlpa': [
{
type: 0,
value: 'Partagé',
},
],
})
);
});
});
});
2 changes: 1 addition & 1 deletion libs/logic-apps-shared/src/intl/src/intl.tsx
Original file line number Diff line number Diff line change
@@ -7,7 +7,7 @@ interface IntlGlobalProviderProps {
}
const cache = createIntlCache();
let INTL: IntlShape | undefined;
export const resetIntl = () => INTL = undefined;
export const resetIntl = () => (INTL = undefined);
const IntlGlobalProvider = (props: IntlGlobalProviderProps) => {
INTL = useIntl();
return <>{props.children}</>;
1 change: 1 addition & 0 deletions libs/vscode-extension/src/lib/models/bundleFeed.ts
Original file line number Diff line number Diff line change
@@ -18,4 +18,5 @@ export interface IBundleDependencyFeed {
dotnet?: string;
funcCoreTools?: string;
nodejs?: string;
dotnetMulti?: string;
}
4 changes: 2 additions & 2 deletions libs/vscode-extension/src/lib/models/project.ts
Original file line number Diff line number Diff line change
@@ -2,7 +2,7 @@ import type { IWorkerRuntime } from './cliFeed';
import type { FuncVersion } from './functions';
import type { IParsedHostJson } from './host';
import type { ProjectLanguage } from './language';
import type { WorkflowProjectType } from './workflow';
import type { TargetFramework, WorkflowProjectType } from './workflow';
import type { IActionContext } from '@microsoft/vscode-azext-utils';
import type { Uri, WorkspaceFolder } from 'vscode';

@@ -75,7 +75,7 @@ export interface IProjectWizardContext extends IActionContext {
workflowProjectType?: WorkflowProjectType;
generateFromOpenAPI?: boolean;
openApiSpecificationFile?: Uri[];
targetFramework?: string | string[];
targetFramework?: TargetFramework;
projectType?: ProjectType;
isWorkspaceWithFunctions?: boolean;
logicAppName?: string;
6 changes: 6 additions & 0 deletions libs/vscode-extension/src/lib/models/workflow.ts
Original file line number Diff line number Diff line change
@@ -109,3 +109,9 @@ export const MismatchBehavior = {
DontChange: 'DontChange',
} as const;
export type MismatchBehavior = (typeof MismatchBehavior)[keyof typeof MismatchBehavior];

export const TargetFramework = {
NetFx: 'net472',
Net8: 'net8',
} as const;
export type TargetFramework = (typeof TargetFramework)[keyof typeof TargetFramework];

0 comments on commit 74a5363

Please sign in to comment.