diff --git a/.azure/pipelines/fast-pr-validation.yml b/.azure/pipelines/fast-pr-validation.yml index 8da4603d26..d0045c031e 100644 --- a/.azure/pipelines/fast-pr-validation.yml +++ b/.azure/pipelines/fast-pr-validation.yml @@ -6,39 +6,18 @@ trigger: jobs: - template: jobs/default-build.yml parameters: + jobName: PR_FastCheck + jobDisplayName: Fast Check agentOs: Windows - jobName: Windows_FastCheck - jobDisplayName: Windows - Fast Check buildArgs: "/t:FastCheck" - artifacts: - publish: false - template: jobs/default-build.yml parameters: - agentOs: Windows - jobName: Compile_Windows + jobName: Windows_Build jobDisplayName: "Build: Windows" - buildArgs: "/p:SkipTests=true" - artifacts: - publish: false -- job: RepoBuilds - pool: - vmImage: vs2017-win2016 - strategy: - maxParallel: 3 - matrix: - DataProtection: - _FolderName: DataProtection - WebSockets: - _FolderName: WebSockets - steps: - - script: src/$(_FolderName)/build.cmd -ci - displayName: Run src/$(_FolderName)/build.cmd - - task: PublishTestResults@2 - displayName: Publish test results - condition: always() - inputs: - testRunner: vstest - testResultsFiles: 'src/$(_FolderName)/artifacts/logs/**/*.trx' + agentOs: Windows + beforeBuild: + - powershell: "& ./src/IISIntegration/tools/UpdateIISExpressCertificate.ps1" + displayName: Setup IISExpress test certificates - template: jobs/iisintegration-job.yml parameters: TestGroupName: IIS diff --git a/.azure/pipelines/jobs/default-build.yml b/.azure/pipelines/jobs/default-build.yml index df7f08f57d..87873f95d8 100644 --- a/.azure/pipelines/jobs/default-build.yml +++ b/.azure/pipelines/jobs/default-build.yml @@ -63,6 +63,7 @@ jobs: - job: ${{ coalesce(parameters.jobName, parameters.agentOs) }} displayName: ${{ coalesce(parameters.jobDisplayName, parameters.agentOs) }} dependsOn: ${{ parameters.dependsOn }} + timeoutInMinutes: 90 workspace: clean: all strategy: diff --git a/.gitmodules b/.gitmodules index f88fba7d65..837bee2191 100644 --- a/.gitmodules +++ b/.gitmodules @@ -42,10 +42,6 @@ path = modules/Hosting url = https://github.com/aspnet/Hosting.git branch = release/2.2 -[submodule "modules/HtmlAbstractions"] - path = modules/HtmlAbstractions - url = https://github.com/aspnet/HtmlAbstractions.git - branch = release/2.2 [submodule "modules/HttpAbstractions"] path = modules/HttpAbstractions url = https://github.com/aspnet/HttpAbstractions.git @@ -66,10 +62,6 @@ path = modules/JavaScriptServices url = https://github.com/aspnet/JavaScriptServices.git branch = release/2.2 -[submodule "modules/JsonPatch"] - path = modules/JsonPatch - url = https://github.com/aspnet/JsonPatch.git - branch = release/2.2 [submodule "modules/KestrelHttpServer"] path = modules/KestrelHttpServer url = https://github.com/aspnet/KestrelHttpServer.git diff --git a/Directory.Build.props b/Directory.Build.props index 7968a8b8ad..b121675096 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -17,6 +17,8 @@ replace PackageLicenseUrl with PackageLicenseExpression. --> $(NoWarn);NU5125 + + $(NoWarn);NU5105 nugetaspnet@microsoft.com @@ -49,6 +51,16 @@ false + + + Microsoft400 + 3PartySHA2 + Microsoft400 + NuGet + VsixSHA2 + MicrosoftJAR + + @@ -75,8 +87,16 @@ true + + netcoreapp2.2;net461 + + + + + + diff --git a/Directory.Build.targets b/Directory.Build.targets index c75e203321..e7fc9b6812 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -1,9 +1,46 @@ + + $(AssemblyName) + false + + + + + + + true + $(PackagesInPatch.Contains(' $(PackageId);')) + + + + + true + + false + + + + + $(BaselinePackageVersion).0 + + $(BaselinePackageVersion) + $(BaselinePackageVersion) + + true + + false + $(IsImplementationProject) true false @@ -17,6 +54,7 @@ + diff --git a/build/CodeSign.props b/build/CodeSign.props index b28f57576b..19e4726024 100644 --- a/build/CodeSign.props +++ b/build/CodeSign.props @@ -56,6 +56,7 @@ + diff --git a/build/CodeSign.targets b/build/CodeSign.targets index 308a95817f..a7ad37a754 100644 --- a/build/CodeSign.targets +++ b/build/CodeSign.targets @@ -4,7 +4,7 @@ $(CodeSignDependsOn);CollectFileSignInfo - + <_RepositoryProject Remove="@(_RepositoryProject)" /> diff --git a/build/PackageArchive.targets b/build/PackageArchive.targets index 5ba1cadcf8..0eea5b6c15 100644 --- a/build/PackageArchive.targets +++ b/build/PackageArchive.targets @@ -8,9 +8,7 @@ - - - + DotNetRestoreSourcePropsPath=$(GeneratedRestoreSourcesPropsPath); diff --git a/build/SharedFx.targets b/build/SharedFx.targets index 016a08afb1..6c47f1a5ee 100644 --- a/build/SharedFx.targets +++ b/build/SharedFx.targets @@ -2,19 +2,20 @@ $(RepositoryRoot)src\Framework\Framework.UnitTests\Framework.UnitTests.csproj $([MSBuild]::NormalizePath($(UnitTestFxProject))) - $(CodeSignDependsOn);GetSharedFxFilesToSign + $(CodeSignDependsOn);GetSharedFxFilesToSign _BuildSharedFxProjects;TestSharedFx $(BuildSharedFxDependsOn);CodeSign $(IntermediateDir)ar\$(SharedFxRid)\ + $(GetArtifactInfo);GetFxProjectArtifactInfo - - - - - - + + + + + + @@ -43,7 +44,7 @@ - <_RestoreGraphProjectInput>@(ProjectToBuild) + <_RestoreGraphProjectInput>@(FxProjectToBuild) $(BuildProperties); SharedFxRid=$(SharedFxRid); @@ -56,11 +57,11 @@ Targets="Restore" Properties="$(SharedFxBuildProperties);RestoreGraphProjectInput=$(_RestoreGraphProjectInput);_DummyTarget=Restore" /> - - + + + <_InspectionTargetsFile>$(MSBuildProjectDirectory)\Project.Inspection.targets + + + + <_Temp Remove="@(_Temp)" /> + + + + + + + + + + + + + + + + + + + diff --git a/build/artifacts.props b/build/artifacts.props index f343b8fe5a..1e4b5c4da5 100644 --- a/build/artifacts.props +++ b/build/artifacts.props @@ -199,7 +199,6 @@ - diff --git a/build/buildorder.props b/build/buildorder.props index c17c8d0952..740c110947 100644 --- a/build/buildorder.props +++ b/build/buildorder.props @@ -7,9 +7,7 @@ - - @@ -18,7 +16,6 @@ - @@ -30,7 +27,6 @@ - diff --git a/build/dependencies.folderbuilds.props b/build/dependencies.folderbuilds.props index a8179a8e37..4b79b9ef4f 100644 --- a/build/dependencies.folderbuilds.props +++ b/build/dependencies.folderbuilds.props @@ -1,7 +1,7 @@  - 2.2.0-preview2-20181108.4 + 2.2.1-build-20181114.3 2.2.0 2.2.0 2.2.0 diff --git a/build/dependencies.props b/build/dependencies.props index dbc107a6b3..2bda6136dc 100644 --- a/build/dependencies.props +++ b/build/dependencies.props @@ -19,6 +19,7 @@ $(KoreBuildVersion) $(KoreBuildVersion) $(KoreBuildVersion) + 2.2.1-build-20181114.3 @@ -30,6 +31,8 @@ 2.2.0 2.2.0 2.2.0 + 2.2.0 + 2.2.0 2.2.0 2.2.0 2.2.0 @@ -85,6 +88,7 @@ 2.2.0 2.2.0 2.2.0 + 2.2.0 2.2.0 diff --git a/build/external-dependencies.props b/build/external-dependencies.props index 3c920875fb..d332904f6e 100644 --- a/build/external-dependencies.props +++ b/build/external-dependencies.props @@ -72,6 +72,7 @@ + diff --git a/build/repo.props b/build/repo.props index 2c14e6176a..486ea83c12 100644 --- a/build/repo.props +++ b/build/repo.props @@ -4,6 +4,8 @@ true + true + false false true @@ -46,6 +48,18 @@ + + + + + + diff --git a/build/repo.targets b/build/repo.targets index 57a608810c..7fb9990fa9 100644 --- a/build/repo.targets +++ b/build/repo.targets @@ -15,20 +15,60 @@ $(IntermediateDir)sources.g.props $(IntermediateDir)branding.g.props - SetTeamCityBuildNumberToVersion;$(PrepareDependsOn);VerifyPackageArtifactConfig;VerifyExternalDependencyConfig;PrepareOutputPaths + SetTeamCityBuildNumberToVersion;$(PrepareDependsOn) + $(PrepareDependsOn);VerifyPackageArtifactConfig;VerifyExternalDependencyConfig;PrepareOutputPaths $(CleanDependsOn);CleanArtifacts;CleanRepoArtifacts - $(RestoreDependsOn);InstallDotNet - $(CompileDependsOn);BuildRepositories;BuildSharedFx - $(PackageDependsOn);CheckExpectedPackagesExist;CodeSign - $(TestDependsOn);_TestRepositories - $(GetArtifactInfoDependsOn);ResolveRepoInfo + $(RestoreDependsOn);InstallDotNet;RestoreProjects + $(CompileDependsOn);BuildProjects + $(CompileDependsOn);PackProjects;BuildRepositories;BuildSharedFx + $(PackageDependsOn);PackProjects + $(PackageDependsOn);CheckExpectedPackagesExist + $(PackageDependsOn);CodeSign + $(TestDependsOn);TestProjects + $(TestDependsOn);_TestRepositories + $(GetArtifactInfoDependsOn);GetProjectArtifactInfo + $(GetArtifactInfoDependsOn);ResolveRepoInfo - + + + + + + + + $(MSBuildThisFileDirectory)..\eng\ProjectReferences.props + + + + + @(_ProjectReferenceProvider->'', '%0A ') + + + ]]> + + + + + + + + + + + + + + $(BuildProperties);MicrosoftNETCoreAppPackageVersion=$(MicrosoftNETCoreAppPackageVersion); @@ -237,11 +277,11 @@ - + - + <_UndeclaredPackageArtifact Include="%(ArtifactInfo.PackageId)" Condition="'%(ArtifactInfo.ArtifactType)' == 'NuGetPackage'" /> <_UndeclaredPackageArtifact Remove="@(PackageArtifact)" /> diff --git a/build/submodules.props b/build/submodules.props index d3321d9ea7..1d429fae9c 100644 --- a/build/submodules.props +++ b/build/submodules.props @@ -53,18 +53,15 @@ - - - @@ -77,6 +74,5 @@ - diff --git a/build/tasks/RepoTasks.csproj b/build/tasks/RepoTasks.csproj index 6be1f375d2..20cbf6cce8 100644 --- a/build/tasks/RepoTasks.csproj +++ b/build/tasks/RepoTasks.csproj @@ -6,6 +6,7 @@ + diff --git a/docs/PreparingPatchUpdates.md b/docs/PreparingPatchUpdates.md new file mode 100644 index 0000000000..34813e8162 --- /dev/null +++ b/docs/PreparingPatchUpdates.md @@ -0,0 +1,41 @@ +Preparing new servicing updates +=============================== + +In order to prepare this repo to build a new servicing update, the following changes need to be made. + +* Increment the patch version in the [version.props](/version.props) file in the repository root. + + ```diff + - 7 + + 8 + ``` + +* Update the package archive baselines. This is used to make sure each build of the package archives we give to Azure only contains new files and does + not require overwriting existing files. See [src/PackageArchive/ZipManifestGenerator/](/src/PackageArchive/ZipManifestGenerator/README.md) for instructions on how to run this tool. + +* Update the package baselines. This is used to ensure packages keep a consistent set of dependencies between releases. + See [eng/tools/BaselineGenerator/](/eng/tools/BaselineGenerator/README.md) for instructions on how to run this tool. + +* **For packages with source code in this repo (not a submodule):** Update the list of packages in [eng/PatchConfig.props](/eng/PatchConfig.props) to list which packages should be patching in this release. + +* **For packages still building from submodules:** Update the list of repositories which will contain changes in [build/submodules.props](/build/submodules.props). + + * `` items represent repos which were released in a previous patch, and will not contain servicing updates in the next patch. + * `` items represent repos which will produce new packages in this patch. + * It is usually best to move everything to `` and then iteratively add them back to `` as new repos receive approval to patch. + * Don't change the `PatchPolicy` attribute. The build system uses this to ensure patching rules are obeyed. + +* For each repository still listed as a ``, update the version.props file in that submodule. For example, https://github.com/aspnet/Templating/pull/824 + + * The version prefix in repos should match the version of ASP.NET Core. + * Exception: SignalR, which is "1.1", not "2.1". + * This leaves holes in versioning, which is okay. This may mean you increment the patch value by more than one. Example: + * EF Core ships patches in 2.1.4 as "2.1.4" + * EF Core does not ship patches in 2.1.5 or 2.1.6 + * EF Core ships in 2.1.7, therefore, EFCore's version.props file should jump from 2.1.4 to 2.1.7. + + ```diff + + - 2.1.4 + + 2.1.7 + ``` diff --git a/eng/Baseline.props b/eng/Baseline.props new file mode 100644 index 0000000000..b8d8c3cb60 --- /dev/null +++ b/eng/Baseline.props @@ -0,0 +1,116 @@ + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + 2.2.0 + + + + 2.2.0 + + + + + 2.2.0 + + + + + + + + + + 2.2.0 + + + + + + + + + + + + + + + 2.2.0 + + + + + 2.2.0 + + + + + + + + + 2.2.0 + + + + + + + + 2.2.0 + + + + + + + + 2.2.0 + + + + + + + + 2.2.0 + + + + + + + + 2.2.0 + + + + + + + + 2.2.0 + + + + + + + 2.2.0 + + + + + + + + 2.2.0 + + + + + + + + \ No newline at end of file diff --git a/eng/Dependencies.props b/eng/Dependencies.props new file mode 100644 index 0000000000..a88732f80c --- /dev/null +++ b/eng/Dependencies.props @@ -0,0 +1,55 @@ + + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/eng/PatchConfig.props b/eng/PatchConfig.props new file mode 100644 index 0000000000..c05b2376c6 --- /dev/null +++ b/eng/PatchConfig.props @@ -0,0 +1,14 @@ + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + + + + + Microsoft.AspNetCore.Server.IIS; + Microsoft.AspNetCore.Server.IISIntegration; + Microsoft.AspNetCore.Server.IntegrationTesting.IIS; + + + + diff --git a/eng/ProjectReferences.props b/eng/ProjectReferences.props new file mode 100644 index 0000000000..0415e44d3e --- /dev/null +++ b/eng/ProjectReferences.props @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/eng/targets/CSharp.Common.props b/eng/targets/CSharp.Common.props new file mode 100644 index 0000000000..b3fc97e2d9 --- /dev/null +++ b/eng/targets/CSharp.Common.props @@ -0,0 +1,14 @@ + + + + 7.2 + + + SHA256 + + + + + + + diff --git a/eng/targets/CSharp.Common.targets b/eng/targets/CSharp.Common.targets new file mode 100644 index 0000000000..a7f7b610b6 --- /dev/null +++ b/eng/targets/CSharp.Common.targets @@ -0,0 +1,5 @@ + + + + + diff --git a/eng/targets/Packaging.targets b/eng/targets/Packaging.targets new file mode 100644 index 0000000000..2a1427c2de --- /dev/null +++ b/eng/targets/Packaging.targets @@ -0,0 +1,30 @@ + + + + + + + + + + + $(PackageOutputPath)$(PackageId).$(PackageVersion).nupkg + + + + + NuGetPackage + $(PackageId) + $(PackageVersion) + $(RepositoryRoot) + true + + + + diff --git a/eng/targets/ResolveReferences.targets b/eng/targets/ResolveReferences.targets new file mode 100644 index 0000000000..caf44207ee --- /dev/null +++ b/eng/targets/ResolveReferences.targets @@ -0,0 +1,135 @@ + + + + + ResolveCustomReferences; + $(ResolveReferencesDependsOn); + + + + + + true + true + true + false + + + true + true + false + + + + <_ImplicitPackageReference Include="@(PackageReference->WithMetadataValue('IsImplicitlyDefined', 'true'))" /> + <_ExplicitPackageReference Include="@(PackageReference)" Exclude="@(_ImplicitPackageReference)" /> + <_ExplicitPackageReference Remove="Internal.AspNetCore.Sdk" /> + + + + + <_ProjectReferenceByAssemblyName Condition="'$(UseProjectReferences)' == 'true'" + Include="@(ProjectReferenceProvider)" + Exclude="@(UnusedProjectReferenceProvider)" /> + + + + + + + + + + + + <_LatestPackageReferenceWithVersion Include="@(Reference)" Condition=" '$(UseLatestPackageReferences)' == 'true' "> + %(LatestPackageReference.Identity) + %(LatestPackageReference.Version) + + <_LatestPackageReferenceWithVersion Remove="@(_LatestPackageReferenceWithVersion)" Condition="'%(Id)' != '%(Identity)' " /> + + + + + + + <_BaselinePackageReferenceWithVersion Include="@(Reference)" Condition=" '$(IsServicingBuild)' == 'true' OR '$(UseLatestPackageReferences)' != 'true' "> + %(BaselinePackageReference.Identity) + %(BaselinePackageReference.Version) + + + <_BaselinePackageReferenceWithVersion Remove="@(_BaselinePackageReferenceWithVersion)" Condition="'%(Id)' != '%(Identity)' " /> + + + + + + + <_PrivatePackageReferenceWithVersion Include="@(Reference->WithMetadataValue('PrivateAssets', 'All'))"> + %(LatestPackageReference.Identity) + %(LatestPackageReference.Version) + + + <_PrivatePackageReferenceWithVersion Remove="@(_PrivatePackageReferenceWithVersion)" Condition="'%(Id)' != '%(Identity)' " /> + + + + + + + <_LatestPackageReferenceWithVersion Remove="@(_LatestPackageReferenceWithVersion)" /> + <_BaselinePackageReferenceWithVersion Remove="@(_BaselinePackageReferenceWithVersion)" /> + <_PrivatePackageReferenceWithVersion Remove="@(_PrivatePackageReferenceWithVersion)" /> + <_ImplicitPackageReference Remove="@(_ImplicitPackageReference)" /> + + + + + <_ExplicitPackageReference Remove="@(_ExplicitPackageReference)" /> + + + + + + + + + + <_TargetFramework Remove="@(_TargetFramework)" /> + <_TargetFramework Include="$(TargetFramework)" Condition="'$(TargetFramework)' != '' "/> + <_TargetFramework Include="$(TargetFrameworks)" Condition="'$(TargetFramework)' == '' "/> + + + + + + + + + + + $([MSBuild]::MakeRelative($(RepositoryRoot), $(MSBuildProjectFullPath))) + + + + diff --git a/eng/tools/BaselineGenerator/BaselineGenerator.csproj b/eng/tools/BaselineGenerator/BaselineGenerator.csproj new file mode 100644 index 0000000000..625777dbd2 --- /dev/null +++ b/eng/tools/BaselineGenerator/BaselineGenerator.csproj @@ -0,0 +1,15 @@ + + + + Exe + netcoreapp2.1 + -o "$(MSBuildThisFileDirectory)../../Baseline.props" + $(MSBuildProjectDirectory) + + + + + + + + diff --git a/eng/tools/BaselineGenerator/Program.cs b/eng/tools/BaselineGenerator/Program.cs new file mode 100644 index 0000000000..689653e199 --- /dev/null +++ b/eng/tools/BaselineGenerator/Program.cs @@ -0,0 +1,138 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.IO; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; +using System.Xml; +using System.Xml.Linq; +using Microsoft.Extensions.CommandLineUtils; +using NuGet.Packaging; +using NuGet.Packaging.Core; + +namespace PackageBaselineGenerator +{ + /// + /// This generates Baseline.props with information about the last RTM release. + /// + class Program : CommandLineApplication + { + static void Main(string[] args) + { + new Program().Execute(args); + } + + private readonly CommandOption _source; + private readonly CommandOption _output; + private readonly CommandOption _feedv3; + + public Program() + { + _source = Option("-s|--source ", "The NuGet v2 source of the package to fetch", CommandOptionType.SingleValue); + _output = Option("-o|--output ", "The generated file output path", CommandOptionType.SingleValue); + _feedv3 = Option("--v3", "Sources is nuget v3", CommandOptionType.NoValue); + + Invoke = () => Run().GetAwaiter().GetResult(); + } + + private async Task Run() + { + var source = _source.HasValue() + ? _source.Value() + : "https://www.nuget.org/api/v2/package"; + + var packageCache = Environment.GetEnvironmentVariable("NUGET_PACKAGES") != null + ? Environment.GetEnvironmentVariable("NUGET_PACKAGES") + : Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".nuget", "packages"); + + var tempDir = Path.Combine(Directory.GetCurrentDirectory(), "obj", "tmp"); + Directory.CreateDirectory(tempDir); + + var input = XDocument.Load(Path.Combine(Directory.GetCurrentDirectory(), "baseline.xml")); + + var output = _output.HasValue() + ? _output.Value() + : Path.Combine(Directory.GetCurrentDirectory(), "Baseline.props"); + + var baselineVersion = input.Root.Attribute("Version").Value; + + var doc = new XDocument( + new XComment(" Auto generated. Do not edit manually, use eng/tools/BaselineGenerator/ to recreate. "), + new XElement("Project", + new XElement("PropertyGroup", + new XElement("MSBuildAllProjects", "$(MSBuildAllProjects);$(MSBuildThisFileFullPath)"), + new XElement("AspNetCoreBaselineVersion", baselineVersion)))); + + var client = new HttpClient(); + + foreach (var pkg in input.Root.Descendants("Package")) + { + var id = pkg.Attribute("Id").Value; + var version = pkg.Attribute("Version").Value; + var packageFileName = $"{id}.{version}.nupkg"; + var nupkgPath = Path.Combine(packageCache, id.ToLowerInvariant(), version, packageFileName); + if (!File.Exists(nupkgPath)) + { + nupkgPath = Path.Combine(tempDir, packageFileName); + } + + if (!File.Exists(nupkgPath)) + { + var url = _feedv3.HasValue() + ? $"{source}/{id.ToLowerInvariant()}/{version}/{id.ToLowerInvariant()}.{version}.nupkg" + : $"{source}/{id}/{version}"; + Console.WriteLine($"Downloading {url}"); + + var response = await client.GetStreamAsync(url); + + using (var file = File.Create(nupkgPath)) + { + await response.CopyToAsync(file); + } + } + + + using (var reader = new PackageArchiveReader(nupkgPath)) + { + var first = true; + foreach (var group in reader.NuspecReader.GetDependencyGroups()) + { + if (first) + { + first = false; + doc.Root.Add(new XComment($" Package: {id}")); + + var propertyGroup = new XElement("PropertyGroup", + new XAttribute("Condition", $" '$(PackageId)' == '{id}' "), + new XElement("BaselinePackageVersion", version)); + doc.Root.Add(propertyGroup); + } + + var itemGroup = new XElement("ItemGroup", new XAttribute("Condition", $" '$(PackageId)' == '{id}' AND '$(TargetFramework)' == '{group.TargetFramework.GetShortFolderName()}' ")); + doc.Root.Add(itemGroup); + + foreach (var dependency in group.Packages) + { + itemGroup.Add(new XElement("BaselinePackageReference", new XAttribute("Include", dependency.Id), new XAttribute("Version", dependency.VersionRange.ToString()))); + } + } + } + } + + var settings = new XmlWriterSettings + { + OmitXmlDeclaration = true, + Encoding = Encoding.UTF8, + Indent = true, + }; + using (var writer = XmlWriter.Create(output, settings)) + { + doc.Save(writer); + } + + return 0; + } + } +} diff --git a/eng/tools/BaselineGenerator/README.md b/eng/tools/BaselineGenerator/README.md new file mode 100644 index 0000000000..1afd97d1b5 --- /dev/null +++ b/eng/tools/BaselineGenerator/README.md @@ -0,0 +1,10 @@ +BaselineGenerator +================= + +This tool is used to generate an MSBuild file which sets the "baseline" against which servicing updates are built. + +## Usage + +1. Add to the [baseline.xml](./baseline.xml) a list of package ID's and their latest released versions. The source of this information can typically + be found in the build.xml file generated during ProdCon builds. See https://github.com/dotnet/versions/blob/master/build-info/dotnet/product/cli/release/2.1.6/build.xml for example. +2. Run `dotnet run` on this project. diff --git a/eng/tools/BaselineGenerator/baseline.xml b/eng/tools/BaselineGenerator/baseline.xml new file mode 100644 index 0000000000..759e73ffa7 --- /dev/null +++ b/eng/tools/BaselineGenerator/baseline.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/eng/tools/Directory.Build.props b/eng/tools/Directory.Build.props new file mode 100644 index 0000000000..fda3ea0cbe --- /dev/null +++ b/eng/tools/Directory.Build.props @@ -0,0 +1,3 @@ + + + diff --git a/eng/tools/Directory.Build.targets b/eng/tools/Directory.Build.targets new file mode 100644 index 0000000000..f75adf7e4d --- /dev/null +++ b/eng/tools/Directory.Build.targets @@ -0,0 +1,2 @@ + + diff --git a/eng/tools/tools.sln b/eng/tools/tools.sln new file mode 100644 index 0000000000..527e47cbc3 --- /dev/null +++ b/eng/tools/tools.sln @@ -0,0 +1,34 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26124.0 +MinimumVisualStudioVersion = 15.0.26124.0 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BaselineGenerator", "BaselineGenerator\BaselineGenerator.csproj", "{CF76A947-3A72-4824-87E6-BF029D84218B}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {CF76A947-3A72-4824-87E6-BF029D84218B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CF76A947-3A72-4824-87E6-BF029D84218B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CF76A947-3A72-4824-87E6-BF029D84218B}.Debug|x64.ActiveCfg = Debug|Any CPU + {CF76A947-3A72-4824-87E6-BF029D84218B}.Debug|x64.Build.0 = Debug|Any CPU + {CF76A947-3A72-4824-87E6-BF029D84218B}.Debug|x86.ActiveCfg = Debug|Any CPU + {CF76A947-3A72-4824-87E6-BF029D84218B}.Debug|x86.Build.0 = Debug|Any CPU + {CF76A947-3A72-4824-87E6-BF029D84218B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CF76A947-3A72-4824-87E6-BF029D84218B}.Release|Any CPU.Build.0 = Release|Any CPU + {CF76A947-3A72-4824-87E6-BF029D84218B}.Release|x64.ActiveCfg = Release|Any CPU + {CF76A947-3A72-4824-87E6-BF029D84218B}.Release|x64.Build.0 = Release|Any CPU + {CF76A947-3A72-4824-87E6-BF029D84218B}.Release|x86.ActiveCfg = Release|Any CPU + {CF76A947-3A72-4824-87E6-BF029D84218B}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/global.json b/global.json index 5c2b5c9e77..369a8b9815 100644 --- a/global.json +++ b/global.json @@ -3,6 +3,6 @@ "version": "2.2.100-rtm-009578" }, "msbuild-sdks": { - "Internal.AspNetCore.Sdk": "2.2.0-preview2-20181109.5" + "Internal.AspNetCore.Sdk": "2.2.1-build-20181114.3" } } diff --git a/korebuild-lock.txt b/korebuild-lock.txt index 268c275813..912f802366 100644 --- a/korebuild-lock.txt +++ b/korebuild-lock.txt @@ -1,2 +1,2 @@ -version:2.2.0-preview2-20181109.5 -commithash:f736f491e6a372b52791e094f8b93e176c2f98c2 +version:2.2.1-build-20181114.3 +commithash:07ca548548a53e0226eef75eab29a67b0ed7c342 diff --git a/modules/HtmlAbstractions b/modules/HtmlAbstractions deleted file mode 160000 index 6c5ca90d81..0000000000 --- a/modules/HtmlAbstractions +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 6c5ca90d81f9013a8652da4c1752bd0b4d18e908 diff --git a/modules/JsonPatch b/modules/JsonPatch deleted file mode 160000 index 3b485909eb..0000000000 --- a/modules/JsonPatch +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 3b485909ebec98d9437058dbcf45dac3ccd1f157 diff --git a/scripts/UpdateDependencies.ps1 b/scripts/UpdateDependencies.ps1 index 7aeb8fd3f8..6adaea833e 100755 --- a/scripts/UpdateDependencies.ps1 +++ b/scripts/UpdateDependencies.ps1 @@ -56,7 +56,6 @@ foreach ($package in $remoteDeps.SelectNodes('//Package')) { if (-not $NoCommit) { $currentBranch = Invoke-Block { & git rev-parse --abbrev-ref HEAD } - $destinationBranch = "dotnetbot/UpdateDeps" Invoke-Block { & git checkout -tb $destinationBranch "origin/$GithubUpstreamBranch" } } diff --git a/src/DataProtection/Abstractions/src/Microsoft.AspNetCore.DataProtection.Abstractions.csproj b/src/DataProtection/Abstractions/src/Microsoft.AspNetCore.DataProtection.Abstractions.csproj index 94dc1366dd..021b3fde2c 100644 --- a/src/DataProtection/Abstractions/src/Microsoft.AspNetCore.DataProtection.Abstractions.csproj +++ b/src/DataProtection/Abstractions/src/Microsoft.AspNetCore.DataProtection.Abstractions.csproj @@ -15,7 +15,7 @@ Microsoft.AspNetCore.DataProtection.IDataProtector - + diff --git a/src/DataProtection/Abstractions/test/Microsoft.AspNetCore.DataProtection.Abstractions.Tests.csproj b/src/DataProtection/Abstractions/test/Microsoft.AspNetCore.DataProtection.Abstractions.Tests.csproj index 4d20666701..b1ad1e4ee2 100644 --- a/src/DataProtection/Abstractions/test/Microsoft.AspNetCore.DataProtection.Abstractions.Tests.csproj +++ b/src/DataProtection/Abstractions/test/Microsoft.AspNetCore.DataProtection.Abstractions.Tests.csproj @@ -5,8 +5,8 @@ - - + + diff --git a/src/DataProtection/AzureKeyVault/src/Microsoft.AspNetCore.DataProtection.AzureKeyVault.csproj b/src/DataProtection/AzureKeyVault/src/Microsoft.AspNetCore.DataProtection.AzureKeyVault.csproj index 86b9195c60..42aa2edb13 100644 --- a/src/DataProtection/AzureKeyVault/src/Microsoft.AspNetCore.DataProtection.AzureKeyVault.csproj +++ b/src/DataProtection/AzureKeyVault/src/Microsoft.AspNetCore.DataProtection.AzureKeyVault.csproj @@ -9,12 +9,9 @@ - - - - - - + + + diff --git a/src/DataProtection/AzureKeyVault/test/Microsoft.AspNetCore.DataProtection.AzureKeyVault.Tests.csproj b/src/DataProtection/AzureKeyVault/test/Microsoft.AspNetCore.DataProtection.AzureKeyVault.Tests.csproj index e2ac01dd32..a66a14ec66 100644 --- a/src/DataProtection/AzureKeyVault/test/Microsoft.AspNetCore.DataProtection.AzureKeyVault.Tests.csproj +++ b/src/DataProtection/AzureKeyVault/test/Microsoft.AspNetCore.DataProtection.AzureKeyVault.Tests.csproj @@ -6,12 +6,9 @@ - - - - - - + + + diff --git a/src/DataProtection/AzureStorage/src/Microsoft.AspNetCore.DataProtection.AzureStorage.csproj b/src/DataProtection/AzureStorage/src/Microsoft.AspNetCore.DataProtection.AzureStorage.csproj index 368e6a9934..d65f55a425 100644 --- a/src/DataProtection/AzureStorage/src/Microsoft.AspNetCore.DataProtection.AzureStorage.csproj +++ b/src/DataProtection/AzureStorage/src/Microsoft.AspNetCore.DataProtection.AzureStorage.csproj @@ -9,11 +9,8 @@ - - - - - + + diff --git a/src/DataProtection/AzureStorage/test/Microsoft.AspNetCore.DataProtection.AzureStorage.Tests.csproj b/src/DataProtection/AzureStorage/test/Microsoft.AspNetCore.DataProtection.AzureStorage.Tests.csproj index 5274e9291c..b6926a12d2 100644 --- a/src/DataProtection/AzureStorage/test/Microsoft.AspNetCore.DataProtection.AzureStorage.Tests.csproj +++ b/src/DataProtection/AzureStorage/test/Microsoft.AspNetCore.DataProtection.AzureStorage.Tests.csproj @@ -6,12 +6,9 @@ - - - - - - + + + diff --git a/src/DataProtection/Cryptography.Internal/test/Microsoft.AspNetCore.Cryptography.Internal.Tests.csproj b/src/DataProtection/Cryptography.Internal/test/Microsoft.AspNetCore.Cryptography.Internal.Tests.csproj index 7feff1613f..33417f3e44 100644 --- a/src/DataProtection/Cryptography.Internal/test/Microsoft.AspNetCore.Cryptography.Internal.Tests.csproj +++ b/src/DataProtection/Cryptography.Internal/test/Microsoft.AspNetCore.Cryptography.Internal.Tests.csproj @@ -10,7 +10,7 @@ - + diff --git a/src/DataProtection/Cryptography.KeyDerivation/src/Microsoft.AspNetCore.Cryptography.KeyDerivation.csproj b/src/DataProtection/Cryptography.KeyDerivation/src/Microsoft.AspNetCore.Cryptography.KeyDerivation.csproj index ac98d14665..668f7ca459 100644 --- a/src/DataProtection/Cryptography.KeyDerivation/src/Microsoft.AspNetCore.Cryptography.KeyDerivation.csproj +++ b/src/DataProtection/Cryptography.KeyDerivation/src/Microsoft.AspNetCore.Cryptography.KeyDerivation.csproj @@ -9,7 +9,7 @@ - + diff --git a/src/DataProtection/Cryptography.KeyDerivation/test/Microsoft.AspNetCore.Cryptography.KeyDerivation.Tests.csproj b/src/DataProtection/Cryptography.KeyDerivation/test/Microsoft.AspNetCore.Cryptography.KeyDerivation.Tests.csproj index 307d09b845..de95349f4e 100644 --- a/src/DataProtection/Cryptography.KeyDerivation/test/Microsoft.AspNetCore.Cryptography.KeyDerivation.Tests.csproj +++ b/src/DataProtection/Cryptography.KeyDerivation/test/Microsoft.AspNetCore.Cryptography.KeyDerivation.Tests.csproj @@ -10,8 +10,8 @@ - - + + diff --git a/src/DataProtection/DataProtection/src/Microsoft.AspNetCore.DataProtection.csproj b/src/DataProtection/DataProtection/src/Microsoft.AspNetCore.DataProtection.csproj index 1428564533..18aa5ddc40 100644 --- a/src/DataProtection/DataProtection/src/Microsoft.AspNetCore.DataProtection.csproj +++ b/src/DataProtection/DataProtection/src/Microsoft.AspNetCore.DataProtection.csproj @@ -14,18 +14,15 @@ - - - - - - - - - - - - + + + + + + + + + diff --git a/src/DataProtection/DataProtection/test/Microsoft.AspNetCore.DataProtection.Tests.csproj b/src/DataProtection/DataProtection/test/Microsoft.AspNetCore.DataProtection.Tests.csproj index 571d92ff14..2e1ba4fd5e 100644 --- a/src/DataProtection/DataProtection/test/Microsoft.AspNetCore.DataProtection.Tests.csproj +++ b/src/DataProtection/DataProtection/test/Microsoft.AspNetCore.DataProtection.Tests.csproj @@ -11,12 +11,12 @@ - - + + - - - + + + diff --git a/src/DataProtection/Directory.Build.props b/src/DataProtection/Directory.Build.props deleted file mode 100644 index dc0f2fb7d4..0000000000 --- a/src/DataProtection/Directory.Build.props +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/src/DataProtection/EntityFrameworkCore/src/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.csproj b/src/DataProtection/EntityFrameworkCore/src/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.csproj index 0903521d3f..85f5e47c55 100644 --- a/src/DataProtection/EntityFrameworkCore/src/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.csproj +++ b/src/DataProtection/EntityFrameworkCore/src/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.csproj @@ -9,15 +9,8 @@ - - - - - - - - - + + diff --git a/src/DataProtection/EntityFrameworkCore/test/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.Test.csproj b/src/DataProtection/EntityFrameworkCore/test/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.Test.csproj index cedaa66faf..3b57a37f97 100644 --- a/src/DataProtection/EntityFrameworkCore/test/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.Test.csproj +++ b/src/DataProtection/EntityFrameworkCore/test/Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.Test.csproj @@ -5,11 +5,8 @@ - - - - - + + diff --git a/src/DataProtection/Extensions/src/Microsoft.AspNetCore.DataProtection.Extensions.csproj b/src/DataProtection/Extensions/src/Microsoft.AspNetCore.DataProtection.Extensions.csproj index e8335310e4..c106a52961 100644 --- a/src/DataProtection/Extensions/src/Microsoft.AspNetCore.DataProtection.Extensions.csproj +++ b/src/DataProtection/Extensions/src/Microsoft.AspNetCore.DataProtection.Extensions.csproj @@ -12,11 +12,8 @@ - - - - - + + diff --git a/src/DataProtection/Extensions/test/Microsoft.AspNetCore.DataProtection.Extensions.Tests.csproj b/src/DataProtection/Extensions/test/Microsoft.AspNetCore.DataProtection.Extensions.Tests.csproj index 338fe2c339..9c16226c18 100644 --- a/src/DataProtection/Extensions/test/Microsoft.AspNetCore.DataProtection.Extensions.Tests.csproj +++ b/src/DataProtection/Extensions/test/Microsoft.AspNetCore.DataProtection.Extensions.Tests.csproj @@ -10,8 +10,9 @@ - - + + + diff --git a/src/DataProtection/StackExchangeRedis/src/Microsoft.AspNetCore.DataProtection.StackExchangeRedis.csproj b/src/DataProtection/StackExchangeRedis/src/Microsoft.AspNetCore.DataProtection.StackExchangeRedis.csproj index 09852c77d0..6e2143d602 100644 --- a/src/DataProtection/StackExchangeRedis/src/Microsoft.AspNetCore.DataProtection.StackExchangeRedis.csproj +++ b/src/DataProtection/StackExchangeRedis/src/Microsoft.AspNetCore.DataProtection.StackExchangeRedis.csproj @@ -9,11 +9,8 @@ - - - - - + + diff --git a/src/DataProtection/StackExchangeRedis/test/Microsoft.AspNetCore.DataProtection.StackExchangeRedis.Tests.csproj b/src/DataProtection/StackExchangeRedis/test/Microsoft.AspNetCore.DataProtection.StackExchangeRedis.Tests.csproj index db735684a2..3e64bbd7bc 100644 --- a/src/DataProtection/StackExchangeRedis/test/Microsoft.AspNetCore.DataProtection.StackExchangeRedis.Tests.csproj +++ b/src/DataProtection/StackExchangeRedis/test/Microsoft.AspNetCore.DataProtection.StackExchangeRedis.Tests.csproj @@ -11,14 +11,11 @@ - - - - - - - - + + + + + diff --git a/src/DataProtection/SystemWeb/src/Microsoft.AspNetCore.DataProtection.SystemWeb.csproj b/src/DataProtection/SystemWeb/src/Microsoft.AspNetCore.DataProtection.SystemWeb.csproj index 03629c1de3..603fe68cc8 100644 --- a/src/DataProtection/SystemWeb/src/Microsoft.AspNetCore.DataProtection.SystemWeb.csproj +++ b/src/DataProtection/SystemWeb/src/Microsoft.AspNetCore.DataProtection.SystemWeb.csproj @@ -13,11 +13,8 @@ - - - - - + + diff --git a/src/DataProtection/build/repo.props b/src/DataProtection/build/repo.props deleted file mode 100644 index b8dd411686..0000000000 --- a/src/DataProtection/build/repo.props +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - true - false - false - $([MSBuild]::NormalizeDirectory('$(MSBuildThisFileDirectory)..\')) - $(DataProtectionRoot)**\*.csproj - - diff --git a/src/DataProtection/samples/AzureBlob/AzureBlob.csproj b/src/DataProtection/samples/AzureBlob/AzureBlob.csproj index ebe9abeb3e..f53a1aaeae 100644 --- a/src/DataProtection/samples/AzureBlob/AzureBlob.csproj +++ b/src/DataProtection/samples/AzureBlob/AzureBlob.csproj @@ -6,14 +6,11 @@ - - - - - - - - + + + + + diff --git a/src/DataProtection/samples/AzureKeyVault/AzureKeyVault.csproj b/src/DataProtection/samples/AzureKeyVault/AzureKeyVault.csproj index 01ebd80419..f437597f26 100644 --- a/src/DataProtection/samples/AzureKeyVault/AzureKeyVault.csproj +++ b/src/DataProtection/samples/AzureKeyVault/AzureKeyVault.csproj @@ -6,15 +6,12 @@ - - - - - - - - - + + + + + + diff --git a/src/DataProtection/samples/CustomEncryptorSample/CustomEncryptorSample.csproj b/src/DataProtection/samples/CustomEncryptorSample/CustomEncryptorSample.csproj index 0248dc87c4..36ffc439e5 100644 --- a/src/DataProtection/samples/CustomEncryptorSample/CustomEncryptorSample.csproj +++ b/src/DataProtection/samples/CustomEncryptorSample/CustomEncryptorSample.csproj @@ -6,13 +6,10 @@ - - - - - - - + + + + diff --git a/src/DataProtection/samples/KeyManagementSample/KeyManagementSample.csproj b/src/DataProtection/samples/KeyManagementSample/KeyManagementSample.csproj index 4acc9d3c3a..f27910c78b 100644 --- a/src/DataProtection/samples/KeyManagementSample/KeyManagementSample.csproj +++ b/src/DataProtection/samples/KeyManagementSample/KeyManagementSample.csproj @@ -6,8 +6,8 @@ - - + + diff --git a/src/DataProtection/samples/NonDISample/NonDISample.csproj b/src/DataProtection/samples/NonDISample/NonDISample.csproj index 6d50f5345e..868c6bf82d 100644 --- a/src/DataProtection/samples/NonDISample/NonDISample.csproj +++ b/src/DataProtection/samples/NonDISample/NonDISample.csproj @@ -6,7 +6,7 @@ - + diff --git a/src/DataProtection/samples/Redis/Redis.csproj b/src/DataProtection/samples/Redis/Redis.csproj index 629b11b8d9..6fd424a3cb 100644 --- a/src/DataProtection/samples/Redis/Redis.csproj +++ b/src/DataProtection/samples/Redis/Redis.csproj @@ -6,13 +6,10 @@ - - - - - - - + + + + diff --git a/src/DataProtection/version.props b/src/DataProtection/version.props deleted file mode 100644 index 6d48efec5b..0000000000 --- a/src/DataProtection/version.props +++ /dev/null @@ -1,10 +0,0 @@ - - - 2.2.0 - rtm - $(VersionPrefix) - $(VersionPrefix)-$(VersionSuffix)-final - t000 - $(VersionSuffix)-$(BuildNumber) - - diff --git a/src/Features/JsonPatch/src/Adapters/AdapterFactory.cs b/src/Features/JsonPatch/src/Adapters/AdapterFactory.cs new file mode 100644 index 0000000000..82aaa56a70 --- /dev/null +++ b/src/Features/JsonPatch/src/Adapters/AdapterFactory.cs @@ -0,0 +1,49 @@ +using Microsoft.AspNetCore.JsonPatch.Internal; +using Newtonsoft.Json.Serialization; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Text; + +namespace Microsoft.AspNetCore.JsonPatch.Adapters +{ + /// + /// The default AdapterFactory to be used for resolving . + /// + public class AdapterFactory : IAdapterFactory + { + /// + public virtual IAdapter Create(object target, IContractResolver contractResolver) + { + if (target == null) + { + throw new ArgumentNullException(nameof(target)); + } + + if (contractResolver == null) + { + throw new ArgumentNullException(nameof(contractResolver)); + } + + var jsonContract = contractResolver.ResolveContract(target.GetType()); + + if (target is IList) + { + return new ListAdapter(); + } + else if (jsonContract is JsonDictionaryContract jsonDictionaryContract) + { + var type = typeof(DictionaryAdapter<,>).MakeGenericType(jsonDictionaryContract.DictionaryKeyType, jsonDictionaryContract.DictionaryValueType); + return (IAdapter)Activator.CreateInstance(type); + } + else if (jsonContract is JsonDynamicContract) + { + return new DynamicObjectAdapter(); + } + else + { + return new PocoAdapter(); + } + } + } +} diff --git a/src/Features/JsonPatch/src/Adapters/IAdapterFactory.cs b/src/Features/JsonPatch/src/Adapters/IAdapterFactory.cs new file mode 100644 index 0000000000..43ca1e65b6 --- /dev/null +++ b/src/Features/JsonPatch/src/Adapters/IAdapterFactory.cs @@ -0,0 +1,22 @@ +using Microsoft.AspNetCore.JsonPatch.Internal; +using Newtonsoft.Json.Serialization; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Microsoft.AspNetCore.JsonPatch.Adapters +{ + /// + /// Defines the operations used for loading an based on the current object and ContractResolver. + /// + public interface IAdapterFactory + { + /// + /// Creates an for the current object + /// + /// The target object + /// The current contract resolver + /// The needed + IAdapter Create(object target, IContractResolver contractResolver); + } +} diff --git a/src/Features/JsonPatch/src/Adapters/IObjectAdapter.cs b/src/Features/JsonPatch/src/Adapters/IObjectAdapter.cs new file mode 100644 index 0000000000..e5206bfa0d --- /dev/null +++ b/src/Features/JsonPatch/src/Adapters/IObjectAdapter.cs @@ -0,0 +1,112 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNetCore.JsonPatch.Operations; + +namespace Microsoft.AspNetCore.JsonPatch.Adapters +{ + /// + /// Defines the operations that can be performed on a JSON patch document. + /// + public interface IObjectAdapter + { + /// + /// Using the "add" operation a new value is inserted into the root of the target + /// document, into the target array at the specified valid index, or to a target object at + /// the specified location. + /// + /// When adding to arrays, the specified index MUST NOT be greater than the number of elements in the array. + /// To append the value to the array, the index of "-" character is used (see [RFC6901]). + /// + /// When adding to an object, if an object member does not already exist, a new member is added to the object at the + /// specified location or if an object member does exist, that member's value is replaced. + /// + /// The operation object MUST contain a "value" member whose content + /// specifies the value to be added. + /// + /// For example: + /// + /// { "op": "add", "path": "/a/b/c", "value": [ "foo", "bar" ] } + /// + /// See RFC 6902 https://tools.ietf.org/html/rfc6902#page-4 + /// + /// The add operation. + /// Object to apply the operation to. + void Add(Operation operation, object objectToApplyTo); + + /// + /// Using the "copy" operation, a value is copied from a specified location to the + /// target location. + /// + /// The operation object MUST contain a "from" member, which references the location in the + /// target document to copy the value from. + /// + /// The "from" location MUST exist for the operation to be successful. + /// + /// For example: + /// + /// { "op": "copy", "from": "/a/b/c", "path": "/a/b/e" } + /// + /// See RFC 6902 https://tools.ietf.org/html/rfc6902#page-7 + /// + /// The copy operation. + /// Object to apply the operation to. + void Copy(Operation operation, object objectToApplyTo); + + /// + /// Using the "move" operation the value at a specified location is removed and + /// added to the target location. + /// + /// The operation object MUST contain a "from" member, which references the location in the + /// target document to move the value from. + /// + /// The "from" location MUST exist for the operation to be successful. + /// + /// For example: + /// + /// { "op": "move", "from": "/a/b/c", "path": "/a/b/d" } + /// + /// A location cannot be moved into one of its children. + /// + /// See RFC 6902 https://tools.ietf.org/html/rfc6902#page-6 + /// + /// The move operation. + /// Object to apply the operation to. + void Move(Operation operation, object objectToApplyTo); + + /// + /// Using the "remove" operation the value at the target location is removed. + /// + /// The target location MUST exist for the operation to be successful. + /// + /// For example: + /// + /// { "op": "remove", "path": "/a/b/c" } + /// + /// If removing an element from an array, any elements above the + /// specified index are shifted one position to the left. + /// + /// See RFC 6902 https://tools.ietf.org/html/rfc6902#page-6 + /// + /// The remove operation. + /// Object to apply the operation to. + void Remove(Operation operation, object objectToApplyTo); + + /// + /// Using the "replace" operation he value at the target location is replaced + /// with a new value. The operation object MUST contain a "value" member + /// which specifies the replacement value. + /// + /// The target location MUST exist for the operation to be successful. + /// + /// For example: + /// + /// { "op": "replace", "path": "/a/b/c", "value": 42 } + /// + /// See RFC 6902 https://tools.ietf.org/html/rfc6902#page-6 + /// + /// The replace operation. + /// Object to apply the operation to. + void Replace(Operation operation, object objectToApplyTo); + } +} \ No newline at end of file diff --git a/src/Features/JsonPatch/src/Adapters/IObjectAdapterWithTest.cs b/src/Features/JsonPatch/src/Adapters/IObjectAdapterWithTest.cs new file mode 100644 index 0000000000..e1b4ce7950 --- /dev/null +++ b/src/Features/JsonPatch/src/Adapters/IObjectAdapterWithTest.cs @@ -0,0 +1,32 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNetCore.JsonPatch.Operations; + +namespace Microsoft.AspNetCore.JsonPatch.Adapters +{ + /// + /// Defines the operations that can be performed on a JSON patch document, including "test". + /// + public interface IObjectAdapterWithTest : IObjectAdapter + { + /// + /// Using the "test" operation a value at the target location is compared for + /// equality to a specified value. + /// + /// The operation object MUST contain a "value" member that specifies + /// value to be compared to the target location's value. + /// + /// The target location MUST be equal to the "value" value for the + /// operation to be considered successful. + /// + /// For example: + /// { "op": "test", "path": "/a/b/c", "value": "foo" } + /// + /// See RFC 6902 https://tools.ietf.org/html/rfc6902#page-7 + /// + /// The test operation. + /// Object to apply the operation to. + void Test(Operation operation, object objectToApplyTo); + } +} diff --git a/src/Features/JsonPatch/src/Adapters/ObjectAdapter.cs b/src/Features/JsonPatch/src/Adapters/ObjectAdapter.cs new file mode 100644 index 0000000000..d625176376 --- /dev/null +++ b/src/Features/JsonPatch/src/Adapters/ObjectAdapter.cs @@ -0,0 +1,348 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNetCore.JsonPatch.Internal; +using Microsoft.AspNetCore.JsonPatch.Operations; +using Newtonsoft.Json.Serialization; + +namespace Microsoft.AspNetCore.JsonPatch.Adapters +{ + /// + public class ObjectAdapter : IObjectAdapterWithTest + { + /// + /// Initializes a new instance of . + /// + /// The . + /// The for logging . + public ObjectAdapter( + IContractResolver contractResolver, + Action logErrorAction): + this(contractResolver, logErrorAction, new AdapterFactory()) + { + } + + /// + /// Initializes a new instance of . + /// + /// The . + /// The for logging . + /// The to use when creating adaptors. + public ObjectAdapter( + IContractResolver contractResolver, + Action logErrorAction, + IAdapterFactory adapterFactory) + { + ContractResolver = contractResolver ?? throw new ArgumentNullException(nameof(contractResolver)); + LogErrorAction = logErrorAction; + AdapterFactory = adapterFactory ?? throw new ArgumentNullException(nameof(adapterFactory)); + } + + /// + /// Gets or sets the . + /// + public IContractResolver ContractResolver { get; } + + /// + /// Gets or sets the + /// + public IAdapterFactory AdapterFactory { get; } + + /// + /// Action for logging . + /// + public Action LogErrorAction { get; } + + public void Add(Operation operation, object objectToApplyTo) + { + if (operation == null) + { + throw new ArgumentNullException(nameof(operation)); + } + + if (objectToApplyTo == null) + { + throw new ArgumentNullException(nameof(objectToApplyTo)); + } + + Add(operation.path, operation.value, objectToApplyTo, operation); + } + + /// + /// Add is used by various operations (eg: add, copy, ...), yet through different operations; + /// This method allows code reuse yet reporting the correct operation on error + /// + private void Add( + string path, + object value, + object objectToApplyTo, + Operation operation) + { + if (path == null) + { + throw new ArgumentNullException(nameof(path)); + } + + if (objectToApplyTo == null) + { + throw new ArgumentNullException(nameof(objectToApplyTo)); + } + + if (operation == null) + { + throw new ArgumentNullException(nameof(operation)); + } + + var parsedPath = new ParsedPath(path); + var visitor = new ObjectVisitor(parsedPath, ContractResolver, AdapterFactory); + + var target = objectToApplyTo; + if (!visitor.TryVisit(ref target, out var adapter, out var errorMessage)) + { + var error = CreatePathNotFoundError(objectToApplyTo, path, operation, errorMessage); + ErrorReporter(error); + return; + } + + if (!adapter.TryAdd(target, parsedPath.LastSegment, ContractResolver, value, out errorMessage)) + { + var error = CreateOperationFailedError(objectToApplyTo, path, operation, errorMessage); + ErrorReporter(error); + return; + } + } + + public void Move(Operation operation, object objectToApplyTo) + { + if (operation == null) + { + throw new ArgumentNullException(nameof(operation)); + } + + if (objectToApplyTo == null) + { + throw new ArgumentNullException(nameof(objectToApplyTo)); + } + + // Get value at 'from' location and add that value to the 'path' location + if (TryGetValue(operation.from, objectToApplyTo, operation, out var propertyValue)) + { + // remove that value + Remove(operation.from, objectToApplyTo, operation); + + // add that value to the path location + Add(operation.path, + propertyValue, + objectToApplyTo, + operation); + } + } + + public void Remove(Operation operation, object objectToApplyTo) + { + if (operation == null) + { + throw new ArgumentNullException(nameof(operation)); + } + + if (objectToApplyTo == null) + { + throw new ArgumentNullException(nameof(objectToApplyTo)); + } + + Remove(operation.path, objectToApplyTo, operation); + } + + /// + /// Remove is used by various operations (eg: remove, move, ...), yet through different operations; + /// This method allows code reuse yet reporting the correct operation on error. The return value + /// contains the type of the item that has been removed (and a bool possibly signifying an error) + /// This can be used by other methods, like replace, to ensure that we can pass in the correctly + /// typed value to whatever method follows. + /// + private void Remove(string path, object objectToApplyTo, Operation operationToReport) + { + var parsedPath = new ParsedPath(path); + var visitor = new ObjectVisitor(parsedPath, ContractResolver, AdapterFactory); + + var target = objectToApplyTo; + if (!visitor.TryVisit(ref target, out var adapter, out var errorMessage)) + { + var error = CreatePathNotFoundError(objectToApplyTo, path, operationToReport, errorMessage); + ErrorReporter(error); + return; + } + + if (!adapter.TryRemove(target, parsedPath.LastSegment, ContractResolver, out errorMessage)) + { + var error = CreateOperationFailedError(objectToApplyTo, path, operationToReport, errorMessage); + ErrorReporter(error); + return; + } + } + + public void Replace(Operation operation, object objectToApplyTo) + { + if (operation == null) + { + throw new ArgumentNullException(nameof(operation)); + } + + if (objectToApplyTo == null) + { + throw new ArgumentNullException(nameof(objectToApplyTo)); + } + + var parsedPath = new ParsedPath(operation.path); + var visitor = new ObjectVisitor(parsedPath, ContractResolver, AdapterFactory); + + var target = objectToApplyTo; + if (!visitor.TryVisit(ref target, out var adapter, out var errorMessage)) + { + var error = CreatePathNotFoundError(objectToApplyTo, operation.path, operation, errorMessage); + ErrorReporter(error); + return; + } + + if (!adapter.TryReplace(target, parsedPath.LastSegment, ContractResolver, operation.value, out errorMessage)) + { + var error = CreateOperationFailedError(objectToApplyTo, operation.path, operation, errorMessage); + ErrorReporter(error); + return; + } + } + + public void Copy(Operation operation, object objectToApplyTo) + { + if (operation == null) + { + throw new ArgumentNullException(nameof(operation)); + } + + if (objectToApplyTo == null) + { + throw new ArgumentNullException(nameof(objectToApplyTo)); + } + + // Get value at 'from' location and add that value to the 'path' location + if (TryGetValue(operation.from, objectToApplyTo, operation, out var propertyValue)) + { + // Create deep copy + var copyResult = ConversionResultProvider.CopyTo(propertyValue, propertyValue.GetType()); + if (copyResult.CanBeConverted) + { + Add(operation.path, + copyResult.ConvertedInstance, + objectToApplyTo, + operation); + } + else + { + var error = CreateOperationFailedError(objectToApplyTo, operation.path, operation, Resources.FormatCannotCopyProperty(operation.from)); + ErrorReporter(error); + return; + } + } + } + + public void Test(Operation operation, object objectToApplyTo) + { + if (operation == null) + { + throw new ArgumentNullException(nameof(operation)); + } + + if (objectToApplyTo == null) + { + throw new ArgumentNullException(nameof(objectToApplyTo)); + } + + var parsedPath = new ParsedPath(operation.path); + var visitor = new ObjectVisitor(parsedPath, ContractResolver, AdapterFactory); + + var target = objectToApplyTo; + if (!visitor.TryVisit(ref target, out var adapter, out var errorMessage)) + { + var error = CreatePathNotFoundError(objectToApplyTo, operation.path, operation, errorMessage); + ErrorReporter(error); + return; + } + + if (!adapter.TryTest(target, parsedPath.LastSegment, ContractResolver, operation.value, out errorMessage)) + { + var error = CreateOperationFailedError(objectToApplyTo, operation.path, operation, errorMessage); + ErrorReporter(error); + return; + } + } + + private bool TryGetValue( + string fromLocation, + object objectToGetValueFrom, + Operation operation, + out object propertyValue) + { + if (fromLocation == null) + { + throw new ArgumentNullException(nameof(fromLocation)); + } + + if (objectToGetValueFrom == null) + { + throw new ArgumentNullException(nameof(objectToGetValueFrom)); + } + + if (operation == null) + { + throw new ArgumentNullException(nameof(operation)); + } + + propertyValue = null; + + var parsedPath = new ParsedPath(fromLocation); + var visitor = new ObjectVisitor(parsedPath, ContractResolver, AdapterFactory); + + var target = objectToGetValueFrom; + if (!visitor.TryVisit(ref target, out var adapter, out var errorMessage)) + { + var error = CreatePathNotFoundError(objectToGetValueFrom, fromLocation, operation, errorMessage); + ErrorReporter(error); + return false; + } + + if (!adapter.TryGet(target, parsedPath.LastSegment, ContractResolver, out propertyValue, out errorMessage)) + { + var error = CreateOperationFailedError(objectToGetValueFrom, fromLocation, operation, errorMessage); + ErrorReporter(error); + return false; + } + + return true; + } + + private Action ErrorReporter + { + get + { + return LogErrorAction ?? Internal.ErrorReporter.Default; + } + } + + private JsonPatchError CreateOperationFailedError(object target, string path, Operation operation, string errorMessage) + { + return new JsonPatchError( + target, + operation, + errorMessage ?? Resources.FormatCannotPerformOperation(operation.op, path)); + } + + private JsonPatchError CreatePathNotFoundError(object target, string path, Operation operation, string errorMessage) + { + return new JsonPatchError( + target, + operation, + errorMessage ?? Resources.FormatTargetLocationNotFound(operation.op, path)); + } + } +} \ No newline at end of file diff --git a/src/Features/JsonPatch/src/Converters/JsonPatchDocumentConverter.cs b/src/Features/JsonPatch/src/Converters/JsonPatchDocumentConverter.cs new file mode 100644 index 0000000000..aed9c48474 --- /dev/null +++ b/src/Features/JsonPatch/src/Converters/JsonPatchDocumentConverter.cs @@ -0,0 +1,76 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using Microsoft.AspNetCore.JsonPatch.Exceptions; +using Microsoft.AspNetCore.JsonPatch.Operations; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Serialization; + +namespace Microsoft.AspNetCore.JsonPatch.Converters +{ + public class JsonPatchDocumentConverter : JsonConverter + { + public override bool CanConvert(Type objectType) + { + return true; + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, + JsonSerializer serializer) + { + if (objectType != typeof(JsonPatchDocument)) + { + throw new ArgumentException(Resources.FormatParameterMustMatchType("objectType", "JsonPatchDocument"), "objectType"); + } + + try + { + if (reader.TokenType == JsonToken.Null) + { + return null; + } + + // load jObject + var jObject = JArray.Load(reader); + + // Create target object for Json => list of operations + var targetOperations = new List(); + + // Create a new reader for this jObject, and set all properties + // to match the original reader. + var jObjectReader = jObject.CreateReader(); + jObjectReader.Culture = reader.Culture; + jObjectReader.DateParseHandling = reader.DateParseHandling; + jObjectReader.DateTimeZoneHandling = reader.DateTimeZoneHandling; + jObjectReader.FloatParseHandling = reader.FloatParseHandling; + + // Populate the object properties + serializer.Populate(jObjectReader, targetOperations); + + // container target: the JsonPatchDocument. + var container = new JsonPatchDocument(targetOperations, new DefaultContractResolver()); + + return container; + } + catch (Exception ex) + { + throw new JsonSerializationException(Resources.InvalidJsonPatchDocument, ex); + } + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + if (value is IJsonPatchDocument) + { + var jsonPatchDoc = (IJsonPatchDocument)value; + var lst = jsonPatchDoc.GetOperations(); + + // write out the operations, no envelope + serializer.Serialize(writer, lst); + } + } + } +} diff --git a/src/Features/JsonPatch/src/Converters/TypedJsonPatchDocumentConverter.cs b/src/Features/JsonPatch/src/Converters/TypedJsonPatchDocumentConverter.cs new file mode 100644 index 0000000000..fd779ba4ee --- /dev/null +++ b/src/Features/JsonPatch/src/Converters/TypedJsonPatchDocumentConverter.cs @@ -0,0 +1,64 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Reflection; +using Microsoft.AspNetCore.JsonPatch.Operations; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Serialization; + +namespace Microsoft.AspNetCore.JsonPatch.Converters +{ + public class TypedJsonPatchDocumentConverter : JsonPatchDocumentConverter + { + public override object ReadJson( + JsonReader reader, + Type objectType, + object existingValue, + JsonSerializer serializer) + { + try + { + if (reader.TokenType == JsonToken.Null) + { + return null; + } + + var genericType = objectType.GetTypeInfo().GenericTypeArguments[0]; + + // load jObject + var jObject = JArray.Load(reader); + + // Create target object for Json => list of operations, typed to genericType + var genericOperation = typeof(Operation<>); + var concreteOperationType = genericOperation.MakeGenericType(genericType); + + var genericList = typeof(List<>); + var concreteList = genericList.MakeGenericType(concreteOperationType); + + var targetOperations = Activator.CreateInstance(concreteList); + + //Create a new reader for this jObject, and set all properties to match the original reader. + var jObjectReader = jObject.CreateReader(); + jObjectReader.Culture = reader.Culture; + jObjectReader.DateParseHandling = reader.DateParseHandling; + jObjectReader.DateTimeZoneHandling = reader.DateTimeZoneHandling; + jObjectReader.FloatParseHandling = reader.FloatParseHandling; + + // Populate the object properties + serializer.Populate(jObjectReader, targetOperations); + + // container target: the typed JsonPatchDocument. + var container = Activator.CreateInstance(objectType, targetOperations, new DefaultContractResolver()); + + return container; + } + catch (Exception ex) + { + throw new JsonSerializationException(Resources.InvalidJsonPatchDocument, ex); + } + } + } +} \ No newline at end of file diff --git a/src/Features/JsonPatch/src/Exceptions/JsonPatchException.cs b/src/Features/JsonPatch/src/Exceptions/JsonPatchException.cs new file mode 100644 index 0000000000..90e080575a --- /dev/null +++ b/src/Features/JsonPatch/src/Exceptions/JsonPatchException.cs @@ -0,0 +1,38 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNetCore.JsonPatch.Operations; + +namespace Microsoft.AspNetCore.JsonPatch.Exceptions +{ + public class JsonPatchException : Exception + { + public Operation FailedOperation { get; private set; } + public object AffectedObject { get; private set; } + + + public JsonPatchException() + { + + } + + public JsonPatchException(JsonPatchError jsonPatchError, Exception innerException) + : base(jsonPatchError.ErrorMessage, innerException) + { + FailedOperation = jsonPatchError.Operation; + AffectedObject = jsonPatchError.AffectedObject; + } + + public JsonPatchException(JsonPatchError jsonPatchError) + : this(jsonPatchError, null) + { + } + + public JsonPatchException(string message, Exception innerException) + : base (message, innerException) + { + + } + } +} \ No newline at end of file diff --git a/src/Features/JsonPatch/src/Helpers/GetValueResult.cs b/src/Features/JsonPatch/src/Helpers/GetValueResult.cs new file mode 100644 index 0000000000..e2e739a027 --- /dev/null +++ b/src/Features/JsonPatch/src/Helpers/GetValueResult.cs @@ -0,0 +1,30 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.AspNetCore.JsonPatch.Helpers +{ + /// + /// Return value for the helper method used by Copy/Move. Needed to ensure we can make a different + /// decision in the calling method when the value is null because it cannot be fetched (HasError = true) + /// versus when it actually is null (much like why RemovedPropertyTypeResult is used for returning + /// type in the Remove operation). + /// + public class GetValueResult + { + public GetValueResult(object propertyValue, bool hasError) + { + PropertyValue = propertyValue; + HasError = hasError; + } + + /// + /// The value of the property we're trying to get + /// + public object PropertyValue { get; private set; } + + /// + /// HasError: true when an error occurred, the operation didn't complete succesfully + /// + public bool HasError { get; private set; } + } +} diff --git a/src/Features/JsonPatch/src/Helpers/JsonPatchProperty.cs b/src/Features/JsonPatch/src/Helpers/JsonPatchProperty.cs new file mode 100644 index 0000000000..041b0104ac --- /dev/null +++ b/src/Features/JsonPatch/src/Helpers/JsonPatchProperty.cs @@ -0,0 +1,43 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Newtonsoft.Json.Serialization; + +namespace Microsoft.AspNetCore.JsonPatch +{ + /// + /// Metadata for JsonProperty. + /// + public class JsonPatchProperty + { + /// + /// Initializes a new instance. + /// + public JsonPatchProperty(JsonProperty property, object parent) + { + if (property == null) + { + throw new ArgumentNullException(nameof(property)); + } + + if (parent == null) + { + throw new ArgumentNullException(nameof(parent)); + } + + Property = property; + Parent = parent; + } + + /// + /// Gets or sets JsonProperty. + /// + public JsonProperty Property { get; set; } + + /// + /// Gets or sets Parent. + /// + public object Parent { get; set; } + } +} \ No newline at end of file diff --git a/src/Features/JsonPatch/src/IJsonPatchDocument.cs b/src/Features/JsonPatch/src/IJsonPatchDocument.cs new file mode 100644 index 0000000000..fc5f5bd4d1 --- /dev/null +++ b/src/Features/JsonPatch/src/IJsonPatchDocument.cs @@ -0,0 +1,16 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNetCore.JsonPatch.Operations; +using System.Collections.Generic; +using Newtonsoft.Json.Serialization; + +namespace Microsoft.AspNetCore.JsonPatch +{ + public interface IJsonPatchDocument + { + IContractResolver ContractResolver { get; set; } + + IList GetOperations(); + } +} \ No newline at end of file diff --git a/src/Features/JsonPatch/src/Internal/ConversionResult.cs b/src/Features/JsonPatch/src/Internal/ConversionResult.cs new file mode 100644 index 0000000000..77181eb18d --- /dev/null +++ b/src/Features/JsonPatch/src/Internal/ConversionResult.cs @@ -0,0 +1,17 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.AspNetCore.JsonPatch.Internal +{ + public class ConversionResult + { + public ConversionResult(bool canBeConverted, object convertedInstance) + { + CanBeConverted = canBeConverted; + ConvertedInstance = convertedInstance; + } + + public bool CanBeConverted { get; } + public object ConvertedInstance { get; } + } +} \ No newline at end of file diff --git a/src/Features/JsonPatch/src/Internal/ConversionResultProvider.cs b/src/Features/JsonPatch/src/Internal/ConversionResultProvider.cs new file mode 100644 index 0000000000..71af0d27fc --- /dev/null +++ b/src/Features/JsonPatch/src/Internal/ConversionResultProvider.cs @@ -0,0 +1,75 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Reflection; +using Newtonsoft.Json; + +namespace Microsoft.AspNetCore.JsonPatch.Internal +{ + public static class ConversionResultProvider + { + public static ConversionResult ConvertTo(object value, Type typeToConvertTo) + { + if (value == null) + { + return new ConversionResult(IsNullableType(typeToConvertTo), null); + } + else if (typeToConvertTo.IsAssignableFrom(value.GetType())) + { + // No need to convert + return new ConversionResult(true, value); + } + else + { + try + { + var deserialized = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(value), typeToConvertTo); + return new ConversionResult(true, deserialized); + } + catch + { + return new ConversionResult(canBeConverted: false, convertedInstance: null); + } + } + } + + public static ConversionResult CopyTo(object value, Type typeToConvertTo) + { + var targetType = typeToConvertTo; + if (value == null) + { + return new ConversionResult(IsNullableType(typeToConvertTo), null); + } + else if (typeToConvertTo.IsAssignableFrom(value.GetType())) + { + // Keep original type + targetType = value.GetType(); + } + try + { + var deserialized = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(value), targetType); + return new ConversionResult(true, deserialized); + } + catch + { + return new ConversionResult(canBeConverted: false, convertedInstance: null); + } + } + + private static bool IsNullableType(Type type) + { + var typeInfo = type.GetTypeInfo(); + if (typeInfo.IsValueType) + { + // value types are only nullable if they are Nullable + return typeInfo.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>); + } + else + { + // reference types are always nullable + return true; + } + } + } +} diff --git a/src/Features/JsonPatch/src/Internal/DictionaryAdapterOfTU.cs b/src/Features/JsonPatch/src/Internal/DictionaryAdapterOfTU.cs new file mode 100644 index 0000000000..be2bbffd86 --- /dev/null +++ b/src/Features/JsonPatch/src/Internal/DictionaryAdapterOfTU.cs @@ -0,0 +1,245 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Serialization; + +namespace Microsoft.AspNetCore.JsonPatch.Internal +{ + public class DictionaryAdapter : IAdapter + { + public virtual bool TryAdd( + object target, + string segment, + IContractResolver contractResolver, + object value, + out string errorMessage) + { + var contract = (JsonDictionaryContract)contractResolver.ResolveContract(target.GetType()); + var key = contract.DictionaryKeyResolver(segment); + var dictionary = (IDictionary)target; + + // As per JsonPatch spec, if a key already exists, adding should replace the existing value + if (!TryConvertKey(key, out var convertedKey, out errorMessage)) + { + return false; + } + + if (!TryConvertValue(value, out var convertedValue, out errorMessage)) + { + return false; + } + + dictionary[convertedKey] = convertedValue; + errorMessage = null; + return true; + } + + public virtual bool TryGet( + object target, + string segment, + IContractResolver contractResolver, + out object value, + out string errorMessage) + { + var contract = (JsonDictionaryContract)contractResolver.ResolveContract(target.GetType()); + var key = contract.DictionaryKeyResolver(segment); + var dictionary = (IDictionary)target; + + if (!TryConvertKey(key, out var convertedKey, out errorMessage)) + { + value = null; + return false; + } + + if (!dictionary.ContainsKey(convertedKey)) + { + value = null; + errorMessage = Resources.FormatTargetLocationAtPathSegmentNotFound(segment); + return false; + } + + value = dictionary[convertedKey]; + errorMessage = null; + return true; + } + + public virtual bool TryRemove( + object target, + string segment, + IContractResolver contractResolver, + out string errorMessage) + { + var contract = (JsonDictionaryContract)contractResolver.ResolveContract(target.GetType()); + var key = contract.DictionaryKeyResolver(segment); + var dictionary = (IDictionary)target; + + if (!TryConvertKey(key, out var convertedKey, out errorMessage)) + { + return false; + } + + // As per JsonPatch spec, the target location must exist for remove to be successful + if (!dictionary.ContainsKey(convertedKey)) + { + errorMessage = Resources.FormatTargetLocationAtPathSegmentNotFound(segment); + return false; + } + + dictionary.Remove(convertedKey); + + errorMessage = null; + return true; + } + + public virtual bool TryReplace( + object target, + string segment, + IContractResolver contractResolver, + object value, + out string errorMessage) + { + var contract = (JsonDictionaryContract)contractResolver.ResolveContract(target.GetType()); + var key = contract.DictionaryKeyResolver(segment); + var dictionary = (IDictionary)target; + + if (!TryConvertKey(key, out var convertedKey, out errorMessage)) + { + return false; + } + + // As per JsonPatch spec, the target location must exist for remove to be successful + if (!dictionary.ContainsKey(convertedKey)) + { + errorMessage = Resources.FormatTargetLocationAtPathSegmentNotFound(segment); + return false; + } + + if (!TryConvertValue(value, out var convertedValue, out errorMessage)) + { + return false; + } + + dictionary[convertedKey] = convertedValue; + + errorMessage = null; + return true; + } + + public virtual bool TryTest( + object target, + string segment, + IContractResolver contractResolver, + object value, + out string errorMessage) + { + var contract = (JsonDictionaryContract)contractResolver.ResolveContract(target.GetType()); + var key = contract.DictionaryKeyResolver(segment); + var dictionary = (IDictionary)target; + + if (!TryConvertKey(key, out var convertedKey, out errorMessage)) + { + return false; + } + + // As per JsonPatch spec, the target location must exist for test to be successful + if (!dictionary.ContainsKey(convertedKey)) + { + errorMessage = Resources.FormatTargetLocationAtPathSegmentNotFound(segment); + return false; + } + + if (!TryConvertValue(value, out var convertedValue, out errorMessage)) + { + return false; + } + + var currentValue = dictionary[convertedKey]; + + // The target segment does not have an assigned value to compare the test value with + if (currentValue == null || string.IsNullOrEmpty(currentValue.ToString())) + { + errorMessage = Resources.FormatValueForTargetSegmentCannotBeNullOrEmpty(segment); + return false; + } + + if (!JToken.DeepEquals(JsonConvert.SerializeObject(currentValue), JsonConvert.SerializeObject(convertedValue))) + { + errorMessage = Resources.FormatValueNotEqualToTestValue(currentValue, value, segment); + return false; + } + else + { + errorMessage = null; + return true; + } + } + + public virtual bool TryTraverse( + object target, + string segment, + IContractResolver contractResolver, + out object nextTarget, + out string errorMessage) + { + var contract = (JsonDictionaryContract)contractResolver.ResolveContract(target.GetType()); + var key = contract.DictionaryKeyResolver(segment); + var dictionary = (IDictionary)target; + + if (!TryConvertKey(key, out var convertedKey, out errorMessage)) + { + nextTarget = null; + return false; + } + + if (dictionary.ContainsKey(convertedKey)) + { + nextTarget = dictionary[convertedKey]; + errorMessage = null; + return true; + } + else + { + nextTarget = null; + errorMessage = null; + return false; + } + } + + protected virtual bool TryConvertKey(string key, out TKey convertedKey, out string errorMessage) + { + var conversionResult = ConversionResultProvider.ConvertTo(key, typeof(TKey)); + if (conversionResult.CanBeConverted) + { + errorMessage = null; + convertedKey = (TKey)conversionResult.ConvertedInstance; + return true; + } + else + { + errorMessage = Resources.FormatInvalidPathSegment(key); + convertedKey = default(TKey); + return false; + } + } + + protected virtual bool TryConvertValue(object value, out TValue convertedValue, out string errorMessage) + { + var conversionResult = ConversionResultProvider.ConvertTo(value, typeof(TValue)); + if (conversionResult.CanBeConverted) + { + errorMessage = null; + convertedValue = (TValue)conversionResult.ConvertedInstance; + return true; + } + else + { + errorMessage = Resources.FormatInvalidValueForProperty(value); + convertedValue = default(TValue); + return false; + } + } + } +} diff --git a/src/Features/JsonPatch/src/Internal/DynamicObjectAdapter.cs b/src/Features/JsonPatch/src/Internal/DynamicObjectAdapter.cs new file mode 100644 index 0000000000..dc3c48266f --- /dev/null +++ b/src/Features/JsonPatch/src/Internal/DynamicObjectAdapter.cs @@ -0,0 +1,243 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Runtime.CompilerServices; +using Microsoft.CSharp.RuntimeBinder; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Serialization; +using CSharpBinder = Microsoft.CSharp.RuntimeBinder; + +namespace Microsoft.AspNetCore.JsonPatch.Internal +{ + public class DynamicObjectAdapter : IAdapter + { + public virtual bool TryAdd( + object target, + string segment, + IContractResolver contractResolver, + object value, + out string errorMessage) + { + if (!TrySetDynamicObjectProperty(target, contractResolver, segment, value, out errorMessage)) + { + return false; + } + + errorMessage = null; + return true; + } + + public virtual bool TryGet( + object target, + string segment, + IContractResolver contractResolver, + out object value, + out string errorMessage) + { + if (!TryGetDynamicObjectProperty(target, contractResolver, segment, out value, out errorMessage)) + { + value = null; + return false; + } + + errorMessage = null; + return true; + } + + public virtual bool TryRemove( + object target, + string segment, + IContractResolver contractResolver, + out string errorMessage) + { + if (!TryGetDynamicObjectProperty(target, contractResolver, segment, out var property, out errorMessage)) + { + return false; + } + + // Setting the value to "null" will use the default value in case of value types, and + // null in case of reference types + object value = null; + if (property.GetType().GetTypeInfo().IsValueType + && Nullable.GetUnderlyingType(property.GetType()) == null) + { + value = Activator.CreateInstance(property.GetType()); + } + + if (!TrySetDynamicObjectProperty(target, contractResolver, segment, value, out errorMessage)) + { + return false; + } + + errorMessage = null; + return true; + + } + + public virtual bool TryReplace( + object target, + string segment, + IContractResolver contractResolver, + object value, + out string errorMessage) + { + if (!TryGetDynamicObjectProperty(target, contractResolver, segment, out var property, out errorMessage)) + { + return false; + } + + if (!TryConvertValue(value, property.GetType(), out var convertedValue)) + { + errorMessage = Resources.FormatInvalidValueForProperty(value); + return false; + } + + if (!TrySetDynamicObjectProperty(target, contractResolver, segment, convertedValue, out errorMessage)) + { + return false; + } + + errorMessage = null; + return true; + } + + public virtual bool TryTest( + object target, + string segment, + IContractResolver contractResolver, + object value, + out string errorMessage) + { + if (!TryGetDynamicObjectProperty(target, contractResolver, segment, out var property, out errorMessage)) + { + return false; + } + + if (!TryConvertValue(value, property.GetType(), out var convertedValue)) + { + errorMessage = Resources.FormatInvalidValueForProperty(value); + return false; + } + + if (!JToken.DeepEquals(JsonConvert.SerializeObject(property), JsonConvert.SerializeObject(convertedValue))) + { + errorMessage = Resources.FormatValueNotEqualToTestValue(property, value, segment); + return false; + } + else + { + errorMessage = null; + return true; + } + } + + public virtual bool TryTraverse( + object target, + string segment, + IContractResolver contractResolver, + out object nextTarget, + out string errorMessage) + { + if (!TryGetDynamicObjectProperty(target, contractResolver, segment, out var property, out errorMessage)) + { + nextTarget = null; + return false; + } + else + { + nextTarget = property; + errorMessage = null; + return true; + } + } + + protected virtual bool TryGetDynamicObjectProperty( + object target, + IContractResolver contractResolver, + string segment, + out object value, + out string errorMessage) + { + var jsonDynamicContract = (JsonDynamicContract)contractResolver.ResolveContract(target.GetType()); + + var propertyName = jsonDynamicContract.PropertyNameResolver(segment); + + var binder = CSharpBinder.Binder.GetMember( + CSharpBinderFlags.None, + propertyName, + target.GetType(), + new List + { + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) + }); + + var callsite = CallSite>.Create(binder); + + try + { + value = callsite.Target(callsite, target); + errorMessage = null; + return true; + } + catch (RuntimeBinderException) + { + value = null; + errorMessage = Resources.FormatTargetLocationAtPathSegmentNotFound(segment); + return false; + } + } + + protected virtual bool TrySetDynamicObjectProperty( + object target, + IContractResolver contractResolver, + string segment, + object value, + out string errorMessage) + { + var jsonDynamicContract = (JsonDynamicContract)contractResolver.ResolveContract(target.GetType()); + + var propertyName = jsonDynamicContract.PropertyNameResolver(segment); + + var binder = CSharpBinder.Binder.SetMember( + CSharpBinderFlags.None, + propertyName, + target.GetType(), + new List + { + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) + }); + + var callsite = CallSite>.Create(binder); + + try + { + callsite.Target(callsite, target, value); + errorMessage = null; + return true; + } + catch (RuntimeBinderException) + { + errorMessage = Resources.FormatTargetLocationAtPathSegmentNotFound(segment); + return false; + } + } + + protected virtual bool TryConvertValue(object value, Type propertyType, out object convertedValue) + { + var conversionResult = ConversionResultProvider.ConvertTo(value, propertyType); + if (!conversionResult.CanBeConverted) + { + convertedValue = null; + return false; + } + + convertedValue = conversionResult.ConvertedInstance; + return true; + } + } +} diff --git a/src/Features/JsonPatch/src/Internal/ErrorReporter.cs b/src/Features/JsonPatch/src/Internal/ErrorReporter.cs new file mode 100644 index 0000000000..76b55a6144 --- /dev/null +++ b/src/Features/JsonPatch/src/Internal/ErrorReporter.cs @@ -0,0 +1,16 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNetCore.JsonPatch.Exceptions; + +namespace Microsoft.AspNetCore.JsonPatch.Internal +{ + internal static class ErrorReporter + { + public static readonly Action Default = (error) => + { + throw new JsonPatchException(error); + }; + } +} diff --git a/src/Features/JsonPatch/src/Internal/IAdapter.cs b/src/Features/JsonPatch/src/Internal/IAdapter.cs new file mode 100644 index 0000000000..ec28131f7d --- /dev/null +++ b/src/Features/JsonPatch/src/Internal/IAdapter.cs @@ -0,0 +1,51 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Newtonsoft.Json.Serialization; + +namespace Microsoft.AspNetCore.JsonPatch.Internal +{ + public interface IAdapter + { + bool TryTraverse( + object target, + string segment, + IContractResolver contractResolver, + out object nextTarget, + out string errorMessage); + + bool TryAdd( + object target, + string segment, + IContractResolver contractResolver, + object value, + out string errorMessage); + + bool TryRemove( + object target, + string segment, + IContractResolver contractResolver, + out string errorMessage); + + bool TryGet( + object target, + string segment, + IContractResolver contractResolver, + out object value, + out string errorMessage); + + bool TryReplace( + object target, + string segment, + IContractResolver contractResolver, + object value, + out string errorMessage); + + bool TryTest( + object target, + string segment, + IContractResolver contractResolver, + object value, + out string errorMessage); + } +} diff --git a/src/Features/JsonPatch/src/Internal/ListAdapter.cs b/src/Features/JsonPatch/src/Internal/ListAdapter.cs new file mode 100644 index 0000000000..597f7b9f5f --- /dev/null +++ b/src/Features/JsonPatch/src/Internal/ListAdapter.cs @@ -0,0 +1,349 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections; +using System.Collections.Generic; +using Microsoft.Extensions.Internal; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Serialization; + +namespace Microsoft.AspNetCore.JsonPatch.Internal +{ + public class ListAdapter : IAdapter + { + public virtual bool TryAdd( + object target, + string segment, + IContractResolver contractResolver, + object value, + out string errorMessage) + { + var list = (IList)target; + + if (!TryGetListTypeArgument(list, out var typeArgument, out errorMessage)) + { + return false; + } + + if (!TryGetPositionInfo(list, segment, OperationType.Add, out var positionInfo, out errorMessage)) + { + return false; + } + + if (!TryConvertValue(value, typeArgument, segment, out var convertedValue, out errorMessage)) + { + return false; + } + + if (positionInfo.Type == PositionType.EndOfList) + { + list.Add(convertedValue); + } + else + { + list.Insert(positionInfo.Index, convertedValue); + } + + errorMessage = null; + return true; + } + + public virtual bool TryGet( + object target, + string segment, + IContractResolver contractResolver, + out object value, + out string errorMessage) + { + var list = (IList)target; + + if (!TryGetListTypeArgument(list, out var typeArgument, out errorMessage)) + { + value = null; + return false; + } + + if (!TryGetPositionInfo(list, segment, OperationType.Get, out var positionInfo, out errorMessage)) + { + value = null; + return false; + } + + if (positionInfo.Type == PositionType.EndOfList) + { + value = list[list.Count - 1]; + } + else + { + value = list[positionInfo.Index]; + } + + errorMessage = null; + return true; + } + + public virtual bool TryRemove( + object target, + string segment, + IContractResolver contractResolver, + out string errorMessage) + { + var list = (IList)target; + + if (!TryGetListTypeArgument(list, out var typeArgument, out errorMessage)) + { + return false; + } + + if (!TryGetPositionInfo(list, segment, OperationType.Remove, out var positionInfo, out errorMessage)) + { + return false; + } + + if (positionInfo.Type == PositionType.EndOfList) + { + list.RemoveAt(list.Count - 1); + } + else + { + list.RemoveAt(positionInfo.Index); + } + + errorMessage = null; + return true; + } + + public virtual bool TryReplace( + object target, + string segment, + IContractResolver contractResolver, + object value, + out string errorMessage) + { + var list = (IList)target; + + if (!TryGetListTypeArgument(list, out var typeArgument, out errorMessage)) + { + return false; + } + + if (!TryGetPositionInfo(list, segment, OperationType.Replace, out var positionInfo, out errorMessage)) + { + return false; + } + + if (!TryConvertValue(value, typeArgument, segment, out var convertedValue, out errorMessage)) + { + return false; + } + + if (positionInfo.Type == PositionType.EndOfList) + { + list[list.Count - 1] = convertedValue; + } + else + { + list[positionInfo.Index] = convertedValue; + } + + errorMessage = null; + return true; + } + + public virtual bool TryTest( + object target, + string segment, + IContractResolver contractResolver, + object value, + out string errorMessage) + { + var list = (IList)target; + + if (!TryGetListTypeArgument(list, out var typeArgument, out errorMessage)) + { + return false; + } + + if (!TryGetPositionInfo(list, segment, OperationType.Replace, out var positionInfo, out errorMessage)) + { + return false; + } + + if (!TryConvertValue(value, typeArgument, segment, out var convertedValue, out errorMessage)) + { + return false; + } + + var currentValue = list[positionInfo.Index]; + if (!JToken.DeepEquals(JsonConvert.SerializeObject(currentValue), JsonConvert.SerializeObject(convertedValue))) + { + errorMessage = Resources.FormatValueAtListPositionNotEqualToTestValue(currentValue, value, positionInfo.Index); + return false; + } + else + { + errorMessage = null; + return true; + } + } + + public virtual bool TryTraverse( + object target, + string segment, + IContractResolver contractResolver, + out object value, + out string errorMessage) + { + var list = target as IList; + if (list == null) + { + value = null; + errorMessage = null; + return false; + } + + var index = -1; + if (!int.TryParse(segment, out index)) + { + value = null; + errorMessage = Resources.FormatInvalidIndexValue(segment); + return false; + } + + if (index < 0 || index >= list.Count) + { + value = null; + errorMessage = Resources.FormatIndexOutOfBounds(segment); + return false; + } + + value = list[index]; + errorMessage = null; + return true; + } + + protected virtual bool TryConvertValue( + object originalValue, + Type listTypeArgument, + string segment, + out object convertedValue, + out string errorMessage) + { + var conversionResult = ConversionResultProvider.ConvertTo(originalValue, listTypeArgument); + if (!conversionResult.CanBeConverted) + { + convertedValue = null; + errorMessage = Resources.FormatInvalidValueForProperty(originalValue); + return false; + } + + convertedValue = conversionResult.ConvertedInstance; + errorMessage = null; + return true; + } + + protected virtual bool TryGetListTypeArgument(IList list, out Type listTypeArgument, out string errorMessage) + { + // Arrays are not supported as they have fixed size and operations like Add, Insert do not make sense + var listType = list.GetType(); + if (listType.IsArray) + { + errorMessage = Resources.FormatPatchNotSupportedForArrays(listType.FullName); + listTypeArgument = null; + return false; + } + else + { + var genericList = ClosedGenericMatcher.ExtractGenericInterface(listType, typeof(IList<>)); + if (genericList == null) + { + errorMessage = Resources.FormatPatchNotSupportedForNonGenericLists(listType.FullName); + listTypeArgument = null; + return false; + } + else + { + listTypeArgument = genericList.GenericTypeArguments[0]; + errorMessage = null; + return true; + } + } + } + + protected virtual bool TryGetPositionInfo( + IList list, + string segment, + OperationType operationType, + out PositionInfo positionInfo, + out string errorMessage) + { + if (segment == "-") + { + positionInfo = new PositionInfo(PositionType.EndOfList, -1); + errorMessage = null; + return true; + } + + var position = -1; + if (int.TryParse(segment, out position)) + { + if (position >= 0 && position < list.Count) + { + positionInfo = new PositionInfo(PositionType.Index, position); + errorMessage = null; + return true; + } + // As per JSON Patch spec, for Add operation the index value representing the number of elements is valid, + // where as for other operations like Remove, Replace, Move and Copy the target index MUST exist. + else if (position == list.Count && operationType == OperationType.Add) + { + positionInfo = new PositionInfo(PositionType.EndOfList, -1); + errorMessage = null; + return true; + } + else + { + positionInfo = new PositionInfo(PositionType.OutOfBounds, position); + errorMessage = Resources.FormatIndexOutOfBounds(segment); + return false; + } + } + else + { + positionInfo = new PositionInfo(PositionType.Invalid, -1); + errorMessage = Resources.FormatInvalidIndexValue(segment); + return false; + } + } + + protected struct PositionInfo + { + public PositionInfo(PositionType type, int index) + { + Type = type; + Index = index; + } + + public PositionType Type { get; } + public int Index { get; } + } + + protected enum PositionType + { + Index, // valid index + EndOfList, // '-' + Invalid, // Ex: not an integer + OutOfBounds + } + + protected enum OperationType + { + Add, + Remove, + Get, + Replace + } + } +} diff --git a/src/Features/JsonPatch/src/Internal/ObjectVisitor.cs b/src/Features/JsonPatch/src/Internal/ObjectVisitor.cs new file mode 100644 index 0000000000..6cb4d7450c --- /dev/null +++ b/src/Features/JsonPatch/src/Internal/ObjectVisitor.cs @@ -0,0 +1,79 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNetCore.JsonPatch.Adapters; +using Newtonsoft.Json.Serialization; + +namespace Microsoft.AspNetCore.JsonPatch.Internal +{ + public class ObjectVisitor + { + private readonly IAdapterFactory _adapterFactory; + private readonly IContractResolver _contractResolver; + private readonly ParsedPath _path; + + /// + /// Initializes a new instance of . + /// + /// The path of the JsonPatch operation + /// The . + public ObjectVisitor(ParsedPath path, IContractResolver contractResolver) + :this(path, contractResolver, new AdapterFactory()) + { + } + + /// + /// Initializes a new instance of . + /// + /// The path of the JsonPatch operation + /// The . + /// The to use when creating adaptors. + public ObjectVisitor(ParsedPath path, IContractResolver contractResolver, IAdapterFactory adapterFactory) + { + _path = path; + _contractResolver = contractResolver ?? throw new ArgumentNullException(nameof(contractResolver)); + _adapterFactory = adapterFactory ?? throw new ArgumentNullException(nameof(adapterFactory)); + } + + public bool TryVisit(ref object target, out IAdapter adapter, out string errorMessage) + { + if (target == null) + { + adapter = null; + errorMessage = null; + return false; + } + + adapter = SelectAdapter(target); + + // Traverse until the penultimate segment to get the target object and adapter + for (var i = 0; i < _path.Segments.Count - 1; i++) + { + if (!adapter.TryTraverse(target, _path.Segments[i], _contractResolver, out var next, out errorMessage)) + { + adapter = null; + return false; + } + + // If we hit a null on an interior segment then we need to stop traversing. + if (next == null) + { + adapter = null; + return false; + } + + target = next; + adapter = SelectAdapter(target); + } + + errorMessage = null; + return true; + } + + private IAdapter SelectAdapter(object targetObject) + { + return _adapterFactory.Create(targetObject, _contractResolver); + } + } +} diff --git a/src/Features/JsonPatch/src/Internal/ParsedPath.cs b/src/Features/JsonPatch/src/Internal/ParsedPath.cs new file mode 100644 index 0000000000..8d0e69aa4d --- /dev/null +++ b/src/Features/JsonPatch/src/Internal/ParsedPath.cs @@ -0,0 +1,92 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNetCore.JsonPatch.Exceptions; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Microsoft.AspNetCore.JsonPatch.Internal +{ + public struct ParsedPath + { + private static readonly string[] Empty = null; + + private readonly string[] _segments; + + public ParsedPath(string path) + { + if (path == null) + { + throw new ArgumentNullException(nameof(path)); + } + + _segments = ParsePath(path); + } + + public string LastSegment + { + get + { + if (_segments == null || _segments.Length == 0) + { + return null; + } + + return _segments[_segments.Length - 1]; + } + } + + public IReadOnlyList Segments => _segments ?? Empty; + + private static string[] ParsePath(string path) + { + var strings = new List(); + var sb = new StringBuilder(path.Length); + + for (var i = 0; i < path.Length; i++) + { + if (path[i] == '/') + { + if (sb.Length > 0) + { + strings.Add(sb.ToString()); + sb.Length = 0; + } + } + else if (path[i] == '~') + { + ++i; + if (i >= path.Length) + { + throw new JsonPatchException(Resources.FormatInvalidValueForPath(path), null); + } + + if (path[i] == '0') + { + sb.Append('~'); + } + else if (path[i] == '1') + { + sb.Append('/'); + } + else + { + throw new JsonPatchException(Resources.FormatInvalidValueForPath(path), null); + } + } + else + { + sb.Append(path[i]); + } + } + + if (sb.Length > 0) + { + strings.Add(sb.ToString()); + } + + return strings.ToArray(); + } + } +} diff --git a/src/Features/JsonPatch/src/Internal/PathHelpers.cs b/src/Features/JsonPatch/src/Internal/PathHelpers.cs new file mode 100644 index 0000000000..f0afedb60e --- /dev/null +++ b/src/Features/JsonPatch/src/Internal/PathHelpers.cs @@ -0,0 +1,32 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNetCore.JsonPatch.Exceptions; +using System; + +namespace Microsoft.AspNetCore.JsonPatch.Internal +{ + internal static class PathHelpers + { + internal static string ValidateAndNormalizePath(string path) + { + // check for most common path errors on create. This is not + // absolutely necessary, but it allows us to already catch mistakes + // on creation of the patch document rather than on execute. + + if (path.Contains("//")) + { + throw new JsonPatchException(Resources.FormatInvalidValueForPath(path), null); + } + + if (!path.StartsWith("/", StringComparison.Ordinal)) + { + return "/" + path; + } + else + { + return path; + } + } + } +} diff --git a/src/Features/JsonPatch/src/Internal/PocoAdapter.cs b/src/Features/JsonPatch/src/Internal/PocoAdapter.cs new file mode 100644 index 0000000000..5ba3e5587b --- /dev/null +++ b/src/Features/JsonPatch/src/Internal/PocoAdapter.cs @@ -0,0 +1,236 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Linq; +using System.Reflection; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Serialization; + +namespace Microsoft.AspNetCore.JsonPatch.Internal +{ + public class PocoAdapter : IAdapter + { + public virtual bool TryAdd( + object target, + string segment, + IContractResolver contractResolver, + object value, + out string errorMessage) + { + if (!TryGetJsonProperty(target, contractResolver, segment, out var jsonProperty)) + { + errorMessage = Resources.FormatTargetLocationAtPathSegmentNotFound(segment); + return false; + } + + if (!jsonProperty.Writable) + { + errorMessage = Resources.FormatCannotUpdateProperty(segment); + return false; + } + + if (!TryConvertValue(value, jsonProperty.PropertyType, out var convertedValue)) + { + errorMessage = Resources.FormatInvalidValueForProperty(value); + return false; + } + + jsonProperty.ValueProvider.SetValue(target, convertedValue); + + errorMessage = null; + return true; + } + + public virtual bool TryGet( + object target, + string segment, + IContractResolver contractResolver, + out object value, + out string errorMessage) + { + if (!TryGetJsonProperty(target, contractResolver, segment, out var jsonProperty)) + { + errorMessage = Resources.FormatTargetLocationAtPathSegmentNotFound(segment); + value = null; + return false; + } + + if (!jsonProperty.Readable) + { + errorMessage = Resources.FormatCannotReadProperty(segment); + value = null; + return false; + } + + value = jsonProperty.ValueProvider.GetValue(target); + errorMessage = null; + return true; + } + + public virtual bool TryRemove( + object target, + string segment, + IContractResolver contractResolver, + out string errorMessage) + { + if (!TryGetJsonProperty(target, contractResolver, segment, out var jsonProperty)) + { + errorMessage = Resources.FormatTargetLocationAtPathSegmentNotFound(segment); + return false; + } + + if (!jsonProperty.Writable) + { + errorMessage = Resources.FormatCannotUpdateProperty(segment); + return false; + } + + // Setting the value to "null" will use the default value in case of value types, and + // null in case of reference types + object value = null; + if (jsonProperty.PropertyType.GetTypeInfo().IsValueType + && Nullable.GetUnderlyingType(jsonProperty.PropertyType) == null) + { + value = Activator.CreateInstance(jsonProperty.PropertyType); + } + + jsonProperty.ValueProvider.SetValue(target, value); + + errorMessage = null; + return true; + } + + public virtual bool TryReplace( + object target, + string segment, + IContractResolver + contractResolver, + object value, + out string errorMessage) + { + if (!TryGetJsonProperty(target, contractResolver, segment, out var jsonProperty)) + { + errorMessage = Resources.FormatTargetLocationAtPathSegmentNotFound(segment); + return false; + } + + if (!jsonProperty.Writable) + { + errorMessage = Resources.FormatCannotUpdateProperty(segment); + return false; + } + + if (!TryConvertValue(value, jsonProperty.PropertyType, out var convertedValue)) + { + errorMessage = Resources.FormatInvalidValueForProperty(value); + return false; + } + + jsonProperty.ValueProvider.SetValue(target, convertedValue); + + errorMessage = null; + return true; + } + + public virtual bool TryTest( + object target, + string segment, + IContractResolver + contractResolver, + object value, + out string errorMessage) + { + if (!TryGetJsonProperty(target, contractResolver, segment, out var jsonProperty)) + { + errorMessage = Resources.FormatTargetLocationAtPathSegmentNotFound(segment); + return false; + } + + if (!jsonProperty.Readable) + { + errorMessage = Resources.FormatCannotReadProperty(segment); + return false; + } + + if (!TryConvertValue(value, jsonProperty.PropertyType, out var convertedValue)) + { + errorMessage = Resources.FormatInvalidValueForProperty(value); + return false; + } + + var currentValue = jsonProperty.ValueProvider.GetValue(target); + if (!JToken.DeepEquals(JsonConvert.SerializeObject(currentValue), JsonConvert.SerializeObject(convertedValue))) + { + errorMessage = Resources.FormatValueNotEqualToTestValue(currentValue, value, segment); + return false; + } + + errorMessage = null; + return true; + } + + public virtual bool TryTraverse( + object target, + string segment, + IContractResolver contractResolver, + out object value, + out string errorMessage) + { + if (target == null) + { + value = null; + errorMessage = null; + return false; + } + + if (TryGetJsonProperty(target, contractResolver, segment, out var jsonProperty)) + { + value = jsonProperty.ValueProvider.GetValue(target); + errorMessage = null; + return true; + } + + value = null; + errorMessage = Resources.FormatTargetLocationAtPathSegmentNotFound(segment); + return false; + } + + protected virtual bool TryGetJsonProperty( + object target, + IContractResolver contractResolver, + string segment, + out JsonProperty jsonProperty) + { + if (contractResolver.ResolveContract(target.GetType()) is JsonObjectContract jsonObjectContract) + { + var pocoProperty = jsonObjectContract + .Properties + .FirstOrDefault(p => string.Equals(p.PropertyName, segment, StringComparison.OrdinalIgnoreCase)); + + if (pocoProperty != null) + { + jsonProperty = pocoProperty; + return true; + } + } + + jsonProperty = null; + return false; + } + + protected virtual bool TryConvertValue(object value, Type propertyType, out object convertedValue) + { + var conversionResult = ConversionResultProvider.ConvertTo(value, propertyType); + if (!conversionResult.CanBeConverted) + { + convertedValue = null; + return false; + } + + convertedValue = conversionResult.ConvertedInstance; + return true; + } + } +} diff --git a/src/Features/JsonPatch/src/JsonPatchDocument.cs b/src/Features/JsonPatch/src/JsonPatchDocument.cs new file mode 100644 index 0000000000..1888ea6b4b --- /dev/null +++ b/src/Features/JsonPatch/src/JsonPatchDocument.cs @@ -0,0 +1,271 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using Microsoft.AspNetCore.JsonPatch.Adapters; +using Microsoft.AspNetCore.JsonPatch.Converters; +using Microsoft.AspNetCore.JsonPatch.Exceptions; +using Microsoft.AspNetCore.JsonPatch.Internal; +using Microsoft.AspNetCore.JsonPatch.Operations; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace Microsoft.AspNetCore.JsonPatch +{ + // Implementation details: the purpose of this type of patch document is to allow creation of such + // documents for cases where there's no class/DTO to work on. Typical use case: backend not built in + // .NET or architecture doesn't contain a shared DTO layer. + [JsonConverter(typeof(JsonPatchDocumentConverter))] + public class JsonPatchDocument : IJsonPatchDocument + { + public List Operations { get; private set; } + + [JsonIgnore] + public IContractResolver ContractResolver { get; set; } + + public JsonPatchDocument() + { + Operations = new List(); + ContractResolver = new DefaultContractResolver(); + } + + public JsonPatchDocument(List operations, IContractResolver contractResolver) + { + if (operations == null) + { + throw new ArgumentNullException(nameof(operations)); + } + + if (contractResolver == null) + { + throw new ArgumentNullException(nameof(contractResolver)); + } + + Operations = operations; + ContractResolver = contractResolver; + } + + /// + /// Add operation. Will result in, for example, + /// { "op": "add", "path": "/a/b/c", "value": [ "foo", "bar" ] } + /// + /// target location + /// value + /// + public JsonPatchDocument Add(string path, object value) + { + if (path == null) + { + throw new ArgumentNullException(nameof(path)); + } + + Operations.Add(new Operation("add", PathHelpers.ValidateAndNormalizePath(path), null, value)); + return this; + } + + /// + /// Remove value at target location. Will result in, for example, + /// { "op": "remove", "path": "/a/b/c" } + /// + /// target location + /// + public JsonPatchDocument Remove(string path) + { + if (path == null) + { + throw new ArgumentNullException(nameof(path)); + } + + Operations.Add(new Operation("remove", PathHelpers.ValidateAndNormalizePath(path), null, null)); + return this; + } + + /// + /// Replace value. Will result in, for example, + /// { "op": "replace", "path": "/a/b/c", "value": 42 } + /// + /// target location + /// value + /// + public JsonPatchDocument Replace(string path, object value) + { + if (path == null) + { + throw new ArgumentNullException(nameof(path)); + } + + Operations.Add(new Operation("replace", PathHelpers.ValidateAndNormalizePath(path), null, value)); + return this; + } + + /// + /// Test value. Will result in, for example, + /// { "op": "test", "path": "/a/b/c", "value": 42 } + /// + /// target location + /// value + /// + public JsonPatchDocument Test(string path, object value) + { + if (path == null) + { + throw new ArgumentNullException(nameof(path)); + } + + Operations.Add(new Operation("test", PathHelpers.ValidateAndNormalizePath(path), null, value)); + return this; + } + + /// + /// Removes value at specified location and add it to the target location. Will result in, for example: + /// { "op": "move", "from": "/a/b/c", "path": "/a/b/d" } + /// + /// source location + /// target location + /// + public JsonPatchDocument Move(string from, string path) + { + if (from == null) + { + throw new ArgumentNullException(nameof(from)); + } + + if (path == null) + { + throw new ArgumentNullException(nameof(path)); + } + + Operations.Add(new Operation("move", PathHelpers.ValidateAndNormalizePath(path), PathHelpers.ValidateAndNormalizePath(from))); + return this; + } + + /// + /// Copy the value at specified location to the target location. Will result in, for example: + /// { "op": "copy", "from": "/a/b/c", "path": "/a/b/e" } + /// + /// source location + /// target location + /// + public JsonPatchDocument Copy(string from, string path) + { + if (from == null) + { + throw new ArgumentNullException(nameof(from)); + } + + if (path == null) + { + throw new ArgumentNullException(nameof(path)); + } + + Operations.Add(new Operation("copy", PathHelpers.ValidateAndNormalizePath(path), PathHelpers.ValidateAndNormalizePath(from))); + return this; + } + + /// + /// Apply this JsonPatchDocument + /// + /// Object to apply the JsonPatchDocument to + public void ApplyTo(object objectToApplyTo) + { + if (objectToApplyTo == null) + { + throw new ArgumentNullException(nameof(objectToApplyTo)); + } + + ApplyTo(objectToApplyTo, new ObjectAdapter(ContractResolver, null, new AdapterFactory())); + } + + /// + /// Apply this JsonPatchDocument + /// + /// Object to apply the JsonPatchDocument to + /// Action to log errors + public void ApplyTo(object objectToApplyTo, Action logErrorAction) + { + ApplyTo(objectToApplyTo, new ObjectAdapter(ContractResolver, logErrorAction, new AdapterFactory()), logErrorAction); + } + + /// + /// Apply this JsonPatchDocument + /// + /// Object to apply the JsonPatchDocument to + /// IObjectAdapter instance to use when applying + /// Action to log errors + public void ApplyTo(object objectToApplyTo, IObjectAdapter adapter, Action logErrorAction) + { + if (objectToApplyTo == null) + { + throw new ArgumentNullException(nameof(objectToApplyTo)); + } + + if (adapter == null) + { + throw new ArgumentNullException(nameof(adapter)); + } + + foreach (var op in Operations) + { + try + { + op.Apply(objectToApplyTo, adapter); + } + catch (JsonPatchException jsonPatchException) + { + var errorReporter = logErrorAction ?? ErrorReporter.Default; + errorReporter(new JsonPatchError(objectToApplyTo, op, jsonPatchException.Message)); + + // As per JSON Patch spec if an operation results in error, further operations should not be executed. + break; + } + } + } + + /// + /// Apply this JsonPatchDocument + /// + /// Object to apply the JsonPatchDocument to + /// IObjectAdapter instance to use when applying + public void ApplyTo(object objectToApplyTo, IObjectAdapter adapter) + { + if (objectToApplyTo == null) + { + throw new ArgumentNullException(nameof(objectToApplyTo)); + } + + if (adapter == null) + { + throw new ArgumentNullException(nameof(adapter)); + } + + // apply each operation in order + foreach (var op in Operations) + { + op.Apply(objectToApplyTo, adapter); + } + } + + IList IJsonPatchDocument.GetOperations() + { + var allOps = new List(); + + if (Operations != null) + { + foreach (var op in Operations) + { + var untypedOp = new Operation(); + + untypedOp.op = op.op; + untypedOp.value = op.value; + untypedOp.path = op.path; + untypedOp.from = op.from; + + allOps.Add(untypedOp); + } + } + + return allOps; + } + } +} diff --git a/src/Features/JsonPatch/src/JsonPatchDocumentOfT.cs b/src/Features/JsonPatch/src/JsonPatchDocumentOfT.cs new file mode 100644 index 0000000000..3db0aac458 --- /dev/null +++ b/src/Features/JsonPatch/src/JsonPatchDocumentOfT.cs @@ -0,0 +1,884 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Linq; +using System.Collections.Generic; +using System.Globalization; +using System.Linq.Expressions; +using Microsoft.AspNetCore.JsonPatch.Adapters; +using Microsoft.AspNetCore.JsonPatch.Converters; +using Microsoft.AspNetCore.JsonPatch.Exceptions; +using Microsoft.AspNetCore.JsonPatch.Internal; +using Microsoft.AspNetCore.JsonPatch.Operations; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace Microsoft.AspNetCore.JsonPatch +{ + // Implementation details: the purpose of this type of patch document is to ensure we can do type-checking + // when producing a JsonPatchDocument. However, we cannot send this "typed" over the wire, as that would require + // including type data in the JsonPatchDocument serialized as JSON (to allow for correct deserialization) - that's + // not according to RFC 6902, and would thus break cross-platform compatibility. + [JsonConverter(typeof(TypedJsonPatchDocumentConverter))] + public class JsonPatchDocument : IJsonPatchDocument where TModel : class + { + public List> Operations { get; private set; } + + [JsonIgnore] + public IContractResolver ContractResolver { get; set; } + + public JsonPatchDocument() + { + Operations = new List>(); + ContractResolver = new DefaultContractResolver(); + } + + // Create from list of operations + public JsonPatchDocument(List> operations, IContractResolver contractResolver) + { + Operations = operations ?? throw new ArgumentNullException(nameof(operations)); + ContractResolver = contractResolver ?? throw new ArgumentNullException(nameof(contractResolver)); + } + + /// + /// Add operation. Will result in, for example, + /// { "op": "add", "path": "/a/b/c", "value": [ "foo", "bar" ] } + /// + /// value type + /// target location + /// value + /// + public JsonPatchDocument Add(Expression> path, TProp value) + { + if (path == null) + { + throw new ArgumentNullException(nameof(path)); + } + + Operations.Add(new Operation( + "add", + GetPath(path, null), + from: null, + value: value)); + + return this; + } + + /// + /// Add value to list at given position + /// + /// value type + /// target location + /// value + /// position + /// + public JsonPatchDocument Add( + Expression>> path, + TProp value, + int position) + { + if (path == null) + { + throw new ArgumentNullException(nameof(path)); + } + + Operations.Add(new Operation( + "add", + GetPath(path, position.ToString()), + from: null, + value: value)); + + return this; + } + + /// + /// Add value to the end of the list + /// + /// value type + /// target location + /// value + /// + public JsonPatchDocument Add(Expression>> path, TProp value) + { + if (path == null) + { + throw new ArgumentNullException(nameof(path)); + } + + Operations.Add(new Operation( + "add", + GetPath(path, "-"), + from: null, + value: value)); + + return this; + } + + /// + /// Remove value at target location. Will result in, for example, + /// { "op": "remove", "path": "/a/b/c" } + /// + /// target location + /// + public JsonPatchDocument Remove(Expression> path) + { + if (path == null) + { + throw new ArgumentNullException(nameof(path)); + } + + Operations.Add(new Operation("remove", GetPath(path, null), from: null)); + + return this; + } + + /// + /// Remove value from list at given position + /// + /// value type + /// target location + /// position + /// + public JsonPatchDocument Remove(Expression>> path, int position) + { + if (path == null) + { + throw new ArgumentNullException(nameof(path)); + } + + Operations.Add(new Operation( + "remove", + GetPath(path, position.ToString()), + from: null)); + + return this; + } + + /// + /// Remove value from end of list + /// + /// value type + /// target location + /// + public JsonPatchDocument Remove(Expression>> path) + { + if (path == null) + { + throw new ArgumentNullException(nameof(path)); + } + + Operations.Add(new Operation( + "remove", + GetPath(path, "-"), + from: null)); + + return this; + } + + /// + /// Replace value. Will result in, for example, + /// { "op": "replace", "path": "/a/b/c", "value": 42 } + /// + /// target location + /// value + /// + public JsonPatchDocument Replace(Expression> path, TProp value) + { + if (path == null) + { + throw new ArgumentNullException(nameof(path)); + } + + Operations.Add(new Operation( + "replace", + GetPath(path, null), + from: null, + value: value)); + + return this; + } + + /// + /// Replace value in a list at given position + /// + /// value type + /// target location + /// value + /// position + /// + public JsonPatchDocument Replace(Expression>> path, + TProp value, int position) + { + if (path == null) + { + throw new ArgumentNullException(nameof(path)); + } + + Operations.Add(new Operation( + "replace", + GetPath(path, position.ToString()), + from: null, + value: value)); + + return this; + } + + /// + /// Replace value at end of a list + /// + /// value type + /// target location + /// value + /// + public JsonPatchDocument Replace(Expression>> path, TProp value) + { + if (path == null) + { + throw new ArgumentNullException(nameof(path)); + } + + Operations.Add(new Operation( + "replace", + GetPath(path, "-"), + from: null, + value: value)); + + return this; + } + + /// + /// Test value. Will result in, for example, + /// { "op": "test", "path": "/a/b/c", "value": 42 } + /// + /// target location + /// value + /// + public JsonPatchDocument Test(Expression> path, TProp value) + { + if (path == null) + { + throw new ArgumentNullException(nameof(path)); + } + + Operations.Add(new Operation( + "test", + GetPath(path, null), + from: null, + value: value)); + + return this; + } + + /// + /// Test value in a list at given position + /// + /// value type + /// target location + /// value + /// position + /// + public JsonPatchDocument Test(Expression>> path, + TProp value, int position) + { + if (path == null) + { + throw new ArgumentNullException(nameof(path)); + } + + Operations.Add(new Operation( + "test", + GetPath(path, position.ToString()), + from: null, + value: value)); + + return this; + } + + /// + /// Test value at end of a list + /// + /// value type + /// target location + /// value + /// + public JsonPatchDocument Test(Expression>> path, TProp value) + { + if (path == null) + { + throw new ArgumentNullException(nameof(path)); + } + + Operations.Add(new Operation( + "test", + GetPath(path, "-"), + from: null, + value: value)); + + return this; + } + + /// + /// Removes value at specified location and add it to the target location. Will result in, for example: + /// { "op": "move", "from": "/a/b/c", "path": "/a/b/d" } + /// + /// source location + /// target location + /// + public JsonPatchDocument Move( + Expression> from, + Expression> path) + { + if (from == null) + { + throw new ArgumentNullException(nameof(from)); + } + + if (path == null) + { + throw new ArgumentNullException(nameof(path)); + } + + Operations.Add(new Operation( + "move", + GetPath(path, null), + GetPath(from, null))); + + return this; + } + + /// + /// Move from a position in a list to a new location + /// + /// + /// source location + /// position + /// target location + /// + public JsonPatchDocument Move( + Expression>> from, + int positionFrom, + Expression> path) + { + if (from == null) + { + throw new ArgumentNullException(nameof(from)); + } + + if (path == null) + { + throw new ArgumentNullException(nameof(path)); + } + + Operations.Add(new Operation( + "move", + GetPath(path, null), + GetPath(from, positionFrom.ToString()))); + + return this; + } + + /// + /// Move from a property to a location in a list + /// + /// + /// source location + /// target location + /// position + /// + public JsonPatchDocument Move( + Expression> from, + Expression>> path, + int positionTo) + { + if (from == null) + { + throw new ArgumentNullException(nameof(from)); + } + + if (path == null) + { + throw new ArgumentNullException(nameof(path)); + } + + Operations.Add(new Operation( + "move", + GetPath(path, positionTo.ToString()), + GetPath(from, null))); + + return this; + } + + /// + /// Move from a position in a list to another location in a list + /// + /// + /// source location + /// position (source) + /// target location + /// position (target) + /// + public JsonPatchDocument Move( + Expression>> from, + int positionFrom, + Expression>> path, + int positionTo) + { + if (from == null) + { + throw new ArgumentNullException(nameof(from)); + } + + if (path == null) + { + throw new ArgumentNullException(nameof(path)); + } + + Operations.Add(new Operation( + "move", + GetPath(path, positionTo.ToString()), + GetPath(from, positionFrom.ToString()))); + + return this; + } + + /// + /// Move from a position in a list to the end of another list + /// + /// + /// source location + /// position + /// target location + /// + public JsonPatchDocument Move( + Expression>> from, + int positionFrom, + Expression>> path) + { + if (from == null) + { + throw new ArgumentNullException(nameof(from)); + } + + if (path == null) + { + throw new ArgumentNullException(nameof(path)); + } + + Operations.Add(new Operation( + "move", + GetPath(path, "-"), + GetPath(from, positionFrom.ToString()))); + + return this; + } + + /// + /// Move to the end of a list + /// + /// + /// source location + /// target location + /// + public JsonPatchDocument Move( + Expression> from, + Expression>> path) + { + if (from == null) + { + throw new ArgumentNullException(nameof(from)); + } + + if (path == null) + { + throw new ArgumentNullException(nameof(path)); + } + + Operations.Add(new Operation( + "move", + GetPath(path, "-"), + GetPath(from, null))); + + return this; + } + + /// + /// Copy the value at specified location to the target location. Willr esult in, for example: + /// { "op": "copy", "from": "/a/b/c", "path": "/a/b/e" } + /// + /// source location + /// target location + /// + public JsonPatchDocument Copy( + Expression> from, + Expression> path) + { + if (from == null) + { + throw new ArgumentNullException(nameof(from)); + } + + if (path == null) + { + throw new ArgumentNullException(nameof(path)); + } + + Operations.Add(new Operation( + "copy", + GetPath(path, null), + GetPath(from, null))); + + return this; + } + + /// + /// Copy from a position in a list to a new location + /// + /// + /// source location + /// position + /// target location + /// + public JsonPatchDocument Copy( + Expression>> from, + int positionFrom, + Expression> path) + { + if (from == null) + { + throw new ArgumentNullException(nameof(from)); + } + + if (path == null) + { + throw new ArgumentNullException(nameof(path)); + } + + Operations.Add(new Operation( + "copy", + GetPath(path, null), + GetPath(from, positionFrom.ToString()))); + + return this; + } + + /// + /// Copy from a property to a location in a list + /// + /// + /// source location + /// target location + /// position + /// + public JsonPatchDocument Copy( + Expression> from, + Expression>> path, + int positionTo) + { + if (from == null) + { + throw new ArgumentNullException(nameof(from)); + } + + if (path == null) + { + throw new ArgumentNullException(nameof(path)); + } + + Operations.Add(new Operation( + "copy", + GetPath(path, positionTo.ToString()), + GetPath(from, null))); + + return this; + } + + /// + /// Copy from a position in a list to a new location in a list + /// + /// + /// source location + /// position (source) + /// target location + /// position (target) + /// + public JsonPatchDocument Copy( + Expression>> from, + int positionFrom, + Expression>> path, + int positionTo) + { + if (from == null) + { + throw new ArgumentNullException(nameof(from)); + } + + if (path == null) + { + throw new ArgumentNullException(nameof(path)); + } + + Operations.Add(new Operation( + "copy", + GetPath(path, positionTo.ToString()), + GetPath(from, positionFrom.ToString()))); + + return this; + } + + /// + /// Copy from a position in a list to the end of another list + /// + /// + /// source location + /// position + /// target location + /// + public JsonPatchDocument Copy( + Expression>> from, + int positionFrom, + Expression>> path) + { + if (from == null) + { + throw new ArgumentNullException(nameof(from)); + } + + if (path == null) + { + throw new ArgumentNullException(nameof(path)); + } + + Operations.Add(new Operation( + "copy", + GetPath(path, "-"), + GetPath(from, positionFrom.ToString()))); + + return this; + } + + /// + /// Copy to the end of a list + /// + /// + /// source location + /// target location + /// + public JsonPatchDocument Copy( + Expression> from, + Expression>> path) + { + if (from == null) + { + throw new ArgumentNullException(nameof(from)); + } + + if (path == null) + { + throw new ArgumentNullException(nameof(path)); + } + + Operations.Add(new Operation( + "copy", + GetPath(path, "-"), + GetPath(from, null))); + + return this; + } + + /// + /// Apply this JsonPatchDocument + /// + /// Object to apply the JsonPatchDocument to + public void ApplyTo(TModel objectToApplyTo) + { + if (objectToApplyTo == null) + { + throw new ArgumentNullException(nameof(objectToApplyTo)); + } + + ApplyTo(objectToApplyTo, new ObjectAdapter(ContractResolver, null, new AdapterFactory())); + } + + /// + /// Apply this JsonPatchDocument + /// + /// Object to apply the JsonPatchDocument to + /// Action to log errors + public void ApplyTo(TModel objectToApplyTo, Action logErrorAction) + { + ApplyTo(objectToApplyTo, new ObjectAdapter(ContractResolver, logErrorAction, new AdapterFactory()), logErrorAction); + } + + /// + /// Apply this JsonPatchDocument + /// + /// Object to apply the JsonPatchDocument to + /// IObjectAdapter instance to use when applying + /// Action to log errors + public void ApplyTo(TModel objectToApplyTo, IObjectAdapter adapter, Action logErrorAction) + { + if (objectToApplyTo == null) + { + throw new ArgumentNullException(nameof(objectToApplyTo)); + } + + if (adapter == null) + { + throw new ArgumentNullException(nameof(adapter)); + } + + foreach (var op in Operations) + { + try + { + op.Apply(objectToApplyTo, adapter); + } + catch (JsonPatchException jsonPatchException) + { + var errorReporter = logErrorAction ?? ErrorReporter.Default; + errorReporter(new JsonPatchError(objectToApplyTo, op, jsonPatchException.Message)); + + // As per JSON Patch spec if an operation results in error, further operations should not be executed. + break; + } + } + } + + /// + /// Apply this JsonPatchDocument + /// + /// Object to apply the JsonPatchDocument to + /// IObjectAdapter instance to use when applying + public void ApplyTo(TModel objectToApplyTo, IObjectAdapter adapter) + { + if (objectToApplyTo == null) + { + throw new ArgumentNullException(nameof(objectToApplyTo)); + } + + if (adapter == null) + { + throw new ArgumentNullException(nameof(adapter)); + } + + // apply each operation in order + foreach (var op in Operations) + { + op.Apply(objectToApplyTo, adapter); + } + } + + IList IJsonPatchDocument.GetOperations() + { + var allOps = new List(); + + if (Operations != null) + { + foreach (var op in Operations) + { + var untypedOp = new Operation + { + op = op.op, + value = op.value, + path = op.path, + from = op.from + }; + + allOps.Add(untypedOp); + } + } + + return allOps; + } + + // Internal for testing + internal string GetPath(Expression> expr, string position) + { + var segments = GetPathSegments(expr.Body); + var path = String.Join("/", segments); + if (position != null) + { + path += "/" + position; + if (segments.Count == 0) + { + return path; + } + } + + return "/" + path; + } + + private List GetPathSegments(Expression expr) + { + var listOfSegments = new List(); + switch (expr.NodeType) + { + case ExpressionType.ArrayIndex: + var binaryExpression = (BinaryExpression)expr; + listOfSegments.AddRange(GetPathSegments(binaryExpression.Left)); + listOfSegments.Add(binaryExpression.Right.ToString()); + return listOfSegments; + + case ExpressionType.Call: + var methodCallExpression = (MethodCallExpression)expr; + listOfSegments.AddRange(GetPathSegments(methodCallExpression.Object)); + listOfSegments.Add(EvaluateExpression(methodCallExpression.Arguments[0])); + return listOfSegments; + + case ExpressionType.Convert: + listOfSegments.AddRange(GetPathSegments(((UnaryExpression)expr).Operand)); + return listOfSegments; + + case ExpressionType.MemberAccess: + var memberExpression = expr as MemberExpression; + listOfSegments.AddRange(GetPathSegments(memberExpression.Expression)); + // Get property name, respecting JsonProperty attribute + listOfSegments.Add(GetPropertyNameFromMemberExpression(memberExpression)); + return listOfSegments; + + case ExpressionType.Parameter: + // Fits "x => x" (the whole document which is "" as JSON pointer) + return listOfSegments; + + default: + throw new InvalidOperationException(Resources.FormatExpressionTypeNotSupported(expr)); + } + } + + private string GetPropertyNameFromMemberExpression(MemberExpression memberExpression) + { + var jsonObjectContract = ContractResolver.ResolveContract(memberExpression.Expression.Type) as JsonObjectContract; + if (jsonObjectContract != null) + { + return jsonObjectContract.Properties + .First(jsonProperty => jsonProperty.UnderlyingName == memberExpression.Member.Name) + .PropertyName; + } + + return null; + } + + private static bool ContinueWithSubPath(ExpressionType expressionType) + { + return (expressionType == ExpressionType.ArrayIndex + || expressionType == ExpressionType.Call + || expressionType == ExpressionType.Convert + || expressionType == ExpressionType.MemberAccess); + + } + + // Evaluates the value of the key or index which may be an int or a string, + // or some other expression type. + // The expression is converted to a delegate and the result of executing the delegate is returned as a string. + private static string EvaluateExpression(Expression expression) + { + var converted = Expression.Convert(expression, typeof(object)); + var fakeParameter = Expression.Parameter(typeof(object), null); + var lambda = Expression.Lambda>(converted, fakeParameter); + var func = lambda.Compile(); + + return Convert.ToString(func(null), CultureInfo.InvariantCulture); + } + } +} \ No newline at end of file diff --git a/src/Features/JsonPatch/src/JsonPatchError.cs b/src/Features/JsonPatch/src/JsonPatchError.cs new file mode 100644 index 0000000000..a49af7a4e2 --- /dev/null +++ b/src/Features/JsonPatch/src/JsonPatchError.cs @@ -0,0 +1,50 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNetCore.JsonPatch.Operations; + +namespace Microsoft.AspNetCore.JsonPatch +{ + /// + /// Captures error message and the related entity and the operation that caused it. + /// + public class JsonPatchError + { + /// + /// Initializes a new instance of . + /// + /// The object that is affected by the error. + /// The that caused the error. + /// The error message. + public JsonPatchError( + object affectedObject, + Operation operation, + string errorMessage) + { + if (errorMessage == null) + { + throw new ArgumentNullException(nameof(errorMessage)); + } + + AffectedObject = affectedObject; + Operation = operation; + ErrorMessage = errorMessage; + } + + /// + /// Gets the object that is affected by the error. + /// + public object AffectedObject { get; } + + /// + /// Gets the that caused the error. + /// + public Operation Operation { get; } + + /// + /// Gets the error message. + /// + public string ErrorMessage { get; } + } +} \ No newline at end of file diff --git a/src/Features/JsonPatch/src/Microsoft.AspNetCore.JsonPatch.csproj b/src/Features/JsonPatch/src/Microsoft.AspNetCore.JsonPatch.csproj new file mode 100644 index 0000000000..3708e92927 --- /dev/null +++ b/src/Features/JsonPatch/src/Microsoft.AspNetCore.JsonPatch.csproj @@ -0,0 +1,17 @@ + + + + ASP.NET Core support for JSON PATCH. + netstandard2.0 + $(NoWarn);CS1591 + true + aspnetcore;json;jsonpatch + + + + + + + + + diff --git a/src/Features/JsonPatch/src/Operations/Operation.cs b/src/Features/JsonPatch/src/Operations/Operation.cs new file mode 100644 index 0000000000..690ade4776 --- /dev/null +++ b/src/Features/JsonPatch/src/Operations/Operation.cs @@ -0,0 +1,82 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNetCore.JsonPatch.Adapters; +using Newtonsoft.Json; + +namespace Microsoft.AspNetCore.JsonPatch.Operations +{ + public class Operation : OperationBase + { + [JsonProperty("value")] + public object value { get; set; } + + public Operation() + { + + } + + public Operation(string op, string path, string from, object value) + : base(op, path, from) + { + this.value = value; + } + + public Operation(string op, string path, string from) + : base(op, path, from) + { + } + + public void Apply(object objectToApplyTo, IObjectAdapter adapter) + { + if (objectToApplyTo == null) + { + throw new ArgumentNullException(nameof(objectToApplyTo)); + } + + if (adapter == null) + { + throw new ArgumentNullException(nameof(adapter)); + } + + switch (OperationType) + { + case OperationType.Add: + adapter.Add(this, objectToApplyTo); + break; + case OperationType.Remove: + adapter.Remove(this, objectToApplyTo); + break; + case OperationType.Replace: + adapter.Replace(this, objectToApplyTo); + break; + case OperationType.Move: + adapter.Move(this, objectToApplyTo); + break; + case OperationType.Copy: + adapter.Copy(this, objectToApplyTo); + break; + case OperationType.Test: + if (adapter is IObjectAdapterWithTest adapterWithTest) + { + adapterWithTest.Test(this, objectToApplyTo); + break; + } + else + { + throw new NotSupportedException(Resources.TestOperationNotSupported); + } + default: + break; + } + } + + public bool ShouldSerializevalue() + { + return (OperationType == OperationType.Add + || OperationType == OperationType.Replace + || OperationType == OperationType.Test); + } + } +} \ No newline at end of file diff --git a/src/Features/JsonPatch/src/Operations/OperationBase.cs b/src/Features/JsonPatch/src/Operations/OperationBase.cs new file mode 100644 index 0000000000..e629e2308d --- /dev/null +++ b/src/Features/JsonPatch/src/Operations/OperationBase.cs @@ -0,0 +1,76 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Newtonsoft.Json; + +namespace Microsoft.AspNetCore.JsonPatch.Operations +{ + public class OperationBase + { + private string _op; + private OperationType _operationType; + + [JsonIgnore] + public OperationType OperationType + { + get + { + return _operationType; + } + } + + [JsonProperty("path")] + public string path { get; set; } + + [JsonProperty("op")] + public string op + { + get + { + return _op; + } + set + { + OperationType result; + if (!Enum.TryParse(value, ignoreCase: true, result: out result)) + { + result = OperationType.Invalid; + } + _operationType = result; + _op = value; + } + } + + [JsonProperty("from")] + public string from { get; set; } + + public OperationBase() + { + + } + + public OperationBase(string op, string path, string from) + { + if (op == null) + { + throw new ArgumentNullException(nameof(op)); + } + + if (path == null) + { + throw new ArgumentNullException(nameof(path)); + } + + this.op = op; + this.path = path; + this.from = from; + } + + public bool ShouldSerializefrom() + { + return (OperationType == OperationType.Move + || OperationType == OperationType.Copy); + } + } +} \ No newline at end of file diff --git a/src/Features/JsonPatch/src/Operations/OperationOfT.cs b/src/Features/JsonPatch/src/Operations/OperationOfT.cs new file mode 100644 index 0000000000..bd13528775 --- /dev/null +++ b/src/Features/JsonPatch/src/Operations/OperationOfT.cs @@ -0,0 +1,94 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNetCore.JsonPatch.Adapters; +using Microsoft.AspNetCore.JsonPatch.Exceptions; + +namespace Microsoft.AspNetCore.JsonPatch.Operations +{ + public class Operation : Operation where TModel : class + { + public Operation() + { + + } + + public Operation(string op, string path, string from, object value) + : base(op, path, from) + { + if (op == null) + { + throw new ArgumentNullException(nameof(op)); + } + + if (path == null) + { + throw new ArgumentNullException(nameof(path)); + } + + this.value = value; + } + + public Operation(string op, string path, string from) + : base(op, path, from) + { + if (op == null) + { + throw new ArgumentNullException(nameof(op)); + } + if (path == null) + { + throw new ArgumentNullException(nameof(path)); + } + + } + + public void Apply(TModel objectToApplyTo, IObjectAdapter adapter) + { + if (objectToApplyTo == null) + { + throw new ArgumentNullException(nameof(objectToApplyTo)); + } + + if (adapter == null) + { + throw new ArgumentNullException(nameof(adapter)); + } + + switch (OperationType) + { + case OperationType.Add: + adapter.Add(this, objectToApplyTo); + break; + case OperationType.Remove: + adapter.Remove(this, objectToApplyTo); + break; + case OperationType.Replace: + adapter.Replace(this, objectToApplyTo); + break; + case OperationType.Move: + adapter.Move(this, objectToApplyTo); + break; + case OperationType.Copy: + adapter.Copy(this, objectToApplyTo); + break; + case OperationType.Test: + if (adapter is IObjectAdapterWithTest adapterWithTest) + { + adapterWithTest.Test(this, objectToApplyTo); + break; + } + else + { + throw new JsonPatchException(new JsonPatchError(objectToApplyTo, this, Resources.TestOperationNotSupported)); + } + case OperationType.Invalid: + throw new JsonPatchException( + Resources.FormatInvalidJsonPatchOperation(op), innerException: null); + default: + break; + } + } + } +} \ No newline at end of file diff --git a/src/Features/JsonPatch/src/Operations/OperationType.cs b/src/Features/JsonPatch/src/Operations/OperationType.cs new file mode 100644 index 0000000000..725646df3a --- /dev/null +++ b/src/Features/JsonPatch/src/Operations/OperationType.cs @@ -0,0 +1,16 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.AspNetCore.JsonPatch.Operations +{ + public enum OperationType + { + Add, + Remove, + Replace, + Move, + Copy, + Test, + Invalid + } +} \ No newline at end of file diff --git a/src/Features/JsonPatch/src/Properties/AssemblyInfo.cs b/src/Features/JsonPatch/src/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..11fa956b64 --- /dev/null +++ b/src/Features/JsonPatch/src/Properties/AssemblyInfo.cs @@ -0,0 +1,7 @@ + +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Microsoft.AspNetCore.JsonPatch.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] diff --git a/src/Features/JsonPatch/src/Properties/Resources.Designer.cs b/src/Features/JsonPatch/src/Properties/Resources.Designer.cs new file mode 100644 index 0000000000..c314465238 --- /dev/null +++ b/src/Features/JsonPatch/src/Properties/Resources.Designer.cs @@ -0,0 +1,338 @@ +// +namespace Microsoft.AspNetCore.JsonPatch +{ + using System.Globalization; + using System.Reflection; + using System.Resources; + + internal static class Resources + { + private static readonly ResourceManager _resourceManager + = new ResourceManager("Microsoft.AspNetCore.JsonPatch.Resources", typeof(Resources).GetTypeInfo().Assembly); + + /// + /// The property at '{0}' could not be copied. + /// + internal static string CannotCopyProperty + { + get => GetString("CannotCopyProperty"); + } + + /// + /// The property at '{0}' could not be copied. + /// + internal static string FormatCannotCopyProperty(object p0) + => string.Format(CultureInfo.CurrentCulture, GetString("CannotCopyProperty"), p0); + + /// + /// The type of the property at path '{0}' could not be determined. + /// + internal static string CannotDeterminePropertyType + { + get => GetString("CannotDeterminePropertyType"); + } + + /// + /// The type of the property at path '{0}' could not be determined. + /// + internal static string FormatCannotDeterminePropertyType(object p0) + => string.Format(CultureInfo.CurrentCulture, GetString("CannotDeterminePropertyType"), p0); + + /// + /// The '{0}' operation at path '{1}' could not be performed. + /// + internal static string CannotPerformOperation + { + get => GetString("CannotPerformOperation"); + } + + /// + /// The '{0}' operation at path '{1}' could not be performed. + /// + internal static string FormatCannotPerformOperation(object p0, object p1) + => string.Format(CultureInfo.CurrentCulture, GetString("CannotPerformOperation"), p0, p1); + + /// + /// The property at '{0}' could not be read. + /// + internal static string CannotReadProperty + { + get => GetString("CannotReadProperty"); + } + + /// + /// The property at '{0}' could not be read. + /// + internal static string FormatCannotReadProperty(object p0) + => string.Format(CultureInfo.CurrentCulture, GetString("CannotReadProperty"), p0); + + /// + /// The property at path '{0}' could not be updated. + /// + internal static string CannotUpdateProperty + { + get => GetString("CannotUpdateProperty"); + } + + /// + /// The property at path '{0}' could not be updated. + /// + internal static string FormatCannotUpdateProperty(object p0) + => string.Format(CultureInfo.CurrentCulture, GetString("CannotUpdateProperty"), p0); + + /// + /// The expression '{0}' is not supported. Supported expressions include member access and indexer expressions. + /// + internal static string ExpressionTypeNotSupported + { + get => GetString("ExpressionTypeNotSupported"); + } + + /// + /// The expression '{0}' is not supported. Supported expressions include member access and indexer expressions. + /// + internal static string FormatExpressionTypeNotSupported(object p0) + => string.Format(CultureInfo.CurrentCulture, GetString("ExpressionTypeNotSupported"), p0); + + /// + /// The index value provided by path segment '{0}' is out of bounds of the array size. + /// + internal static string IndexOutOfBounds + { + get => GetString("IndexOutOfBounds"); + } + + /// + /// The index value provided by path segment '{0}' is out of bounds of the array size. + /// + internal static string FormatIndexOutOfBounds(object p0) + => string.Format(CultureInfo.CurrentCulture, GetString("IndexOutOfBounds"), p0); + + /// + /// The path segment '{0}' is invalid for an array index. + /// + internal static string InvalidIndexValue + { + get => GetString("InvalidIndexValue"); + } + + /// + /// The path segment '{0}' is invalid for an array index. + /// + internal static string FormatInvalidIndexValue(object p0) + => string.Format(CultureInfo.CurrentCulture, GetString("InvalidIndexValue"), p0); + + /// + /// The JSON patch document was malformed and could not be parsed. + /// + internal static string InvalidJsonPatchDocument + { + get => GetString("InvalidJsonPatchDocument"); + } + + /// + /// The JSON patch document was malformed and could not be parsed. + /// + internal static string FormatInvalidJsonPatchDocument() + => GetString("InvalidJsonPatchDocument"); + + /// + /// Invalid JsonPatch operation '{0}'. + /// + internal static string InvalidJsonPatchOperation + { + get => GetString("InvalidJsonPatchOperation"); + } + + /// + /// Invalid JsonPatch operation '{0}'. + /// + internal static string FormatInvalidJsonPatchOperation(object p0) + => string.Format(CultureInfo.CurrentCulture, GetString("InvalidJsonPatchOperation"), p0); + + /// + /// The provided path segment '{0}' cannot be converted to the target type. + /// + internal static string InvalidPathSegment + { + get => GetString("InvalidPathSegment"); + } + + /// + /// The provided path segment '{0}' cannot be converted to the target type. + /// + internal static string FormatInvalidPathSegment(object p0) + => string.Format(CultureInfo.CurrentCulture, GetString("InvalidPathSegment"), p0); + + /// + /// The provided string '{0}' is an invalid path. + /// + internal static string InvalidValueForPath + { + get => GetString("InvalidValueForPath"); + } + + /// + /// The provided string '{0}' is an invalid path. + /// + internal static string FormatInvalidValueForPath(object p0) + => string.Format(CultureInfo.CurrentCulture, GetString("InvalidValueForPath"), p0); + + /// + /// The value '{0}' is invalid for target location. + /// + internal static string InvalidValueForProperty + { + get => GetString("InvalidValueForProperty"); + } + + /// + /// The value '{0}' is invalid for target location. + /// + internal static string FormatInvalidValueForProperty(object p0) + => string.Format(CultureInfo.CurrentCulture, GetString("InvalidValueForProperty"), p0); + + /// + /// '{0}' must be of type '{1}'. + /// + internal static string ParameterMustMatchType + { + get => GetString("ParameterMustMatchType"); + } + + /// + /// '{0}' must be of type '{1}'. + /// + internal static string FormatParameterMustMatchType(object p0, object p1) + => string.Format(CultureInfo.CurrentCulture, GetString("ParameterMustMatchType"), p0, p1); + + /// + /// The type '{0}' which is an array is not supported for json patch operations as it has a fixed size. + /// + internal static string PatchNotSupportedForArrays + { + get => GetString("PatchNotSupportedForArrays"); + } + + /// + /// The type '{0}' which is an array is not supported for json patch operations as it has a fixed size. + /// + internal static string FormatPatchNotSupportedForArrays(object p0) + => string.Format(CultureInfo.CurrentCulture, GetString("PatchNotSupportedForArrays"), p0); + + /// + /// The type '{0}' which is a non generic list is not supported for json patch operations. Only generic list types are supported. + /// + internal static string PatchNotSupportedForNonGenericLists + { + get => GetString("PatchNotSupportedForNonGenericLists"); + } + + /// + /// The type '{0}' which is a non generic list is not supported for json patch operations. Only generic list types are supported. + /// + internal static string FormatPatchNotSupportedForNonGenericLists(object p0) + => string.Format(CultureInfo.CurrentCulture, GetString("PatchNotSupportedForNonGenericLists"), p0); + + /// + /// The target location specified by path segment '{0}' was not found. + /// + internal static string TargetLocationAtPathSegmentNotFound + { + get => GetString("TargetLocationAtPathSegmentNotFound"); + } + + /// + /// The target location specified by path segment '{0}' was not found. + /// + internal static string FormatTargetLocationAtPathSegmentNotFound(object p0) + => string.Format(CultureInfo.CurrentCulture, GetString("TargetLocationAtPathSegmentNotFound"), p0); + + /// + /// For operation '{0}', the target location specified by path '{1}' was not found. + /// + internal static string TargetLocationNotFound + { + get => GetString("TargetLocationNotFound"); + } + + /// + /// For operation '{0}', the target location specified by path '{1}' was not found. + /// + internal static string FormatTargetLocationNotFound(object p0, object p1) + => string.Format(CultureInfo.CurrentCulture, GetString("TargetLocationNotFound"), p0, p1); + + /// + /// The test operation is not supported. + /// + internal static string TestOperationNotSupported + { + get => GetString("TestOperationNotSupported"); + } + + /// + /// The test operation is not supported. + /// + internal static string FormatTestOperationNotSupported() + => GetString("TestOperationNotSupported"); + + /// + /// The current value '{0}' at position '{2}' is not equal to the test value '{1}'. + /// + internal static string ValueAtListPositionNotEqualToTestValue + { + get => GetString("ValueAtListPositionNotEqualToTestValue"); + } + + /// + /// The current value '{0}' at position '{2}' is not equal to the test value '{1}'. + /// + internal static string FormatValueAtListPositionNotEqualToTestValue(object p0, object p1, object p2) + => string.Format(CultureInfo.CurrentCulture, GetString("ValueAtListPositionNotEqualToTestValue"), p0, p1, p2); + + /// + /// The value at '{0}' cannot be null or empty to perform the test operation. + /// + internal static string ValueForTargetSegmentCannotBeNullOrEmpty + { + get => GetString("ValueForTargetSegmentCannotBeNullOrEmpty"); + } + + /// + /// The value at '{0}' cannot be null or empty to perform the test operation. + /// + internal static string FormatValueForTargetSegmentCannotBeNullOrEmpty(object p0) + => string.Format(CultureInfo.CurrentCulture, GetString("ValueForTargetSegmentCannotBeNullOrEmpty"), p0); + + /// + /// The current value '{0}' at path '{2}' is not equal to the test value '{1}'. + /// + internal static string ValueNotEqualToTestValue + { + get => GetString("ValueNotEqualToTestValue"); + } + + /// + /// The current value '{0}' at path '{2}' is not equal to the test value '{1}'. + /// + internal static string FormatValueNotEqualToTestValue(object p0, object p1, object p2) + => string.Format(CultureInfo.CurrentCulture, GetString("ValueNotEqualToTestValue"), p0, p1, p2); + + private static string GetString(string name, params string[] formatterNames) + { + var value = _resourceManager.GetString(name); + + System.Diagnostics.Debug.Assert(value != null); + + if (formatterNames != null) + { + for (var i = 0; i < formatterNames.Length; i++) + { + value = value.Replace("{" + formatterNames[i] + "}", "{" + i + "}"); + } + } + + return value; + } + } +} diff --git a/src/Features/JsonPatch/src/Resources.resx b/src/Features/JsonPatch/src/Resources.resx new file mode 100644 index 0000000000..87cc399c62 --- /dev/null +++ b/src/Features/JsonPatch/src/Resources.resx @@ -0,0 +1,186 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + The property at '{0}' could not be copied. + + + The type of the property at path '{0}' could not be determined. + + + The '{0}' operation at path '{1}' could not be performed. + + + The property at '{0}' could not be read. + + + The property at path '{0}' could not be updated. + + + The expression '{0}' is not supported. Supported expressions include member access and indexer expressions. + + + The index value provided by path segment '{0}' is out of bounds of the array size. + + + The path segment '{0}' is invalid for an array index. + + + The JSON patch document was malformed and could not be parsed. + + + Invalid JsonPatch operation '{0}'. + + + The provided path segment '{0}' cannot be converted to the target type. + + + The provided string '{0}' is an invalid path. + + + The value '{0}' is invalid for target location. + + + '{0}' must be of type '{1}'. + + + The type '{0}' which is an array is not supported for json patch operations as it has a fixed size. + + + The type '{0}' which is a non generic list is not supported for json patch operations. Only generic list types are supported. + + + The target location specified by path segment '{0}' was not found. + + + For operation '{0}', the target location specified by path '{1}' was not found. + + + The test operation is not supported. + + + The current value '{0}' at position '{2}' is not equal to the test value '{1}'. + + + The value at '{0}' cannot be null or empty to perform the test operation. + + + The current value '{0}' at path '{2}' is not equal to the test value '{1}'. + + \ No newline at end of file diff --git a/src/Features/JsonPatch/src/baseline.netcore.json b/src/Features/JsonPatch/src/baseline.netcore.json new file mode 100644 index 0000000000..3d90a8f017 --- /dev/null +++ b/src/Features/JsonPatch/src/baseline.netcore.json @@ -0,0 +1,1985 @@ +{ + "AssemblyIdentity": "Microsoft.AspNetCore.JsonPatch, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", + "Types": [ + { + "Name": "Microsoft.AspNetCore.JsonPatch.JsonPatchProperty", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_Property", + "Parameters": [], + "ReturnType": "Newtonsoft.Json.Serialization.JsonProperty", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_Property", + "Parameters": [ + { + "Name": "value", + "Type": "Newtonsoft.Json.Serialization.JsonProperty" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Parent", + "Parameters": [], + "ReturnType": "System.Object", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_Parent", + "Parameters": [ + { + "Name": "value", + "Type": "System.Object" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "property", + "Type": "Newtonsoft.Json.Serialization.JsonProperty" + }, + { + "Name": "parent", + "Type": "System.Object" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.JsonPatch.IJsonPatchDocument", + "Visibility": "Public", + "Kind": "Interface", + "Abstract": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_ContractResolver", + "Parameters": [], + "ReturnType": "Newtonsoft.Json.Serialization.IContractResolver", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_ContractResolver", + "Parameters": [ + { + "Name": "value", + "Type": "Newtonsoft.Json.Serialization.IContractResolver" + } + ], + "ReturnType": "System.Void", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetOperations", + "Parameters": [], + "ReturnType": "System.Collections.Generic.IList", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.JsonPatch.JsonPatchDocument", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.JsonPatch.IJsonPatchDocument" + ], + "Members": [ + { + "Kind": "Method", + "Name": "get_Operations", + "Parameters": [], + "ReturnType": "System.Collections.Generic.List", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_ContractResolver", + "Parameters": [], + "ReturnType": "Newtonsoft.Json.Serialization.IContractResolver", + "Sealed": true, + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.JsonPatch.IJsonPatchDocument", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_ContractResolver", + "Parameters": [ + { + "Name": "value", + "Type": "Newtonsoft.Json.Serialization.IContractResolver" + } + ], + "ReturnType": "System.Void", + "Sealed": true, + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.JsonPatch.IJsonPatchDocument", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "Add", + "Parameters": [ + { + "Name": "path", + "Type": "System.String" + }, + { + "Name": "value", + "Type": "System.Object" + } + ], + "ReturnType": "Microsoft.AspNetCore.JsonPatch.JsonPatchDocument", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "Remove", + "Parameters": [ + { + "Name": "path", + "Type": "System.String" + } + ], + "ReturnType": "Microsoft.AspNetCore.JsonPatch.JsonPatchDocument", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "Replace", + "Parameters": [ + { + "Name": "path", + "Type": "System.String" + }, + { + "Name": "value", + "Type": "System.Object" + } + ], + "ReturnType": "Microsoft.AspNetCore.JsonPatch.JsonPatchDocument", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "Test", + "Parameters": [ + { + "Name": "path", + "Type": "System.String" + }, + { + "Name": "value", + "Type": "System.Object" + } + ], + "ReturnType": "Microsoft.AspNetCore.JsonPatch.JsonPatchDocument", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "Move", + "Parameters": [ + { + "Name": "from", + "Type": "System.String" + }, + { + "Name": "path", + "Type": "System.String" + } + ], + "ReturnType": "Microsoft.AspNetCore.JsonPatch.JsonPatchDocument", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "Copy", + "Parameters": [ + { + "Name": "from", + "Type": "System.String" + }, + { + "Name": "path", + "Type": "System.String" + } + ], + "ReturnType": "Microsoft.AspNetCore.JsonPatch.JsonPatchDocument", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ApplyTo", + "Parameters": [ + { + "Name": "objectToApplyTo", + "Type": "System.Object" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ApplyTo", + "Parameters": [ + { + "Name": "objectToApplyTo", + "Type": "System.Object" + }, + { + "Name": "logErrorAction", + "Type": "System.Action" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ApplyTo", + "Parameters": [ + { + "Name": "objectToApplyTo", + "Type": "System.Object" + }, + { + "Name": "adapter", + "Type": "Microsoft.AspNetCore.JsonPatch.Adapters.IObjectAdapter" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "operations", + "Type": "System.Collections.Generic.List" + }, + { + "Name": "contractResolver", + "Type": "Newtonsoft.Json.Serialization.IContractResolver" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.JsonPatch.JsonPatchDocument", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.JsonPatch.IJsonPatchDocument" + ], + "Members": [ + { + "Kind": "Method", + "Name": "get_Operations", + "Parameters": [], + "ReturnType": "System.Collections.Generic.List>", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_ContractResolver", + "Parameters": [], + "ReturnType": "Newtonsoft.Json.Serialization.IContractResolver", + "Sealed": true, + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.JsonPatch.IJsonPatchDocument", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_ContractResolver", + "Parameters": [ + { + "Name": "value", + "Type": "Newtonsoft.Json.Serialization.IContractResolver" + } + ], + "ReturnType": "System.Void", + "Sealed": true, + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.JsonPatch.IJsonPatchDocument", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "Add", + "Parameters": [ + { + "Name": "path", + "Type": "System.Linq.Expressions.Expression>" + }, + { + "Name": "value", + "Type": "T0" + } + ], + "ReturnType": "Microsoft.AspNetCore.JsonPatch.JsonPatchDocument", + "Visibility": "Public", + "GenericParameter": [ + { + "ParameterName": "TProp", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Kind": "Method", + "Name": "Add", + "Parameters": [ + { + "Name": "path", + "Type": "System.Linq.Expressions.Expression>>" + }, + { + "Name": "value", + "Type": "T0" + }, + { + "Name": "position", + "Type": "System.Int32" + } + ], + "ReturnType": "Microsoft.AspNetCore.JsonPatch.JsonPatchDocument", + "Visibility": "Public", + "GenericParameter": [ + { + "ParameterName": "TProp", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Kind": "Method", + "Name": "Add", + "Parameters": [ + { + "Name": "path", + "Type": "System.Linq.Expressions.Expression>>" + }, + { + "Name": "value", + "Type": "T0" + } + ], + "ReturnType": "Microsoft.AspNetCore.JsonPatch.JsonPatchDocument", + "Visibility": "Public", + "GenericParameter": [ + { + "ParameterName": "TProp", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Kind": "Method", + "Name": "Remove", + "Parameters": [ + { + "Name": "path", + "Type": "System.Linq.Expressions.Expression>" + } + ], + "ReturnType": "Microsoft.AspNetCore.JsonPatch.JsonPatchDocument", + "Visibility": "Public", + "GenericParameter": [ + { + "ParameterName": "TProp", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Kind": "Method", + "Name": "Remove", + "Parameters": [ + { + "Name": "path", + "Type": "System.Linq.Expressions.Expression>>" + }, + { + "Name": "position", + "Type": "System.Int32" + } + ], + "ReturnType": "Microsoft.AspNetCore.JsonPatch.JsonPatchDocument", + "Visibility": "Public", + "GenericParameter": [ + { + "ParameterName": "TProp", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Kind": "Method", + "Name": "Remove", + "Parameters": [ + { + "Name": "path", + "Type": "System.Linq.Expressions.Expression>>" + } + ], + "ReturnType": "Microsoft.AspNetCore.JsonPatch.JsonPatchDocument", + "Visibility": "Public", + "GenericParameter": [ + { + "ParameterName": "TProp", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Kind": "Method", + "Name": "Replace", + "Parameters": [ + { + "Name": "path", + "Type": "System.Linq.Expressions.Expression>" + }, + { + "Name": "value", + "Type": "T0" + } + ], + "ReturnType": "Microsoft.AspNetCore.JsonPatch.JsonPatchDocument", + "Visibility": "Public", + "GenericParameter": [ + { + "ParameterName": "TProp", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Kind": "Method", + "Name": "Replace", + "Parameters": [ + { + "Name": "path", + "Type": "System.Linq.Expressions.Expression>>" + }, + { + "Name": "value", + "Type": "T0" + }, + { + "Name": "position", + "Type": "System.Int32" + } + ], + "ReturnType": "Microsoft.AspNetCore.JsonPatch.JsonPatchDocument", + "Visibility": "Public", + "GenericParameter": [ + { + "ParameterName": "TProp", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Kind": "Method", + "Name": "Replace", + "Parameters": [ + { + "Name": "path", + "Type": "System.Linq.Expressions.Expression>>" + }, + { + "Name": "value", + "Type": "T0" + } + ], + "ReturnType": "Microsoft.AspNetCore.JsonPatch.JsonPatchDocument", + "Visibility": "Public", + "GenericParameter": [ + { + "ParameterName": "TProp", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Kind": "Method", + "Name": "Test", + "Parameters": [ + { + "Name": "path", + "Type": "System.Linq.Expressions.Expression>" + }, + { + "Name": "value", + "Type": "T0" + } + ], + "ReturnType": "Microsoft.AspNetCore.JsonPatch.JsonPatchDocument", + "Visibility": "Public", + "GenericParameter": [ + { + "ParameterName": "TProp", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Kind": "Method", + "Name": "Test", + "Parameters": [ + { + "Name": "path", + "Type": "System.Linq.Expressions.Expression>>" + }, + { + "Name": "value", + "Type": "T0" + }, + { + "Name": "position", + "Type": "System.Int32" + } + ], + "ReturnType": "Microsoft.AspNetCore.JsonPatch.JsonPatchDocument", + "Visibility": "Public", + "GenericParameter": [ + { + "ParameterName": "TProp", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Kind": "Method", + "Name": "Test", + "Parameters": [ + { + "Name": "path", + "Type": "System.Linq.Expressions.Expression>>" + }, + { + "Name": "value", + "Type": "T0" + } + ], + "ReturnType": "Microsoft.AspNetCore.JsonPatch.JsonPatchDocument", + "Visibility": "Public", + "GenericParameter": [ + { + "ParameterName": "TProp", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Kind": "Method", + "Name": "Move", + "Parameters": [ + { + "Name": "from", + "Type": "System.Linq.Expressions.Expression>" + }, + { + "Name": "path", + "Type": "System.Linq.Expressions.Expression>" + } + ], + "ReturnType": "Microsoft.AspNetCore.JsonPatch.JsonPatchDocument", + "Visibility": "Public", + "GenericParameter": [ + { + "ParameterName": "TProp", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Kind": "Method", + "Name": "Move", + "Parameters": [ + { + "Name": "from", + "Type": "System.Linq.Expressions.Expression>>" + }, + { + "Name": "positionFrom", + "Type": "System.Int32" + }, + { + "Name": "path", + "Type": "System.Linq.Expressions.Expression>" + } + ], + "ReturnType": "Microsoft.AspNetCore.JsonPatch.JsonPatchDocument", + "Visibility": "Public", + "GenericParameter": [ + { + "ParameterName": "TProp", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Kind": "Method", + "Name": "Move", + "Parameters": [ + { + "Name": "from", + "Type": "System.Linq.Expressions.Expression>" + }, + { + "Name": "path", + "Type": "System.Linq.Expressions.Expression>>" + }, + { + "Name": "positionTo", + "Type": "System.Int32" + } + ], + "ReturnType": "Microsoft.AspNetCore.JsonPatch.JsonPatchDocument", + "Visibility": "Public", + "GenericParameter": [ + { + "ParameterName": "TProp", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Kind": "Method", + "Name": "Move", + "Parameters": [ + { + "Name": "from", + "Type": "System.Linq.Expressions.Expression>>" + }, + { + "Name": "positionFrom", + "Type": "System.Int32" + }, + { + "Name": "path", + "Type": "System.Linq.Expressions.Expression>>" + }, + { + "Name": "positionTo", + "Type": "System.Int32" + } + ], + "ReturnType": "Microsoft.AspNetCore.JsonPatch.JsonPatchDocument", + "Visibility": "Public", + "GenericParameter": [ + { + "ParameterName": "TProp", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Kind": "Method", + "Name": "Move", + "Parameters": [ + { + "Name": "from", + "Type": "System.Linq.Expressions.Expression>>" + }, + { + "Name": "positionFrom", + "Type": "System.Int32" + }, + { + "Name": "path", + "Type": "System.Linq.Expressions.Expression>>" + } + ], + "ReturnType": "Microsoft.AspNetCore.JsonPatch.JsonPatchDocument", + "Visibility": "Public", + "GenericParameter": [ + { + "ParameterName": "TProp", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Kind": "Method", + "Name": "Move", + "Parameters": [ + { + "Name": "from", + "Type": "System.Linq.Expressions.Expression>" + }, + { + "Name": "path", + "Type": "System.Linq.Expressions.Expression>>" + } + ], + "ReturnType": "Microsoft.AspNetCore.JsonPatch.JsonPatchDocument", + "Visibility": "Public", + "GenericParameter": [ + { + "ParameterName": "TProp", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Kind": "Method", + "Name": "Copy", + "Parameters": [ + { + "Name": "from", + "Type": "System.Linq.Expressions.Expression>" + }, + { + "Name": "path", + "Type": "System.Linq.Expressions.Expression>" + } + ], + "ReturnType": "Microsoft.AspNetCore.JsonPatch.JsonPatchDocument", + "Visibility": "Public", + "GenericParameter": [ + { + "ParameterName": "TProp", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Kind": "Method", + "Name": "Copy", + "Parameters": [ + { + "Name": "from", + "Type": "System.Linq.Expressions.Expression>>" + }, + { + "Name": "positionFrom", + "Type": "System.Int32" + }, + { + "Name": "path", + "Type": "System.Linq.Expressions.Expression>" + } + ], + "ReturnType": "Microsoft.AspNetCore.JsonPatch.JsonPatchDocument", + "Visibility": "Public", + "GenericParameter": [ + { + "ParameterName": "TProp", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Kind": "Method", + "Name": "Copy", + "Parameters": [ + { + "Name": "from", + "Type": "System.Linq.Expressions.Expression>" + }, + { + "Name": "path", + "Type": "System.Linq.Expressions.Expression>>" + }, + { + "Name": "positionTo", + "Type": "System.Int32" + } + ], + "ReturnType": "Microsoft.AspNetCore.JsonPatch.JsonPatchDocument", + "Visibility": "Public", + "GenericParameter": [ + { + "ParameterName": "TProp", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Kind": "Method", + "Name": "Copy", + "Parameters": [ + { + "Name": "from", + "Type": "System.Linq.Expressions.Expression>>" + }, + { + "Name": "positionFrom", + "Type": "System.Int32" + }, + { + "Name": "path", + "Type": "System.Linq.Expressions.Expression>>" + }, + { + "Name": "positionTo", + "Type": "System.Int32" + } + ], + "ReturnType": "Microsoft.AspNetCore.JsonPatch.JsonPatchDocument", + "Visibility": "Public", + "GenericParameter": [ + { + "ParameterName": "TProp", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Kind": "Method", + "Name": "Copy", + "Parameters": [ + { + "Name": "from", + "Type": "System.Linq.Expressions.Expression>>" + }, + { + "Name": "positionFrom", + "Type": "System.Int32" + }, + { + "Name": "path", + "Type": "System.Linq.Expressions.Expression>>" + } + ], + "ReturnType": "Microsoft.AspNetCore.JsonPatch.JsonPatchDocument", + "Visibility": "Public", + "GenericParameter": [ + { + "ParameterName": "TProp", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Kind": "Method", + "Name": "Copy", + "Parameters": [ + { + "Name": "from", + "Type": "System.Linq.Expressions.Expression>" + }, + { + "Name": "path", + "Type": "System.Linq.Expressions.Expression>>" + } + ], + "ReturnType": "Microsoft.AspNetCore.JsonPatch.JsonPatchDocument", + "Visibility": "Public", + "GenericParameter": [ + { + "ParameterName": "TProp", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Kind": "Method", + "Name": "ApplyTo", + "Parameters": [ + { + "Name": "objectToApplyTo", + "Type": "T0" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ApplyTo", + "Parameters": [ + { + "Name": "objectToApplyTo", + "Type": "T0" + }, + { + "Name": "logErrorAction", + "Type": "System.Action" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ApplyTo", + "Parameters": [ + { + "Name": "objectToApplyTo", + "Type": "T0" + }, + { + "Name": "adapter", + "Type": "Microsoft.AspNetCore.JsonPatch.Adapters.IObjectAdapter" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "operations", + "Type": "System.Collections.Generic.List>" + }, + { + "Name": "contractResolver", + "Type": "Newtonsoft.Json.Serialization.IContractResolver" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TModel", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.JsonPatch.JsonPatchError", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_AffectedObject", + "Parameters": [], + "ReturnType": "System.Object", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Operation", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.JsonPatch.Operations.Operation", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_ErrorMessage", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "affectedObject", + "Type": "System.Object" + }, + { + "Name": "operation", + "Type": "Microsoft.AspNetCore.JsonPatch.Operations.Operation" + }, + { + "Name": "errorMessage", + "Type": "System.String" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.JsonPatch.Operations.Operation", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.AspNetCore.JsonPatch.Operations.OperationBase", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_value", + "Parameters": [], + "ReturnType": "System.Object", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_value", + "Parameters": [ + { + "Name": "value", + "Type": "System.Object" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "Apply", + "Parameters": [ + { + "Name": "objectToApplyTo", + "Type": "System.Object" + }, + { + "Name": "adapter", + "Type": "Microsoft.AspNetCore.JsonPatch.Adapters.IObjectAdapter" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ShouldSerializevalue", + "Parameters": [], + "ReturnType": "System.Boolean", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "op", + "Type": "System.String" + }, + { + "Name": "path", + "Type": "System.String" + }, + { + "Name": "from", + "Type": "System.String" + }, + { + "Name": "value", + "Type": "System.Object" + } + ], + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "op", + "Type": "System.String" + }, + { + "Name": "path", + "Type": "System.String" + }, + { + "Name": "from", + "Type": "System.String" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.JsonPatch.Operations.OperationBase", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_OperationType", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.JsonPatch.Operations.OperationType", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_path", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_path", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_op", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_op", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_from", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_from", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ShouldSerializefrom", + "Parameters": [], + "ReturnType": "System.Boolean", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "op", + "Type": "System.String" + }, + { + "Name": "path", + "Type": "System.String" + }, + { + "Name": "from", + "Type": "System.String" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.JsonPatch.Operations.Operation", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.AspNetCore.JsonPatch.Operations.Operation", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "Apply", + "Parameters": [ + { + "Name": "objectToApplyTo", + "Type": "T0" + }, + { + "Name": "adapter", + "Type": "Microsoft.AspNetCore.JsonPatch.Adapters.IObjectAdapter" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "op", + "Type": "System.String" + }, + { + "Name": "path", + "Type": "System.String" + }, + { + "Name": "from", + "Type": "System.String" + }, + { + "Name": "value", + "Type": "System.Object" + } + ], + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "op", + "Type": "System.String" + }, + { + "Name": "path", + "Type": "System.String" + }, + { + "Name": "from", + "Type": "System.String" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TModel", + "ParameterPosition": 0, + "Class": true, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.JsonPatch.Operations.OperationType", + "Visibility": "Public", + "Kind": "Enumeration", + "Sealed": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Field", + "Name": "Add", + "Parameters": [], + "GenericParameter": [], + "Literal": "0" + }, + { + "Kind": "Field", + "Name": "Remove", + "Parameters": [], + "GenericParameter": [], + "Literal": "1" + }, + { + "Kind": "Field", + "Name": "Replace", + "Parameters": [], + "GenericParameter": [], + "Literal": "2" + }, + { + "Kind": "Field", + "Name": "Move", + "Parameters": [], + "GenericParameter": [], + "Literal": "3" + }, + { + "Kind": "Field", + "Name": "Copy", + "Parameters": [], + "GenericParameter": [], + "Literal": "4" + }, + { + "Kind": "Field", + "Name": "Test", + "Parameters": [], + "GenericParameter": [], + "Literal": "5" + }, + { + "Kind": "Field", + "Name": "Invalid", + "Parameters": [], + "GenericParameter": [], + "Literal": "6" + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.JsonPatch.Helpers.GetValueResult", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_PropertyValue", + "Parameters": [], + "ReturnType": "System.Object", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_HasError", + "Parameters": [], + "ReturnType": "System.Boolean", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "propertyValue", + "Type": "System.Object" + }, + { + "Name": "hasError", + "Type": "System.Boolean" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.JsonPatch.Exceptions.JsonPatchException", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "System.Exception", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_FailedOperation", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.JsonPatch.Operations.Operation", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_AffectedObject", + "Parameters": [], + "ReturnType": "System.Object", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "jsonPatchError", + "Type": "Microsoft.AspNetCore.JsonPatch.JsonPatchError" + }, + { + "Name": "innerException", + "Type": "System.Exception" + } + ], + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "jsonPatchError", + "Type": "Microsoft.AspNetCore.JsonPatch.JsonPatchError" + } + ], + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "message", + "Type": "System.String" + }, + { + "Name": "innerException", + "Type": "System.Exception" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.JsonPatch.Converters.JsonPatchDocumentConverter", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Newtonsoft.Json.JsonConverter", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "CanConvert", + "Parameters": [ + { + "Name": "objectType", + "Type": "System.Type" + } + ], + "ReturnType": "System.Boolean", + "Virtual": true, + "Override": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ReadJson", + "Parameters": [ + { + "Name": "reader", + "Type": "Newtonsoft.Json.JsonReader" + }, + { + "Name": "objectType", + "Type": "System.Type" + }, + { + "Name": "existingValue", + "Type": "System.Object" + }, + { + "Name": "serializer", + "Type": "Newtonsoft.Json.JsonSerializer" + } + ], + "ReturnType": "System.Object", + "Virtual": true, + "Override": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "WriteJson", + "Parameters": [ + { + "Name": "writer", + "Type": "Newtonsoft.Json.JsonWriter" + }, + { + "Name": "value", + "Type": "System.Object" + }, + { + "Name": "serializer", + "Type": "Newtonsoft.Json.JsonSerializer" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Override": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.JsonPatch.Converters.TypedJsonPatchDocumentConverter", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.AspNetCore.JsonPatch.Converters.JsonPatchDocumentConverter", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "ReadJson", + "Parameters": [ + { + "Name": "reader", + "Type": "Newtonsoft.Json.JsonReader" + }, + { + "Name": "objectType", + "Type": "System.Type" + }, + { + "Name": "existingValue", + "Type": "System.Object" + }, + { + "Name": "serializer", + "Type": "Newtonsoft.Json.JsonSerializer" + } + ], + "ReturnType": "System.Object", + "Virtual": true, + "Override": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.JsonPatch.Adapters.IObjectAdapter", + "Visibility": "Public", + "Kind": "Interface", + "Abstract": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "Add", + "Parameters": [ + { + "Name": "operation", + "Type": "Microsoft.AspNetCore.JsonPatch.Operations.Operation" + }, + { + "Name": "objectToApplyTo", + "Type": "System.Object" + } + ], + "ReturnType": "System.Void", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "Copy", + "Parameters": [ + { + "Name": "operation", + "Type": "Microsoft.AspNetCore.JsonPatch.Operations.Operation" + }, + { + "Name": "objectToApplyTo", + "Type": "System.Object" + } + ], + "ReturnType": "System.Void", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "Move", + "Parameters": [ + { + "Name": "operation", + "Type": "Microsoft.AspNetCore.JsonPatch.Operations.Operation" + }, + { + "Name": "objectToApplyTo", + "Type": "System.Object" + } + ], + "ReturnType": "System.Void", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "Remove", + "Parameters": [ + { + "Name": "operation", + "Type": "Microsoft.AspNetCore.JsonPatch.Operations.Operation" + }, + { + "Name": "objectToApplyTo", + "Type": "System.Object" + } + ], + "ReturnType": "System.Void", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "Replace", + "Parameters": [ + { + "Name": "operation", + "Type": "Microsoft.AspNetCore.JsonPatch.Operations.Operation" + }, + { + "Name": "objectToApplyTo", + "Type": "System.Object" + } + ], + "ReturnType": "System.Void", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.JsonPatch.Adapters.IObjectAdapterWithTest", + "Visibility": "Public", + "Kind": "Interface", + "Abstract": true, + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.JsonPatch.Adapters.IObjectAdapter" + ], + "Members": [ + { + "Kind": "Method", + "Name": "Test", + "Parameters": [ + { + "Name": "operation", + "Type": "Microsoft.AspNetCore.JsonPatch.Operations.Operation" + }, + { + "Name": "objectToApplyTo", + "Type": "System.Object" + } + ], + "ReturnType": "System.Void", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.JsonPatch.Adapters.ObjectAdapter", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.JsonPatch.Adapters.IObjectAdapterWithTest" + ], + "Members": [ + { + "Kind": "Method", + "Name": "Add", + "Parameters": [ + { + "Name": "operation", + "Type": "Microsoft.AspNetCore.JsonPatch.Operations.Operation" + }, + { + "Name": "objectToApplyTo", + "Type": "System.Object" + } + ], + "ReturnType": "System.Void", + "Sealed": true, + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.JsonPatch.Adapters.IObjectAdapter", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "Copy", + "Parameters": [ + { + "Name": "operation", + "Type": "Microsoft.AspNetCore.JsonPatch.Operations.Operation" + }, + { + "Name": "objectToApplyTo", + "Type": "System.Object" + } + ], + "ReturnType": "System.Void", + "Sealed": true, + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.JsonPatch.Adapters.IObjectAdapter", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "Move", + "Parameters": [ + { + "Name": "operation", + "Type": "Microsoft.AspNetCore.JsonPatch.Operations.Operation" + }, + { + "Name": "objectToApplyTo", + "Type": "System.Object" + } + ], + "ReturnType": "System.Void", + "Sealed": true, + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.JsonPatch.Adapters.IObjectAdapter", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "Remove", + "Parameters": [ + { + "Name": "operation", + "Type": "Microsoft.AspNetCore.JsonPatch.Operations.Operation" + }, + { + "Name": "objectToApplyTo", + "Type": "System.Object" + } + ], + "ReturnType": "System.Void", + "Sealed": true, + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.JsonPatch.Adapters.IObjectAdapter", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "Replace", + "Parameters": [ + { + "Name": "operation", + "Type": "Microsoft.AspNetCore.JsonPatch.Operations.Operation" + }, + { + "Name": "objectToApplyTo", + "Type": "System.Object" + } + ], + "ReturnType": "System.Void", + "Sealed": true, + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.JsonPatch.Adapters.IObjectAdapter", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_ContractResolver", + "Parameters": [], + "ReturnType": "Newtonsoft.Json.Serialization.IContractResolver", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_LogErrorAction", + "Parameters": [], + "ReturnType": "System.Action", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "Test", + "Parameters": [ + { + "Name": "operation", + "Type": "Microsoft.AspNetCore.JsonPatch.Operations.Operation" + }, + { + "Name": "objectToApplyTo", + "Type": "System.Object" + } + ], + "ReturnType": "System.Void", + "Sealed": true, + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.JsonPatch.Adapters.IObjectAdapterWithTest", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "contractResolver", + "Type": "Newtonsoft.Json.Serialization.IContractResolver" + }, + { + "Name": "logErrorAction", + "Type": "System.Action" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + } + ] +} \ No newline at end of file diff --git a/src/Features/JsonPatch/test/Adapters/AdapterFactoryTests.cs b/src/Features/JsonPatch/test/Adapters/AdapterFactoryTests.cs new file mode 100644 index 0000000000..1e961c29e9 --- /dev/null +++ b/src/Features/JsonPatch/test/Adapters/AdapterFactoryTests.cs @@ -0,0 +1,73 @@ +using Microsoft.AspNetCore.JsonPatch.Adapters; +using Microsoft.AspNetCore.JsonPatch.Internal; +using Moq; +using Newtonsoft.Json.Serialization; +using System; +using System.Collections.Generic; +using System.Dynamic; +using System.Text; +using Xunit; + +namespace Microsoft.AspNetCore.JsonPatch.Test.Adapters +{ + public class AdapterFactoryTests + { + [Fact] + public void GetListAdapterForListTargets() + { + // Arrange + AdapterFactory factory = new AdapterFactory(); + + //Act: + IAdapter adapter = factory.Create(new List(), new DefaultContractResolver()); + + // Assert + Assert.Equal(typeof(ListAdapter), adapter.GetType()); + } + + [Fact] + public void GetDictionaryAdapterForDictionaryObjects() + { + // Arrange + AdapterFactory factory = new AdapterFactory(); + + //Act: + IAdapter adapter = factory.Create(new Dictionary(), new DefaultContractResolver()); + + // Assert + Assert.Equal(typeof(DictionaryAdapter), adapter.GetType()); + } + + private class PocoModel + {} + + + [Fact] + public void GetPocoAdapterForGenericObjects() + { + // Arrange + AdapterFactory factory = new AdapterFactory(); + + //Act: + IAdapter adapter = factory.Create(new PocoModel(), new DefaultContractResolver()); + + // Assert + Assert.Equal(typeof(PocoAdapter), adapter.GetType()); + } + + + + [Fact] + public void GetDynamicAdapterForGenericObjects() + { + // Arrange + AdapterFactory factory = new AdapterFactory(); + + //Act: + IAdapter adapter = factory.Create(new TestDynamicObject(), new DefaultContractResolver()); + + // Assert + Assert.Equal(typeof(DynamicObjectAdapter), adapter.GetType()); + } + } +} diff --git a/src/Features/JsonPatch/test/Adapters/TestDynamicObject.cs b/src/Features/JsonPatch/test/Adapters/TestDynamicObject.cs new file mode 100644 index 0000000000..08371f25c6 --- /dev/null +++ b/src/Features/JsonPatch/test/Adapters/TestDynamicObject.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; +using System.Dynamic; +using System.Text; + +namespace Microsoft.AspNetCore.JsonPatch.Test.Adapters +{ + public class TestDynamicObject : DynamicObject + { } +} diff --git a/src/Features/JsonPatch/test/CustomNamingStrategyTests.cs b/src/Features/JsonPatch/test/CustomNamingStrategyTests.cs new file mode 100644 index 0000000000..ebc45874d9 --- /dev/null +++ b/src/Features/JsonPatch/test/CustomNamingStrategyTests.cs @@ -0,0 +1,153 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; +using System.Dynamic; +using Newtonsoft.Json.Serialization; +using Xunit; + +namespace Microsoft.AspNetCore.JsonPatch +{ + public class CustomNamingStrategyTests + { + [Fact] + public void AddProperty_ToDynamicTestObject_WithCustomNamingStrategy() + { + // Arrange + var contractResolver = new DefaultContractResolver + { + NamingStrategy = new TestNamingStrategy() + }; + + dynamic targetObject = new DynamicTestObject(); + targetObject.Test = 1; + + var patchDocument = new JsonPatchDocument(); + patchDocument.Add("NewInt", 1); + patchDocument.ContractResolver = contractResolver; + + // Act + patchDocument.ApplyTo(targetObject); + + // Assert + Assert.Equal(1, targetObject.customNewInt); + Assert.Equal(1, targetObject.Test); + } + + [Fact] + public void CopyPropertyValue_ToDynamicTestObject_WithCustomNamingStrategy() + { + // Arrange + var contractResolver = new DefaultContractResolver + { + NamingStrategy = new TestNamingStrategy() + }; + + dynamic targetObject = new DynamicTestObject(); + targetObject.customStringProperty = "A"; + targetObject.customAnotherStringProperty = "B"; + + var patchDocument = new JsonPatchDocument(); + patchDocument.Copy("StringProperty", "AnotherStringProperty"); + patchDocument.ContractResolver = contractResolver; + + // Act + patchDocument.ApplyTo(targetObject); + + // Assert + Assert.Equal("A", targetObject.customAnotherStringProperty); + } + + [Fact] + public void MovePropertyValue_ForExpandoObject_WithCustomNamingStrategy() + { + // Arrange + var contractResolver = new DefaultContractResolver + { + NamingStrategy = new TestNamingStrategy() + }; + + dynamic targetObject = new ExpandoObject(); + targetObject.customStringProperty = "A"; + targetObject.customAnotherStringProperty = "B"; + + var patchDocument = new JsonPatchDocument(); + patchDocument.Move("StringProperty", "AnotherStringProperty"); + patchDocument.ContractResolver = contractResolver; + + // Act + patchDocument.ApplyTo(targetObject); + var cont = targetObject as IDictionary; + cont.TryGetValue("customStringProperty", out var valueFromDictionary); + + // Assert + Assert.Equal("A", targetObject.customAnotherStringProperty); + Assert.Null(valueFromDictionary); + } + + [Fact] + public void RemoveProperty_FromDictionaryObject_WithCustomNamingStrategy() + { + // Arrange + var contractResolver = new DefaultContractResolver + { + NamingStrategy = new TestNamingStrategy() + }; + + var targetObject = new Dictionary() + { + { "customTest", 1}, + }; + + var patchDocument = new JsonPatchDocument(); + patchDocument.Remove("Test"); + patchDocument.ContractResolver = contractResolver; + + // Act + patchDocument.ApplyTo(targetObject); + var cont = targetObject as IDictionary; + cont.TryGetValue("customTest", out var valueFromDictionary); + + // Assert + Assert.Equal(0, valueFromDictionary); + } + + [Fact] + public void ReplacePropertyValue_ForExpandoObject_WithCustomNamingStrategy() + { + // Arrange + var contractResolver = new DefaultContractResolver + { + NamingStrategy = new TestNamingStrategy() + }; + + dynamic targetObject = new ExpandoObject(); + targetObject.customTest = 1; + + var patchDocument = new JsonPatchDocument(); + patchDocument.Replace("Test", 2); + patchDocument.ContractResolver = contractResolver; + + // Act + patchDocument.ApplyTo(targetObject); + + // Assert + Assert.Equal(2, targetObject.customTest); + } + + private class TestNamingStrategy : NamingStrategy + { + public new bool ProcessDictionaryKeys => true; + + public override string GetDictionaryKey(string key) + { + return "custom" + key; + } + + protected override string ResolvePropertyName(string name) + { + return name; + } + } + } +} diff --git a/src/Features/JsonPatch/test/IntegrationTests/AnonymousObjectIntegrationTest.cs b/src/Features/JsonPatch/test/IntegrationTests/AnonymousObjectIntegrationTest.cs new file mode 100644 index 0000000000..4f290aae2f --- /dev/null +++ b/src/Features/JsonPatch/test/IntegrationTests/AnonymousObjectIntegrationTest.cs @@ -0,0 +1,190 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNetCore.JsonPatch.Exceptions; +using Xunit; + +namespace Microsoft.AspNetCore.JsonPatch.IntegrationTests +{ + public class AnonymousObjectIntegrationTest + { + [Fact] + public void AddNewProperty_ShouldFail() + { + // Arrange + var targetObject = new { }; + + var patchDocument = new JsonPatchDocument(); + patchDocument.Add("NewProperty", 4); + + // Act + var exception = Assert.Throws(() => + { + patchDocument.ApplyTo(targetObject); + }); + + // Assert + Assert.Equal("The target location specified by path segment 'NewProperty' was not found.", + exception.Message); + } + + [Fact] + public void AddNewProperty_ToNestedAnonymousObject_ShouldFail() + { + // Arrange + dynamic targetObject = new + { + Test = 1, + nested = new { } + }; + + var patchDocument = new JsonPatchDocument(); + patchDocument.Add("Nested/NewInt", 1); + + // Act + var exception = Assert.Throws(() => + { + patchDocument.ApplyTo(targetObject); + }); + + // Assert + Assert.Equal("The target location specified by path segment 'NewInt' was not found.", + exception.Message); + } + + [Fact] + public void AddDoesNotReplace() + { + // Arrange + var targetObject = new + { + StringProperty = "A" + }; + + var patchDocument = new JsonPatchDocument(); + patchDocument.Add("StringProperty", "B"); + + // Act + var exception = Assert.Throws(() => + { + patchDocument.ApplyTo(targetObject); + }); + + // Assert + Assert.Equal("The property at path 'StringProperty' could not be updated.", + exception.Message); + } + + [Fact] + public void RemoveProperty_ShouldFail() + { + // Arrange + dynamic targetObject = new + { + Test = 1 + }; + + var patchDocument = new JsonPatchDocument(); + patchDocument.Remove("Test"); + + // Act + var exception = Assert.Throws(() => + { + patchDocument.ApplyTo(targetObject); + }); + + // Assert + Assert.Equal("The property at path 'Test' could not be updated.", + exception.Message); + } + + [Fact] + public void ReplaceProperty_ShouldFail() + { + // Arrange + var targetObject = new + { + StringProperty = "A", + AnotherStringProperty = "B" + }; + + var patchDocument = new JsonPatchDocument(); + patchDocument.Replace("StringProperty", "AnotherStringProperty"); + + // Act + var exception = Assert.Throws(() => + { + patchDocument.ApplyTo(targetObject); + }); + + // Assert + Assert.Equal("The property at path 'StringProperty' could not be updated.", + exception.Message); + } + + [Fact] + public void MoveProperty_ShouldFail() + { + // Arrange + var targetObject = new + { + StringProperty = "A", + AnotherStringProperty = "B" + }; + + var patchDocument = new JsonPatchDocument(); + patchDocument.Move("StringProperty", "AnotherStringProperty"); + + // Act + var exception = Assert.Throws(() => + { + patchDocument.ApplyTo(targetObject); + }); + + // Assert + Assert.Equal("The property at path 'StringProperty' could not be updated.", + exception.Message); + } + + [Fact] + public void TestStringProperty_IsSucessful() + { + // Arrange + var targetObject = new + { + StringProperty = "A", + AnotherStringProperty = "B" + }; + + var patchDocument = new JsonPatchDocument(); + patchDocument.Test("StringProperty", "A"); + + // Act & Assert + patchDocument.ApplyTo(targetObject); + } + + [Fact] + public void TestStringProperty_Fails() + { + // Arrange + var targetObject = new + { + StringProperty = "A", + AnotherStringProperty = "B" + }; + + var patchDocument = new JsonPatchDocument(); + patchDocument.Test("StringProperty", "B"); + + // Act + var exception = Assert.Throws(() => + { + patchDocument.ApplyTo(targetObject); + }); + + // Assert + Assert.Equal("The current value 'A' at path 'StringProperty' is not equal to the test value 'B'.", + exception.Message); + } + } +} diff --git a/src/Features/JsonPatch/test/IntegrationTests/DictionaryIntegrationTest.cs b/src/Features/JsonPatch/test/IntegrationTests/DictionaryIntegrationTest.cs new file mode 100644 index 0000000000..da990e3e8c --- /dev/null +++ b/src/Features/JsonPatch/test/IntegrationTests/DictionaryIntegrationTest.cs @@ -0,0 +1,319 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; +using Microsoft.AspNetCore.JsonPatch.Exceptions; +using Xunit; + +namespace Microsoft.AspNetCore.JsonPatch.IntegrationTests +{ + public class DictionaryTest + { + [Fact] + public void TestIntegerValue_IsSuccessful() + { + // Arrange + var model = new IntDictionary(); + model.DictionaryOfStringToInteger["one"] = 1; + model.DictionaryOfStringToInteger["two"] = 2; + var patchDocument = new JsonPatchDocument(); + patchDocument.Test("/DictionaryOfStringToInteger/two", 2); + + // Act & Assert + patchDocument.ApplyTo(model); + } + + [Fact] + public void AddIntegerValue_Succeeds() + { + // Arrange + var model = new IntDictionary(); + model.DictionaryOfStringToInteger["one"] = 1; + model.DictionaryOfStringToInteger["two"] = 2; + var patchDocument = new JsonPatchDocument(); + patchDocument.Add("/DictionaryOfStringToInteger/three", 3); + + // Act + patchDocument.ApplyTo(model); + + // Assert + Assert.Equal(3, model.DictionaryOfStringToInteger.Count); + Assert.Equal(1, model.DictionaryOfStringToInteger["one"]); + Assert.Equal(2, model.DictionaryOfStringToInteger["two"]); + Assert.Equal(3, model.DictionaryOfStringToInteger["three"]); + } + + [Fact] + public void RemoveIntegerValue_Succeeds() + { + // Arrange + var model = new IntDictionary(); + model.DictionaryOfStringToInteger["one"] = 1; + model.DictionaryOfStringToInteger["two"] = 2; + var patchDocument = new JsonPatchDocument(); + patchDocument.Remove("/DictionaryOfStringToInteger/two"); + + // Act + patchDocument.ApplyTo(model); + + // Assert + Assert.Equal(1, model.DictionaryOfStringToInteger.Count); + Assert.Equal(1, model.DictionaryOfStringToInteger["one"]); + } + + [Fact] + public void MoveIntegerValue_Succeeds() + { + // Arrange + var model = new IntDictionary(); + model.DictionaryOfStringToInteger["one"] = 1; + model.DictionaryOfStringToInteger["two"] = 2; + var patchDocument = new JsonPatchDocument(); + patchDocument.Move("/DictionaryOfStringToInteger/one", "/DictionaryOfStringToInteger/two"); + + // Act + patchDocument.ApplyTo(model); + + // Assert + Assert.Equal(1, model.DictionaryOfStringToInteger.Count); + Assert.Equal(1, model.DictionaryOfStringToInteger["two"]); + } + + [Fact] + public void ReplaceIntegerValue_Succeeds() + { + // Arrange + var model = new IntDictionary(); + model.DictionaryOfStringToInteger["one"] = 1; + model.DictionaryOfStringToInteger["two"] = 2; + var patchDocument = new JsonPatchDocument(); + patchDocument.Replace("/DictionaryOfStringToInteger/two", 20); + + // Act + patchDocument.ApplyTo(model); + + // Assert + Assert.Equal(2, model.DictionaryOfStringToInteger.Count); + Assert.Equal(1, model.DictionaryOfStringToInteger["one"]); + Assert.Equal(20, model.DictionaryOfStringToInteger["two"]); + } + + [Fact] + public void CopyIntegerValue_Succeeds() + { + // Arrange + var model = new IntDictionary(); + model.DictionaryOfStringToInteger["one"] = 1; + model.DictionaryOfStringToInteger["two"] = 2; + var patchDocument = new JsonPatchDocument(); + patchDocument.Copy("/DictionaryOfStringToInteger/one", "/DictionaryOfStringToInteger/two"); + + // Act + patchDocument.ApplyTo(model); + + // Assert + Assert.Equal(2, model.DictionaryOfStringToInteger.Count); + Assert.Equal(1, model.DictionaryOfStringToInteger["one"]); + Assert.Equal(1, model.DictionaryOfStringToInteger["two"]); + } + + private class Customer + { + public string Name { get; set; } + public Address Address { get; set; } + } + + private class Address + { + public string City { get; set; } + } + + private class IntDictionary + { + public IDictionary DictionaryOfStringToInteger { get; } = new Dictionary(); + } + + private class CustomerDictionary + { + public IDictionary DictionaryOfStringToCustomer { get; } = new Dictionary(); + } + + [Fact] + public void TestPocoObject_Succeeds() + { + // Arrange + var key1 = 100; + var value1 = new Customer() { Name = "James" }; + var model = new CustomerDictionary(); + model.DictionaryOfStringToCustomer[key1] = value1; + var patchDocument = new JsonPatchDocument(); + patchDocument.Test($"/DictionaryOfStringToCustomer/{key1}/Name", "James"); + + // Act & Assert + patchDocument.ApplyTo(model); + } + + [Fact] + public void TestPocoObject_FailsWhenTestValueIsNotEqualToObjectValue() + { + // Arrange + var key1 = 100; + var value1 = new Customer() { Name = "James" }; + var model = new CustomerDictionary(); + model.DictionaryOfStringToCustomer[key1] = value1; + var patchDocument = new JsonPatchDocument(); + patchDocument.Test($"/DictionaryOfStringToCustomer/{key1}/Name", "Mike"); + + // Act + var exception = Assert.Throws(() => + { + patchDocument.ApplyTo(model); + }); + + // Assert + Assert.Equal("The current value 'James' at path 'Name' is not equal to the test value 'Mike'.", exception.Message); + } + + [Fact] + public void AddReplacesPocoObject_Succeeds() + { + // Arrange + var key1 = 100; + var value1 = new Customer() { Name = "Jamesss" }; + var key2 = 200; + var value2 = new Customer() { Name = "Mike" }; + var model = new CustomerDictionary(); + model.DictionaryOfStringToCustomer[key1] = value1; + model.DictionaryOfStringToCustomer[key2] = value2; + var patchDocument = new JsonPatchDocument(); + patchDocument.Add($"/DictionaryOfStringToCustomer/{key1}/Name", "James"); + + // Act + patchDocument.ApplyTo(model); + + // Assert + Assert.Equal(2, model.DictionaryOfStringToCustomer.Count); + var actualValue1 = model.DictionaryOfStringToCustomer[key1]; + Assert.NotNull(actualValue1); + Assert.Equal("James", actualValue1.Name); + } + + [Fact] + public void RemovePocoObject_Succeeds() + { + // Arrange + var key1 = 100; + var value1 = new Customer() { Name = "Jamesss" }; + var key2 = 200; + var value2 = new Customer() { Name = "Mike" }; + var model = new CustomerDictionary(); + model.DictionaryOfStringToCustomer[key1] = value1; + model.DictionaryOfStringToCustomer[key2] = value2; + var patchDocument = new JsonPatchDocument(); + patchDocument.Remove($"/DictionaryOfStringToCustomer/{key1}/Name"); + + // Act + patchDocument.ApplyTo(model); + + // Assert + var actualValue1 = model.DictionaryOfStringToCustomer[key1]; + Assert.Null(actualValue1.Name); + } + + [Fact] + public void MovePocoObject_Succeeds() + { + // Arrange + var key1 = 100; + var value1 = new Customer() { Name = "James" }; + var key2 = 200; + var value2 = new Customer() { Name = "Mike" }; + var model = new CustomerDictionary(); + model.DictionaryOfStringToCustomer[key1] = value1; + model.DictionaryOfStringToCustomer[key2] = value2; + var patchDocument = new JsonPatchDocument(); + patchDocument.Move($"/DictionaryOfStringToCustomer/{key1}/Name", $"/DictionaryOfStringToCustomer/{key2}/Name"); + + // Act + patchDocument.ApplyTo(model); + + // Assert + var actualValue2 = model.DictionaryOfStringToCustomer[key2]; + Assert.NotNull(actualValue2); + Assert.Equal("James", actualValue2.Name); + } + + [Fact] + public void CopyPocoObject_Succeeds() + { + // Arrange + var key1 = 100; + var value1 = new Customer() { Name = "James" }; + var key2 = 200; + var value2 = new Customer() { Name = "Mike" }; + var model = new CustomerDictionary(); + model.DictionaryOfStringToCustomer[key1] = value1; + model.DictionaryOfStringToCustomer[key2] = value2; + var patchDocument = new JsonPatchDocument(); + patchDocument.Copy($"/DictionaryOfStringToCustomer/{key1}/Name", $"/DictionaryOfStringToCustomer/{key2}/Name"); + + // Act + patchDocument.ApplyTo(model); + + // Assert + Assert.Equal(2, model.DictionaryOfStringToCustomer.Count); + var actualValue2 = model.DictionaryOfStringToCustomer[key2]; + Assert.NotNull(actualValue2); + Assert.Equal("James", actualValue2.Name); + } + + [Fact] + public void ReplacePocoObject_Succeeds() + { + // Arrange + var key1 = 100; + var value1 = new Customer() { Name = "Jamesss" }; + var key2 = 200; + var value2 = new Customer() { Name = "Mike" }; + var model = new CustomerDictionary(); + model.DictionaryOfStringToCustomer[key1] = value1; + model.DictionaryOfStringToCustomer[key2] = value2; + var patchDocument = new JsonPatchDocument(); + patchDocument.Replace($"/DictionaryOfStringToCustomer/{key1}/Name", "James"); + + // Act + patchDocument.ApplyTo(model); + + // Assert + Assert.Equal(2, model.DictionaryOfStringToCustomer.Count); + var actualValue1 = model.DictionaryOfStringToCustomer[key1]; + Assert.NotNull(actualValue1); + Assert.Equal("James", actualValue1.Name); + } + + [Fact] + public void ReplacePocoObject_WithEscaping_Succeeds() + { + // Arrange + var key1 = "Foo/Name"; + var value1 = 100; + var key2 = "Foo"; + var value2 = 200; + var model = new IntDictionary(); + model.DictionaryOfStringToInteger[key1] = value1; + model.DictionaryOfStringToInteger[key2] = value2; + var patchDocument = new JsonPatchDocument(); + patchDocument.Replace($"/DictionaryOfStringToInteger/Foo~1Name", 300); + + // Act + patchDocument.ApplyTo(model); + + // Assert + Assert.Equal(2, model.DictionaryOfStringToInteger.Count); + var actualValue1 = model.DictionaryOfStringToInteger[key1]; + var actualValue2 = model.DictionaryOfStringToInteger[key2]; + Assert.Equal(300, actualValue1); + Assert.Equal(200, actualValue2); + } + } +} diff --git a/src/Features/JsonPatch/test/IntegrationTests/DynamicObjectIntegrationTest.cs b/src/Features/JsonPatch/test/IntegrationTests/DynamicObjectIntegrationTest.cs new file mode 100644 index 0000000000..ec15951a85 --- /dev/null +++ b/src/Features/JsonPatch/test/IntegrationTests/DynamicObjectIntegrationTest.cs @@ -0,0 +1,236 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; +using Microsoft.AspNetCore.JsonPatch.Exceptions; +using Xunit; + +namespace Microsoft.AspNetCore.JsonPatch.IntegrationTests +{ + public class DynamicObjectIntegrationTest + { + [Fact] + public void AddResults_ShouldReplaceExistingPropertyValue_InNestedDynamicObject() + { + // Arrange + dynamic dynamicTestObject = new DynamicTestObject(); + dynamicTestObject.Nested = new NestedObject(); + dynamicTestObject.Nested.DynamicProperty = new DynamicTestObject(); + dynamicTestObject.Nested.DynamicProperty.InBetweenFirst = new DynamicTestObject(); + dynamicTestObject.Nested.DynamicProperty.InBetweenFirst.InBetweenSecond = new DynamicTestObject(); + dynamicTestObject.Nested.DynamicProperty.InBetweenFirst.InBetweenSecond.StringProperty = "A"; + + var patchDocument = new JsonPatchDocument(); + patchDocument.Add("/Nested/DynamicProperty/InBetweenFirst/InBetweenSecond/StringProperty", "B"); + + // Act + patchDocument.ApplyTo(dynamicTestObject); + + // Assert + Assert.Equal("B", dynamicTestObject.Nested.DynamicProperty.InBetweenFirst.InBetweenSecond.StringProperty); + } + + [Fact] + public void ShouldNotBeAbleToAdd_ToNonExistingProperty_ThatIsNotTheRoot() + { + //Adding to a Nonexistent Target + // + // An example target JSON document: + // { "foo": "bar" } + // A JSON Patch document: + // [ + // { "op": "add", "path": "/baz/bat", "value": "qux" } + // ] + // This JSON Patch document, applied to the target JSON document above, + // would result in an error (therefore, it would not be applied), + // because the "add" operation's target location that references neither + // the root of the document, nor a member of an existing object, nor a + // member of an existing array. + + // Arrange + var nestedObject = new NestedObject() + { + DynamicProperty = new DynamicTestObject() + }; + + var patchDocument = new JsonPatchDocument(); + patchDocument.Add("DynamicProperty/OtherProperty/IntProperty", 1); + + // Act + var exception = Assert.Throws(() => + { + patchDocument.ApplyTo(nestedObject); + }); + + // Assert + Assert.Equal("The target location specified by path segment 'OtherProperty' was not found.", exception.Message); + } + + [Fact] + public void CopyProperties_InNestedDynamicObject() + { + // Arrange + dynamic dynamicTestObject = new DynamicTestObject(); + dynamicTestObject.NestedDynamicObject = new DynamicTestObject(); + dynamicTestObject.NestedDynamicObject.StringProperty = "A"; + dynamicTestObject.NestedDynamicObject.AnotherStringProperty = "B"; + + var patchDocument = new JsonPatchDocument(); + patchDocument.Copy("NestedDynamicObject/StringProperty", "NestedDynamicObject/AnotherStringProperty"); + + // Act + patchDocument.ApplyTo(dynamicTestObject); + + // Assert + Assert.Equal("A", dynamicTestObject.NestedDynamicObject.AnotherStringProperty); + } + + + [Fact] + public void MoveToNonExistingProperty_InDynamicObject_ShouldAddNewProperty() + { + // Arrange + dynamic dynamicTestObject = new DynamicTestObject(); + dynamicTestObject.StringProperty = "A"; + + var patchDocument = new JsonPatchDocument(); + patchDocument.Move("StringProperty", "AnotherStringProperty"); + + // Act + patchDocument.ApplyTo(dynamicTestObject); + dynamicTestObject.TryGetValue("StringProperty", out object valueFromDictionary); + + // Assert + Assert.Equal("A", dynamicTestObject.AnotherStringProperty); + Assert.Null(valueFromDictionary); + } + + [Fact] + public void MovePropertyValue_FromDynamicObject_ToTypedObject() + { + // Arrange + dynamic dynamicTestObject = new DynamicTestObject(); + dynamicTestObject.StringProperty = "A"; + dynamicTestObject.SimpleObject = new SimpleObject() { AnotherStringProperty = "B" }; + + var patchDocument = new JsonPatchDocument(); + patchDocument.Move("StringProperty", "SimpleObject/AnotherStringProperty"); + + // Act + patchDocument.ApplyTo(dynamicTestObject); + dynamicTestObject.TryGetValue("StringProperty", out object valueFromDictionary); + + // Assert + Assert.Equal("A", dynamicTestObject.SimpleObject.AnotherStringProperty); + Assert.Null(valueFromDictionary); + } + + [Fact] + public void RemoveNestedProperty_FromDynamicObject() + { + // Arrange + dynamic dynamicTestObject = new DynamicTestObject(); + dynamicTestObject.Test = new DynamicTestObject(); + dynamicTestObject.Test.AnotherTest = "A"; + + var patchDocument = new JsonPatchDocument(); + patchDocument.Remove("Test"); + + // Act + patchDocument.ApplyTo(dynamicTestObject); + dynamicTestObject.TryGetValue("Test", out object valueFromDictionary); + + // Assert + Assert.Null(valueFromDictionary); + } + + [Fact] + public void RemoveFromNestedObject_InDynamicObject_MixedCase_ThrowsPathNotFoundException() + { + // Arrange + dynamic dynamicTestObject = new DynamicTestObject(); + dynamicTestObject.SimpleObject = new SimpleObject() + { + StringProperty = "A" + }; + + var patchDocument = new JsonPatchDocument(); + patchDocument.Remove("Simpleobject/stringProperty"); + + // Act + var exception = Assert.Throws(() => + { + patchDocument.ApplyTo(dynamicTestObject); + }); + + // Assert + Assert.Equal("The target location specified by path segment 'Simpleobject' was not found.", exception.Message); + } + + + [Fact] + public void ReplaceNestedTypedObject_InDynamicObject() + { + // Arrange + dynamic dynamicTestObject = new DynamicTestObject(); + dynamicTestObject.SimpleObject = new SimpleObject() + { + IntegerValue = 5, + IntegerList = new List() { 1, 2, 3 } + }; + + var newObject = new SimpleObject() + { + DoubleValue = 1 + }; + + var patchDocument = new JsonPatchDocument(); + patchDocument.Replace("SimpleObject", newObject); + + // Act + patchDocument.ApplyTo(dynamicTestObject); + + // Assert + Assert.Equal(1, dynamicTestObject.SimpleObject.DoubleValue); + Assert.Equal(0, dynamicTestObject.SimpleObject.IntegerValue); + Assert.Null(dynamicTestObject.SimpleObject.IntegerList); + } + + [Fact] + public void TestStringPropertyValue_IsSuccessful() + { + // Arrange + dynamic dynamicTestObject = new DynamicTestObject(); + dynamicTestObject.Property = "A"; + + var patchDocument = new JsonPatchDocument(); + patchDocument.Test("Property", "A"); + + // Act & Assert + patchDocument.ApplyTo(dynamicTestObject); + } + + [Fact] + public void TestIntegerPropertyValue_ThrowsJsonPatchException_IfTestFails() + { + // Arrange + dynamic dynamicTestObject = new DynamicTestObject(); + dynamicTestObject.Nested = new SimpleObject() + { + IntegerList = new List() { 1, 2, 3 } + }; + + var patchDocument = new JsonPatchDocument(); + patchDocument.Test("Nested/IntegerList/0", 2); + + // Act + var exception = Assert.Throws(() => + { + patchDocument.ApplyTo(dynamicTestObject); + }); + + // Assert + Assert.Equal("The current value '1' at position '0' is not equal to the test value '2'.", exception.Message); + } + } +} diff --git a/src/Features/JsonPatch/test/IntegrationTests/ExpandoObjectIntegrationTest.cs b/src/Features/JsonPatch/test/IntegrationTests/ExpandoObjectIntegrationTest.cs new file mode 100644 index 0000000000..b1b58556f0 --- /dev/null +++ b/src/Features/JsonPatch/test/IntegrationTests/ExpandoObjectIntegrationTest.cs @@ -0,0 +1,327 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Dynamic; +using Microsoft.AspNetCore.JsonPatch.Exceptions; +using Xunit; + +namespace Microsoft.AspNetCore.JsonPatch.IntegrationTests +{ + public class ExpandoObjectIntegrationTest + { + [Fact] + public void AddNewIntProperty() + { + // Arrange + dynamic targetObject = new ExpandoObject(); + targetObject.Test = 1; + + var patchDocument = new JsonPatchDocument(); + patchDocument.Add("NewInt", 1); + + // Act + patchDocument.ApplyTo(targetObject); + + // Assert + Assert.Equal(1, targetObject.NewInt); + Assert.Equal(1, targetObject.Test); + } + + [Fact] + public void AddNewProperty_ToTypedObject_InExpandoObject() + { + // Arrange + dynamic dynamicProperty = new ExpandoObject(); + dynamicProperty.StringProperty = "A"; + + var targetObject = new NestedObject() + { + DynamicProperty = dynamicProperty + }; + + var patchDocument = new JsonPatchDocument(); + patchDocument.Add("DynamicProperty/StringProperty", "B"); + + // Act + patchDocument.ApplyTo(targetObject); + + // Assert + Assert.Equal("B", targetObject.DynamicProperty.StringProperty); + } + + [Fact] + public void AddReplaces_ExistingProperty() + { + // Arrange + dynamic targetObject = new ExpandoObject(); + targetObject.StringProperty = "A"; + + var patchDocument = new JsonPatchDocument(); + patchDocument.Add("StringProperty", "B"); + + // Act + patchDocument.ApplyTo(targetObject); + + // Assert + Assert.Equal("B", targetObject.StringProperty); + } + + [Fact] + public void AddReplaces_ExistingProperty_InNestedExpandoObject() + { + // Arrange + dynamic targetObject = new ExpandoObject(); + targetObject.InBetweenFirst = new ExpandoObject(); + targetObject.InBetweenFirst.InBetweenSecond = new ExpandoObject(); + targetObject.InBetweenFirst.InBetweenSecond.StringProperty = "A"; + + var patchDocument = new JsonPatchDocument(); + patchDocument.Add("/InBetweenFirst/InBetweenSecond/StringProperty", "B"); + + // Act + patchDocument.ApplyTo(targetObject); + + // Assert + Assert.Equal("B", targetObject.InBetweenFirst.InBetweenSecond.StringProperty); + } + + [Fact] + public void ShouldNotReplaceProperty_WithDifferentCase() + { + // Arrange + dynamic targetObject = new ExpandoObject(); + targetObject.StringProperty = "A"; + + var patchDocument = new JsonPatchDocument(); + patchDocument.Add("stringproperty", "B"); + + // Act + patchDocument.ApplyTo(targetObject); + + // Assert + Assert.Equal("A", targetObject.StringProperty); + Assert.Equal("B", targetObject.stringproperty); + } + + [Fact] + public void TestIntegerProperty_IsSucessful() + { + // Arrange + dynamic targetObject = new ExpandoObject(); + targetObject.Test = 1; + + var patchDocument = new JsonPatchDocument(); + patchDocument.Test("Test", 1); + + // Act & Assert + patchDocument.ApplyTo(targetObject); + } + + [Fact] + public void TestStringProperty_ThrowsJsonPatchException_IfTestFails() + { + // Arrange + dynamic targetObject = new ExpandoObject(); + targetObject.Test = "Value"; + + var patchDocument = new JsonPatchDocument(); + patchDocument.Test("Test", "TestValue"); + + // Act + var exception = Assert.Throws(() => + { + patchDocument.ApplyTo(targetObject); + }); + + // Assert + Assert.Equal("The current value 'Value' at path 'Test' is not equal to the test value 'TestValue'.", + exception.Message); + } + + [Fact] + public void CopyStringProperty_ToAnotherStringProperty() + { + // Arrange + dynamic targetObject = new ExpandoObject(); + + targetObject.StringProperty = "A"; + targetObject.AnotherStringProperty = "B"; + + var patchDocument = new JsonPatchDocument(); + patchDocument.Copy("StringProperty", "AnotherStringProperty"); + + // Act + patchDocument.ApplyTo(targetObject); + + // Assert + Assert.Equal("A", targetObject.AnotherStringProperty); + } + + [Fact] + public void MoveIntegerValue_ToAnotherIntegerProperty() + { + // Arrange + dynamic targetObject = new ExpandoObject(); + targetObject.IntegerValue = 100; + targetObject.AnotherIntegerValue = 200; + + var patchDocument = new JsonPatchDocument(); + patchDocument.Move("IntegerValue", "AnotherIntegerValue"); + + // Act + patchDocument.ApplyTo(targetObject); + + Assert.Equal(100, targetObject.AnotherIntegerValue); + + var cont = targetObject as IDictionary; + cont.TryGetValue("IntegerValue", out object valueFromDictionary); + + // Assert + Assert.Null(valueFromDictionary); + } + + [Fact] + public void Move_ToNonExistingProperty() + { + // Arrange + dynamic targetObject = new ExpandoObject(); + targetObject.StringProperty = "A"; + + var patchDocument = new JsonPatchDocument(); + patchDocument.Move("StringProperty", "AnotherStringProperty"); + + // Act + patchDocument.ApplyTo(targetObject); + + Assert.Equal("A", targetObject.AnotherStringProperty); + + var cont = targetObject as IDictionary; + cont.TryGetValue("StringProperty", out var valueFromDictionary); + + // Assert + Assert.Null(valueFromDictionary); + } + + [Fact] + public void RemoveProperty_ShouldFail_IfItDoesntExist() + { + // Arrange + dynamic targetObject = new ExpandoObject(); + targetObject.Test = 1; + + var patchDocument = new JsonPatchDocument(); + patchDocument.Remove("NonExisting"); + + // Act + var exception = Assert.Throws(() => + { + patchDocument.ApplyTo(targetObject); + }); + + // Assert + Assert.Equal("The target location specified by path segment 'NonExisting' was not found.", exception.Message); + } + + [Fact] + public void RemoveStringProperty() + { + // Arrange + dynamic targetObject = new ExpandoObject(); + targetObject.Test = 1; + + var patchDocument = new JsonPatchDocument(); + patchDocument.Remove("Test"); + + // Act + patchDocument.ApplyTo(targetObject); + + var cont = targetObject as IDictionary; + cont.TryGetValue("Test", out object valueFromDictionary); + + // Assert + Assert.Null(valueFromDictionary); + } + + [Fact] + public void RemoveProperty_MixedCase_ThrowsPathNotFoundException() + { + // Arrange + dynamic targetObject = new ExpandoObject(); + targetObject.Test = 1; + + var patchDocument = new JsonPatchDocument(); + patchDocument.Remove("test"); + + // Act + var exception = Assert.Throws(() => + { + patchDocument.ApplyTo(targetObject); + }); + + // Assert + Assert.Equal("The target location specified by path segment 'test' was not found.", exception.Message); + } + + [Fact] + public void RemoveNestedProperty() + { + // Arrange + dynamic targetObject = new ExpandoObject(); + targetObject.Test = new ExpandoObject(); + targetObject.Test.AnotherTest = "A"; + + var patchDocument = new JsonPatchDocument(); + patchDocument.Remove("Test"); + + // Act + patchDocument.ApplyTo(targetObject); + + var cont = targetObject as IDictionary; + cont.TryGetValue("Test", out object valueFromDictionary); + + // Assert + Assert.Null(valueFromDictionary); + } + + [Fact] + public void RemoveNestedProperty_MixedCase_ThrowsPathNotFoundException() + { + // Arrange + dynamic targetObject = new ExpandoObject(); + targetObject.Test = new ExpandoObject(); + targetObject.Test.AnotherTest = "A"; + + var patchDocument = new JsonPatchDocument(); + patchDocument.Remove("test"); + + // Act + var exception = Assert.Throws(() => + { + patchDocument.ApplyTo(targetObject); + }); + + // Assert + Assert.Equal("The target location specified by path segment 'test' was not found.", exception.Message); + } + + [Fact] + public void ReplaceGuid() + { + // Arrange + dynamic targetObject = new ExpandoObject(); + targetObject.GuidValue = Guid.NewGuid(); + + var newGuid = Guid.NewGuid(); + var patchDocument = new JsonPatchDocument(); + patchDocument.Replace("GuidValue", newGuid); + + // Act + patchDocument.ApplyTo(targetObject); + + // Assert + Assert.Equal(newGuid, targetObject.GuidValue); + } + } +} \ No newline at end of file diff --git a/src/Features/JsonPatch/test/IntegrationTests/ListIntegrationTest.cs b/src/Features/JsonPatch/test/IntegrationTests/ListIntegrationTest.cs new file mode 100644 index 0000000000..5e261ea08b --- /dev/null +++ b/src/Features/JsonPatch/test/IntegrationTests/ListIntegrationTest.cs @@ -0,0 +1,366 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; +using System.Collections.ObjectModel; +using Microsoft.AspNetCore.JsonPatch.Exceptions; +using Xunit; + +namespace Microsoft.AspNetCore.JsonPatch.IntegrationTests +{ + public class ListIntegrationTest + { + [Fact] + public void TestInList_IsSuccessful() + { + // Arrange + var targetObject = new SimpleObjectWithNestedObject() + { + SimpleObject = new SimpleObject() + { + IntegerList = new List() { 1, 2, 3 } + } + }; + + var patchDocument = new JsonPatchDocument(); + patchDocument.Test(o => o.SimpleObject.IntegerList, 3, 2); + + // Act & Assert + patchDocument.ApplyTo(targetObject); + } + + [Fact] + public void TestInList_InvalidPosition() + { + // Arrange + var targetObject = new SimpleObjectWithNestedObject() + { + SimpleObject = new SimpleObject() + { + IntegerList = new List() { 1, 2, 3 } + } + }; + + var patchDocument = new JsonPatchDocument(); + patchDocument.Test(o => o.SimpleObject.IntegerList, 4, -1); + + // Act & Assert + var exception = Assert.Throws(() => { patchDocument.ApplyTo(targetObject); }); + Assert.Equal("The index value provided by path segment '-1' is out of bounds of the array size.", + exception.Message); + } + + [Fact] + public void AddToIntegerIList() + { + // Arrange + var targetObject = new SimpleObjectWithNestedObject() + { + SimpleObject = new SimpleObject() + { + IntegerIList = new List() { 1, 2, 3 } + } + }; + + var patchDocument = new JsonPatchDocument(); + patchDocument.Add(o => (List)o.SimpleObject.IntegerIList, 4, 0); + + // Act + patchDocument.ApplyTo(targetObject); + + // Assert + Assert.Equal(new List() { 4, 1, 2, 3 }, targetObject.SimpleObject.IntegerIList); + } + + [Fact] + public void AddToComplextTypeList_SpecifyIndex() + { + // Arrange + var targetObject = new SimpleObjectWithNestedObject() + { + SimpleObjectList = new List() + { + new SimpleObject + { + StringProperty = "String1" + }, + new SimpleObject + { + StringProperty = "String2" + } + } + }; + + var patchDocument = new JsonPatchDocument(); + patchDocument.Add(o => o.SimpleObjectList[0].StringProperty, "ChangedString1"); + + // Act + patchDocument.ApplyTo(targetObject); + + // Assert + Assert.Equal("ChangedString1", targetObject.SimpleObjectList[0].StringProperty); + } + + [Fact] + public void AddToListAppend() + { + // Arrange + var targetObject = new SimpleObjectWithNestedObject() + { + SimpleObject = new SimpleObject() + { + IntegerList = new List() { 1, 2, 3 } + } + }; + + var patchDocument = new JsonPatchDocument(); + patchDocument.Add(o => o.SimpleObject.IntegerList, 4); + + // Act + patchDocument.ApplyTo(targetObject); + + // Assert + Assert.Equal(new List() { 1, 2, 3, 4 }, targetObject.SimpleObject.IntegerList); + } + + [Fact] + public void RemoveFromList() + { + // Arrange + var targetObject = new SimpleObject() + { + IntegerList = new List() { 1, 2, 3 } + }; + + var patchDocument = new JsonPatchDocument(); + patchDocument.Remove("IntegerList/2"); + + // Act + patchDocument.ApplyTo(targetObject); + + // Assert + Assert.Equal(new List() { 1, 2 }, targetObject.IntegerList); + } + + [Theory] + [InlineData("3")] + [InlineData("-1")] + public void RemoveFromList_InvalidPosition(string position) + { + // Arrange + var targetObject = new SimpleObject() + { + IntegerList = new List() { 1, 2, 3 } + }; + + var patchDocument = new JsonPatchDocument(); + patchDocument.Remove("IntegerList/" + position); + + // Act + var exception = Assert.Throws(() => + { + patchDocument.ApplyTo(targetObject); + }); + + // Assert + Assert.Equal($"The index value provided by path segment '{position}' is out of bounds of the array size.", exception.Message); + } + + [Fact] + public void Remove_FromEndOfList() + { + // Arrange + var targetObject = new SimpleObjectWithNestedObject() + { + SimpleObject = new SimpleObject() + { + IntegerList = new List() { 1, 2, 3 } + } + }; + + var patchDocument = new JsonPatchDocument(); + patchDocument.Remove(o => o.SimpleObject.IntegerList); + + // Act + patchDocument.ApplyTo(targetObject); + + // Assert + Assert.Equal(new List() { 1, 2 }, targetObject.SimpleObject.IntegerList); + } + + [Fact] + public void ReplaceFullList_WithCollection() + { + // Arrange + var targetObject = new SimpleObject() + { + IntegerList = new List() { 1, 2, 3 } + }; + + var patchDocument = new JsonPatchDocument(); + patchDocument.Replace("IntegerList", new Collection() { 4, 5, 6 }); + + // Act + patchDocument.ApplyTo(targetObject); + + // Assert + Assert.Equal(new List() { 4, 5, 6 }, targetObject.IntegerList); + } + + [Fact] + public void Replace_AtEndOfList() + { + // Arrange + var targetObject = new SimpleObjectWithNestedObject() + { + SimpleObject = new SimpleObject() + { + IntegerList = new List() { 1, 2, 3 } + } + }; + + var patchDocument = new JsonPatchDocument(); + patchDocument.Replace(o => o.SimpleObject.IntegerList, 5); + + // Act + patchDocument.ApplyTo(targetObject); + + // Assert + Assert.Equal(new List() { 1, 2, 5 }, targetObject.SimpleObject.IntegerList); + } + + [Fact] + public void Replace_InList_InvalidPosition() + { + // Arrange + var targetObject = new SimpleObjectWithNestedObject() + { + SimpleObject = new SimpleObject() + { + IntegerList = new List() { 1, 2, 3 } + } + }; + + var patchDocument = new JsonPatchDocument(); + patchDocument.Replace(o => o.SimpleObject.IntegerList, 5, -1); + + // Act + var exception = Assert.Throws(() => { patchDocument.ApplyTo(targetObject); }); + + // Assert + Assert.Equal("The index value provided by path segment '-1' is out of bounds of the array size.", exception.Message); + } + + [Fact] + public void CopyFromListToEndOfList() + { + // Arrange + var targetObject = new SimpleObject() + { + IntegerList = new List() { 1, 2, 3 } + }; + + var patchDocument = new JsonPatchDocument(); + patchDocument.Copy("IntegerList/0", "IntegerList/-"); + + // Act + patchDocument.ApplyTo(targetObject); + + // Assert + Assert.Equal(new List() { 1, 2, 3, 1 }, targetObject.IntegerList); + } + + [Fact] + public void CopyFromListToNonList() + { + // Arrange + var targetObject = new SimpleObject() + { + IntegerList = new List() { 1, 2, 3 } + }; + + var patchDocument = new JsonPatchDocument(); + patchDocument.Copy("IntegerList/0", "IntegerValue"); + + // Act + patchDocument.ApplyTo(targetObject); + + // Assert + Assert.Equal(1, targetObject.IntegerValue); + } + + [Fact] + public void MoveToEndOfList() + { + // Arrange + var targetObject = new SimpleObject() + { + IntegerValue = 5, + IntegerList = new List() { 1, 2, 3 } + }; + + var patchDocument = new JsonPatchDocument(); + patchDocument.Move("IntegerValue", "IntegerList/-"); + + // Act + patchDocument.ApplyTo(targetObject); + + // Assert + Assert.Equal(0, targetObject.IntegerValue); + Assert.Equal(new List() { 1, 2, 3, 5 }, targetObject.IntegerList); + } + + [Fact] + public void Move_KeepsObjectReferenceInList() + { + // Arrange + var simpleObject1 = new SimpleObject() { IntegerValue = 1 }; + var simpleObject2 = new SimpleObject() { IntegerValue = 2 }; + var simpleObject3 = new SimpleObject() { IntegerValue = 3 }; + var targetObject = new SimpleObjectWithNestedObject() + { + SimpleObjectList = new List() { + simpleObject1, + simpleObject2, + simpleObject3 + } + }; + + var patchDocument = new JsonPatchDocument(); + patchDocument.Move(o => o.SimpleObjectList, 0, o => o.SimpleObjectList, 1); + + // Act + patchDocument.ApplyTo(targetObject); + + // Assert + Assert.Equal(new List() { simpleObject2, simpleObject1, simpleObject3 }, targetObject.SimpleObjectList); + Assert.Equal(2, targetObject.SimpleObjectList[0].IntegerValue); + Assert.Equal(1, targetObject.SimpleObjectList[1].IntegerValue); + Assert.Same(simpleObject2, targetObject.SimpleObjectList[0]); + Assert.Same(simpleObject1, targetObject.SimpleObjectList[1]); + } + + [Fact] + public void MoveFromList_ToNonList_BetweenHierarchy() + { + // Arrange + var targetObject = new SimpleObjectWithNestedObject() + { + SimpleObject = new SimpleObject() + { + IntegerList = new List() { 1, 2, 3 } + } + }; + + var patchDocument = new JsonPatchDocument(); + patchDocument.Move(o => o.SimpleObject.IntegerList, 0, o => o.IntegerValue); + + // Act + patchDocument.ApplyTo(targetObject); + + // Assert + Assert.Equal(new List() { 2, 3 }, targetObject.SimpleObject.IntegerList); + Assert.Equal(1, targetObject.IntegerValue); + } + } +} diff --git a/src/Features/JsonPatch/test/IntegrationTests/NestedObjectIntegrationTest.cs b/src/Features/JsonPatch/test/IntegrationTests/NestedObjectIntegrationTest.cs new file mode 100644 index 0000000000..92c0e7fb2d --- /dev/null +++ b/src/Features/JsonPatch/test/IntegrationTests/NestedObjectIntegrationTest.cs @@ -0,0 +1,342 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Dynamic; +using Newtonsoft.Json; +using Xunit; + +namespace Microsoft.AspNetCore.JsonPatch.IntegrationTests +{ + public class NestedObjectIntegrationTest + { + [Fact] + public void Replace_DTOWithNullCheck() + { + // Arrange + var targetObject = new SimpleObjectWithNestedObjectWithNullCheck() + { + SimpleObjectWithNullCheck = new SimpleObjectWithNullCheck() + { + StringProperty = "A" + } + }; + + var patchDocument = new JsonPatchDocument(); + patchDocument.Replace(o => o.SimpleObjectWithNullCheck.StringProperty, "B"); + + // Act + patchDocument.ApplyTo(targetObject); + + // Assert + Assert.Equal("B", targetObject.SimpleObjectWithNullCheck.StringProperty); + } + + [Fact] + public void ReplaceNestedObject_WithSerialization() + { + // Arrange + var targetObject = new SimpleObjectWithNestedObject() + { + IntegerValue = 1 + }; + + var newNested = new NestedObject() { StringProperty = "B" }; + var patchDocument = new JsonPatchDocument(); + patchDocument.Replace(o => o.NestedObject, newNested); + + var serialized = JsonConvert.SerializeObject(patchDocument); + var deserialized = JsonConvert.DeserializeObject>(serialized); + + // Act + deserialized.ApplyTo(targetObject); + + // Assert + Assert.Equal("B", targetObject.NestedObject.StringProperty); + } + + [Fact] + public void TestStringProperty_InNestedObject() + { + // Arrange + var targetObject = new SimpleObjectWithNestedObject() + { + NestedObject = new NestedObject() { StringProperty = "A"} + }; + + var patchDocument = new JsonPatchDocument(); + patchDocument.Test(o => o.StringProperty, "A"); + + // Act + patchDocument.ApplyTo(targetObject.NestedObject); + + // Assert + Assert.Equal("A", targetObject.NestedObject.StringProperty); + } + + [Fact] + public void TestNestedObject() + { + // Arrange + var targetObject = new SimpleObjectWithNestedObject() + { + NestedObject = new NestedObject() { StringProperty = "B"} + }; + + var testNested = new NestedObject() { StringProperty = "B" }; + var patchDocument = new JsonPatchDocument(); + patchDocument.Test(o => o.NestedObject, testNested); + + // Act + patchDocument.ApplyTo(targetObject); + + // Assert + Assert.Equal("B", targetObject.NestedObject.StringProperty); + } + + [Fact] + public void AddReplaces_ExistingStringProperty() + { + // Arrange + var targetObject = new SimpleObjectWithNestedObject() + { + SimpleObject = new SimpleObject() + { + StringProperty = "A" + } + }; + + var patchDocument = new JsonPatchDocument(); + patchDocument.Add(o => o.SimpleObject.StringProperty, "B"); + + // Act + patchDocument.ApplyTo(targetObject); + + // Assert + Assert.Equal("B", targetObject.SimpleObject.StringProperty); + } + + [Fact] + public void AddNewProperty_ToExpandoOject_InTypedObject() + { + var targetObject = new NestedObject() + { + DynamicProperty = new ExpandoObject() + }; + + var patchDocument = new JsonPatchDocument(); + patchDocument.Add("DynamicProperty/NewInt", 1); + + patchDocument.ApplyTo(targetObject); + + Assert.Equal(1, targetObject.DynamicProperty.NewInt); + } + + [Fact] + public void RemoveStringProperty() + { + // Arrange + var targetObject = new SimpleObjectWithNestedObject() + { + SimpleObject = new SimpleObject() + { + StringProperty = "A" + } + }; + + var patchDocument = new JsonPatchDocument(); + patchDocument.Remove(o => o.SimpleObject.StringProperty); + + // Act + patchDocument.ApplyTo(targetObject); + + // Assert + Assert.Null(targetObject.SimpleObject.StringProperty); + } + + [Fact] + public void CopyStringProperty_ToAnotherStringProperty() + { + // Arrange + var targetObject = new SimpleObjectWithNestedObject() + { + SimpleObject = new SimpleObject() + { + StringProperty = "A", + AnotherStringProperty = "B" + } + }; + + var patchDocument = new JsonPatchDocument(); + patchDocument.Copy(o => o.SimpleObject.StringProperty, o => o.SimpleObject.AnotherStringProperty); + + // Act + patchDocument.ApplyTo(targetObject); + + // Assert + Assert.Equal("A", targetObject.SimpleObject.AnotherStringProperty); + } + + [Fact] + public void Copy_DeepClonesObject() + { + // Arrange + var targetObject = new SimpleObjectWithNestedObject() + { + SimpleObject = new SimpleObject() + { + StringProperty = "A", + AnotherStringProperty = "B" + }, + InheritedObject = new InheritedObject() + { + StringProperty = "C", + AnotherStringProperty = "D" + } + }; + + var patchDocument = new JsonPatchDocument(); + patchDocument.Copy(o => o.InheritedObject, o => o.SimpleObject); + + // Act + patchDocument.ApplyTo(targetObject); + + // Assert + Assert.Equal("C", targetObject.SimpleObject.StringProperty); + Assert.Equal("D", targetObject.SimpleObject.AnotherStringProperty); + Assert.Equal("C", targetObject.InheritedObject.StringProperty); + Assert.Equal("D", targetObject.InheritedObject.AnotherStringProperty); + Assert.NotSame(targetObject.SimpleObject.StringProperty, targetObject.InheritedObject.StringProperty); + } + + [Fact] + public void Copy_KeepsObjectType() + { + // Arrange + var targetObject = new SimpleObjectWithNestedObject() + { + SimpleObject = new SimpleObject(), + InheritedObject = new InheritedObject() + }; + + var patchDocument = new JsonPatchDocument(); + patchDocument.Copy(o => o.InheritedObject, o => o.SimpleObject); + + // Act + patchDocument.ApplyTo(targetObject); + + // Assert + Assert.Equal(typeof(InheritedObject), targetObject.SimpleObject.GetType()); + } + + [Fact] + public void Copy_BreaksObjectReference() + { + // Arrange + var targetObject = new SimpleObjectWithNestedObject() + { + SimpleObject = new SimpleObject(), + InheritedObject = new InheritedObject() + }; + + var patchDocument = new JsonPatchDocument(); + patchDocument.Copy(o => o.InheritedObject, o => o.SimpleObject); + + // Act + patchDocument.ApplyTo(targetObject); + + // Assert + Assert.NotSame(targetObject.SimpleObject, targetObject.InheritedObject); + } + + [Fact] + public void MoveIntegerValue_ToAnotherIntegerProperty() + { + // Arrange + var targetObject = new SimpleObjectWithNestedObject() + { + SimpleObject = new SimpleObject() + { + IntegerValue = 2, + AnotherIntegerValue = 3 + } + }; + + var patchDocument = new JsonPatchDocument(); + patchDocument.Move(o => o.SimpleObject.IntegerValue, o => o.SimpleObject.AnotherIntegerValue); + + // Act + patchDocument.ApplyTo(targetObject); + + // Assert + Assert.Equal(2, targetObject.SimpleObject.AnotherIntegerValue); + Assert.Equal(0, targetObject.SimpleObject.IntegerValue); + } + + [Fact] + public void Move_KeepsObjectReference() + { + // Arrange + var sDto = new SimpleObject() + { + StringProperty = "A", + AnotherStringProperty = "B" + }; + var iDto = new InheritedObject() + { + StringProperty = "C", + AnotherStringProperty = "D" + }; + var targetObject = new SimpleObjectWithNestedObject() + { + SimpleObject = sDto, + InheritedObject = iDto + }; + + var patchDocument = new JsonPatchDocument(); + patchDocument.Move(o => o.InheritedObject, o => o.SimpleObject); + + // Act + patchDocument.ApplyTo(targetObject); + + // Assert + Assert.Equal("C", targetObject.SimpleObject.StringProperty); + Assert.Equal("D", targetObject.SimpleObject.AnotherStringProperty); + Assert.Same(iDto, targetObject.SimpleObject); + Assert.Null(targetObject.InheritedObject); + } + + private class SimpleObjectWithNullCheck + { + private string stringProperty; + + public string StringProperty + { + get + { + return stringProperty; + } + + set + { + if (value == null) + { + throw new ArgumentNullException(); + } + + stringProperty = value; + } + } + } + + private class SimpleObjectWithNestedObjectWithNullCheck + { + public SimpleObjectWithNullCheck SimpleObjectWithNullCheck { get; set; } + + public SimpleObjectWithNestedObjectWithNullCheck() + { + SimpleObjectWithNullCheck = new SimpleObjectWithNullCheck(); + } + } + } +} diff --git a/src/Features/JsonPatch/test/IntegrationTests/SimpleObjectIntegrationTest.cs b/src/Features/JsonPatch/test/IntegrationTests/SimpleObjectIntegrationTest.cs new file mode 100644 index 0000000000..4672d9c97b --- /dev/null +++ b/src/Features/JsonPatch/test/IntegrationTests/SimpleObjectIntegrationTest.cs @@ -0,0 +1,155 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Dynamic; +using Microsoft.AspNetCore.JsonPatch.Exceptions; +using Xunit; + +namespace Microsoft.AspNetCore.JsonPatch.IntegrationTests +{ + public class SimpleObjectIntegrationTest + { + [Fact] + public void TestDoubleValueProperty() + { + // Arrange + var targetObject = new SimpleObject() + { + DoubleValue = 9.8 + }; + + var patchDocument = new JsonPatchDocument(); + patchDocument.Test("DoubleValue", 9.8); + + // Act & Assert + patchDocument.ApplyTo(targetObject); + } + + [Fact] + public void CopyStringProperty_ToAnotherStringProperty() + { + // Arrange + var targetObject = new SimpleObject() + { + StringProperty = "A", + AnotherStringProperty = "B" + }; + + var patchDocument = new JsonPatchDocument(); + patchDocument.Copy("StringProperty", "AnotherStringProperty"); + + // Act + patchDocument.ApplyTo(targetObject); + + // Assert + Assert.Equal("A", targetObject.AnotherStringProperty); + } + + [Fact] + public void MoveIntegerProperty_ToAnotherIntegerProperty() + { + // Arrange + var targetObject = new SimpleObject() + { + IntegerValue = 2, + AnotherIntegerValue = 3 + }; + + var patchDocument = new JsonPatchDocument(); + patchDocument.Move("IntegerValue", "AnotherIntegerValue"); + + // Act + patchDocument.ApplyTo(targetObject); + + // Assert + Assert.Equal(2, targetObject.AnotherIntegerValue); + Assert.Equal(0, targetObject.IntegerValue); + } + + [Fact] + public void RemoveDecimalPropertyValue() + { + // Arrange + var targetObject = new SimpleObject() + { + DecimalValue = 9.8M + }; + + var patchDocument = new JsonPatchDocument(); + patchDocument.Remove("DecimalValue"); + + // Act + patchDocument.ApplyTo(targetObject); + + // Assert + Assert.Equal(0, targetObject.DecimalValue); + } + + [Fact] + public void ReplaceGuid() + { + // Arrange + var targetObject = new SimpleObject() + { + GuidValue = Guid.NewGuid() + }; + + var newGuid = Guid.NewGuid(); + var patchDocument = new JsonPatchDocument(); + patchDocument.Replace("GuidValue", newGuid); + + // Act + patchDocument.ApplyTo(targetObject); + + // Assert + Assert.Equal(newGuid, targetObject.GuidValue); + } + + [Fact] + public void AddReplacesGuid() + { + // Arrange + var targetObject = new SimpleObject() + { + GuidValue = Guid.NewGuid() + }; + + var newGuid = Guid.NewGuid(); + var patchDocument = new JsonPatchDocument(); + patchDocument.Add("GuidValue", newGuid); + + // Act + patchDocument.ApplyTo(targetObject); + + // Assert + Assert.Equal(newGuid, targetObject.GuidValue); + } + + // https://github.com/aspnet/AspNetCore/issues/3634 + [Fact] + public void Regression_AspNetCore3634() + { + // Assert + var document = new JsonPatchDocument(); + document.Move("/Object", "/Object/goodbye"); + + dynamic @object = new ExpandoObject(); + @object.hello = "world"; + + var target = new Regression_AspNetCore3634_Object(); + target.Object = @object; + + // Act + var ex = Assert.Throws(() => document.ApplyTo(target)); + + // Assert + Assert.Equal("For operation 'move', the target location specified by path '/Object/goodbye' was not found.", ex.Message); + } + + private class Regression_AspNetCore3634_Object + { + public dynamic Object { get; set; } + } + } +} diff --git a/src/Features/JsonPatch/test/Internal/DictionaryAdapterTest.cs b/src/Features/JsonPatch/test/Internal/DictionaryAdapterTest.cs new file mode 100644 index 0000000000..a0dcd82ab0 --- /dev/null +++ b/src/Features/JsonPatch/test/Internal/DictionaryAdapterTest.cs @@ -0,0 +1,311 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using Newtonsoft.Json.Serialization; +using Xunit; + +namespace Microsoft.AspNetCore.JsonPatch.Internal +{ + public class DictionaryAdapterTest + { + [Fact] + public void Add_KeyWhichAlreadyExists_ReplacesExistingValue() + { + // Arrange + var key = "Status"; + var dictionary = new Dictionary(StringComparer.Ordinal); + dictionary[key] = 404; + var dictionaryAdapter = new DictionaryAdapter(); + var resolver = new DefaultContractResolver(); + + // Act + var addStatus = dictionaryAdapter.TryAdd(dictionary, key, resolver, 200, out var message); + + // Assert + Assert.True(addStatus); + Assert.True(string.IsNullOrEmpty(message), "Expected no error message"); + Assert.Single(dictionary); + Assert.Equal(200, dictionary[key]); + } + + [Fact] + public void Add_IntKeyWhichAlreadyExists_ReplacesExistingValue() + { + // Arrange + var intKey = 1; + var dictionary = new Dictionary(); + dictionary[intKey] = "Mike"; + var dictionaryAdapter = new DictionaryAdapter(); + var resolver = new DefaultContractResolver(); + + // Act + var addStatus = dictionaryAdapter.TryAdd(dictionary, intKey.ToString(), resolver, "James", out var message); + + // Assert + Assert.True(addStatus); + Assert.True(string.IsNullOrEmpty(message), "Expected no error message"); + Assert.Single(dictionary); + Assert.Equal("James", dictionary[intKey]); + } + + [Fact] + public void GetInvalidKey_ThrowsInvalidPathSegmentException() + { + // Arrange + var dictionaryAdapter = new DictionaryAdapter(); + var resolver = new DefaultContractResolver(); + var key = 1; + var dictionary = new Dictionary(); + + // Act + var addStatus = dictionaryAdapter.TryAdd(dictionary, key.ToString(), resolver, "James", out var message); + + // Assert + Assert.True(addStatus); + Assert.True(string.IsNullOrEmpty(message), "Expected no error message"); + Assert.Single(dictionary); + Assert.Equal("James", dictionary[key]); + + // Act + var guidKey = new Guid(); + var getStatus = dictionaryAdapter.TryGet(dictionary, guidKey.ToString(), resolver, out var outValue, out message); + + // Assert + Assert.False(getStatus); + Assert.Equal($"The provided path segment '{guidKey.ToString()}' cannot be converted to the target type.", message); + Assert.Null(outValue); + } + + [Fact] + public void Get_UsingCaseSensitiveKey_FailureScenario() + { + // Arrange + var dictionaryAdapter = new DictionaryAdapter(); + var resolver = new DefaultContractResolver(); + var nameKey = "Name"; + var dictionary = new Dictionary(StringComparer.Ordinal); + + // Act + var addStatus = dictionaryAdapter.TryAdd(dictionary, nameKey, resolver, "James", out var message); + + // Assert + Assert.True(addStatus); + Assert.True(string.IsNullOrEmpty(message), "Expected no error message"); + Assert.Single(dictionary); + Assert.Equal("James", dictionary[nameKey]); + + // Act + var getStatus = dictionaryAdapter.TryGet(dictionary, nameKey.ToUpper(), resolver, out var outValue, out message); + + // Assert + Assert.False(getStatus); + Assert.Equal("The target location specified by path segment 'NAME' was not found.", message); + Assert.Null(outValue); + } + + [Fact] + public void Get_UsingCaseSensitiveKey_SuccessScenario() + { + // Arrange + var dictionaryAdapter = new DictionaryAdapter(); + var resolver = new DefaultContractResolver(); + var nameKey = "Name"; + var dictionary = new Dictionary(StringComparer.Ordinal); + + // Act + var addStatus = dictionaryAdapter.TryAdd(dictionary, nameKey, resolver, "James", out var message); + + // Assert + Assert.True(addStatus); + Assert.True(string.IsNullOrEmpty(message), "Expected no error message"); + Assert.Single(dictionary); + Assert.Equal("James", dictionary[nameKey]); + + // Act + addStatus = dictionaryAdapter.TryGet(dictionary, nameKey, resolver, out var outValue, out message); + + // Assert + Assert.True(addStatus); + Assert.True(string.IsNullOrEmpty(message), "Expected no error message"); + Assert.Equal("James", outValue?.ToString()); + } + + [Fact] + public void ReplacingExistingItem() + { + // Arrange + var nameKey = "Name"; + var dictionary = new Dictionary(StringComparer.Ordinal); + dictionary.Add(nameKey, "Mike"); + var dictionaryAdapter = new DictionaryAdapter(); + var resolver = new DefaultContractResolver(); + + // Act + var replaceStatus = dictionaryAdapter.TryReplace(dictionary, nameKey, resolver, "James", out var message); + + // Assert + Assert.True(replaceStatus); + Assert.True(string.IsNullOrEmpty(message), "Expected no error message"); + Assert.Single(dictionary); + Assert.Equal("James", dictionary[nameKey]); + } + + [Fact] + public void ReplacingExistingItem_WithGuidKey() + { + // Arrange + var guidKey = new Guid(); + var dictionary = new Dictionary(); + dictionary.Add(guidKey, "Mike"); + var dictionaryAdapter = new DictionaryAdapter(); + var resolver = new DefaultContractResolver(); + + // Act + var replaceStatus = dictionaryAdapter.TryReplace(dictionary, guidKey.ToString(), resolver, "James", out var message); + + // Assert + Assert.True(replaceStatus); + Assert.True(string.IsNullOrEmpty(message), "Expected no error message"); + Assert.Single(dictionary); + Assert.Equal("James", dictionary[guidKey]); + } + + [Fact] + public void ReplacingWithInvalidValue_ThrowsInvalidValueForPropertyException() + { + // Arrange + var guidKey = new Guid(); + var dictionary = new Dictionary(); + dictionary.Add(guidKey, 5); + var dictionaryAdapter = new DictionaryAdapter(); + var resolver = new DefaultContractResolver(); + + // Act + var replaceStatus = dictionaryAdapter.TryReplace(dictionary, guidKey.ToString(), resolver, "test", out var message); + + // Assert + Assert.False(replaceStatus); + Assert.Equal("The value 'test' is invalid for target location.", message); + Assert.Equal(5, dictionary[guidKey]); + } + + [Fact] + public void Replace_NonExistingKey_Fails() + { + // Arrange + var nameKey = "Name"; + var dictionary = new Dictionary(StringComparer.Ordinal); + var dictionaryAdapter = new DictionaryAdapter(); + var resolver = new DefaultContractResolver(); + + // Act + var replaceStatus = dictionaryAdapter.TryReplace(dictionary, nameKey, resolver, "Mike", out var message); + + // Assert + Assert.False(replaceStatus); + Assert.Equal("The target location specified by path segment 'Name' was not found.", message); + Assert.Empty(dictionary); + } + + [Fact] + public void Remove_NonExistingKey_Fails() + { + // Arrange + var nameKey = "Name"; + var dictionary = new Dictionary(StringComparer.Ordinal); + var dictionaryAdapter = new DictionaryAdapter(); + var resolver = new DefaultContractResolver(); + + // Act + var removeStatus = dictionaryAdapter.TryRemove(dictionary, nameKey, resolver, out var message); + + // Assert + Assert.False(removeStatus); + Assert.Equal("The target location specified by path segment 'Name' was not found.", message); + Assert.Empty(dictionary); + } + + [Fact] + public void Remove_RemovesFromDictionary() + { + // Arrange + var nameKey = "Name"; + var dictionary = new Dictionary(StringComparer.Ordinal); + dictionary[nameKey] = "James"; + var dictionaryAdapter = new DictionaryAdapter(); + var resolver = new DefaultContractResolver(); + + // Act + var removeStatus = dictionaryAdapter.TryRemove(dictionary, nameKey, resolver, out var message); + + //Assert + Assert.True(removeStatus); + Assert.True(string.IsNullOrEmpty(message), "Expected no error message"); + Assert.Empty(dictionary); + } + + [Fact] + public void Remove_RemovesFromDictionary_WithUriKey() + { + // Arrange + var uriKey = new Uri("http://www.test.com/name"); + var dictionary = new Dictionary(); + dictionary[uriKey] = "James"; + var dictionaryAdapter = new DictionaryAdapter(); + var resolver = new DefaultContractResolver(); + + // Act + var removeStatus = dictionaryAdapter.TryRemove(dictionary, uriKey.ToString(), resolver, out var message); + + //Assert + Assert.True(removeStatus); + Assert.True(string.IsNullOrEmpty(message), "Expected no error message"); + Assert.Empty(dictionary); + } + + [Fact] + public void Test_DoesNotThrowException_IfTestIsSuccessful() + { + // Arrange + var key = "Name"; + var dictionary = new Dictionary>(); + var value = new List() + { + "James", + 2, + new Customer("James", 25) + }; + dictionary[key] = value; + var dictionaryAdapter = new DictionaryAdapter>(); + var resolver = new DefaultContractResolver(); + + // Act + var testStatus = dictionaryAdapter.TryTest(dictionary, key, resolver, value, out var message); + + //Assert + Assert.True(testStatus); + Assert.True(string.IsNullOrEmpty(message), "Expected no error message"); + } + + [Fact] + public void Test_ThrowsJsonPatchException_IfTestFails() + { + // Arrange + var key = "Name"; + var dictionary = new Dictionary(); + dictionary[key] = "James"; + var dictionaryAdapter = new DictionaryAdapter(); + var resolver = new DefaultContractResolver(); + var expectedErrorMessage = "The current value 'James' at path 'Name' is not equal to the test value 'John'."; + + // Act + var testStatus = dictionaryAdapter.TryTest(dictionary, key, resolver, "John", out var errorMessage); + + //Assert + Assert.False(testStatus); + Assert.Equal(expectedErrorMessage, errorMessage); + } + } +} diff --git a/src/Features/JsonPatch/test/Internal/DynamicObjectAdapterTest.cs b/src/Features/JsonPatch/test/Internal/DynamicObjectAdapterTest.cs new file mode 100644 index 0000000000..96b1aee935 --- /dev/null +++ b/src/Features/JsonPatch/test/Internal/DynamicObjectAdapterTest.cs @@ -0,0 +1,274 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; +using Newtonsoft.Json.Serialization; +using Xunit; + +namespace Microsoft.AspNetCore.JsonPatch.Internal +{ + public class DynamicObjectAdapterTest + { + [Fact] + public void TryAdd_AddsNewProperty() + { + // Arrange + var adapter = new DynamicObjectAdapter(); + dynamic target = new DynamicTestObject(); + var segment = "NewProperty"; + var resolver = new DefaultContractResolver(); + + // Act + var status = adapter.TryAdd(target, segment, resolver, "new", out string errorMessage); + + // Assert + Assert.True(status); + Assert.Null(errorMessage); + Assert.Equal("new", target.NewProperty); + } + + [Fact] + public void TryAdd_ReplacesExistingPropertyValue() + { + // Arrange + var adapter = new DynamicObjectAdapter(); + dynamic target = new DynamicTestObject(); + target.List = new List() { 1, 2, 3 }; + var value = new List() { "stringValue1", "stringValue2" }; + var segment = "List"; + var resolver = new DefaultContractResolver(); + + // Act + var status = adapter.TryAdd(target, segment, resolver, value, out string errorMessage); + + // Assert + Assert.True(status); + Assert.Null(errorMessage); + Assert.Equal(value, target.List); + } + + [Fact] + public void TryGet_GetsPropertyValue_ForExistingProperty() + { + // Arrange + var adapter = new DynamicObjectAdapter(); + dynamic target = new DynamicTestObject(); + var segment = "NewProperty"; + var resolver = new DefaultContractResolver(); + + // Act 1 + var addStatus = adapter.TryAdd(target, segment, resolver, "new", out string errorMessage); + + // Assert 1 + Assert.True(addStatus); + Assert.Null(errorMessage); + Assert.Equal("new", target.NewProperty); + + // Act 2 + var getStatus = adapter.TryGet(target, segment, resolver, out object getValue, out string getErrorMessage); + + // Assert 2 + Assert.True(getStatus); + Assert.Null(getErrorMessage); + Assert.Equal(getValue, target.NewProperty); + } + + [Fact] + public void TryGet_ThrowsPathNotFoundException_ForNonExistingProperty() + { + // Arrange + var adapter = new DynamicObjectAdapter(); + dynamic target = new DynamicTestObject(); + var segment = "NewProperty"; + var resolver = new DefaultContractResolver(); + + // Act + var getStatus = adapter.TryGet(target, segment, resolver, out object getValue, out string getErrorMessage); + + // Assert + Assert.False(getStatus); + Assert.Null(getValue); + Assert.Equal($"The target location specified by path segment '{segment}' was not found.", getErrorMessage); + } + + [Fact] + public void TryTraverse_FindsNextTarget() + { + // Arrange + var adapter = new DynamicObjectAdapter(); + dynamic target = new DynamicTestObject(); + target.NestedObject = new DynamicTestObject(); + target.NestedObject.NewProperty = "A"; + var segment = "NestedObject"; + var resolver = new DefaultContractResolver(); + + // Act + var status = adapter.TryTraverse(target, segment, resolver, out object nextTarget, out string errorMessage); + + // Assert + Assert.True(status); + Assert.Null(errorMessage); + Assert.Equal(target.NestedObject, nextTarget); + } + + [Fact] + public void TryTraverse_ThrowsPathNotFoundException_ForNonExistingProperty() + { + // Arrange + var adapter = new DynamicObjectAdapter(); + dynamic target = new DynamicTestObject(); + target.NestedObject = new DynamicTestObject(); + var segment = "NewProperty"; + var resolver = new DefaultContractResolver(); + + // Act + var status = adapter.TryTraverse(target.NestedObject, segment, resolver, out object nextTarget, out string errorMessage); + + // Assert + Assert.False(status); + Assert.Equal($"The target location specified by path segment '{segment}' was not found.", errorMessage); + } + + [Fact] + public void TryReplace_ReplacesPropertyValue() + { + // Arrange + var adapter = new DynamicObjectAdapter(); + dynamic target = new DynamicTestObject(); + target.NewProperty = new object(); + var segment = "NewProperty"; + var resolver = new DefaultContractResolver(); + + // Act + var status = adapter.TryReplace(target, segment, resolver, "new", out string errorMessage); + + // Assert + Assert.True(status); + Assert.Null(errorMessage); + Assert.Equal("new", target.NewProperty); + } + + [Fact] + public void TryReplace_ThrowsPathNotFoundException_ForNonExistingProperty() + { + // Arrange + var adapter = new DynamicObjectAdapter(); + dynamic target = new DynamicTestObject(); + var segment = "NewProperty"; + var resolver = new DefaultContractResolver(); + + // Act + var status = adapter.TryReplace(target, segment, resolver, "test", out string errorMessage); + + // Assert + Assert.False(status); + Assert.Equal($"The target location specified by path segment '{segment}' was not found.", errorMessage); + } + + [Fact] + public void TryReplace_ThrowsPropertyInvalidException_IfNewValueIsNotTheSameTypeAsInitialValue() + { + // Arrange + var adapter = new DynamicObjectAdapter(); + dynamic target = new DynamicTestObject(); + target.NewProperty = 1; + var segment = "NewProperty"; + var resolver = new DefaultContractResolver(); + + // Act + var status = adapter.TryReplace(target, segment, resolver, "test", out string errorMessage); + + // Assert + Assert.False(status); + Assert.Equal($"The value 'test' is invalid for target location.", errorMessage); + } + + [Theory] + [InlineData(1, 0)] + [InlineData("new", null)] + public void TryRemove_SetsPropertyToDefaultOrNull(object value, object expectedValue) + { + // Arrange + var adapter = new DynamicObjectAdapter(); + dynamic target = new DynamicTestObject(); + var segment = "NewProperty"; + var resolver = new DefaultContractResolver(); + + // Act 1 + var addStatus = adapter.TryAdd(target, segment, resolver, value, out string errorMessage); + + // Assert 1 + Assert.True(addStatus); + Assert.Null(errorMessage); + Assert.Equal(value, target.NewProperty); + + // Act 2 + var removeStatus = adapter.TryRemove(target, segment, resolver, out string removeErrorMessage); + + // Assert 2 + Assert.True(removeStatus); + Assert.Null(removeErrorMessage); + Assert.Equal(expectedValue, target.NewProperty); + } + + [Fact] + public void TryRemove_ThrowsPathNotFoundException_ForNonExistingProperty() + { + // Arrange + var adapter = new DynamicObjectAdapter(); + dynamic target = new DynamicTestObject(); + var segment = "NewProperty"; + var resolver = new DefaultContractResolver(); + + // Act + var removeStatus = adapter.TryRemove(target, segment, resolver, out string removeErrorMessage); + + // Assert + Assert.False(removeStatus); + Assert.Equal($"The target location specified by path segment '{segment}' was not found.", removeErrorMessage); + } + + [Fact] + public void TryTest_DoesNotThrowException_IfTestSuccessful() + { + var adapter = new DynamicObjectAdapter(); + dynamic target = new DynamicTestObject(); + var value = new List() + { + "Joana", + 2, + new Customer("Joana", 25) + }; + target.NewProperty = value; + var segment = "NewProperty"; + var resolver = new DefaultContractResolver(); + + // Act + var testStatus = adapter.TryTest(target, segment, resolver, value, out string errorMessage); + + // Assert + Assert.Equal(value, target.NewProperty); + Assert.True(testStatus); + Assert.True(string.IsNullOrEmpty(errorMessage), "Expected no error message"); + } + + [Fact] + public void TryTest_ThrowsJsonPatchException_IfTestFails() + { + // Arrange + var adapter = new DynamicObjectAdapter(); + dynamic target = new DynamicTestObject(); + target.NewProperty = "Joana"; + var segment = "NewProperty"; + var resolver = new DefaultContractResolver(); + var expectedErrorMessage = $"The current value 'Joana' at path '{segment}' is not equal to the test value 'John'."; + + // Act + var testStatus = adapter.TryTest(target, segment, resolver, "John", out string errorMessage); + + // Assert + Assert.False(testStatus); + Assert.Equal(expectedErrorMessage, errorMessage); + } + } +} diff --git a/src/Features/JsonPatch/test/Internal/ListAdapterTest.cs b/src/Features/JsonPatch/test/Internal/ListAdapterTest.cs new file mode 100644 index 0000000000..f31e57541b --- /dev/null +++ b/src/Features/JsonPatch/test/Internal/ListAdapterTest.cs @@ -0,0 +1,497 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections; +using System.Collections.Generic; +using Moq; +using Newtonsoft.Json.Serialization; +using Xunit; + +namespace Microsoft.AspNetCore.JsonPatch.Internal +{ + public class ListAdapterTest + { + [Fact] + public void Patch_OnArrayObject_Fails() + { + // Arrange + var resolver = new Mock(MockBehavior.Strict); + var targetObject = new[] { 20, 30 }; + var listAdapter = new ListAdapter(); + + // Act + var addStatus = listAdapter.TryAdd(targetObject, "0", resolver.Object, "40", out var message); + + // Assert + Assert.False(addStatus); + Assert.Equal($"The type '{targetObject.GetType().FullName}' which is an array is not supported for json patch operations as it has a fixed size.", message); + } + + [Fact] + public void Patch_OnNonGenericListObject_Fails() + { + // Arrange + var resolver = new Mock(MockBehavior.Strict); + var targetObject = new ArrayList(); + targetObject.Add(20); + targetObject.Add(30); + var listAdapter = new ListAdapter(); + + // Act + var addStatus = listAdapter.TryAdd(targetObject, "-", resolver.Object, "40", out var message); + + // Assert + Assert.False(addStatus); + Assert.Equal($"The type '{targetObject.GetType().FullName}' which is a non generic list is not supported for json patch operations. Only generic list types are supported.", message); + } + + [Fact] + public void Add_WithIndexSameAsNumberOfElements_Works() + { + // Arrange + var resolver = new Mock(MockBehavior.Strict); + var targetObject = new List() { "James", "Mike" }; + var listAdapter = new ListAdapter(); + var position = targetObject.Count.ToString(); + + // Act + var addStatus = listAdapter.TryAdd(targetObject, position, resolver.Object, "Rob", out var message); + + // Assert + Assert.Null(message); + Assert.True(addStatus); + Assert.Equal(3, targetObject.Count); + Assert.Equal(new List() { "James", "Mike", "Rob" }, targetObject); + } + + [Theory] + [InlineData("-1")] + [InlineData("-2")] + [InlineData("3")] + public void Add_WithOutOfBoundsIndex_Fails(string position) + { + // Arrange + var resolver = new Mock(MockBehavior.Strict); + var targetObject = new List() { "James", "Mike" }; + var listAdapter = new ListAdapter(); + + // Act + var addStatus = listAdapter.TryAdd(targetObject, position, resolver.Object, "40", out var message); + + // Assert + Assert.False(addStatus); + Assert.Equal($"The index value provided by path segment '{position}' is out of bounds of the array size.", message); + } + + [Theory] + [InlineData("_")] + [InlineData("blah")] + public void Patch_WithInvalidPositionFormat_Fails(string position) + { + // Arrange + var resolver = new Mock(MockBehavior.Strict); + var targetObject = new List() { "James", "Mike" }; + var listAdapter = new ListAdapter(); + + // Act + var addStatus = listAdapter.TryAdd(targetObject, position, resolver.Object, "40", out var message); + + // Assert + Assert.False(addStatus); + Assert.Equal($"The path segment '{position}' is invalid for an array index.", message); + } + + public static TheoryData, List> AppendAtEndOfListData + { + get + { + return new TheoryData, List>() + { + { + new List() { }, + new List() { 20 } + }, + { + new List() { 5, 10 }, + new List() { 5, 10, 20 } + } + }; + } + } + + [Theory] + [MemberData(nameof(AppendAtEndOfListData))] + public void Add_Appends_AtTheEnd(List targetObject, List expected) + { + // Arrange + var resolver = new Mock(MockBehavior.Strict); + var listAdapter = new ListAdapter(); + + // Act + var addStatus = listAdapter.TryAdd(targetObject, "-", resolver.Object, "20", out var message); + + // Assert + Assert.True(addStatus); + Assert.True(string.IsNullOrEmpty(message), "Expected no error message"); + Assert.Equal(expected.Count, targetObject.Count); + Assert.Equal(expected, targetObject); + } + + [Fact] + public void Add_NullObject_ToReferenceTypeListWorks() + { + // Arrange + var resolver = new Mock(MockBehavior.Strict); + var listAdapter = new ListAdapter(); + var targetObject = new List() { "James", "Mike" }; + + // Act + var addStatus = listAdapter.TryAdd(targetObject, "-", resolver.Object, value: null, errorMessage: out var message); + + // Assert + Assert.True(addStatus); + Assert.True(string.IsNullOrEmpty(message), "Expected no error message"); + Assert.Equal(3, targetObject.Count); + Assert.Equal(new List() { "James", "Mike", null }, targetObject); + } + + [Fact] + public void Add_CompatibleTypeWorks() + { + // Arrange + var sDto = new SimpleObject(); + var iDto = new InheritedObject(); + var resolver = new Mock(MockBehavior.Strict); + var targetObject = new List() { sDto }; + var listAdapter = new ListAdapter(); + + // Act + var addStatus = listAdapter.TryAdd(targetObject, "-", resolver.Object, iDto, out var message); + + // Assert + Assert.True(addStatus); + Assert.True(string.IsNullOrEmpty(message), "Expected no error message"); + Assert.Equal(2, targetObject.Count); + Assert.Equal(new List() { sDto, iDto }, targetObject); + } + + [Fact] + public void Add_NonCompatibleType_Fails() + { + // Arrange + var resolver = new Mock(MockBehavior.Strict); + var targetObject = new List() { 10, 20 }; + var listAdapter = new ListAdapter(); + + // Act + var addStatus = listAdapter.TryAdd(targetObject, "-", resolver.Object, "James", out var message); + + // Assert + Assert.False(addStatus); + Assert.Equal("The value 'James' is invalid for target location.", message); + } + + public static TheoryData AddingDifferentComplexTypeWorksData + { + get + { + return new TheoryData() + { + { + new List() { }, + "a", + "-", + new List() { "a" } + }, + { + new List() { "a", "b" }, + "c", + "-", + new List() { "a", "b", "c" } + }, + { + new List() { "a", "b" }, + "c", + "0", + new List() { "c", "a", "b" } + }, + { + new List() { "a", "b" }, + "c", + "1", + new List() { "a", "c", "b" } + } + }; + } + } + + [Theory] + [MemberData(nameof(AddingDifferentComplexTypeWorksData))] + public void Add_DifferentComplexTypeWorks(IList targetObject, object value, string position, IList expected) + { + // Arrange + var resolver = new Mock(MockBehavior.Strict); + var listAdapter = new ListAdapter(); + + // Act + var addStatus = listAdapter.TryAdd(targetObject, position, resolver.Object, value, out var message); + + // Assert + Assert.True(addStatus); + Assert.True(string.IsNullOrEmpty(message), "Expected no error message"); + Assert.Equal(expected.Count, targetObject.Count); + Assert.Equal(expected, targetObject); + } + + public static TheoryData AddingKeepsObjectReferenceData + { + get + { + var sDto1 = new SimpleObject(); + var sDto2 = new SimpleObject(); + var sDto3 = new SimpleObject(); + return new TheoryData() + { + { + new List() { }, + sDto1, + "-", + new List() { sDto1 } + }, + { + new List() { sDto1, sDto2 }, + sDto3, + "-", + new List() { sDto1, sDto2, sDto3 } + }, + { + new List() { sDto1, sDto2 }, + sDto3, + "0", + new List() { sDto3, sDto1, sDto2 } + }, + { + new List() { sDto1, sDto2 }, + sDto3, + "1", + new List() { sDto1, sDto3, sDto2 } + } + }; + } + } + + [Theory] + [MemberData(nameof(AddingKeepsObjectReferenceData))] + public void Add_KeepsObjectReference(IList targetObject, object value, string position, IList expected) + { + // Arrange + var resolver = new Mock(MockBehavior.Strict); + var listAdapter = new ListAdapter(); + + // Act + var addStatus = listAdapter.TryAdd(targetObject, position, resolver.Object, value, out var message); + + // Assert + Assert.True(addStatus); + Assert.True(string.IsNullOrEmpty(message), "Expected no error message"); + Assert.Equal(expected.Count, targetObject.Count); + Assert.Equal(expected, targetObject); + } + + [Theory] + [InlineData(new int[] { }, "0")] + [InlineData(new[] { 10, 20 }, "-1")] + [InlineData(new[] { 10, 20 }, "2")] + public void Get_IndexOutOfBounds(int[] input, string position) + { + // Arrange + var resolver = new Mock(MockBehavior.Strict); + var targetObject = new List(input); + var listAdapter = new ListAdapter(); + + // Act + var getStatus = listAdapter.TryGet(targetObject, position, resolver.Object, out var value, out var message); + + // Assert + Assert.False(getStatus); + Assert.Equal($"The index value provided by path segment '{position}' is out of bounds of the array size.", message); + } + + [Theory] + [InlineData(new[] { 10, 20 }, "0", 10)] + [InlineData(new[] { 10, 20 }, "1", 20)] + [InlineData(new[] { 10 }, "0", 10)] + public void Get(int[] input, string position, object expected) + { + // Arrange + var resolver = new Mock(MockBehavior.Strict); + var targetObject = new List(input); + var listAdapter = new ListAdapter(); + + // Act + var getStatus = listAdapter.TryGet(targetObject, position, resolver.Object, out var value, out var message); + + // Assert + Assert.True(getStatus); + Assert.Equal(expected, value); + Assert.Equal(new List(input), targetObject); + } + + [Theory] + [InlineData(new int[] { }, "0")] + [InlineData(new[] { 10, 20 }, "-1")] + [InlineData(new[] { 10, 20 }, "2")] + public void Remove_IndexOutOfBounds(int[] input, string position) + { + // Arrange + var resolver = new Mock(MockBehavior.Strict); + var targetObject = new List(input); + var listAdapter = new ListAdapter(); + + // Act + var removeStatus = listAdapter.TryRemove(targetObject, position, resolver.Object, out var message); + + // Assert + Assert.False(removeStatus); + Assert.Equal($"The index value provided by path segment '{position}' is out of bounds of the array size.", message); + } + + [Theory] + [InlineData(new[] { 10, 20 }, "0", new[] { 20 })] + [InlineData(new[] { 10, 20 }, "1", new[] { 10 })] + [InlineData(new[] { 10 }, "0", new int[] { })] + public void Remove(int[] input, string position, int[] expected) + { + // Arrange + var resolver = new Mock(MockBehavior.Strict); + var targetObject = new List(input); + var listAdapter = new ListAdapter(); + + // Act + var removeStatus = listAdapter.TryRemove(targetObject, position, resolver.Object, out var message); + + // Assert + Assert.True(removeStatus); + Assert.Equal(new List(expected), targetObject); + } + + [Fact] + public void Replace_NonCompatibleType_Fails() + { + // Arrange + var resolver = new Mock(MockBehavior.Strict); + var targetObject = new List() { 10, 20 }; + var listAdapter = new ListAdapter(); + + // Act + var replaceStatus = listAdapter.TryReplace(targetObject, "-", resolver.Object, "James", out var message); + + // Assert + Assert.False(replaceStatus); + Assert.Equal("The value 'James' is invalid for target location.", message); + } + + [Fact] + public void Replace_ReplacesValue_AtTheEnd() + { + // Arrange + var resolver = new Mock(MockBehavior.Strict); + var targetObject = new List() { 10, 20 }; + var listAdapter = new ListAdapter(); + + // Act + var replaceStatus = listAdapter.TryReplace(targetObject, "-", resolver.Object, "30", out var message); + + // Assert + Assert.True(replaceStatus); + Assert.True(string.IsNullOrEmpty(message), "Expected no error message"); + Assert.Equal(new List() { 10, 30 }, targetObject); + } + + public static TheoryData> ReplacesValuesAtPositionData + { + get + { + return new TheoryData>() + { + { + "0", + new List() { 30, 20 } + }, + { + "1", + new List() { 10, 30 } + } + }; + } + } + + [Theory] + [MemberData(nameof(ReplacesValuesAtPositionData))] + public void Replace_ReplacesValue_AtGivenPosition(string position, List expected) + { + // Arrange + var resolver = new Mock(MockBehavior.Strict); + var targetObject = new List() { 10, 20 }; + var listAdapter = new ListAdapter(); + + // Act + var replaceStatus = listAdapter.TryReplace(targetObject, position, resolver.Object, "30", out var message); + + // Assert + Assert.True(replaceStatus); + Assert.True(string.IsNullOrEmpty(message), "Expected no error message"); + Assert.Equal(expected, targetObject); + } + + [Fact] + public void Test_DoesNotThrowException_IfTestIsSuccessful() + { + // Arrange + var resolver = new Mock(MockBehavior.Strict); + var targetObject = new List() { 10, 20 }; + var listAdapter = new ListAdapter(); + + // Act + var testStatus = listAdapter.TryTest(targetObject, "0", resolver.Object, "10", out var message); + + //Assert + Assert.True(testStatus); + Assert.True(string.IsNullOrEmpty(message), "Expected no error message"); + } + + [Fact] + public void Test_ThrowsJsonPatchException_IfTestFails() + { + // Arrange + var resolver = new Mock(MockBehavior.Strict); + var targetObject = new List() { 10, 20 }; + var listAdapter = new ListAdapter(); + var expectedErrorMessage = "The current value '20' at position '1' is not equal to the test value '10'."; + + // Act + var testStatus = listAdapter.TryTest(targetObject, "1", resolver.Object, "10", out var errorMessage); + + //Assert + Assert.False(testStatus); + Assert.Equal(expectedErrorMessage, errorMessage); + } + + [Fact] + public void Test_ThrowsJsonPatchException_IfListPositionOutOfBounds() + { + // Arrange + var resolver = new Mock(MockBehavior.Strict); + var targetObject = new List() { 10, 20 }; + var listAdapter = new ListAdapter(); + var expectedErrorMessage = "The index value provided by path segment '2' is out of bounds of the array size."; + + // Act + var testStatus = listAdapter.TryTest(targetObject, "2", resolver.Object, "10", out var errorMessage); + + //Assert + Assert.False(testStatus); + Assert.Equal(expectedErrorMessage, errorMessage); + } + } +} diff --git a/src/Features/JsonPatch/test/Internal/ObjectVisitorTest.cs b/src/Features/JsonPatch/test/Internal/ObjectVisitorTest.cs new file mode 100644 index 0000000000..44f6fc81ae --- /dev/null +++ b/src/Features/JsonPatch/test/Internal/ObjectVisitorTest.cs @@ -0,0 +1,240 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; +using System.Dynamic; +using Newtonsoft.Json.Serialization; +using Xunit; + +namespace Microsoft.AspNetCore.JsonPatch.Internal +{ + public class ObjectVisitorTest + { + private class Class1 + { + public string Name { get; set; } + public IList States { get; set; } = new List(); + public IDictionary Countries = new Dictionary(); + public dynamic Items { get; set; } = new ExpandoObject(); + } + + private class Class1Nested + { + public List Customers { get; set; } = new List(); + } + + public static IEnumerable ReturnsListAdapterData + { + get + { + var model = new Class1(); + yield return new object[] { model, "/States/-", model.States }; + yield return new object[] { model.States, "/-", model.States }; + + var nestedModel = new Class1Nested(); + nestedModel.Customers.Add(new Class1()); + yield return new object[] { nestedModel, "/Customers/0/States/-", nestedModel.Customers[0].States }; + yield return new object[] { nestedModel, "/Customers/0/States/0", nestedModel.Customers[0].States }; + yield return new object[] { nestedModel.Customers, "/0/States/-", nestedModel.Customers[0].States }; + yield return new object[] { nestedModel.Customers[0], "/States/-", nestedModel.Customers[0].States }; + } + } + + [Theory] + [MemberData(nameof(ReturnsListAdapterData))] + public void Visit_ValidPathToArray_ReturnsListAdapter(object targetObject, string path, object expectedTargetObject) + { + // Arrange + var visitor = new ObjectVisitor(new ParsedPath(path), new DefaultContractResolver()); + + // Act + var visitStatus = visitor.TryVisit(ref targetObject, out var adapter, out var message); + + // Assert + Assert.True(visitStatus); + Assert.True(string.IsNullOrEmpty(message), "Expected no error message"); + Assert.Same(expectedTargetObject, targetObject); + Assert.IsType(adapter); + } + + public static IEnumerable ReturnsDictionaryAdapterData + { + get + { + var model = new Class1(); + yield return new object[] { model, "/Countries/USA", model.Countries }; + yield return new object[] { model.Countries, "/USA", model.Countries }; + + var nestedModel = new Class1Nested(); + nestedModel.Customers.Add(new Class1()); + yield return new object[] { nestedModel, "/Customers/0/Countries/USA", nestedModel.Customers[0].Countries }; + yield return new object[] { nestedModel.Customers, "/0/Countries/USA", nestedModel.Customers[0].Countries }; + yield return new object[] { nestedModel.Customers[0], "/Countries/USA", nestedModel.Customers[0].Countries }; + } + } + + [Theory] + [MemberData(nameof(ReturnsDictionaryAdapterData))] + public void Visit_ValidPathToDictionary_ReturnsDictionaryAdapter(object targetObject, string path, object expectedTargetObject) + { + // Arrange + var visitor = new ObjectVisitor(new ParsedPath(path), new DefaultContractResolver()); + + // Act + var visitStatus = visitor.TryVisit(ref targetObject, out var adapter, out var message); + + // Assert + Assert.True(visitStatus); + Assert.True(string.IsNullOrEmpty(message), "Expected no error message"); + Assert.Same(expectedTargetObject, targetObject); + Assert.Equal(typeof(DictionaryAdapter), adapter.GetType()); + } + + public static IEnumerable ReturnsExpandoAdapterData + { + get + { + var nestedModel = new Class1Nested(); + nestedModel.Customers.Add(new Class1()); + yield return new object[] { nestedModel, "/Customers/0/Items/Name", nestedModel.Customers[0].Items }; + yield return new object[] { nestedModel.Customers, "/0/Items/Name", nestedModel.Customers[0].Items }; + yield return new object[] { nestedModel.Customers[0], "/Items/Name", nestedModel.Customers[0].Items }; + } + } + + [Theory] + [MemberData(nameof(ReturnsExpandoAdapterData))] + public void Visit_ValidPathToExpandoObject_ReturnsExpandoAdapter(object targetObject, string path, object expectedTargetObject) + { + // Arrange + var contractResolver = new DefaultContractResolver(); + var visitor = new ObjectVisitor(new ParsedPath(path), contractResolver); + + // Act + var visitStatus = visitor.TryVisit(ref targetObject, out var adapter, out var message); + + // Assert + Assert.True(visitStatus); + Assert.True(string.IsNullOrEmpty(message), "Expected no error message"); + Assert.Same(expectedTargetObject, targetObject); + Assert.Same(typeof(DictionaryAdapter), adapter.GetType()); + } + + public static IEnumerable ReturnsPocoAdapterData + { + get + { + var model = new Class1(); + yield return new object[] { model, "/Name", model }; + + var nestedModel = new Class1Nested(); + nestedModel.Customers.Add(new Class1()); + yield return new object[] { nestedModel, "/Customers/0/Name", nestedModel.Customers[0] }; + yield return new object[] { nestedModel.Customers, "/0/Name", nestedModel.Customers[0] }; + yield return new object[] { nestedModel.Customers[0], "/Name", nestedModel.Customers[0] }; + } + } + + [Theory] + [MemberData(nameof(ReturnsPocoAdapterData))] + public void Visit_ValidPath_ReturnsExpandoAdapter(object targetObject, string path, object expectedTargetObject) + { + // Arrange + var visitor = new ObjectVisitor(new ParsedPath(path), new DefaultContractResolver()); + + // Act + var visitStatus = visitor.TryVisit(ref targetObject, out var adapter, out var message); + + // Assert + Assert.True(visitStatus); + Assert.True(string.IsNullOrEmpty(message), "Expected no error message"); + Assert.Same(expectedTargetObject, targetObject); + Assert.IsType(adapter); + } + + [Theory] + [InlineData("0")] + [InlineData("-1")] + public void Visit_InvalidIndexToArray_Fails(string position) + { + // Arrange + var visitor = new ObjectVisitor(new ParsedPath($"/Customers/{position}/States/-"), new DefaultContractResolver()); + var automobileDepartment = new Class1Nested(); + object targetObject = automobileDepartment; + + // Act + var visitStatus = visitor.TryVisit(ref targetObject, out var adapter, out var message); + + // Assert + Assert.False(visitStatus); + Assert.Equal($"The index value provided by path segment '{position}' is out of bounds of the array size.", message); + } + + [Theory] + [InlineData("-")] + [InlineData("foo")] + public void Visit_InvalidIndexFormatToArray_Fails(string position) + { + // Arrange + var visitor = new ObjectVisitor(new ParsedPath($"/Customers/{position}/States/-"), new DefaultContractResolver()); + var automobileDepartment = new Class1Nested(); + object targetObject = automobileDepartment; + + // Act + var visitStatus = visitor.TryVisit(ref targetObject, out var adapter, out var message); + + // Assert + Assert.False(visitStatus); + Assert.Equal($"The path segment '{position}' is invalid for an array index.", message); + } + + [Fact] + public void Visit_DoesNotValidate_FinalPathSegment() + { + // Arrange + var visitor = new ObjectVisitor(new ParsedPath($"/NonExisting"), new DefaultContractResolver()); + var model = new Class1(); + object targetObject = model; + + // Act + var visitStatus = visitor.TryVisit(ref targetObject, out var adapter, out var message); + + // Assert + Assert.True(visitStatus); + Assert.True(string.IsNullOrEmpty(message), "Expected no error message"); + Assert.IsType(adapter); + } + + [Fact] + public void Visit_NullInteriorTarget_ReturnsFalse() + { + // Arrange + var visitor = new ObjectVisitor(new ParsedPath("/States/0"), new DefaultContractResolver()); + + // Act + object target = new Class1() { States = null, }; + var visitStatus = visitor.TryVisit(ref target, out var adapter, out var message); + + // Assert + Assert.False(visitStatus); + Assert.Null(adapter); + Assert.Null(message); + } + + [Fact] + public void Visit_NullTarget_ReturnsNullAdapter() + { + // Arrange + var visitor = new ObjectVisitor(new ParsedPath("test"), new DefaultContractResolver()); + + // Act + object target = null; + var visitStatus = visitor.TryVisit(ref target, out var adapter, out var message); + + // Assert + Assert.False(visitStatus); + Assert.Null(adapter); + Assert.Null(message); + } + } +} diff --git a/src/Features/JsonPatch/test/Internal/ParsedPathTests.cs b/src/Features/JsonPatch/test/Internal/ParsedPathTests.cs new file mode 100644 index 0000000000..c23abcdb5e --- /dev/null +++ b/src/Features/JsonPatch/test/Internal/ParsedPathTests.cs @@ -0,0 +1,42 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNetCore.JsonPatch.Exceptions; +using Xunit; + +namespace Microsoft.AspNetCore.JsonPatch.Internal +{ + public class ParsedPathTests + { + [Theory] + [InlineData("foo/bar~0baz", new string[] { "foo", "bar~baz" })] + [InlineData("foo/bar~00baz", new string[] { "foo", "bar~0baz" })] + [InlineData("foo/bar~01baz", new string[] { "foo", "bar~1baz" })] + [InlineData("foo/bar~10baz", new string[] { "foo", "bar/0baz" })] + [InlineData("foo/bar~1baz", new string[] { "foo", "bar/baz" })] + [InlineData("foo/bar~0/~0/~1~1/~0~0/baz", new string[] { "foo", "bar~", "~", "//", "~~", "baz" })] + [InlineData("~0~1foo", new string[] { "~/foo" })] + public void ParsingValidPathShouldSucceed(string path, string[] expected) + { + // Arrange & Act + var parsedPath = new ParsedPath(path); + + // Assert + Assert.Equal(expected, parsedPath.Segments); + } + + [Theory] + [InlineData("foo/bar~")] + [InlineData("~")] + [InlineData("~2")] + [InlineData("foo~3bar")] + public void PathWithInvalidEscapeSequenceShouldFail(string path) + { + // Arrange, Act & Assert + Assert.Throws(() => + { + var parsedPath = new ParsedPath(path); + }); + } + } +} diff --git a/src/Features/JsonPatch/test/Internal/PocoAdapterTest.cs b/src/Features/JsonPatch/test/Internal/PocoAdapterTest.cs new file mode 100644 index 0000000000..9a31ea11a8 --- /dev/null +++ b/src/Features/JsonPatch/test/Internal/PocoAdapterTest.cs @@ -0,0 +1,241 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Newtonsoft.Json.Serialization; +using Xunit; + +namespace Microsoft.AspNetCore.JsonPatch.Internal +{ + public class PocoAdapterTest + { + [Fact] + public void TryAdd_ReplacesExistingProperty() + { + // Arrange + var adapter = new PocoAdapter(); + var contractResolver = new DefaultContractResolver(); + var model = new Customer + { + Name = "Joana" + }; + + // Act + var addStatus = adapter.TryAdd(model, "Name", contractResolver, "John", out var errorMessage); + + // Assert + Assert.Equal("John", model.Name); + Assert.True(addStatus); + Assert.True(string.IsNullOrEmpty(errorMessage), "Expected no error message"); + } + + [Fact] + public void TryAdd_ThrowsJsonPatchException_IfPropertyDoesNotExist() + { + // Arrange + var adapter = new PocoAdapter(); + var contractResolver = new DefaultContractResolver(); + var model = new Customer + { + Name = "Joana" + }; + var expectedErrorMessage = "The target location specified by path segment 'LastName' was not found."; + + // Act + var addStatus = adapter.TryAdd(model, "LastName", contractResolver, "Smith", out var errorMessage); + + // Assert + Assert.False(addStatus); + Assert.Equal(expectedErrorMessage, errorMessage); + } + + [Fact] + public void TryGet_ExistingProperty() + { + // Arrange + var adapter = new PocoAdapter(); + var contractResolver = new DefaultContractResolver(); + var model = new Customer + { + Name = "Joana" + }; + + // Act + var getStatus = adapter.TryGet(model, "Name", contractResolver, out var value, out var errorMessage); + + // Assert + Assert.Equal("Joana", value); + Assert.True(getStatus); + Assert.True(string.IsNullOrEmpty(errorMessage), "Expected no error message"); + } + + [Fact] + public void TryGet_ThrowsJsonPatchException_IfPropertyDoesNotExist() + { + // Arrange + var adapter = new PocoAdapter(); + var contractResolver = new DefaultContractResolver(); + var model = new Customer + { + Name = "Joana" + }; + var expectedErrorMessage = "The target location specified by path segment 'LastName' was not found."; + + // Act + var getStatus = adapter.TryGet(model, "LastName", contractResolver, out var value, out var errorMessage); + + // Assert + Assert.Null(value); + Assert.False(getStatus); + Assert.Equal(expectedErrorMessage, errorMessage); + } + + [Fact] + public void TryRemove_SetsPropertyToNull() + { + // Arrange + var adapter = new PocoAdapter(); + var contractResolver = new DefaultContractResolver(); + var model = new Customer + { + Name = "Joana" + }; + + // Act + var removeStatus = adapter.TryRemove(model, "Name", contractResolver, out var errorMessage); + + // Assert + Assert.Null(model.Name); + Assert.True(removeStatus); + Assert.True(string.IsNullOrEmpty(errorMessage), "Expected no error message"); + } + + [Fact] + public void TryRemove_ThrowsJsonPatchException_IfPropertyDoesNotExist() + { + // Arrange + var adapter = new PocoAdapter(); + var contractResolver = new DefaultContractResolver(); + var model = new Customer + { + Name = "Joana" + }; + var expectedErrorMessage = "The target location specified by path segment 'LastName' was not found."; + + // Act + var removeStatus = adapter.TryRemove(model, "LastName", contractResolver, out var errorMessage); + + // Assert + Assert.False(removeStatus); + Assert.Equal(expectedErrorMessage, errorMessage); + } + + [Fact] + public void TryReplace_OverwritesExistingValue() + { + // Arrange + var adapter = new PocoAdapter(); + var contractResolver = new DefaultContractResolver(); + var model = new Customer + { + Name = "Joana" + }; + + // Act + var replaceStatus = adapter.TryReplace(model, "Name", contractResolver, "John", out var errorMessage); + + // Assert + Assert.Equal("John", model.Name); + Assert.True(replaceStatus); + Assert.True(string.IsNullOrEmpty(errorMessage), "Expected no error message"); + } + + [Fact] + public void TryReplace_ThrowsJsonPatchException_IfNewValueIsInvalidType() + { + // Arrange + var adapter = new PocoAdapter(); + var contractResolver = new DefaultContractResolver(); + var model = new Customer + { + Age = 25 + }; + + var expectedErrorMessage = "The value 'TwentySix' is invalid for target location."; + + // Act + var replaceStatus = adapter.TryReplace(model, "Age", contractResolver, "TwentySix", out var errorMessage); + + // Assert + Assert.Equal(25, model.Age); + Assert.False(replaceStatus); + Assert.Equal(expectedErrorMessage, errorMessage); + } + + [Fact] + public void TryReplace_ThrowsJsonPatchException_IfPropertyDoesNotExist() + { + // Arrange + var adapter = new PocoAdapter(); + var contractResolver = new DefaultContractResolver(); + var model = new Customer + { + Name = "Joana" + }; + var expectedErrorMessage = "The target location specified by path segment 'LastName' was not found."; + + // Act + var replaceStatus = adapter.TryReplace(model, "LastName", contractResolver, "Smith", out var errorMessage); + + // Assert + Assert.Equal("Joana", model.Name); + Assert.False(replaceStatus); + Assert.Equal(expectedErrorMessage, errorMessage); + } + + [Fact] + public void TryTest_DoesNotThrowException_IfTestSuccessful() + { + var adapter = new PocoAdapter(); + var contractResolver = new DefaultContractResolver(); + var model = new Customer + { + Name = "Joana" + }; + + // Act + var testStatus = adapter.TryTest(model, "Name", contractResolver, "Joana", out var errorMessage); + + // Assert + Assert.Equal("Joana", model.Name); + Assert.True(testStatus); + Assert.True(string.IsNullOrEmpty(errorMessage), "Expected no error message"); + } + + [Fact] + public void TryTest_ThrowsJsonPatchException_IfTestFails() + { + // Arrange + var adapter = new PocoAdapter(); + var contractResolver = new DefaultContractResolver(); + var model = new Customer + { + Name = "Joana" + }; + var expectedErrorMessage = "The current value 'Joana' at path 'Name' is not equal to the test value 'John'."; + + // Act + var testStatus = adapter.TryTest(model, "Name", contractResolver, "John", out var errorMessage); + + // Assert + Assert.False(testStatus); + Assert.Equal(expectedErrorMessage, errorMessage); + } + + private class Customer + { + public string Name { get; set; } + + public int Age { get; set; } + } + } +} diff --git a/src/Features/JsonPatch/test/JsonPatchDocumentGetPathTest.cs b/src/Features/JsonPatch/test/JsonPatchDocumentGetPathTest.cs new file mode 100644 index 0000000000..266202c7a5 --- /dev/null +++ b/src/Features/JsonPatch/test/JsonPatchDocumentGetPathTest.cs @@ -0,0 +1,122 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using Xunit; + +namespace Microsoft.AspNetCore.JsonPatch +{ + public class JsonPatchDocumentGetPathTest + { + [Fact] + public void ExpressionType_MemberAccess() + { + // Arrange + var patchDocument = new JsonPatchDocument(); + + // Act + var path = patchDocument.GetPath(p => p.SimpleObject.IntegerList, "-"); + + // Assert + Assert.Equal("/SimpleObject/IntegerList/-", path); + } + + [Fact] + public void ExpressionType_ArrayIndex() + { + // Arrange + var patchDocument = new JsonPatchDocument(); + + // Act + var path = patchDocument.GetPath(p => p[3], null); + + // Assert + Assert.Equal("/3", path); + } + + [Fact] + public void ExpressionType_Call() + { + // Arrange + var patchDocument = new JsonPatchDocument>(); + + // Act + var path = patchDocument.GetPath(p => p["key"], "3"); + + // Assert + Assert.Equal("/key/3", path); + } + + [Fact] + public void ExpressionType_Parameter_NullPosition() + { + // Arrange + var patchDocument = new JsonPatchDocument(); + + // Act + var path = patchDocument.GetPath(p => p, null); + + // Assert + Assert.Equal("/", path); + } + + [Fact] + public void ExpressionType_Parameter_WithPosition() + { + // Arrange + var patchDocument = new JsonPatchDocument(); + + // Act + var path = patchDocument.GetPath(p => p, "-"); + + // Assert + Assert.Equal("/-", path); + } + + [Fact] + public void ExpressionType_Convert() + { + // Arrange + var patchDocument = new JsonPatchDocument(); + + // Act + var path = patchDocument.GetPath(p => (BaseClass)p.DerivedObject, null); + + // Assert + Assert.Equal("/DerivedObject", path); + } + + [Fact] + public void ExpressionType_NotSupported() + { + // Arrange + var patchDocument = new JsonPatchDocument(); + + // Act + var exception = Assert.Throws(() => + { + patchDocument.GetPath(p => p.IntegerValue >= 4, null); + }); + + // Assert + Assert.Equal("The expression '(p.IntegerValue >= 4)' is not supported. Supported expressions include member access and indexer expressions.", exception.Message); + } + } + + internal class DerivedClass : BaseClass + { + public DerivedClass() + { + } + } + + internal class NestedObjectWithDerivedClass + { + public DerivedClass DerivedObject { get; set; } + } + + internal class BaseClass + { + } +} diff --git a/src/Features/JsonPatch/test/JsonPatchDocumentJsonPropertyAttributeTest.cs b/src/Features/JsonPatch/test/JsonPatchDocumentJsonPropertyAttributeTest.cs new file mode 100644 index 0000000000..a9c3d8500b --- /dev/null +++ b/src/Features/JsonPatch/test/JsonPatchDocumentJsonPropertyAttributeTest.cs @@ -0,0 +1,90 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Linq; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using Xunit; + +namespace Microsoft.AspNetCore.JsonPatch +{ + public class JsonPatchDocumentJsonPropertyAttributeTest + { + [Fact] + public void Add_RespectsJsonPropertyAttribute() + { + // Arrange + var patchDocument = new JsonPatchDocument(); + + // Act + patchDocument.Add(p => p.Name, "John"); + + // Assert + var pathToCheck = patchDocument.Operations.First().path; + Assert.Equal("/AnotherName", pathToCheck); + } + + [Fact] + public void Add_RespectsJsonPropertyAttribute_WithDotWhitespaceAndBackslashInName() + { + // Arrange + var obj = new JsonPropertyObjectWithStrangeNames(); + var patchDocument = new JsonPatchDocument(); + + // Act + patchDocument.Add("/First Name.", "John"); + patchDocument.Add("Last\\Name", "Doe"); + patchDocument.ApplyTo(obj); + + // Assert + Assert.Equal("John", obj.FirstName); + Assert.Equal("Doe", obj.LastName); + } + + [Fact] + public void Move_FallsbackToPropertyName_WhenJsonPropertyAttributeName_IsEmpty() + { + // Arrange + var patchDocument = new JsonPatchDocument(); + + // Act + patchDocument.Move(m => m.StringProperty, m => m.StringProperty2); + + // Assert + var fromPath = patchDocument.Operations.First().from; + Assert.Equal("/StringProperty", fromPath); + var toPath = patchDocument.Operations.First().path; + Assert.Equal("/StringProperty2", toPath); + } + + private class JsonPropertyObject + { + [JsonProperty("AnotherName")] + public string Name { get; set; } + } + + private class JsonPropertyObjectWithStrangeNames + { + [JsonProperty("First Name.")] + public string FirstName { get; set; } + + [JsonProperty("Last\\Name")] + public string LastName { get; set; } + } + + private class JsonPropertyWithNoPropertyName + { + [JsonProperty] + public string StringProperty { get; set; } + + [JsonProperty] + public string[] ArrayProperty { get; set; } + + [JsonProperty] + public string StringProperty2 { get; set; } + + [JsonProperty] + public string SSN { get; set; } + } + } +} diff --git a/src/Features/JsonPatch/test/JsonPatchDocumentTest.cs b/src/Features/JsonPatch/test/JsonPatchDocumentTest.cs new file mode 100644 index 0000000000..197e514cee --- /dev/null +++ b/src/Features/JsonPatch/test/JsonPatchDocumentTest.cs @@ -0,0 +1,162 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNetCore.JsonPatch.Exceptions; +using Newtonsoft.Json; +using Xunit; + +namespace Microsoft.AspNetCore.JsonPatch +{ + public class JsonPatchDocumentTest + { + [Fact] + public void InvalidPathAtBeginningShouldThrowException() + { + // Arrange + var patchDocument = new JsonPatchDocument(); + + // Act + var exception = Assert.Throws(() => + { + patchDocument.Add("//NewInt", 1); + }); + + // Assert + Assert.Equal( + "The provided string '//NewInt' is an invalid path.", + exception.Message); + } + + [Fact] + public void InvalidPathAtEndShouldThrowException() + { + // Arrange + var patchDocument = new JsonPatchDocument(); + + // Act + var exception = Assert.Throws(() => + { + patchDocument.Add("NewInt//", 1); + }); + + // Assert + Assert.Equal( + "The provided string 'NewInt//' is an invalid path.", + exception.Message); + } + + [Fact] + public void NonGenericPatchDocToGenericMustSerialize() + { + // Arrange + var targetObject = new SimpleObject() + { + StringProperty = "A", + AnotherStringProperty = "B" + }; + + var patchDocument = new JsonPatchDocument(); + patchDocument.Copy("StringProperty", "AnotherStringProperty"); + + var serialized = JsonConvert.SerializeObject(patchDocument); + var deserialized = JsonConvert.DeserializeObject>(serialized); + + // Act + deserialized.ApplyTo(targetObject); + + // Assert + Assert.Equal("A", targetObject.AnotherStringProperty); + } + + [Fact] + public void GenericPatchDocToNonGenericMustSerialize() + { + // Arrange + var targetObject = new SimpleObject() + { + StringProperty = "A", + AnotherStringProperty = "B" + }; + + var patchDocTyped = new JsonPatchDocument(); + patchDocTyped.Copy(o => o.StringProperty, o => o.AnotherStringProperty); + + var patchDocUntyped = new JsonPatchDocument(); + patchDocUntyped.Copy("StringProperty", "AnotherStringProperty"); + + var serializedTyped = JsonConvert.SerializeObject(patchDocTyped); + var serializedUntyped = JsonConvert.SerializeObject(patchDocUntyped); + var deserialized = JsonConvert.DeserializeObject(serializedTyped); + + // Act + deserialized.ApplyTo(targetObject); + + // Assert + Assert.Equal("A", targetObject.AnotherStringProperty); + } + + [Fact] + public void Deserialization_Successful_ForValidJsonPatchDocument() + { + // Arrange + var doc = new SimpleObject() + { + StringProperty = "A", + DecimalValue = 10, + DoubleValue = 10, + FloatValue = 10, + IntegerValue = 10 + }; + + var patchDocument = new JsonPatchDocument(); + patchDocument.Replace(o => o.StringProperty, "B"); + patchDocument.Replace(o => o.DecimalValue, 12); + patchDocument.Replace(o => o.DoubleValue, 12); + patchDocument.Replace(o => o.FloatValue, 12); + patchDocument.Replace(o => o.IntegerValue, 12); + + // default: no envelope + var serialized = JsonConvert.SerializeObject(patchDocument); + + // Act + var deserialized = JsonConvert.DeserializeObject>(serialized); + + // Assert + Assert.IsType>(deserialized); + } + + [Fact] + public void Deserialization_Fails_ForInvalidJsonPatchDocument() + { + // Arrange + var serialized = "{\"Operations\": [{ \"op\": \"replace\", \"path\": \"/title\", \"value\": \"New Title\"}]}"; + + // Act + var exception = Assert.Throws(() => + { + var deserialized + = JsonConvert.DeserializeObject(serialized); + }); + + // Assert + Assert.Equal("The JSON patch document was malformed and could not be parsed.", exception.Message); + } + + [Fact] + public void Deserialization_Fails_ForInvalidTypedJsonPatchDocument() + { + // Arrange + var serialized = "{\"Operations\": [{ \"op\": \"replace\", \"path\": \"/title\", \"value\": \"New Title\"}]}"; + + // Act + var exception = Assert.Throws(() => + { + var deserialized + = JsonConvert.DeserializeObject>(serialized); + }); + + // Assert + Assert.Equal("The JSON patch document was malformed and could not be parsed.", exception.Message); + } + } +} diff --git a/src/Features/JsonPatch/test/Microsoft.AspNetCore.JsonPatch.Tests.csproj b/src/Features/JsonPatch/test/Microsoft.AspNetCore.JsonPatch.Tests.csproj new file mode 100644 index 0000000000..f3bf607e18 --- /dev/null +++ b/src/Features/JsonPatch/test/Microsoft.AspNetCore.JsonPatch.Tests.csproj @@ -0,0 +1,11 @@ + + + + netcoreapp2.2;net461 + + + + + + + diff --git a/src/Features/JsonPatch/test/OperationBaseTests.cs b/src/Features/JsonPatch/test/OperationBaseTests.cs new file mode 100644 index 0000000000..955344404f --- /dev/null +++ b/src/Features/JsonPatch/test/OperationBaseTests.cs @@ -0,0 +1,41 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Xunit; + +namespace Microsoft.AspNetCore.JsonPatch.Operations +{ + public class OperationBaseTests + { + [Theory] + [InlineData("ADd", OperationType.Add)] + [InlineData("Copy", OperationType.Copy)] + [InlineData("mOVE", OperationType.Move)] + [InlineData("REMOVE", OperationType.Remove)] + [InlineData("replace", OperationType.Replace)] + [InlineData("TeSt", OperationType.Test)] + public void SetValidOperationType(string op, OperationType operationType) + { + // Arrange + var operationBase = new OperationBase(); + operationBase.op = op; + + // Act & Assert + Assert.Equal(operationType, operationBase.OperationType); + } + + [Theory] + [InlineData("invalid", OperationType.Invalid)] + [InlineData("coppy", OperationType.Invalid)] + [InlineData("notvalid", OperationType.Invalid)] + public void InvalidOperationType_SetsOperationTypeInvalid(string op, OperationType operationType) + { + // Arrange + var operationBase = new OperationBase(); + operationBase.op = op; + + // Act & Assert + Assert.Equal(operationType, operationBase.OperationType); + } + } +} diff --git a/src/Features/JsonPatch/test/TestErrorLogger.cs b/src/Features/JsonPatch/test/TestErrorLogger.cs new file mode 100644 index 0000000000..2cd6a4453e --- /dev/null +++ b/src/Features/JsonPatch/test/TestErrorLogger.cs @@ -0,0 +1,15 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.AspNetCore.JsonPatch +{ + public class TestErrorLogger where T : class + { + public string ErrorMessage { get; set; } + + public void LogErrorMessage(JsonPatchError patchError) + { + ErrorMessage = patchError.ErrorMessage; + } + } +} diff --git a/src/Features/JsonPatch/test/TestObjectModels/Customer.cs b/src/Features/JsonPatch/test/TestObjectModels/Customer.cs new file mode 100644 index 0000000000..c8a5aa22b1 --- /dev/null +++ b/src/Features/JsonPatch/test/TestObjectModels/Customer.cs @@ -0,0 +1,17 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.AspNetCore.JsonPatch.Internal +{ + internal class Customer + { + private string _name; + private int _age; + + public Customer(string name, int age) + { + _name = name; + _age = age; + } + } +} diff --git a/src/Features/JsonPatch/test/TestObjectModels/DynamicTestObject.cs b/src/Features/JsonPatch/test/TestObjectModels/DynamicTestObject.cs new file mode 100644 index 0000000000..94ecf1685d --- /dev/null +++ b/src/Features/JsonPatch/test/TestObjectModels/DynamicTestObject.cs @@ -0,0 +1,87 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; +using System.Dynamic; + +namespace Microsoft.AspNetCore.JsonPatch +{ + public class DynamicTestObject : DynamicObject + { + private Dictionary _dictionary = new Dictionary(); + + public object this[string key] { get => ((IDictionary)_dictionary)[key]; set => ((IDictionary)_dictionary)[key] = value; } + + public ICollection Keys => ((IDictionary)_dictionary).Keys; + + public ICollection Values => ((IDictionary)_dictionary).Values; + + public int Count => ((IDictionary)_dictionary).Count; + + public bool IsReadOnly => ((IDictionary)_dictionary).IsReadOnly; + + public void Add(string key, object value) + { + ((IDictionary)_dictionary).Add(key, value); + } + + public void Add(KeyValuePair item) + { + ((IDictionary)_dictionary).Add(item); + } + + public void Clear() + { + ((IDictionary)_dictionary).Clear(); + } + + public bool Contains(KeyValuePair item) + { + return ((IDictionary)_dictionary).Contains(item); + } + + public bool ContainsKey(string key) + { + return ((IDictionary)_dictionary).ContainsKey(key); + } + + public void CopyTo(KeyValuePair[] array, int arrayIndex) + { + ((IDictionary)_dictionary).CopyTo(array, arrayIndex); + } + + public IEnumerator> GetEnumerator() + { + return ((IDictionary)_dictionary).GetEnumerator(); + } + + public bool Remove(string key) + { + return ((IDictionary)_dictionary).Remove(key); + } + + public bool Remove(KeyValuePair item) + { + return ((IDictionary)_dictionary).Remove(item); + } + + public bool TryGetValue(string key, out object value) + { + return ((IDictionary)_dictionary).TryGetValue(key, out value); + } + + public override bool TryGetMember(GetMemberBinder binder, out object result) + { + var name = binder.Name; + + return TryGetValue(name, out result); + } + + public override bool TrySetMember(SetMemberBinder binder, object value) + { + _dictionary[binder.Name] = value; + + return true; + } + } +} diff --git a/src/Features/JsonPatch/test/TestObjectModels/InheritedObject.cs b/src/Features/JsonPatch/test/TestObjectModels/InheritedObject.cs new file mode 100644 index 0000000000..37b8a10dc9 --- /dev/null +++ b/src/Features/JsonPatch/test/TestObjectModels/InheritedObject.cs @@ -0,0 +1,10 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.AspNetCore.JsonPatch +{ + public class InheritedObject : SimpleObject + { + public string AdditionalStringProperty { get; set; } + } +} \ No newline at end of file diff --git a/src/Features/JsonPatch/test/TestObjectModels/NestedObject.cs b/src/Features/JsonPatch/test/TestObjectModels/NestedObject.cs new file mode 100644 index 0000000000..1b42d0d7ef --- /dev/null +++ b/src/Features/JsonPatch/test/TestObjectModels/NestedObject.cs @@ -0,0 +1,11 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.AspNetCore.JsonPatch +{ + public class NestedObject + { + public string StringProperty { get; set; } + public dynamic DynamicProperty { get; set; } + } +} \ No newline at end of file diff --git a/src/Features/JsonPatch/test/TestObjectModels/SimpleObject.cs b/src/Features/JsonPatch/test/TestObjectModels/SimpleObject.cs new file mode 100644 index 0000000000..651a91bdcf --- /dev/null +++ b/src/Features/JsonPatch/test/TestObjectModels/SimpleObject.cs @@ -0,0 +1,23 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; + +namespace Microsoft.AspNetCore.JsonPatch +{ + public class SimpleObject + { + public List SimpleObjectList { get; set; } + public List IntegerList { get; set; } + public IList IntegerIList { get; set; } + public int IntegerValue { get; set; } + public int AnotherIntegerValue { get; set; } + public string StringProperty { get; set; } + public string AnotherStringProperty { get; set; } + public decimal DecimalValue { get; set; } + public double DoubleValue { get; set; } + public float FloatValue { get; set; } + public Guid GuidValue { get; set; } + } +} \ No newline at end of file diff --git a/src/Features/JsonPatch/test/TestObjectModels/SimpleObjectWithNestedObject.cs b/src/Features/JsonPatch/test/TestObjectModels/SimpleObjectWithNestedObject.cs new file mode 100644 index 0000000000..4d7c0e2bd8 --- /dev/null +++ b/src/Features/JsonPatch/test/TestObjectModels/SimpleObjectWithNestedObject.cs @@ -0,0 +1,30 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; + +namespace Microsoft.AspNetCore.JsonPatch +{ + public class SimpleObjectWithNestedObject + { + public int IntegerValue { get; set; } + + public NestedObject NestedObject { get; set; } + + public SimpleObject SimpleObject { get; set; } + + public InheritedObject InheritedObject { get; set; } + + public List SimpleObjectList { get; set; } + + public IList SimpleObjectIList { get; set; } + + public SimpleObjectWithNestedObject() + { + NestedObject = new NestedObject(); + SimpleObject = new SimpleObject(); + InheritedObject = new InheritedObject(); + SimpleObjectList = new List(); + } + } +} \ No newline at end of file diff --git a/src/Framework/Directory.Build.props b/src/Framework/Directory.Build.props index df110bdbcd..ad7375dce5 100644 --- a/src/Framework/Directory.Build.props +++ b/src/Framework/Directory.Build.props @@ -1,6 +1,10 @@ + + true + + true diff --git a/src/Framework/Directory.Build.targets b/src/Framework/Directory.Build.targets index 1ff9f7da4b..80354ba35b 100644 --- a/src/Framework/Directory.Build.targets +++ b/src/Framework/Directory.Build.targets @@ -26,14 +26,4 @@ - - - - NuGetPackage - $(PackageId) - $(PackageVersion) - - - - diff --git a/src/Framework/Framework.UnitTests/Framework.UnitTests.csproj b/src/Framework/Framework.UnitTests/Framework.UnitTests.csproj index 5b3faf326a..f1eb186b64 100644 --- a/src/Framework/Framework.UnitTests/Framework.UnitTests.csproj +++ b/src/Framework/Framework.UnitTests/Framework.UnitTests.csproj @@ -27,10 +27,6 @@ - - - - diff --git a/src/Html/Abstractions/src/HtmlContentBuilder.cs b/src/Html/Abstractions/src/HtmlContentBuilder.cs new file mode 100644 index 0000000000..e61d9f7dc8 --- /dev/null +++ b/src/Html/Abstractions/src/HtmlContentBuilder.cs @@ -0,0 +1,207 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Text.Encodings.Web; + +namespace Microsoft.AspNetCore.Html +{ + /// + /// An implementation using an in memory list. + /// + public class HtmlContentBuilder : IHtmlContentBuilder + { + /// + /// Creates a new . + /// + public HtmlContentBuilder() + : this(new List()) + { + } + + /// + /// Creates a new with the given initial capacity. + /// + /// The initial capacity of the backing store. + public HtmlContentBuilder(int capacity) + : this(new List(capacity)) + { + } + + /// + /// Gets the number of elements in the . + /// + public int Count => Entries.Count; + + /// + /// Creates a new with the given list of entries. + /// + /// + /// The list of entries. The will use this list without making a copy. + /// + public HtmlContentBuilder(IList entries) + { + if (entries == null) + { + throw new ArgumentNullException(nameof(entries)); + } + + Entries = entries; + } + + // This is not List because that would lead to wrapping all strings to IHtmlContent + // which is not space performant. + // + // In general unencoded strings are added here. We're optimizing for that case, and allocating + // a wrapper when encoded strings are used. + // + // internal for testing. + internal IList Entries { get; } + + /// + public IHtmlContentBuilder Append(string unencoded) + { + if (!string.IsNullOrEmpty(unencoded)) + { + Entries.Add(unencoded); + } + + return this; + } + + /// + public IHtmlContentBuilder AppendHtml(IHtmlContent htmlContent) + { + if (htmlContent == null) + { + return this; + } + + Entries.Add(htmlContent); + return this; + } + + /// + public IHtmlContentBuilder AppendHtml(string encoded) + { + if (!string.IsNullOrEmpty(encoded)) + { + Entries.Add(new HtmlString(encoded)); + } + + return this; + } + + /// + public IHtmlContentBuilder Clear() + { + Entries.Clear(); + return this; + } + + /// + public void CopyTo(IHtmlContentBuilder destination) + { + if (destination == null) + { + throw new ArgumentNullException(nameof(destination)); + } + + for (var i = 0; i < Entries.Count; i++) + { + var entry = Entries[i]; + + string entryAsString; + IHtmlContentContainer entryAsContainer; + if ((entryAsString = entry as string) != null) + { + destination.Append(entryAsString); + } + else if ((entryAsContainer = entry as IHtmlContentContainer) != null) + { + // Since we're copying, do a deep flatten. + entryAsContainer.CopyTo(destination); + } + else + { + // Only string, IHtmlContent values can be added to the buffer. + destination.AppendHtml((IHtmlContent)entry); + } + } + } + + /// + public void MoveTo(IHtmlContentBuilder destination) + { + if (destination == null) + { + throw new ArgumentNullException(nameof(destination)); + } + + for (var i = 0; i < Entries.Count; i++) + { + var entry = Entries[i]; + + string entryAsString; + IHtmlContentContainer entryAsContainer; + if ((entryAsString = entry as string) != null) + { + destination.Append(entryAsString); + } + else if ((entryAsContainer = entry as IHtmlContentContainer) != null) + { + // Since we're moving, do a deep flatten. + entryAsContainer.MoveTo(destination); + } + else + { + // Only string, IHtmlContent values can be added to the buffer. + destination.AppendHtml((IHtmlContent)entry); + } + } + + Entries.Clear(); + } + + /// + public void WriteTo(TextWriter writer, HtmlEncoder encoder) + { + if (writer == null) + { + throw new ArgumentNullException(nameof(writer)); + } + + if (encoder == null) + { + throw new ArgumentNullException(nameof(encoder)); + } + + for (var i = 0; i < Entries.Count; i++) + { + var entry = Entries[i]; + + var entryAsString = entry as string; + if (entryAsString != null) + { + encoder.Encode(writer, entryAsString); + } + else + { + // Only string, IHtmlContent values can be added to the buffer. + ((IHtmlContent)entry).WriteTo(writer, encoder); + } + } + } + + private string DebuggerToString() + { + using (var writer = new StringWriter()) + { + WriteTo(writer, HtmlEncoder.Default); + return writer.ToString(); + } + } + } +} diff --git a/src/Html/Abstractions/src/HtmlContentBuilderExtensions.cs b/src/Html/Abstractions/src/HtmlContentBuilderExtensions.cs new file mode 100644 index 0000000000..a5a83fdbb3 --- /dev/null +++ b/src/Html/Abstractions/src/HtmlContentBuilderExtensions.cs @@ -0,0 +1,223 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Text.Encodings.Web; + +namespace Microsoft.AspNetCore.Html +{ + /// + /// Extension methods for . + /// + public static class HtmlContentBuilderExtensions + { + /// + /// Appends the specified to the existing content after replacing each format + /// item with the HTML encoded representation of the corresponding item in the + /// array. + /// + /// The . + /// + /// The composite format (see http://msdn.microsoft.com/en-us/library/txafckwd.aspx). + /// The format string is assumed to be HTML encoded as-provided, and no further encoding will be performed. + /// + /// + /// The object array to format. Each element in the array will be formatted and then HTML encoded. + /// + /// A reference to this instance after the append operation has completed. + public static IHtmlContentBuilder AppendFormat( + this IHtmlContentBuilder builder, + string format, + params object[] args) + { + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } + + if (format == null) + { + throw new ArgumentNullException(nameof(format)); + } + + if (args == null) + { + throw new ArgumentNullException(nameof(args)); + } + + builder.AppendHtml(new HtmlFormattableString(format, args)); + return builder; + } + + /// + /// Appends the specified to the existing content with information from the + /// after replacing each format item with the HTML encoded + /// representation of the corresponding item in the array. + /// + /// The . + /// An object that supplies culture-specific formatting information. + /// + /// The composite format (see http://msdn.microsoft.com/en-us/library/txafckwd.aspx). + /// The format string is assumed to be HTML encoded as-provided, and no further encoding will be performed. + /// + /// + /// The object array to format. Each element in the array will be formatted and then HTML encoded. + /// + /// A reference to this instance after the append operation has completed. + public static IHtmlContentBuilder AppendFormat( + this IHtmlContentBuilder builder, + IFormatProvider formatProvider, + string format, + params object[] args) + { + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } + + if (format == null) + { + throw new ArgumentNullException(nameof(format)); + } + + if (args == null) + { + throw new ArgumentNullException(nameof(args)); + } + + builder.AppendHtml(new HtmlFormattableString(formatProvider, format, args)); + return builder; + } + + /// + /// Appends an . + /// + /// The . + /// The . + public static IHtmlContentBuilder AppendLine(this IHtmlContentBuilder builder) + { + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } + + builder.AppendHtml(HtmlString.NewLine); + return builder; + } + + /// + /// Appends an after appending the value. + /// The value is treated as unencoded as-provided, and will be HTML encoded before writing to output. + /// + /// The . + /// The to append. + /// The . + public static IHtmlContentBuilder AppendLine(this IHtmlContentBuilder builder, string unencoded) + { + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } + + builder.Append(unencoded); + builder.AppendHtml(HtmlString.NewLine); + return builder; + } + + /// + /// Appends an after appending the value. + /// + /// The . + /// The to append. + /// The . + public static IHtmlContentBuilder AppendLine(this IHtmlContentBuilder builder, IHtmlContent content) + { + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } + + builder.AppendHtml(content); + builder.AppendHtml(HtmlString.NewLine); + return builder; + } + + /// + /// Appends an after appending the value. + /// The value is treated as HTML encoded as-provided, and no further encoding will be performed. + /// + /// The . + /// The HTML encoded to append. + /// The . + public static IHtmlContentBuilder AppendHtmlLine(this IHtmlContentBuilder builder, string encoded) + { + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } + + builder.AppendHtml(encoded); + builder.AppendHtml(HtmlString.NewLine); + return builder; + } + + /// + /// Sets the content to the value. The value is treated as unencoded as-provided, + /// and will be HTML encoded before writing to output. + /// + /// The . + /// The value that replaces the content. + /// The . + public static IHtmlContentBuilder SetContent(this IHtmlContentBuilder builder, string unencoded) + { + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } + + builder.Clear(); + builder.Append(unencoded); + return builder; + } + + /// + /// Sets the content to the value. + /// + /// The . + /// The value that replaces the content. + /// The . + public static IHtmlContentBuilder SetHtmlContent(this IHtmlContentBuilder builder, IHtmlContent content) + { + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } + + builder.Clear(); + builder.AppendHtml(content); + return builder; + } + + /// + /// Sets the content to the value. The value is treated as HTML encoded as-provided, and + /// no further encoding will be performed. + /// + /// The . + /// The HTML encoded that replaces the content. + /// The . + public static IHtmlContentBuilder SetHtmlContent(this IHtmlContentBuilder builder, string encoded) + { + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } + + builder.Clear(); + builder.AppendHtml(encoded); + return builder; + } + } +} diff --git a/src/Html/Abstractions/src/HtmlFormattableString.cs b/src/Html/Abstractions/src/HtmlFormattableString.cs new file mode 100644 index 0000000000..24bc7c5e2f --- /dev/null +++ b/src/Html/Abstractions/src/HtmlFormattableString.cs @@ -0,0 +1,184 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Text.Encodings.Web; + +namespace Microsoft.AspNetCore.Html +{ + /// + /// An implementation of composite string formatting + /// (see https://msdn.microsoft.com/en-us/library/txafckwd(v=vs.110).aspx) which HTML encodes + /// formatted arguments. + /// + [DebuggerDisplay("{DebuggerToString()}")] + public class HtmlFormattableString : IHtmlContent + { + private readonly IFormatProvider _formatProvider; + private readonly string _format; + private readonly object[] _args; + + /// + /// Creates a new with the given and + /// . + /// + /// A composite format string. + /// An array that contains objects to format. + public HtmlFormattableString(string format, params object[] args) + : this(formatProvider: null, format: format, args: args) + { + } + + /// + /// Creates a new with the given , + /// and . + /// + /// An object that provides culture-specific formatting information. + /// A composite format string. + /// An array that contains objects to format. + public HtmlFormattableString(IFormatProvider formatProvider, string format, params object[] args) + { + if (format == null) + { + throw new ArgumentNullException(nameof(format)); + } + + if (args == null) + { + throw new ArgumentNullException(nameof(args)); + } + + _formatProvider = formatProvider ?? CultureInfo.CurrentCulture; + _format = format; + _args = args; + } + + /// + public void WriteTo(TextWriter writer, HtmlEncoder encoder) + { + if (writer == null) + { + throw new ArgumentNullException(nameof(writer)); + } + + if (encoder == null) + { + throw new ArgumentNullException(nameof(encoder)); + } + + var formatProvider = new EncodingFormatProvider(_formatProvider, encoder); + writer.Write(string.Format(formatProvider, _format, _args)); + } + + private string DebuggerToString() + { + using (var writer = new StringWriter()) + { + WriteTo(writer, HtmlEncoder.Default); + return writer.ToString(); + } + } + + // This class implements Html encoding via an ICustomFormatter. Passing an instance of this + // class into a string.Format method or anything similar will evaluate arguments implementing + // IHtmlContent without HTML encoding them, and will give other arguments the standard + // composite format string treatment, and then HTML encode the result. + // + // Plenty of examples of ICustomFormatter and the interactions with string.Format here: + // https://msdn.microsoft.com/en-us/library/system.string.format(v=vs.110).aspx#Format6_Example + private class EncodingFormatProvider : IFormatProvider, ICustomFormatter + { + private readonly HtmlEncoder _encoder; + private readonly IFormatProvider _formatProvider; + + private StringWriter _writer; + + public EncodingFormatProvider(IFormatProvider formatProvider, HtmlEncoder encoder) + { + Debug.Assert(formatProvider != null); + Debug.Assert(encoder != null); + + _formatProvider = formatProvider; + _encoder = encoder; + } + + public string Format(string format, object arg, IFormatProvider formatProvider) + { + // These are the cases we need to special case. We trust the HtmlString or IHtmlContent instance + // to do the right thing with encoding. + var htmlString = arg as HtmlString; + if (htmlString != null) + { + return htmlString.ToString(); + } + + var htmlContent = arg as IHtmlContent; + if (htmlContent != null) + { + _writer = _writer ?? new StringWriter(); + + htmlContent.WriteTo(_writer, _encoder); + + var result = _writer.ToString(); + _writer.GetStringBuilder().Clear(); + + return result; + } + + // If we get here then 'arg' is not an IHtmlContent, and we want to handle it the way a normal + // string.Format would work, but then HTML encode the result. + // + // First check for an ICustomFormatter - if the IFormatProvider is a CultureInfo, then it's likely + // that ICustomFormatter will be null. + var customFormatter = (ICustomFormatter)_formatProvider.GetFormat(typeof(ICustomFormatter)); + if (customFormatter != null) + { + var result = customFormatter.Format(format, arg, _formatProvider); + if (result != null) + { + return _encoder.Encode(result); + } + } + + // Next check if 'arg' is an IFormattable (DateTime is an example). + // + // An IFormattable will likely call back into the IFormatterProvider and ask for more information + // about how to format itself. This is the typical case when IFormatterProvider is a CultureInfo. + var formattable = arg as IFormattable; + if (formattable != null) + { + var result = formattable.ToString(format, _formatProvider); + if (result != null) + { + return _encoder.Encode(result); + } + } + + // If we get here then there's nothing really smart left to try. + if (arg != null) + { + var result = arg.ToString(); + if (result != null) + { + return _encoder.Encode(result); + } + } + + return string.Empty; + } + + public object GetFormat(Type formatType) + { + if (formatType == typeof(ICustomFormatter)) + { + return this; + } + + return null; + } + } + } +} diff --git a/src/Html/Abstractions/src/HtmlString.cs b/src/Html/Abstractions/src/HtmlString.cs new file mode 100644 index 0000000000..e7a516bd04 --- /dev/null +++ b/src/Html/Abstractions/src/HtmlString.cs @@ -0,0 +1,61 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.IO; +using System.Text.Encodings.Web; + +namespace Microsoft.AspNetCore.Html +{ + /// + /// An implementation that wraps an HTML encoded . + /// + public class HtmlString : IHtmlContent + { + /// + /// An instance for . + /// + public static readonly HtmlString NewLine = new HtmlString(Environment.NewLine); + + /// + /// An instance for . + /// + public static readonly HtmlString Empty = new HtmlString(string.Empty); + + /// + /// Creates a new . + /// + /// The HTML encoded value. + public HtmlString(string value) + { + Value = value; + } + + /// + /// Gets the HTML encoded value. + /// + public string Value { get; } + + /// + public void WriteTo(TextWriter writer, HtmlEncoder encoder) + { + if (writer == null) + { + throw new ArgumentNullException(nameof(writer)); + } + + if (encoder == null) + { + throw new ArgumentNullException(nameof(encoder)); + } + + writer.Write(Value); + } + + /// + public override string ToString() + { + return Value ?? string.Empty; + } + } +} diff --git a/src/Html/Abstractions/src/IHtmlContent.cs b/src/Html/Abstractions/src/IHtmlContent.cs new file mode 100644 index 0000000000..2e9a0f19e2 --- /dev/null +++ b/src/Html/Abstractions/src/IHtmlContent.cs @@ -0,0 +1,22 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.IO; +using System.Text.Encodings.Web; + +namespace Microsoft.AspNetCore.Html +{ + /// + /// HTML content which can be written to a TextWriter. + /// + public interface IHtmlContent + { + /// + /// Writes the content by encoding it with the specified + /// to the specified . + /// + /// The to which the content is written. + /// The which encodes the content to be written. + void WriteTo(TextWriter writer, HtmlEncoder encoder); + } +} \ No newline at end of file diff --git a/src/Html/Abstractions/src/IHtmlContentBuilder.cs b/src/Html/Abstractions/src/IHtmlContentBuilder.cs new file mode 100644 index 0000000000..912fe442aa --- /dev/null +++ b/src/Html/Abstractions/src/IHtmlContentBuilder.cs @@ -0,0 +1,40 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.AspNetCore.Html +{ + /// + /// A builder for HTML content. + /// + public interface IHtmlContentBuilder : IHtmlContentContainer + { + /// + /// Appends an instance. + /// + /// The to append. + /// The . + IHtmlContentBuilder AppendHtml(IHtmlContent content); + + /// + /// Appends a value. The value is treated as unencoded as-provided, and will be HTML + /// encoded before writing to output. + /// + /// The to append. + /// The . + IHtmlContentBuilder Append(string unencoded); + + /// + /// Appends an HTML encoded value. The value is treated as HTML encoded as-provided, and + /// no further encoding will be performed. + /// + /// The HTML encoded to append. + /// The . + IHtmlContentBuilder AppendHtml(string encoded); + + /// + /// Clears the content. + /// + /// The . + IHtmlContentBuilder Clear(); + } +} diff --git a/src/Html/Abstractions/src/IHtmlContentContainer.cs b/src/Html/Abstractions/src/IHtmlContentContainer.cs new file mode 100644 index 0000000000..f17811433c --- /dev/null +++ b/src/Html/Abstractions/src/IHtmlContentContainer.cs @@ -0,0 +1,30 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.AspNetCore.Html +{ + /// + /// Defines a contract for instances made up of several components which + /// can be copied into an . + /// + public interface IHtmlContentContainer : IHtmlContent + { + /// + /// Copies the contained content of this into . + /// + /// The . + void CopyTo(IHtmlContentBuilder builder); + + /// + /// + /// Moves the contained content of this into . + /// + /// + /// After is called, this instance should be left + /// in an empty state. + /// + /// + /// The . + void MoveTo(IHtmlContentBuilder builder); + } +} diff --git a/src/Html/Abstractions/src/Microsoft.AspNetCore.Html.Abstractions.csproj b/src/Html/Abstractions/src/Microsoft.AspNetCore.Html.Abstractions.csproj new file mode 100644 index 0000000000..f914769d17 --- /dev/null +++ b/src/Html/Abstractions/src/Microsoft.AspNetCore.Html.Abstractions.csproj @@ -0,0 +1,18 @@ + + + + ASP.NET Core HTML abstractions used for building HTML content. + +Commonly used types: +Microsoft.AspNetCore.Html.HtmlString +Microsoft.AspNetCore.Html.IHtmlContent + netstandard2.0 + true + aspnetcore + + + + + + + diff --git a/src/Html/Abstractions/src/Properties/AssemblyInfo.cs b/src/Html/Abstractions/src/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..cb0b47442c --- /dev/null +++ b/src/Html/Abstractions/src/Properties/AssemblyInfo.cs @@ -0,0 +1,8 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Reflection; +using System.Resources; +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Html.Abstractions.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] diff --git a/src/Html/Abstractions/src/baseline.netcore.json b/src/Html/Abstractions/src/baseline.netcore.json new file mode 100644 index 0000000000..29f855f97b --- /dev/null +++ b/src/Html/Abstractions/src/baseline.netcore.json @@ -0,0 +1,626 @@ +{ + "AssemblyIdentity": "Microsoft.AspNetCore.Html.Abstractions, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", + "Types": [ + { + "Name": "Microsoft.AspNetCore.Html.HtmlContentBuilder", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.Html.IHtmlContentBuilder" + ], + "Members": [ + { + "Kind": "Method", + "Name": "CopyTo", + "Parameters": [ + { + "Name": "destination", + "Type": "Microsoft.AspNetCore.Html.IHtmlContentBuilder" + } + ], + "ReturnType": "System.Void", + "Sealed": true, + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Html.IHtmlContentContainer", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "MoveTo", + "Parameters": [ + { + "Name": "destination", + "Type": "Microsoft.AspNetCore.Html.IHtmlContentBuilder" + } + ], + "ReturnType": "System.Void", + "Sealed": true, + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Html.IHtmlContentContainer", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "WriteTo", + "Parameters": [ + { + "Name": "writer", + "Type": "System.IO.TextWriter" + }, + { + "Name": "encoder", + "Type": "System.Text.Encodings.Web.HtmlEncoder" + } + ], + "ReturnType": "System.Void", + "Sealed": true, + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Html.IHtmlContent", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Count", + "Parameters": [], + "ReturnType": "System.Int32", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "Append", + "Parameters": [ + { + "Name": "unencoded", + "Type": "System.String" + } + ], + "ReturnType": "Microsoft.AspNetCore.Html.IHtmlContentBuilder", + "Sealed": true, + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Html.IHtmlContentBuilder", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "AppendHtml", + "Parameters": [ + { + "Name": "htmlContent", + "Type": "Microsoft.AspNetCore.Html.IHtmlContent" + } + ], + "ReturnType": "Microsoft.AspNetCore.Html.IHtmlContentBuilder", + "Sealed": true, + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Html.IHtmlContentBuilder", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "AppendHtml", + "Parameters": [ + { + "Name": "encoded", + "Type": "System.String" + } + ], + "ReturnType": "Microsoft.AspNetCore.Html.IHtmlContentBuilder", + "Sealed": true, + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Html.IHtmlContentBuilder", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "Clear", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Html.IHtmlContentBuilder", + "Sealed": true, + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Html.IHtmlContentBuilder", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "capacity", + "Type": "System.Int32" + } + ], + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "entries", + "Type": "System.Collections.Generic.IList" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Html.HtmlContentBuilderExtensions", + "Visibility": "Public", + "Kind": "Class", + "Abstract": true, + "Static": true, + "Sealed": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "AppendFormat", + "Parameters": [ + { + "Name": "builder", + "Type": "Microsoft.AspNetCore.Html.IHtmlContentBuilder" + }, + { + "Name": "format", + "Type": "System.String" + }, + { + "Name": "args", + "Type": "System.Object[]", + "IsParams": true + } + ], + "ReturnType": "Microsoft.AspNetCore.Html.IHtmlContentBuilder", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "AppendFormat", + "Parameters": [ + { + "Name": "builder", + "Type": "Microsoft.AspNetCore.Html.IHtmlContentBuilder" + }, + { + "Name": "formatProvider", + "Type": "System.IFormatProvider" + }, + { + "Name": "format", + "Type": "System.String" + }, + { + "Name": "args", + "Type": "System.Object[]", + "IsParams": true + } + ], + "ReturnType": "Microsoft.AspNetCore.Html.IHtmlContentBuilder", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "AppendLine", + "Parameters": [ + { + "Name": "builder", + "Type": "Microsoft.AspNetCore.Html.IHtmlContentBuilder" + } + ], + "ReturnType": "Microsoft.AspNetCore.Html.IHtmlContentBuilder", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "AppendLine", + "Parameters": [ + { + "Name": "builder", + "Type": "Microsoft.AspNetCore.Html.IHtmlContentBuilder" + }, + { + "Name": "unencoded", + "Type": "System.String" + } + ], + "ReturnType": "Microsoft.AspNetCore.Html.IHtmlContentBuilder", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "AppendLine", + "Parameters": [ + { + "Name": "builder", + "Type": "Microsoft.AspNetCore.Html.IHtmlContentBuilder" + }, + { + "Name": "content", + "Type": "Microsoft.AspNetCore.Html.IHtmlContent" + } + ], + "ReturnType": "Microsoft.AspNetCore.Html.IHtmlContentBuilder", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "AppendHtmlLine", + "Parameters": [ + { + "Name": "builder", + "Type": "Microsoft.AspNetCore.Html.IHtmlContentBuilder" + }, + { + "Name": "encoded", + "Type": "System.String" + } + ], + "ReturnType": "Microsoft.AspNetCore.Html.IHtmlContentBuilder", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "SetContent", + "Parameters": [ + { + "Name": "builder", + "Type": "Microsoft.AspNetCore.Html.IHtmlContentBuilder" + }, + { + "Name": "unencoded", + "Type": "System.String" + } + ], + "ReturnType": "Microsoft.AspNetCore.Html.IHtmlContentBuilder", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "SetHtmlContent", + "Parameters": [ + { + "Name": "builder", + "Type": "Microsoft.AspNetCore.Html.IHtmlContentBuilder" + }, + { + "Name": "content", + "Type": "Microsoft.AspNetCore.Html.IHtmlContent" + } + ], + "ReturnType": "Microsoft.AspNetCore.Html.IHtmlContentBuilder", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "SetHtmlContent", + "Parameters": [ + { + "Name": "builder", + "Type": "Microsoft.AspNetCore.Html.IHtmlContentBuilder" + }, + { + "Name": "encoded", + "Type": "System.String" + } + ], + "ReturnType": "Microsoft.AspNetCore.Html.IHtmlContentBuilder", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Html.HtmlFormattableString", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.Html.IHtmlContent" + ], + "Members": [ + { + "Kind": "Method", + "Name": "WriteTo", + "Parameters": [ + { + "Name": "writer", + "Type": "System.IO.TextWriter" + }, + { + "Name": "encoder", + "Type": "System.Text.Encodings.Web.HtmlEncoder" + } + ], + "ReturnType": "System.Void", + "Sealed": true, + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Html.IHtmlContent", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "format", + "Type": "System.String" + }, + { + "Name": "args", + "Type": "System.Object[]", + "IsParams": true + } + ], + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "formatProvider", + "Type": "System.IFormatProvider" + }, + { + "Name": "format", + "Type": "System.String" + }, + { + "Name": "args", + "Type": "System.Object[]", + "IsParams": true + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Html.HtmlString", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.Html.IHtmlContent" + ], + "Members": [ + { + "Kind": "Method", + "Name": "get_Value", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "WriteTo", + "Parameters": [ + { + "Name": "writer", + "Type": "System.IO.TextWriter" + }, + { + "Name": "encoder", + "Type": "System.Text.Encodings.Web.HtmlEncoder" + } + ], + "ReturnType": "System.Void", + "Sealed": true, + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Html.IHtmlContent", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ToString", + "Parameters": [], + "ReturnType": "System.String", + "Virtual": true, + "Override": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Field", + "Name": "NewLine", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Html.HtmlString", + "Static": true, + "ReadOnly": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Field", + "Name": "Empty", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Html.HtmlString", + "Static": true, + "ReadOnly": true, + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Html.IHtmlContent", + "Visibility": "Public", + "Kind": "Interface", + "Abstract": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "WriteTo", + "Parameters": [ + { + "Name": "writer", + "Type": "System.IO.TextWriter" + }, + { + "Name": "encoder", + "Type": "System.Text.Encodings.Web.HtmlEncoder" + } + ], + "ReturnType": "System.Void", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Html.IHtmlContentBuilder", + "Visibility": "Public", + "Kind": "Interface", + "Abstract": true, + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.Html.IHtmlContentContainer" + ], + "Members": [ + { + "Kind": "Method", + "Name": "AppendHtml", + "Parameters": [ + { + "Name": "content", + "Type": "Microsoft.AspNetCore.Html.IHtmlContent" + } + ], + "ReturnType": "Microsoft.AspNetCore.Html.IHtmlContentBuilder", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "Append", + "Parameters": [ + { + "Name": "unencoded", + "Type": "System.String" + } + ], + "ReturnType": "Microsoft.AspNetCore.Html.IHtmlContentBuilder", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "AppendHtml", + "Parameters": [ + { + "Name": "encoded", + "Type": "System.String" + } + ], + "ReturnType": "Microsoft.AspNetCore.Html.IHtmlContentBuilder", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "Clear", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Html.IHtmlContentBuilder", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Html.IHtmlContentContainer", + "Visibility": "Public", + "Kind": "Interface", + "Abstract": true, + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.Html.IHtmlContent" + ], + "Members": [ + { + "Kind": "Method", + "Name": "CopyTo", + "Parameters": [ + { + "Name": "builder", + "Type": "Microsoft.AspNetCore.Html.IHtmlContentBuilder" + } + ], + "ReturnType": "System.Void", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "MoveTo", + "Parameters": [ + { + "Name": "builder", + "Type": "Microsoft.AspNetCore.Html.IHtmlContentBuilder" + } + ], + "ReturnType": "System.Void", + "GenericParameter": [] + } + ], + "GenericParameters": [] + } + ] +} \ No newline at end of file diff --git a/src/Html/Abstractions/test/HtmlContentBuilderExtensionsTest.cs b/src/Html/Abstractions/test/HtmlContentBuilderExtensionsTest.cs new file mode 100644 index 0000000000..c14daeeebb --- /dev/null +++ b/src/Html/Abstractions/test/HtmlContentBuilderExtensionsTest.cs @@ -0,0 +1,463 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Text.Encodings.Web; +using Microsoft.AspNetCore.Testing; +using Microsoft.Extensions.WebEncoders.Testing; +using Xunit; + +namespace Microsoft.AspNetCore.Html.Test +{ + public class HtmlContentBuilderExtensionsTest + { + [Fact] + public void Builder_AppendLine_Empty() + { + // Arrange + var builder = new TestHtmlContentBuilder(); + + // Act + builder.AppendLine(); + + // Assert + Assert.Collection( + builder.Entries, + entry => Assert.Equal(Environment.NewLine, HtmlContentToString(entry))); + } + + [Fact] + public void Builder_AppendLine_String() + { + // Arrange + var builder = new TestHtmlContentBuilder(); + + // Act + builder.AppendLine("Hi"); + + // Assert + Assert.Collection( + builder.Entries, + entry => Assert.Equal("Hi", Assert.IsType(entry).Value), + entry => Assert.Equal(Environment.NewLine, HtmlContentToString(entry))); + } + + [Fact] + public void Builder_AppendLine_IHtmlContent() + { + // Arrange + var builder = new TestHtmlContentBuilder(); + var content = new OtherHtmlContent("Hi"); + + // Act + builder.AppendLine(content); + + // Assert + Assert.Collection( + builder.Entries, + entry => Assert.Same(content, entry), + entry => Assert.Equal(Environment.NewLine, HtmlContentToString(entry))); + } + + [Fact] + public void Builder_AppendHtmlLine_String() + { + // Arrange + var builder = new TestHtmlContentBuilder(); + + // Act + builder.AppendHtmlLine("Hi"); + + // Assert + Assert.Collection( + builder.Entries, + entry => Assert.Equal("Hi", Assert.IsType(entry).Value), + entry => Assert.Equal(Environment.NewLine, HtmlContentToString(entry))); + } + + [Fact] + public void Builder_SetContent_String() + { + // Arrange + var builder = new TestHtmlContentBuilder(); + builder.Append("Existing Content. Will be Cleared."); + + // Act + builder.SetContent("Hi"); + + // Assert + Assert.Collection( + builder.Entries, + entry => Assert.Equal("Hi", Assert.IsType(entry).Value)); + } + + [Fact] + public void Builder_SetContent_IHtmlContent() + { + // Arrange + var builder = new TestHtmlContentBuilder(); + builder.Append("Existing Content. Will be Cleared."); + + var content = new OtherHtmlContent("Hi"); + + // Act + builder.SetHtmlContent(content); + + // Assert + Assert.Collection( + builder.Entries, + entry => Assert.Same(content, entry)); + } + + [Fact] + public void Builder_SetHtmlContent_String() + { + // Arrange + var builder = new TestHtmlContentBuilder(); + builder.Append("Existing Content. Will be Cleared."); + + // Act + builder.SetHtmlContent("Hi"); + + // Assert + Assert.Collection( + builder.Entries, + entry => Assert.Equal("Hi", Assert.IsType(entry).Value)); + } + + [Fact] + public void Builder_AppendFormat() + { + // Arrange + var builder = new TestHtmlContentBuilder(); + + // Act + builder.AppendFormat("{0} {1} {2} {3}!", "First", "Second", "Third", "Fourth"); + + // Assert + Assert.Equal( + "HtmlEncode[[First]] HtmlEncode[[Second]] HtmlEncode[[Third]] HtmlEncode[[Fourth]]!", + HtmlContentToString(builder)); + } + + [Fact] + public void Builder_AppendFormat_HtmlContent() + { + // Arrange + var builder = new TestHtmlContentBuilder(); + + // Act + builder.AppendFormat("{0}!", new EncodedString("First")); + + // Assert + Assert.Equal( + "First!", + HtmlContentToString(builder)); + } + + [Fact] + public void Builder_AppendFormat_HtmlString() + { + // Arrange + var builder = new TestHtmlContentBuilder(); + + // Act + builder.AppendFormat("{0}!", new HtmlString("First")); + + // Assert + Assert.Equal("First!", HtmlContentToString(builder)); + } + + [Fact] + public void Builder_AppendFormatContent_With1Argument() + { + // Arrange + var builder = new TestHtmlContentBuilder(); + + // Act + builder.AppendFormat("0x{0:X} - hex equivalent for 50.", 50); + + // Assert + Assert.Equal( + "0xHtmlEncode[[32]] - hex equivalent for 50.", + HtmlContentToString(builder)); + } + + [Fact] + public void Builder_AppendFormatContent_With2Arguments() + { + // Arrange + var builder = new TestHtmlContentBuilder(); + + // Act + builder.AppendFormat("0x{0:X} - hex equivalent for {1}.", 50, 50); + + // Assert + Assert.Equal( + "0xHtmlEncode[[32]] - hex equivalent for HtmlEncode[[50]].", + HtmlContentToString(builder)); + } + + [Fact] + public void Builder_AppendFormatContent_With3Arguments() + { + // Arrange + var builder = new TestHtmlContentBuilder(); + + // Act + builder.AppendFormat("0x{0:X} - {1} equivalent for {2}.", 50, "hex", 50); + + // Assert + Assert.Equal( + "0xHtmlEncode[[32]] - HtmlEncode[[hex]] equivalent for HtmlEncode[[50]].", + HtmlContentToString(builder)); + } + + [Fact] + public void Builder_AppendFormat_WithAlignmentComponent() + { + // Arrange + var builder = new TestHtmlContentBuilder(); + + // Act + builder.AppendFormat("{0, -25} World!", "Hello"); + + // Assert + Assert.Equal( + "HtmlEncode[[Hello]] World!", + HtmlContentToString(builder)); + } + + [Fact] + public void Builder_AppendFormat_WithFormatStringComponent() + { + // Arrange + var builder = new TestHtmlContentBuilder(); + + // Act + builder.AppendFormat("0x{0:X}", 50); + + // Assert + Assert.Equal("0xHtmlEncode[[32]]", HtmlContentToString(builder)); + } + + [Fact] + public void Builder_AppendFormat_WithCulture() + { + // Arrange + var builder = new TestHtmlContentBuilder(); + + // Act + builder.AppendFormat( + CultureInfo.InvariantCulture, + "Numbers in InvariantCulture - {0, -5:N} {1} {2} {3}!", + 1.1, + 2.98, + 145.82, + 32.86); + + // Assert + Assert.Equal( + "Numbers in InvariantCulture - HtmlEncode[[1.10]] HtmlEncode[[2.98]] " + + "HtmlEncode[[145.82]] HtmlEncode[[32.86]]!", + HtmlContentToString(builder)); + } + + [Fact] + public void Builder_AppendFormat_WithCulture_1Argument() + { + // Arrange + var builder = new TestHtmlContentBuilder(); + + // Act + builder.AppendFormat( + CultureInfo.InvariantCulture, + "Numbers in InvariantCulture - {0:N}!", + 1.1); + + // Assert + Assert.Equal( + "Numbers in InvariantCulture - HtmlEncode[[1.10]]!", + HtmlContentToString(builder)); + } + + [Fact] + public void Builder_AppendFormat_WithCulture_2Arguments() + { + // Arrange + var builder = new TestHtmlContentBuilder(); + + // Act + builder.AppendFormat( + CultureInfo.InvariantCulture, + "Numbers in InvariantCulture - {0:N} {1}!", + 1.1, + 2.98); + + // Assert + Assert.Equal( + "Numbers in InvariantCulture - HtmlEncode[[1.10]] HtmlEncode[[2.98]]!", + HtmlContentToString(builder)); + } + + [Fact] + public void Builder_AppendFormat_WithCulture_3Arguments() + { + // Arrange + var builder = new TestHtmlContentBuilder(); + + // Act + builder.AppendFormat( + CultureInfo.InvariantCulture, + "Numbers in InvariantCulture - {0:N} {1} {2}!", + 1.1, + 2.98, + 3.12); + + // Assert + Assert.Equal( + "Numbers in InvariantCulture - HtmlEncode[[1.10]] HtmlEncode[[2.98]] HtmlEncode[[3.12]]!", + HtmlContentToString(builder)); + } + + [Fact] + public void Builder_AppendFormat_WithDifferentCulture() + { + // Arrange + var builder = new TestHtmlContentBuilder(); + var culture = new CultureInfo("fr-FR"); + + // Act + builder.AppendFormat(culture, "{0} in french!", 1.21); + + // Assert + Assert.Equal( + "HtmlEncode[[1,21]] in french!", + HtmlContentToString(builder)); + } + + [Fact] + [ReplaceCulture("de-DE", "de-DE")] + public void Builder_AppendFormat_WithDifferentCurrentCulture() + { + // Arrange + var builder = new TestHtmlContentBuilder(); + + // Act + builder.AppendFormat(CultureInfo.CurrentCulture, "{0:D}", DateTime.Parse("01/02/2015")); + + // Assert + Assert.Equal( + "HtmlEncode[[Sonntag, 1. Februar 2015]]", + HtmlContentToString(builder)); + } + + private static string HtmlContentToString(IHtmlContent content) + { + using (var writer = new StringWriter()) + { + content.WriteTo(writer, new HtmlTestEncoder()); + return writer.ToString(); + } + } + + private class TestHtmlContentBuilder : IHtmlContentBuilder + { + public List Entries { get; } = new List(); + + public IHtmlContentBuilder Append(string unencoded) + { + Entries.Add(new UnencodedString(unencoded)); + return this; + } + + public IHtmlContentBuilder AppendHtml(IHtmlContent content) + { + Entries.Add(content); + return this; + } + + public IHtmlContentBuilder AppendHtml(string encoded) + { + Entries.Add(new EncodedString(encoded)); + return this; + } + + public IHtmlContentBuilder Clear() + { + Entries.Clear(); + return this; + } + + public void CopyTo(IHtmlContentBuilder destination) + { + foreach (var entry in Entries) + { + destination.AppendHtml(entry); + } + } + + public void MoveTo(IHtmlContentBuilder destination) + { + CopyTo(destination); + Clear(); + } + + public void WriteTo(TextWriter writer, HtmlEncoder encoder) + { + foreach (var entry in Entries) + { + entry.WriteTo(writer, encoder); + } + } + } + + private class EncodedString : IHtmlContent + { + public EncodedString(string value) + { + Value = value; + } + + public string Value { get; } + + public void WriteTo(TextWriter writer, HtmlEncoder encoder) + { + writer.Write(Value); + } + } + + private class UnencodedString : IHtmlContent + { + public UnencodedString(string value) + { + Value = value; + } + + public string Value { get; } + + public void WriteTo(TextWriter writer, HtmlEncoder encoder) + { + encoder.Encode(writer, Value); + } + } + + private class OtherHtmlContent : IHtmlContent + { + public OtherHtmlContent(string value) + { + Value = value; + } + + public string Value { get; } + + public void WriteTo(TextWriter writer, HtmlEncoder encoder) + { + throw new NotImplementedException(); + } + } + } +} diff --git a/src/Html/Abstractions/test/HtmlContentBuilderTest.cs b/src/Html/Abstractions/test/HtmlContentBuilderTest.cs new file mode 100644 index 0000000000..c3cb7d1954 --- /dev/null +++ b/src/Html/Abstractions/test/HtmlContentBuilderTest.cs @@ -0,0 +1,276 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.IO; +using System.Text.Encodings.Web; +using Microsoft.AspNetCore.Html; +using Microsoft.Extensions.WebEncoders.Testing; +using Xunit; + +namespace Microsoft.Extensions.Internal +{ + public class HtmlContentBuilderTest + { + [Fact] + public void AppendString_AppendsAString() + { + // Arrange + var content = new HtmlContentBuilder(); + + // Act + content.Append("Hello"); + + // Assert + Assert.Equal(1, content.Count); + var result = Assert.Single(content.Entries); + Assert.IsType(result); + } + + [Fact] + public void AppendString_WrittenAsEncoded() + { + // Arrange + var content = new HtmlContentBuilder(); + content.Append("Hello"); + + var writer = new StringWriter(); + + // Act + content.WriteTo(writer, new HtmlTestEncoder()); + + // Assert + Assert.Equal("HtmlEncode[[Hello]]", writer.ToString()); + } + + [Fact] + public void AppendHtml_DoesNotGetWrittenAsEncoded() + { + // Arrange + var content = new HtmlContentBuilder(); + content.AppendHtml("Hello"); + + var writer = new StringWriter(); + + // Act + content.WriteTo(writer, new HtmlTestEncoder()); + + // Assert + Assert.Equal("Hello", writer.ToString()); + } + + [Fact] + public void AppendIHtmlContent_AppendsAsIs() + { + // Arrange + var content = new HtmlContentBuilder(); + var writer = new StringWriter(); + + // Act + content.AppendHtml(new TestHtmlContent("Hello")); + + // Assert + Assert.Equal(1, content.Count); + var result = Assert.Single(content.Entries); + var testHtmlContent = Assert.IsType(result); + testHtmlContent.WriteTo(writer, new HtmlTestEncoder()); + Assert.Equal("Written from TestHtmlContent: Hello", writer.ToString()); + } + + [Fact] + public void CanAppendMultipleItems() + { + // Arrange + var content = new HtmlContentBuilder(); + + // Act + content.AppendHtml(new TestHtmlContent("hello")); + content.Append("Test"); + + // Assert + Assert.Equal(2, content.Count); + Assert.Collection( + content.Entries, + entry => Assert.Equal("Written from TestHtmlContent: hello", entry.ToString()), + entry => Assert.Equal("Test", entry)); + } + + [Fact] + public void Clear_DeletesAllItems() + { + // Arrange + var content = new HtmlContentBuilder(); + content.AppendHtml(new TestHtmlContent("hello")); + content.Append("Test"); + + // Act + content.Clear(); + + // Assert + Assert.Equal(0, content.Count); + Assert.Empty(content.Entries); + } + + [Fact] + public void CopyTo_CopiesAllItems() + { + // Arrange + var source = new HtmlContentBuilder(); + source.AppendHtml(new TestHtmlContent("hello")); + source.Append("Test"); + + var destination = new HtmlContentBuilder(); + destination.Append("some-content"); + + // Act + source.CopyTo(destination); + + // Assert + Assert.Equal(2, source.Count); + Assert.Equal(3, destination.Count); + Assert.Collection( + destination.Entries, + entry => Assert.Equal("some-content", Assert.IsType(entry)), + entry => Assert.Equal(new TestHtmlContent("hello"), Assert.IsType(entry)), + entry => Assert.Equal("Test", Assert.IsType(entry))); + } + + [Fact] + public void CopyTo_DoesDeepCopy() + { + // Arrange + var source = new HtmlContentBuilder(); + + var nested = new HtmlContentBuilder(); + source.AppendHtml(nested); + nested.AppendHtml(new TestHtmlContent("hello")); + source.Append("Test"); + + var destination = new HtmlContentBuilder(); + destination.Append("some-content"); + + // Act + source.CopyTo(destination); + + // Assert + Assert.Equal(2, source.Count); + Assert.Equal(1, nested.Count); + Assert.Equal(3, destination.Count); + Assert.Collection( + destination.Entries, + entry => Assert.Equal("some-content", Assert.IsType(entry)), + entry => Assert.Equal(new TestHtmlContent("hello"), Assert.IsType(entry)), + entry => Assert.Equal("Test", Assert.IsType(entry))); + } + + [Fact] + public void MoveTo_CopiesAllItems_AndClears() + { + // Arrange + var source = new HtmlContentBuilder(); + source.AppendHtml(new TestHtmlContent("hello")); + source.Append("Test"); + + var destination = new HtmlContentBuilder(); + destination.Append("some-content"); + + // Act + source.MoveTo(destination); + + // Assert + Assert.Equal(0, source.Count); + Assert.Equal(3, destination.Count); + Assert.Collection( + destination.Entries, + entry => Assert.Equal("some-content", Assert.IsType(entry)), + entry => Assert.Equal(new TestHtmlContent("hello"), Assert.IsType(entry)), + entry => Assert.Equal("Test", Assert.IsType(entry))); + } + + [Fact] + public void MoveTo_DoesDeepMove() + { + // Arrange + var source = new HtmlContentBuilder(); + + var nested = new HtmlContentBuilder(); + source.AppendHtml(nested); + nested.AppendHtml(new TestHtmlContent("hello")); + source.Append("Test"); + + var destination = new HtmlContentBuilder(); + destination.Append("some-content"); + + // Act + source.MoveTo(destination); + + // Assert + Assert.Equal(0, source.Count); + Assert.Equal(0, nested.Count); + Assert.Equal(3, destination.Count); + Assert.Collection( + destination.Entries, + entry => Assert.Equal("some-content", Assert.IsType(entry)), + entry => Assert.Equal(new TestHtmlContent("hello"), Assert.IsType(entry)), + entry => Assert.Equal("Test", Assert.IsType(entry))); + } + + [Fact] + public void WriteTo_WritesAllItems() + { + // Arrange + var content = new HtmlContentBuilder(); + var writer = new StringWriter(); + content.AppendHtml(new TestHtmlContent("Hello")); + content.Append("Test"); + + // Act + content.WriteTo(writer, new HtmlTestEncoder()); + + // Assert + Assert.Equal(2, content.Count); + Assert.Equal("Written from TestHtmlContent: HelloHtmlEncode[[Test]]", writer.ToString()); + } + + private class TestHtmlContent : IHtmlContent, IEquatable + { + private string _content; + + public TestHtmlContent(string content) + { + _content = content; + } + + public void WriteTo(TextWriter writer, HtmlEncoder encoder) + { + writer.Write(ToString()); + } + + public override string ToString() + { + return "Written from TestHtmlContent: " + _content; + } + + public override int GetHashCode() + { + return _content.GetHashCode(); + } + + public override bool Equals(object obj) + { + var other = obj as TestHtmlContent; + if (other != null) + { + return Equals(other); + } + + return base.Equals(obj); + } + + public bool Equals(TestHtmlContent other) + { + return string.Equals(_content, other._content); + } + } + } +} diff --git a/src/Html/Abstractions/test/HtmlFormattableStringTest.cs b/src/Html/Abstractions/test/HtmlFormattableStringTest.cs new file mode 100644 index 0000000000..64e000751e --- /dev/null +++ b/src/Html/Abstractions/test/HtmlFormattableStringTest.cs @@ -0,0 +1,217 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Globalization; +using System.IO; +using Microsoft.AspNetCore.Testing; +using Microsoft.Extensions.WebEncoders.Testing; +using Xunit; + +namespace Microsoft.AspNetCore.Html +{ + public class HtmlFormattableStringTest + { + [Fact] + public void HtmlFormattableString_EmptyArgs() + { + // Arrange + var formattableString = new HtmlFormattableString("Hello, World!"); + + // Act + var result = HtmlContentToString(formattableString); + + // Assert + Assert.Equal("Hello, World!", result); + } + + [Fact] + public void HtmlFormattableString_EmptyArgsAndCulture() + { + // Arrange + var formattableString = new HtmlFormattableString(CultureInfo.CurrentCulture, "Hello, World!"); + + // Act + var result = HtmlContentToString(formattableString); + + // Assert + Assert.Equal("Hello, World!", result); + } + + [Fact] + public void HtmlFormattableString_MultipleArguments() + { + // Arrange + var formattableString = new HtmlFormattableString("{0} {1} {2} {3}!", "First", "Second", "Third", "Fourth"); + + // Act + var result = HtmlContentToString(formattableString); + + // Assert + Assert.Equal( + "HtmlEncode[[First]] HtmlEncode[[Second]] HtmlEncode[[Third]] HtmlEncode[[Fourth]]!", + result); + } + + [Fact] + public void HtmlFormattableString_WithHtmlString() + { + // Arrange + var formattableString = new HtmlFormattableString("{0}!", new HtmlString("First")); + + // Act + var result = HtmlContentToString(formattableString); + + // Assert + Assert.Equal("First!", result); + } + + [Fact] + public void HtmlFormattableString_WithOtherIHtmlContent() + { + // Arrange + var builder = new HtmlContentBuilder(); + builder.Append("First"); + + var formattableString = new HtmlFormattableString("{0}!", builder); + + // Act + var result = HtmlContentToString(formattableString); + + // Assert + Assert.Equal("HtmlEncode[[First]]!", result); + } + + // This test is needed to ensure the shared StringWriter gets cleared. + [Fact] + public void HtmlFormattableString_WithMultipleHtmlContentArguments() + { + // Arrange + var formattableString = new HtmlFormattableString( + "Happy {0}, {1}!", + new HtmlString("Birthday"), + new HtmlContentBuilder().Append("Billy")); + + // Act + var result = HtmlContentToString(formattableString); + + // Assert + Assert.Equal("Happy Birthday, HtmlEncode[[Billy]]!", result); + } + + [Fact] + public void HtmlFormattableString_WithHtmlString_AndOffset() + { + // Arrange + var formattableString = new HtmlFormattableString("{0, 20}!", new HtmlString("First")); + + // Act + var result = HtmlContentToString(formattableString); + + // Assert + Assert.Equal(" First!", result); + } + + [Fact] + public void HtmlFormattableString_With3Arguments() + { + // Arrange + var formattableString = new HtmlFormattableString("0x{0:X} - {1} equivalent for {2}.", 50, "hex", 50); + + // Act + var result = HtmlContentToString(formattableString); + + // Assert + Assert.Equal( + "0xHtmlEncode[[32]] - HtmlEncode[[hex]] equivalent for HtmlEncode[[50]].", + result); + } + + [Fact] + public void HtmlFormattableString_WithAlignmentComponent() + { + // Arrange + var formattableString = new HtmlFormattableString("{0, -25} World!", "Hello"); + + // Act + var result = HtmlContentToString(formattableString); + + // Assert + Assert.Equal( + "HtmlEncode[[Hello]] World!", result); + } + + [Fact] + public void HtmlFormattableString_WithFormatStringComponent() + { + // Arrange + var formattableString = new HtmlFormattableString("0x{0:X}", 50); + + // Act + var result = HtmlContentToString(formattableString); + + // Assert + Assert.Equal("0xHtmlEncode[[32]]", result); + } + + [Fact] + public void HtmlFormattableString_WithCulture() + { + // Arrange + var formattableString = new HtmlFormattableString( + CultureInfo.InvariantCulture, + "Numbers in InvariantCulture - {0, -5:N} {1} {2} {3}!", + 1.1, + 2.98, + 145.82, + 32.86); + + // Act + var result = HtmlContentToString(formattableString); + + // Assert + Assert.Equal( + "Numbers in InvariantCulture - HtmlEncode[[1.10]] HtmlEncode[[2.98]] " + + "HtmlEncode[[145.82]] HtmlEncode[[32.86]]!", + result); + } + + [Fact] + [ReplaceCulture("en-US", "en-US")] + public void HtmlFormattableString_UsesPassedInCulture() + { + // Arrange + var culture = new CultureInfo("fr-FR"); + var formattableString = new HtmlFormattableString(culture, "{0} in french!", 1.21); + + // Act + var result = HtmlContentToString(formattableString); + + // Assert + Assert.Equal("HtmlEncode[[1,21]] in french!", result); + } + + [Fact] + [ReplaceCulture("de-DE", "de-DE")] + public void HtmlFormattableString_UsesCurrentCulture() + { + // Arrange + var formattableString = new HtmlFormattableString("{0:D}", DateTime.Parse("01/02/2015")); + + // Act + var result = HtmlContentToString(formattableString); + + // Assert + Assert.Equal("HtmlEncode[[Sonntag, 1. Februar 2015]]", result); + } + + private static string HtmlContentToString(IHtmlContent content) + { + using (var writer = new StringWriter()) + { + content.WriteTo(writer, new HtmlTestEncoder()); + return writer.ToString(); + } + } + } +} diff --git a/src/Html/Abstractions/test/Microsoft.AspNetCore.Html.Abstractions.Tests.csproj b/src/Html/Abstractions/test/Microsoft.AspNetCore.Html.Abstractions.Tests.csproj new file mode 100644 index 0000000000..0e77dbbfb3 --- /dev/null +++ b/src/Html/Abstractions/test/Microsoft.AspNetCore.Html.Abstractions.Tests.csproj @@ -0,0 +1,12 @@ + + + + $(StandardTestTfms) + + + + + + + + diff --git a/src/IISIntegration/Directory.Build.props b/src/IISIntegration/Directory.Build.props index 82bbde4ef9..9b394c5bd7 100644 --- a/src/IISIntegration/Directory.Build.props +++ b/src/IISIntegration/Directory.Build.props @@ -6,6 +6,8 @@ + false + true false diff --git a/src/Installers/Windows/AspNetCoreModule-Setup/ANCMPackageResolver/ANCMPackageResolver.csproj b/src/Installers/Windows/AspNetCoreModule-Setup/ANCMPackageResolver/ANCMPackageResolver.csproj index e09c9574bb..c585d69ae2 100644 --- a/src/Installers/Windows/AspNetCoreModule-Setup/ANCMPackageResolver/ANCMPackageResolver.csproj +++ b/src/Installers/Windows/AspNetCoreModule-Setup/ANCMPackageResolver/ANCMPackageResolver.csproj @@ -7,6 +7,7 @@ + @@ -23,4 +24,4 @@ - \ No newline at end of file + diff --git a/src/Installers/Windows/AspNetCoreModule-Setup/Directory.Build.props b/src/Installers/Windows/AspNetCoreModule-Setup/Directory.Build.props index 4892060bb8..f6801907b6 100644 --- a/src/Installers/Windows/AspNetCoreModule-Setup/Directory.Build.props +++ b/src/Installers/Windows/AspNetCoreModule-Setup/Directory.Build.props @@ -21,7 +21,7 @@ The actual handler folder version is dependent on the ANCMMsiVersion --> 2.0.0 - + $(RepositoryRoot)src\Installers\Windows\AspNetCoreModule-Setup\ $(AspNetCoreSetupRoot)IIS-Setup\ $(IIS-Setup)IIS-Common\ diff --git a/src/Middleware/Middleware.sln b/src/Middleware/Middleware.sln new file mode 100644 index 0000000000..28a99b57c6 --- /dev/null +++ b/src/Middleware/Middleware.sln @@ -0,0 +1,120 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26124.0 +MinimumVisualStudioVersion = 15.0.26124.0 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WebSockets", "WebSockets", "{E0D9867D-C23D-43EB-8D9C-DE0398A25432}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{A86EE055-ACD3-4BAC-A51D-1B3C71067AE0}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EchoApp", "WebSockets\samples\EchoApp\EchoApp.csproj", "{0792C20B-1D18-4D7C-9C0F-A6F45A0F378E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestServer", "WebSockets\samples\TestServer\TestServer.csproj", "{4E5F5FCC-172C-44D9-BEA0-39098A79CD0B}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.WebSockets", "WebSockets\src\Microsoft.AspNetCore.WebSockets.csproj", "{BECAA6A1-1AA4-415E-ADF3-07C103333826}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AutobahnTestApp", "WebSockets\test\AutobahnTestApp\AutobahnTestApp.csproj", "{76B25812-AAFB-45BA-A71A-24F0C654ADFB}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.WebSockets.ConformanceTests", "WebSockets\test\ConformanceTests\Microsoft.AspNetCore.WebSockets.ConformanceTests.csproj", "{88BDEE69-4DE3-40B5-A478-677EA355FB52}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.WebSockets.Tests", "WebSockets\test\UnitTests\Microsoft.AspNetCore.WebSockets.Tests.csproj", "{93970702-1BDB-4A8C-B7F6-020294464BB6}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {0792C20B-1D18-4D7C-9C0F-A6F45A0F378E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0792C20B-1D18-4D7C-9C0F-A6F45A0F378E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0792C20B-1D18-4D7C-9C0F-A6F45A0F378E}.Debug|x64.ActiveCfg = Debug|Any CPU + {0792C20B-1D18-4D7C-9C0F-A6F45A0F378E}.Debug|x64.Build.0 = Debug|Any CPU + {0792C20B-1D18-4D7C-9C0F-A6F45A0F378E}.Debug|x86.ActiveCfg = Debug|Any CPU + {0792C20B-1D18-4D7C-9C0F-A6F45A0F378E}.Debug|x86.Build.0 = Debug|Any CPU + {0792C20B-1D18-4D7C-9C0F-A6F45A0F378E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0792C20B-1D18-4D7C-9C0F-A6F45A0F378E}.Release|Any CPU.Build.0 = Release|Any CPU + {0792C20B-1D18-4D7C-9C0F-A6F45A0F378E}.Release|x64.ActiveCfg = Release|Any CPU + {0792C20B-1D18-4D7C-9C0F-A6F45A0F378E}.Release|x64.Build.0 = Release|Any CPU + {0792C20B-1D18-4D7C-9C0F-A6F45A0F378E}.Release|x86.ActiveCfg = Release|Any CPU + {0792C20B-1D18-4D7C-9C0F-A6F45A0F378E}.Release|x86.Build.0 = Release|Any CPU + {4E5F5FCC-172C-44D9-BEA0-39098A79CD0B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4E5F5FCC-172C-44D9-BEA0-39098A79CD0B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4E5F5FCC-172C-44D9-BEA0-39098A79CD0B}.Debug|x64.ActiveCfg = Debug|Any CPU + {4E5F5FCC-172C-44D9-BEA0-39098A79CD0B}.Debug|x64.Build.0 = Debug|Any CPU + {4E5F5FCC-172C-44D9-BEA0-39098A79CD0B}.Debug|x86.ActiveCfg = Debug|Any CPU + {4E5F5FCC-172C-44D9-BEA0-39098A79CD0B}.Debug|x86.Build.0 = Debug|Any CPU + {4E5F5FCC-172C-44D9-BEA0-39098A79CD0B}.Release|Any CPU.ActiveCfg = Debug|Any CPU + {4E5F5FCC-172C-44D9-BEA0-39098A79CD0B}.Release|Any CPU.Build.0 = Debug|Any CPU + {4E5F5FCC-172C-44D9-BEA0-39098A79CD0B}.Release|x64.ActiveCfg = Debug|Any CPU + {4E5F5FCC-172C-44D9-BEA0-39098A79CD0B}.Release|x64.Build.0 = Debug|Any CPU + {4E5F5FCC-172C-44D9-BEA0-39098A79CD0B}.Release|x86.ActiveCfg = Debug|Any CPU + {4E5F5FCC-172C-44D9-BEA0-39098A79CD0B}.Release|x86.Build.0 = Debug|Any CPU + {BECAA6A1-1AA4-415E-ADF3-07C103333826}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BECAA6A1-1AA4-415E-ADF3-07C103333826}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BECAA6A1-1AA4-415E-ADF3-07C103333826}.Debug|x64.ActiveCfg = Debug|Any CPU + {BECAA6A1-1AA4-415E-ADF3-07C103333826}.Debug|x64.Build.0 = Debug|Any CPU + {BECAA6A1-1AA4-415E-ADF3-07C103333826}.Debug|x86.ActiveCfg = Debug|Any CPU + {BECAA6A1-1AA4-415E-ADF3-07C103333826}.Debug|x86.Build.0 = Debug|Any CPU + {BECAA6A1-1AA4-415E-ADF3-07C103333826}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BECAA6A1-1AA4-415E-ADF3-07C103333826}.Release|Any CPU.Build.0 = Release|Any CPU + {BECAA6A1-1AA4-415E-ADF3-07C103333826}.Release|x64.ActiveCfg = Release|Any CPU + {BECAA6A1-1AA4-415E-ADF3-07C103333826}.Release|x64.Build.0 = Release|Any CPU + {BECAA6A1-1AA4-415E-ADF3-07C103333826}.Release|x86.ActiveCfg = Release|Any CPU + {BECAA6A1-1AA4-415E-ADF3-07C103333826}.Release|x86.Build.0 = Release|Any CPU + {76B25812-AAFB-45BA-A71A-24F0C654ADFB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {76B25812-AAFB-45BA-A71A-24F0C654ADFB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {76B25812-AAFB-45BA-A71A-24F0C654ADFB}.Debug|x64.ActiveCfg = Debug|Any CPU + {76B25812-AAFB-45BA-A71A-24F0C654ADFB}.Debug|x64.Build.0 = Debug|Any CPU + {76B25812-AAFB-45BA-A71A-24F0C654ADFB}.Debug|x86.ActiveCfg = Debug|Any CPU + {76B25812-AAFB-45BA-A71A-24F0C654ADFB}.Debug|x86.Build.0 = Debug|Any CPU + {76B25812-AAFB-45BA-A71A-24F0C654ADFB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {76B25812-AAFB-45BA-A71A-24F0C654ADFB}.Release|Any CPU.Build.0 = Release|Any CPU + {76B25812-AAFB-45BA-A71A-24F0C654ADFB}.Release|x64.ActiveCfg = Release|Any CPU + {76B25812-AAFB-45BA-A71A-24F0C654ADFB}.Release|x64.Build.0 = Release|Any CPU + {76B25812-AAFB-45BA-A71A-24F0C654ADFB}.Release|x86.ActiveCfg = Release|Any CPU + {76B25812-AAFB-45BA-A71A-24F0C654ADFB}.Release|x86.Build.0 = Release|Any CPU + {88BDEE69-4DE3-40B5-A478-677EA355FB52}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {88BDEE69-4DE3-40B5-A478-677EA355FB52}.Debug|Any CPU.Build.0 = Debug|Any CPU + {88BDEE69-4DE3-40B5-A478-677EA355FB52}.Debug|x64.ActiveCfg = Debug|Any CPU + {88BDEE69-4DE3-40B5-A478-677EA355FB52}.Debug|x64.Build.0 = Debug|Any CPU + {88BDEE69-4DE3-40B5-A478-677EA355FB52}.Debug|x86.ActiveCfg = Debug|Any CPU + {88BDEE69-4DE3-40B5-A478-677EA355FB52}.Debug|x86.Build.0 = Debug|Any CPU + {88BDEE69-4DE3-40B5-A478-677EA355FB52}.Release|Any CPU.ActiveCfg = Release|Any CPU + {88BDEE69-4DE3-40B5-A478-677EA355FB52}.Release|Any CPU.Build.0 = Release|Any CPU + {88BDEE69-4DE3-40B5-A478-677EA355FB52}.Release|x64.ActiveCfg = Release|Any CPU + {88BDEE69-4DE3-40B5-A478-677EA355FB52}.Release|x64.Build.0 = Release|Any CPU + {88BDEE69-4DE3-40B5-A478-677EA355FB52}.Release|x86.ActiveCfg = Release|Any CPU + {88BDEE69-4DE3-40B5-A478-677EA355FB52}.Release|x86.Build.0 = Release|Any CPU + {93970702-1BDB-4A8C-B7F6-020294464BB6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {93970702-1BDB-4A8C-B7F6-020294464BB6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {93970702-1BDB-4A8C-B7F6-020294464BB6}.Debug|x64.ActiveCfg = Debug|Any CPU + {93970702-1BDB-4A8C-B7F6-020294464BB6}.Debug|x64.Build.0 = Debug|Any CPU + {93970702-1BDB-4A8C-B7F6-020294464BB6}.Debug|x86.ActiveCfg = Debug|Any CPU + {93970702-1BDB-4A8C-B7F6-020294464BB6}.Debug|x86.Build.0 = Debug|Any CPU + {93970702-1BDB-4A8C-B7F6-020294464BB6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {93970702-1BDB-4A8C-B7F6-020294464BB6}.Release|Any CPU.Build.0 = Release|Any CPU + {93970702-1BDB-4A8C-B7F6-020294464BB6}.Release|x64.ActiveCfg = Release|Any CPU + {93970702-1BDB-4A8C-B7F6-020294464BB6}.Release|x64.Build.0 = Release|Any CPU + {93970702-1BDB-4A8C-B7F6-020294464BB6}.Release|x86.ActiveCfg = Release|Any CPU + {93970702-1BDB-4A8C-B7F6-020294464BB6}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {A86EE055-ACD3-4BAC-A51D-1B3C71067AE0} = {E0D9867D-C23D-43EB-8D9C-DE0398A25432} + {0792C20B-1D18-4D7C-9C0F-A6F45A0F378E} = {A86EE055-ACD3-4BAC-A51D-1B3C71067AE0} + {4E5F5FCC-172C-44D9-BEA0-39098A79CD0B} = {A86EE055-ACD3-4BAC-A51D-1B3C71067AE0} + {BECAA6A1-1AA4-415E-ADF3-07C103333826} = {E0D9867D-C23D-43EB-8D9C-DE0398A25432} + {76B25812-AAFB-45BA-A71A-24F0C654ADFB} = {E0D9867D-C23D-43EB-8D9C-DE0398A25432} + {88BDEE69-4DE3-40B5-A478-677EA355FB52} = {E0D9867D-C23D-43EB-8D9C-DE0398A25432} + {93970702-1BDB-4A8C-B7F6-020294464BB6} = {E0D9867D-C23D-43EB-8D9C-DE0398A25432} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {83786312-A93B-4BB4-AB06-7C6913A59AFA} + EndGlobalSection +EndGlobal diff --git a/src/Middleware/WebSockets/README.md b/src/Middleware/WebSockets/README.md new file mode 100644 index 0000000000..19061a2ad5 --- /dev/null +++ b/src/Middleware/WebSockets/README.md @@ -0,0 +1,12 @@ +WebSockets +========== + +Contains a managed implementation of the WebSocket protocol, along with server integration components. + +## System Requirements + +This repo has a few special system requirements/prerequisites. + +1. Windows IIS Express tests require IIS Express 10 and Windows 8 for WebSockets support +2. HttpListener/ASP.NET 4.6 samples require at least Windows 8 +3. Autobahn Test Suite requires special installation see the README.md in [test/AutobahnTestApp](./test/AutobahnTestApp/README.md) diff --git a/src/WebSockets/samples/AutobahnTestAppAspNet4/AutobahnTestAppAspNet4.csproj.aspnet4 b/src/Middleware/WebSockets/samples/AutobahnTestAppAspNet4/AutobahnTestAppAspNet4.csproj.aspnet4 similarity index 100% rename from src/WebSockets/samples/AutobahnTestAppAspNet4/AutobahnTestAppAspNet4.csproj.aspnet4 rename to src/Middleware/WebSockets/samples/AutobahnTestAppAspNet4/AutobahnTestAppAspNet4.csproj.aspnet4 diff --git a/src/WebSockets/samples/AutobahnTestAppAspNet4/EchoSocket.ashx b/src/Middleware/WebSockets/samples/AutobahnTestAppAspNet4/EchoSocket.ashx similarity index 100% rename from src/WebSockets/samples/AutobahnTestAppAspNet4/EchoSocket.ashx rename to src/Middleware/WebSockets/samples/AutobahnTestAppAspNet4/EchoSocket.ashx diff --git a/src/WebSockets/samples/AutobahnTestAppAspNet4/EchoSocket.ashx.cs b/src/Middleware/WebSockets/samples/AutobahnTestAppAspNet4/EchoSocket.ashx.cs similarity index 100% rename from src/WebSockets/samples/AutobahnTestAppAspNet4/EchoSocket.ashx.cs rename to src/Middleware/WebSockets/samples/AutobahnTestAppAspNet4/EchoSocket.ashx.cs diff --git a/src/WebSockets/samples/AutobahnTestAppAspNet4/Properties/AssemblyInfo.cs b/src/Middleware/WebSockets/samples/AutobahnTestAppAspNet4/Properties/AssemblyInfo.cs similarity index 100% rename from src/WebSockets/samples/AutobahnTestAppAspNet4/Properties/AssemblyInfo.cs rename to src/Middleware/WebSockets/samples/AutobahnTestAppAspNet4/Properties/AssemblyInfo.cs diff --git a/src/WebSockets/samples/AutobahnTestAppAspNet4/Web.Debug.config b/src/Middleware/WebSockets/samples/AutobahnTestAppAspNet4/Web.Debug.config similarity index 100% rename from src/WebSockets/samples/AutobahnTestAppAspNet4/Web.Debug.config rename to src/Middleware/WebSockets/samples/AutobahnTestAppAspNet4/Web.Debug.config diff --git a/src/WebSockets/samples/AutobahnTestAppAspNet4/Web.Release.config b/src/Middleware/WebSockets/samples/AutobahnTestAppAspNet4/Web.Release.config similarity index 100% rename from src/WebSockets/samples/AutobahnTestAppAspNet4/Web.Release.config rename to src/Middleware/WebSockets/samples/AutobahnTestAppAspNet4/Web.Release.config diff --git a/src/WebSockets/samples/AutobahnTestAppAspNet4/Web.config b/src/Middleware/WebSockets/samples/AutobahnTestAppAspNet4/Web.config similarity index 100% rename from src/WebSockets/samples/AutobahnTestAppAspNet4/Web.config rename to src/Middleware/WebSockets/samples/AutobahnTestAppAspNet4/Web.config diff --git a/src/WebSockets/samples/AutobahnTestAppAspNet4/packages.config b/src/Middleware/WebSockets/samples/AutobahnTestAppAspNet4/packages.config similarity index 100% rename from src/WebSockets/samples/AutobahnTestAppAspNet4/packages.config rename to src/Middleware/WebSockets/samples/AutobahnTestAppAspNet4/packages.config diff --git a/src/WebSockets/samples/AutobahnTestAppAspNet4/wstest-spec.json b/src/Middleware/WebSockets/samples/AutobahnTestAppAspNet4/wstest-spec.json similarity index 100% rename from src/WebSockets/samples/AutobahnTestAppAspNet4/wstest-spec.json rename to src/Middleware/WebSockets/samples/AutobahnTestAppAspNet4/wstest-spec.json diff --git a/src/WebSockets/samples/AutobahnTestAppHttpListener/App.config b/src/Middleware/WebSockets/samples/AutobahnTestAppHttpListener/App.config similarity index 100% rename from src/WebSockets/samples/AutobahnTestAppHttpListener/App.config rename to src/Middleware/WebSockets/samples/AutobahnTestAppHttpListener/App.config diff --git a/src/WebSockets/samples/AutobahnTestAppHttpListener/AutobahnTestAppHttpListener.csproj.net461 b/src/Middleware/WebSockets/samples/AutobahnTestAppHttpListener/AutobahnTestAppHttpListener.csproj.net461 similarity index 100% rename from src/WebSockets/samples/AutobahnTestAppHttpListener/AutobahnTestAppHttpListener.csproj.net461 rename to src/Middleware/WebSockets/samples/AutobahnTestAppHttpListener/AutobahnTestAppHttpListener.csproj.net461 diff --git a/src/WebSockets/samples/AutobahnTestAppHttpListener/Program.cs b/src/Middleware/WebSockets/samples/AutobahnTestAppHttpListener/Program.cs similarity index 100% rename from src/WebSockets/samples/AutobahnTestAppHttpListener/Program.cs rename to src/Middleware/WebSockets/samples/AutobahnTestAppHttpListener/Program.cs diff --git a/src/WebSockets/samples/AutobahnTestAppHttpListener/Properties/AssemblyInfo.cs b/src/Middleware/WebSockets/samples/AutobahnTestAppHttpListener/Properties/AssemblyInfo.cs similarity index 100% rename from src/WebSockets/samples/AutobahnTestAppHttpListener/Properties/AssemblyInfo.cs rename to src/Middleware/WebSockets/samples/AutobahnTestAppHttpListener/Properties/AssemblyInfo.cs diff --git a/src/Middleware/WebSockets/samples/EchoApp/EchoApp.csproj b/src/Middleware/WebSockets/samples/EchoApp/EchoApp.csproj new file mode 100644 index 0000000000..7ea4ba53b0 --- /dev/null +++ b/src/Middleware/WebSockets/samples/EchoApp/EchoApp.csproj @@ -0,0 +1,16 @@ + + + + netcoreapp2.2;net461 + + + + + + + + + + + + diff --git a/src/WebSockets/samples/EchoApp/Program.cs b/src/Middleware/WebSockets/samples/EchoApp/Program.cs similarity index 100% rename from src/WebSockets/samples/EchoApp/Program.cs rename to src/Middleware/WebSockets/samples/EchoApp/Program.cs diff --git a/src/WebSockets/samples/EchoApp/Properties/launchSettings.json b/src/Middleware/WebSockets/samples/EchoApp/Properties/launchSettings.json similarity index 100% rename from src/WebSockets/samples/EchoApp/Properties/launchSettings.json rename to src/Middleware/WebSockets/samples/EchoApp/Properties/launchSettings.json diff --git a/src/WebSockets/samples/EchoApp/Startup.cs b/src/Middleware/WebSockets/samples/EchoApp/Startup.cs similarity index 100% rename from src/WebSockets/samples/EchoApp/Startup.cs rename to src/Middleware/WebSockets/samples/EchoApp/Startup.cs diff --git a/src/WebSockets/samples/EchoApp/wwwroot/index.html b/src/Middleware/WebSockets/samples/EchoApp/wwwroot/index.html similarity index 100% rename from src/WebSockets/samples/EchoApp/wwwroot/index.html rename to src/Middleware/WebSockets/samples/EchoApp/wwwroot/index.html diff --git a/src/WebSockets/samples/TestServer/App.config b/src/Middleware/WebSockets/samples/TestServer/App.config similarity index 100% rename from src/WebSockets/samples/TestServer/App.config rename to src/Middleware/WebSockets/samples/TestServer/App.config diff --git a/src/WebSockets/samples/TestServer/Program.cs b/src/Middleware/WebSockets/samples/TestServer/Program.cs similarity index 100% rename from src/WebSockets/samples/TestServer/Program.cs rename to src/Middleware/WebSockets/samples/TestServer/Program.cs diff --git a/src/WebSockets/samples/TestServer/Properties/AssemblyInfo.cs b/src/Middleware/WebSockets/samples/TestServer/Properties/AssemblyInfo.cs similarity index 100% rename from src/WebSockets/samples/TestServer/Properties/AssemblyInfo.cs rename to src/Middleware/WebSockets/samples/TestServer/Properties/AssemblyInfo.cs diff --git a/src/WebSockets/samples/TestServer/TestServer.csproj b/src/Middleware/WebSockets/samples/TestServer/TestServer.csproj similarity index 100% rename from src/WebSockets/samples/TestServer/TestServer.csproj rename to src/Middleware/WebSockets/samples/TestServer/TestServer.csproj diff --git a/src/WebSockets/build/setup-wstest.ps1 b/src/Middleware/WebSockets/setup-wstest.ps1 similarity index 100% rename from src/WebSockets/build/setup-wstest.ps1 rename to src/Middleware/WebSockets/setup-wstest.ps1 diff --git a/src/WebSockets/build/setup-wstest.sh b/src/Middleware/WebSockets/setup-wstest.sh old mode 100755 new mode 100644 similarity index 100% rename from src/WebSockets/build/setup-wstest.sh rename to src/Middleware/WebSockets/setup-wstest.sh diff --git a/src/WebSockets/src/Microsoft.AspNetCore.WebSockets/ExtendedWebSocketAcceptContext.cs b/src/Middleware/WebSockets/src/ExtendedWebSocketAcceptContext.cs similarity index 100% rename from src/WebSockets/src/Microsoft.AspNetCore.WebSockets/ExtendedWebSocketAcceptContext.cs rename to src/Middleware/WebSockets/src/ExtendedWebSocketAcceptContext.cs diff --git a/src/WebSockets/src/Microsoft.AspNetCore.WebSockets/Internal/Constants.cs b/src/Middleware/WebSockets/src/Internal/Constants.cs similarity index 100% rename from src/WebSockets/src/Microsoft.AspNetCore.WebSockets/Internal/Constants.cs rename to src/Middleware/WebSockets/src/Internal/Constants.cs diff --git a/src/WebSockets/src/Microsoft.AspNetCore.WebSockets/Internal/HandshakeHelpers.cs b/src/Middleware/WebSockets/src/Internal/HandshakeHelpers.cs similarity index 100% rename from src/WebSockets/src/Microsoft.AspNetCore.WebSockets/Internal/HandshakeHelpers.cs rename to src/Middleware/WebSockets/src/Internal/HandshakeHelpers.cs diff --git a/src/Middleware/WebSockets/src/Microsoft.AspNetCore.WebSockets.csproj b/src/Middleware/WebSockets/src/Microsoft.AspNetCore.WebSockets.csproj new file mode 100644 index 0000000000..12c92f3d55 --- /dev/null +++ b/src/Middleware/WebSockets/src/Microsoft.AspNetCore.WebSockets.csproj @@ -0,0 +1,19 @@ + + + + ASP.NET Core web socket middleware for use on top of opaque servers. + netstandard2.0 + $(NoWarn);CS1591 + true + true + aspnetcore + + + + + + + + + + diff --git a/src/WebSockets/src/Microsoft.AspNetCore.WebSockets/WebSocketMiddleware.cs b/src/Middleware/WebSockets/src/WebSocketMiddleware.cs similarity index 100% rename from src/WebSockets/src/Microsoft.AspNetCore.WebSockets/WebSocketMiddleware.cs rename to src/Middleware/WebSockets/src/WebSocketMiddleware.cs diff --git a/src/WebSockets/src/Microsoft.AspNetCore.WebSockets/WebSocketMiddlewareExtensions.cs b/src/Middleware/WebSockets/src/WebSocketMiddlewareExtensions.cs similarity index 100% rename from src/WebSockets/src/Microsoft.AspNetCore.WebSockets/WebSocketMiddlewareExtensions.cs rename to src/Middleware/WebSockets/src/WebSocketMiddlewareExtensions.cs diff --git a/src/WebSockets/src/Microsoft.AspNetCore.WebSockets/WebSocketOptions.cs b/src/Middleware/WebSockets/src/WebSocketOptions.cs similarity index 100% rename from src/WebSockets/src/Microsoft.AspNetCore.WebSockets/WebSocketOptions.cs rename to src/Middleware/WebSockets/src/WebSocketOptions.cs diff --git a/src/WebSockets/src/Microsoft.AspNetCore.WebSockets/WebSocketsDependencyInjectionExtensions.cs b/src/Middleware/WebSockets/src/WebSocketsDependencyInjectionExtensions.cs similarity index 100% rename from src/WebSockets/src/Microsoft.AspNetCore.WebSockets/WebSocketsDependencyInjectionExtensions.cs rename to src/Middleware/WebSockets/src/WebSocketsDependencyInjectionExtensions.cs diff --git a/src/WebSockets/src/Microsoft.AspNetCore.WebSockets/baseline.netcore.json b/src/Middleware/WebSockets/src/baseline.netcore.json similarity index 100% rename from src/WebSockets/src/Microsoft.AspNetCore.WebSockets/baseline.netcore.json rename to src/Middleware/WebSockets/src/baseline.netcore.json diff --git a/src/WebSockets/test/AutobahnTestApp/AutobahnTestApp.csproj b/src/Middleware/WebSockets/test/AutobahnTestApp/AutobahnTestApp.csproj similarity index 70% rename from src/WebSockets/test/AutobahnTestApp/AutobahnTestApp.csproj rename to src/Middleware/WebSockets/test/AutobahnTestApp/AutobahnTestApp.csproj index 8f470696bc..43595a6d8c 100644 --- a/src/WebSockets/test/AutobahnTestApp/AutobahnTestApp.csproj +++ b/src/Middleware/WebSockets/test/AutobahnTestApp/AutobahnTestApp.csproj @@ -1,7 +1,8 @@ - netcoreapp2.2 + netcoreapp2.2 + true @@ -9,21 +10,20 @@ - + + + + + + + + + + - - - - - - - - - - diff --git a/src/WebSockets/test/AutobahnTestApp/Program.cs b/src/Middleware/WebSockets/test/AutobahnTestApp/Program.cs similarity index 100% rename from src/WebSockets/test/AutobahnTestApp/Program.cs rename to src/Middleware/WebSockets/test/AutobahnTestApp/Program.cs diff --git a/src/WebSockets/test/AutobahnTestApp/Properties/launchSettings.json b/src/Middleware/WebSockets/test/AutobahnTestApp/Properties/launchSettings.json similarity index 100% rename from src/WebSockets/test/AutobahnTestApp/Properties/launchSettings.json rename to src/Middleware/WebSockets/test/AutobahnTestApp/Properties/launchSettings.json diff --git a/src/WebSockets/test/AutobahnTestApp/README.md b/src/Middleware/WebSockets/test/AutobahnTestApp/README.md similarity index 100% rename from src/WebSockets/test/AutobahnTestApp/README.md rename to src/Middleware/WebSockets/test/AutobahnTestApp/README.md diff --git a/src/WebSockets/test/AutobahnTestApp/Startup.cs b/src/Middleware/WebSockets/test/AutobahnTestApp/Startup.cs similarity index 100% rename from src/WebSockets/test/AutobahnTestApp/Startup.cs rename to src/Middleware/WebSockets/test/AutobahnTestApp/Startup.cs diff --git a/src/WebSockets/test/AutobahnTestApp/TestResources/testCert.pfx b/src/Middleware/WebSockets/test/AutobahnTestApp/TestResources/testCert.pfx similarity index 100% rename from src/WebSockets/test/AutobahnTestApp/TestResources/testCert.pfx rename to src/Middleware/WebSockets/test/AutobahnTestApp/TestResources/testCert.pfx diff --git a/src/WebSockets/test/AutobahnTestApp/TestResources/testCert.txt b/src/Middleware/WebSockets/test/AutobahnTestApp/TestResources/testCert.txt similarity index 100% rename from src/WebSockets/test/AutobahnTestApp/TestResources/testCert.txt rename to src/Middleware/WebSockets/test/AutobahnTestApp/TestResources/testCert.txt diff --git a/src/WebSockets/test/AutobahnTestApp/scripts/RunAutobahnTests.ps1 b/src/Middleware/WebSockets/test/AutobahnTestApp/scripts/RunAutobahnTests.ps1 similarity index 100% rename from src/WebSockets/test/AutobahnTestApp/scripts/RunAutobahnTests.ps1 rename to src/Middleware/WebSockets/test/AutobahnTestApp/scripts/RunAutobahnTests.ps1 diff --git a/src/WebSockets/test/AutobahnTestApp/scripts/autobahn.spec.json b/src/Middleware/WebSockets/test/AutobahnTestApp/scripts/autobahn.spec.json similarity index 100% rename from src/WebSockets/test/AutobahnTestApp/scripts/autobahn.spec.json rename to src/Middleware/WebSockets/test/AutobahnTestApp/scripts/autobahn.spec.json diff --git a/src/WebSockets/test/Microsoft.AspNetCore.WebSockets.ConformanceTest/Autobahn/AutobahnCaseResult.cs b/src/Middleware/WebSockets/test/ConformanceTests/Autobahn/AutobahnCaseResult.cs similarity index 100% rename from src/WebSockets/test/Microsoft.AspNetCore.WebSockets.ConformanceTest/Autobahn/AutobahnCaseResult.cs rename to src/Middleware/WebSockets/test/ConformanceTests/Autobahn/AutobahnCaseResult.cs diff --git a/src/WebSockets/test/Microsoft.AspNetCore.WebSockets.ConformanceTest/Autobahn/AutobahnExpectations.cs b/src/Middleware/WebSockets/test/ConformanceTests/Autobahn/AutobahnExpectations.cs similarity index 100% rename from src/WebSockets/test/Microsoft.AspNetCore.WebSockets.ConformanceTest/Autobahn/AutobahnExpectations.cs rename to src/Middleware/WebSockets/test/ConformanceTests/Autobahn/AutobahnExpectations.cs diff --git a/src/WebSockets/test/Microsoft.AspNetCore.WebSockets.ConformanceTest/Autobahn/AutobahnResult.cs b/src/Middleware/WebSockets/test/ConformanceTests/Autobahn/AutobahnResult.cs similarity index 100% rename from src/WebSockets/test/Microsoft.AspNetCore.WebSockets.ConformanceTest/Autobahn/AutobahnResult.cs rename to src/Middleware/WebSockets/test/ConformanceTests/Autobahn/AutobahnResult.cs diff --git a/src/WebSockets/test/Microsoft.AspNetCore.WebSockets.ConformanceTest/Autobahn/AutobahnServerResult.cs b/src/Middleware/WebSockets/test/ConformanceTests/Autobahn/AutobahnServerResult.cs similarity index 100% rename from src/WebSockets/test/Microsoft.AspNetCore.WebSockets.ConformanceTest/Autobahn/AutobahnServerResult.cs rename to src/Middleware/WebSockets/test/ConformanceTests/Autobahn/AutobahnServerResult.cs diff --git a/src/WebSockets/test/Microsoft.AspNetCore.WebSockets.ConformanceTest/Autobahn/AutobahnSpec.cs b/src/Middleware/WebSockets/test/ConformanceTests/Autobahn/AutobahnSpec.cs similarity index 100% rename from src/WebSockets/test/Microsoft.AspNetCore.WebSockets.ConformanceTest/Autobahn/AutobahnSpec.cs rename to src/Middleware/WebSockets/test/ConformanceTests/Autobahn/AutobahnSpec.cs diff --git a/src/WebSockets/test/Microsoft.AspNetCore.WebSockets.ConformanceTest/Autobahn/AutobahnTester.cs b/src/Middleware/WebSockets/test/ConformanceTests/Autobahn/AutobahnTester.cs similarity index 100% rename from src/WebSockets/test/Microsoft.AspNetCore.WebSockets.ConformanceTest/Autobahn/AutobahnTester.cs rename to src/Middleware/WebSockets/test/ConformanceTests/Autobahn/AutobahnTester.cs diff --git a/src/WebSockets/test/Microsoft.AspNetCore.WebSockets.ConformanceTest/Autobahn/Executable.cs b/src/Middleware/WebSockets/test/ConformanceTests/Autobahn/Executable.cs similarity index 100% rename from src/WebSockets/test/Microsoft.AspNetCore.WebSockets.ConformanceTest/Autobahn/Executable.cs rename to src/Middleware/WebSockets/test/ConformanceTests/Autobahn/Executable.cs diff --git a/src/WebSockets/test/Microsoft.AspNetCore.WebSockets.ConformanceTest/Autobahn/Expectation.cs b/src/Middleware/WebSockets/test/ConformanceTests/Autobahn/Expectation.cs similarity index 100% rename from src/WebSockets/test/Microsoft.AspNetCore.WebSockets.ConformanceTest/Autobahn/Expectation.cs rename to src/Middleware/WebSockets/test/ConformanceTests/Autobahn/Expectation.cs diff --git a/src/WebSockets/test/Microsoft.AspNetCore.WebSockets.ConformanceTest/Autobahn/ServerSpec.cs b/src/Middleware/WebSockets/test/ConformanceTests/Autobahn/ServerSpec.cs similarity index 100% rename from src/WebSockets/test/Microsoft.AspNetCore.WebSockets.ConformanceTest/Autobahn/ServerSpec.cs rename to src/Middleware/WebSockets/test/ConformanceTests/Autobahn/ServerSpec.cs diff --git a/src/WebSockets/test/Microsoft.AspNetCore.WebSockets.ConformanceTest/Autobahn/Wstest.cs b/src/Middleware/WebSockets/test/ConformanceTests/Autobahn/Wstest.cs similarity index 100% rename from src/WebSockets/test/Microsoft.AspNetCore.WebSockets.ConformanceTest/Autobahn/Wstest.cs rename to src/Middleware/WebSockets/test/ConformanceTests/Autobahn/Wstest.cs diff --git a/src/WebSockets/test/Microsoft.AspNetCore.WebSockets.ConformanceTest/AutobahnTests.cs b/src/Middleware/WebSockets/test/ConformanceTests/AutobahnTests.cs similarity index 100% rename from src/WebSockets/test/Microsoft.AspNetCore.WebSockets.ConformanceTest/AutobahnTests.cs rename to src/Middleware/WebSockets/test/ConformanceTests/AutobahnTests.cs diff --git a/src/WebSockets/test/Microsoft.AspNetCore.WebSockets.ConformanceTest/Helpers.cs b/src/Middleware/WebSockets/test/ConformanceTests/Helpers.cs similarity index 100% rename from src/WebSockets/test/Microsoft.AspNetCore.WebSockets.ConformanceTest/Helpers.cs rename to src/Middleware/WebSockets/test/ConformanceTests/Helpers.cs diff --git a/src/WebSockets/test/Microsoft.AspNetCore.WebSockets.ConformanceTest/Http.config b/src/Middleware/WebSockets/test/ConformanceTests/Http.config similarity index 100% rename from src/WebSockets/test/Microsoft.AspNetCore.WebSockets.ConformanceTest/Http.config rename to src/Middleware/WebSockets/test/ConformanceTests/Http.config diff --git a/src/Middleware/WebSockets/test/ConformanceTests/Microsoft.AspNetCore.WebSockets.ConformanceTests.csproj b/src/Middleware/WebSockets/test/ConformanceTests/Microsoft.AspNetCore.WebSockets.ConformanceTests.csproj new file mode 100644 index 0000000000..13e075308f --- /dev/null +++ b/src/Middleware/WebSockets/test/ConformanceTests/Microsoft.AspNetCore.WebSockets.ConformanceTests.csproj @@ -0,0 +1,14 @@ + + + + netcoreapp2.2 + + + + + + + + + + diff --git a/src/WebSockets/test/Microsoft.AspNetCore.WebSockets.ConformanceTest/SkipIfWsTestNotPresentAttribute.cs b/src/Middleware/WebSockets/test/ConformanceTests/SkipIfWsTestNotPresentAttribute.cs similarity index 100% rename from src/WebSockets/test/Microsoft.AspNetCore.WebSockets.ConformanceTest/SkipIfWsTestNotPresentAttribute.cs rename to src/Middleware/WebSockets/test/ConformanceTests/SkipIfWsTestNotPresentAttribute.cs diff --git a/src/Middleware/WebSockets/test/Directory.Build.props b/src/Middleware/WebSockets/test/Directory.Build.props new file mode 100644 index 0000000000..d5cd124cc9 --- /dev/null +++ b/src/Middleware/WebSockets/test/Directory.Build.props @@ -0,0 +1,27 @@ + + + + + + + true + + + + + + 2.2.0 + 2.2.0 + 2.2.0 + 2.2.0 + 2.2.0 + 0.6.0-rtm-final + 2.2.0 + 2.2.0 + 2.2.0 + + + diff --git a/src/WebSockets/test/Microsoft.AspNetCore.WebSockets.Test/AddWebSocketsTests.cs b/src/Middleware/WebSockets/test/UnitTests/AddWebSocketsTests.cs similarity index 100% rename from src/WebSockets/test/Microsoft.AspNetCore.WebSockets.Test/AddWebSocketsTests.cs rename to src/Middleware/WebSockets/test/UnitTests/AddWebSocketsTests.cs diff --git a/src/WebSockets/test/Microsoft.AspNetCore.WebSockets.Test/BufferStream.cs b/src/Middleware/WebSockets/test/UnitTests/BufferStream.cs similarity index 100% rename from src/WebSockets/test/Microsoft.AspNetCore.WebSockets.Test/BufferStream.cs rename to src/Middleware/WebSockets/test/UnitTests/BufferStream.cs diff --git a/src/WebSockets/test/Microsoft.AspNetCore.WebSockets.Test/DuplexStream.cs b/src/Middleware/WebSockets/test/UnitTests/DuplexStream.cs similarity index 100% rename from src/WebSockets/test/Microsoft.AspNetCore.WebSockets.Test/DuplexStream.cs rename to src/Middleware/WebSockets/test/UnitTests/DuplexStream.cs diff --git a/src/WebSockets/test/Microsoft.AspNetCore.WebSockets.Test/IWebHostPortExtensions.cs b/src/Middleware/WebSockets/test/UnitTests/IWebHostPortExtensions.cs similarity index 100% rename from src/WebSockets/test/Microsoft.AspNetCore.WebSockets.Test/IWebHostPortExtensions.cs rename to src/Middleware/WebSockets/test/UnitTests/IWebHostPortExtensions.cs diff --git a/src/WebSockets/test/Microsoft.AspNetCore.WebSockets.Test/KestrelWebSocketHelpers.cs b/src/Middleware/WebSockets/test/UnitTests/KestrelWebSocketHelpers.cs similarity index 100% rename from src/WebSockets/test/Microsoft.AspNetCore.WebSockets.Test/KestrelWebSocketHelpers.cs rename to src/Middleware/WebSockets/test/UnitTests/KestrelWebSocketHelpers.cs diff --git a/src/Middleware/WebSockets/test/UnitTests/Microsoft.AspNetCore.WebSockets.Tests.csproj b/src/Middleware/WebSockets/test/UnitTests/Microsoft.AspNetCore.WebSockets.Tests.csproj new file mode 100644 index 0000000000..fc5fb268d5 --- /dev/null +++ b/src/Middleware/WebSockets/test/UnitTests/Microsoft.AspNetCore.WebSockets.Tests.csproj @@ -0,0 +1,14 @@ + + + + $(StandardTestTfms) + + + + + + + + + + diff --git a/src/WebSockets/test/Microsoft.AspNetCore.WebSockets.Test/SendReceiveTests.cs b/src/Middleware/WebSockets/test/UnitTests/SendReceiveTests.cs similarity index 100% rename from src/WebSockets/test/Microsoft.AspNetCore.WebSockets.Test/SendReceiveTests.cs rename to src/Middleware/WebSockets/test/UnitTests/SendReceiveTests.cs diff --git a/src/WebSockets/test/Microsoft.AspNetCore.WebSockets.Test/WebSocketMiddlewareTests.cs b/src/Middleware/WebSockets/test/UnitTests/WebSocketMiddlewareTests.cs similarity index 100% rename from src/WebSockets/test/Microsoft.AspNetCore.WebSockets.Test/WebSocketMiddlewareTests.cs rename to src/Middleware/WebSockets/test/UnitTests/WebSocketMiddlewareTests.cs diff --git a/src/WebSockets/test/Microsoft.AspNetCore.WebSockets.Test/WebSocketPair.cs b/src/Middleware/WebSockets/test/UnitTests/WebSocketPair.cs similarity index 100% rename from src/WebSockets/test/Microsoft.AspNetCore.WebSockets.Test/WebSocketPair.cs rename to src/Middleware/WebSockets/test/UnitTests/WebSocketPair.cs diff --git a/src/PackageArchive/Archive.CiServer.Patch.Compat/ArchiveBaseline.2.1.6.txt b/src/PackageArchive/Archive.CiServer.Patch.Compat/ArchiveBaseline.2.1.6.txt new file mode 100644 index 0000000000..62153a6836 --- /dev/null +++ b/src/PackageArchive/Archive.CiServer.Patch.Compat/ArchiveBaseline.2.1.6.txt @@ -0,0 +1,675 @@ +libuv\1.10.0\.nupkg.metadata +libuv\1.10.0\.signature.p7s +messagepack\1.7.3.4\.nupkg.metadata +messagepack\1.7.3.4\.signature.p7s +microsoft.applicationinsights.aspnetcore\2.1.1\.nupkg.metadata +microsoft.applicationinsights.aspnetcore\2.1.1\.signature.p7s +microsoft.applicationinsights.dependencycollector\2.4.1\.nupkg.metadata +microsoft.applicationinsights.dependencycollector\2.4.1\.signature.p7s +microsoft.applicationinsights\2.4.0\.nupkg.metadata +microsoft.applicationinsights\2.4.0\.signature.p7s +microsoft.aspnet.webapi.client\5.2.6\.nupkg.metadata +microsoft.aspnetcore.all\2.1.1\.nupkg.metadata +microsoft.aspnetcore.antiforgery\2.1.1\.nupkg.metadata +microsoft.aspnetcore.app\2.1.1\.nupkg.metadata +microsoft.aspnetcore.applicationinsights.hostingstartup\2.1.1\.nupkg.metadata +microsoft.aspnetcore.authentication.abstractions\2.1.1\.nupkg.metadata +microsoft.aspnetcore.authentication.azuread.ui\2.1.1\.nupkg.metadata +microsoft.aspnetcore.authentication.azureadb2c.ui\2.1.1\.nupkg.metadata +microsoft.aspnetcore.authentication.cookies\2.1.1\.nupkg.metadata +microsoft.aspnetcore.authentication.core\2.1.1\.nupkg.metadata +microsoft.aspnetcore.authentication.facebook\2.1.1\.nupkg.metadata +microsoft.aspnetcore.authentication.google\2.1.1\.nupkg.metadata +microsoft.aspnetcore.authentication.jwtbearer\2.1.1\.nupkg.metadata +microsoft.aspnetcore.authentication.microsoftaccount\2.1.1\.nupkg.metadata +microsoft.aspnetcore.authentication.oauth\2.1.1\.nupkg.metadata +microsoft.aspnetcore.authentication.openidconnect\2.1.1\.nupkg.metadata +microsoft.aspnetcore.authentication.twitter\2.1.1\.nupkg.metadata +microsoft.aspnetcore.authentication.wsfederation\2.1.1\.nupkg.metadata +microsoft.aspnetcore.authentication\2.1.1\.nupkg.metadata +microsoft.aspnetcore.authorization.policy\2.1.1\.nupkg.metadata +microsoft.aspnetcore.authorization\2.1.1\.nupkg.metadata +microsoft.aspnetcore.azureappservices.hostingstartup\2.1.1\.nupkg.metadata +microsoft.aspnetcore.azureappservicesintegration\2.1.1\.nupkg.metadata +microsoft.aspnetcore.connections.abstractions\2.1.1\.nupkg.metadata +microsoft.aspnetcore.cookiepolicy\2.1.1\.nupkg.metadata +microsoft.aspnetcore.cors\2.1.1\.nupkg.metadata +microsoft.aspnetcore.cryptography.internal\2.1.1\.nupkg.metadata +microsoft.aspnetcore.cryptography.keyderivation\2.1.1\.nupkg.metadata +microsoft.aspnetcore.dataprotection.abstractions\2.1.1\.nupkg.metadata +microsoft.aspnetcore.dataprotection.azurekeyvault\2.1.1\.nupkg.metadata +microsoft.aspnetcore.dataprotection.azurestorage\2.1.1\.nupkg.metadata +microsoft.aspnetcore.dataprotection.extensions\2.1.1\.nupkg.metadata +microsoft.aspnetcore.dataprotection\2.1.1\.nupkg.metadata +microsoft.aspnetcore.diagnostics.abstractions\2.1.1\.nupkg.metadata +microsoft.aspnetcore.diagnostics.entityframeworkcore\2.1.1\.nupkg.metadata +microsoft.aspnetcore.diagnostics\2.1.1\.nupkg.metadata +microsoft.aspnetcore.hostfiltering\2.1.1\.nupkg.metadata +microsoft.aspnetcore.hosting.abstractions\2.1.1\.nupkg.metadata +microsoft.aspnetcore.hosting.server.abstractions\2.1.1\.nupkg.metadata +microsoft.aspnetcore.hosting\2.1.1\.nupkg.metadata +microsoft.aspnetcore.html.abstractions\2.1.1\.nupkg.metadata +microsoft.aspnetcore.http.abstractions\2.1.1\.nupkg.metadata +microsoft.aspnetcore.http.connections.common\1.0.1\.nupkg.metadata +microsoft.aspnetcore.http.connections\1.0.1\.nupkg.metadata +microsoft.aspnetcore.http.extensions\2.1.1\.nupkg.metadata +microsoft.aspnetcore.http.features\2.1.1\.nupkg.metadata +microsoft.aspnetcore.http\2.1.1\.nupkg.metadata +microsoft.aspnetcore.httpoverrides\2.1.1\.nupkg.metadata +microsoft.aspnetcore.httpspolicy\2.1.1\.nupkg.metadata +microsoft.aspnetcore.identity.entityframeworkcore\2.1.1\.nupkg.metadata +microsoft.aspnetcore.identity.ui\2.1.1\.nupkg.metadata +microsoft.aspnetcore.identity\2.1.1\.nupkg.metadata +microsoft.aspnetcore.jsonpatch\2.1.1\.nupkg.metadata +microsoft.aspnetcore.localization.routing\2.1.1\.nupkg.metadata +microsoft.aspnetcore.localization\2.1.1\.nupkg.metadata +microsoft.aspnetcore.middlewareanalysis\2.1.1\.nupkg.metadata +microsoft.aspnetcore.mvc.abstractions\2.1.1\.nupkg.metadata +microsoft.aspnetcore.mvc.analyzers\2.1.1\.nupkg.metadata +microsoft.aspnetcore.mvc.apiexplorer\2.1.1\.nupkg.metadata +microsoft.aspnetcore.mvc.core\2.1.1\.nupkg.metadata +microsoft.aspnetcore.mvc.cors\2.1.1\.nupkg.metadata +microsoft.aspnetcore.mvc.dataannotations\2.1.1\.nupkg.metadata +microsoft.aspnetcore.mvc.formatters.json\2.1.1\.nupkg.metadata +microsoft.aspnetcore.mvc.formatters.xml\2.1.1\.nupkg.metadata +microsoft.aspnetcore.mvc.localization\2.1.1\.nupkg.metadata +microsoft.aspnetcore.mvc.razor.extensions\2.1.1\.nupkg.metadata +microsoft.aspnetcore.mvc.razor.viewcompilation\2.1.1\.nupkg.metadata +microsoft.aspnetcore.mvc.razor\2.1.1\.nupkg.metadata +microsoft.aspnetcore.mvc.razorpages\2.1.1\.nupkg.metadata +microsoft.aspnetcore.mvc.taghelpers\2.1.1\.nupkg.metadata +microsoft.aspnetcore.mvc.viewfeatures\2.1.1\.nupkg.metadata +microsoft.aspnetcore.mvc\2.1.1\.nupkg.metadata +microsoft.aspnetcore.nodeservices\2.1.1\.nupkg.metadata +microsoft.aspnetcore.owin\2.1.1\.nupkg.metadata +microsoft.aspnetcore.razor.design\2.1.2\.nupkg.metadata +microsoft.aspnetcore.razor.language\2.1.1\.nupkg.metadata +microsoft.aspnetcore.razor.runtime\2.1.1\.nupkg.metadata +microsoft.aspnetcore.razor\2.1.1\.nupkg.metadata +microsoft.aspnetcore.responsecaching.abstractions\2.1.1\.nupkg.metadata +microsoft.aspnetcore.responsecaching\2.1.1\.nupkg.metadata +microsoft.aspnetcore.responsecompression\2.1.1\.nupkg.metadata +microsoft.aspnetcore.rewrite\2.1.1\.nupkg.metadata +microsoft.aspnetcore.routing.abstractions\2.1.1\.nupkg.metadata +microsoft.aspnetcore.routing\2.1.1\.nupkg.metadata +microsoft.aspnetcore.server.httpsys\2.1.1\.nupkg.metadata +microsoft.aspnetcore.server.iisintegration\2.1.1\.nupkg.metadata +microsoft.aspnetcore.server.kestrel.core\2.1.1\.nupkg.metadata +microsoft.aspnetcore.server.kestrel.https\2.1.1\.nupkg.metadata +microsoft.aspnetcore.server.kestrel.transport.abstractions\2.1.1\.nupkg.metadata +microsoft.aspnetcore.server.kestrel.transport.libuv\2.1.1\.nupkg.metadata +microsoft.aspnetcore.server.kestrel.transport.sockets\2.1.1\.nupkg.metadata +microsoft.aspnetcore.server.kestrel\2.1.1\.nupkg.metadata +microsoft.aspnetcore.session\2.1.1\.nupkg.metadata +microsoft.aspnetcore.signalr.common\1.0.1\.nupkg.metadata +microsoft.aspnetcore.signalr.core\1.0.1\.nupkg.metadata +microsoft.aspnetcore.signalr.protocols.json\1.0.1\.nupkg.metadata +microsoft.aspnetcore.signalr.redis\1.0.1\.nupkg.metadata +microsoft.aspnetcore.signalr\1.0.1\.nupkg.metadata +microsoft.aspnetcore.spaservices.extensions\2.1.1\.nupkg.metadata +microsoft.aspnetcore.spaservices\2.1.1\.nupkg.metadata +microsoft.aspnetcore.staticfiles\2.1.1\.nupkg.metadata +microsoft.aspnetcore.websockets\2.1.1\.nupkg.metadata +microsoft.aspnetcore.webutilities\2.1.1\.nupkg.metadata +microsoft.aspnetcore\2.1.1\.nupkg.metadata +microsoft.azure.keyvault.webkey\2.0.7\.nupkg.metadata +microsoft.azure.keyvault.webkey\2.0.7\.signature.p7s +microsoft.azure.keyvault\2.3.2\.nupkg.metadata +microsoft.azure.keyvault\2.3.2\.signature.p7s +microsoft.azure.services.appauthentication\1.0.1\.nupkg.metadata +microsoft.azure.services.appauthentication\1.0.1\.signature.p7s +microsoft.codeanalysis.analyzers\1.1.0\.nupkg.metadata +microsoft.codeanalysis.common\2.8.0\.nupkg.metadata +microsoft.codeanalysis.csharp.workspaces\2.8.0\.nupkg.metadata +microsoft.codeanalysis.csharp\2.8.0\.nupkg.metadata +microsoft.codeanalysis.razor\2.1.1\.nupkg.metadata +microsoft.codeanalysis.workspaces.common\2.8.0\.nupkg.metadata +microsoft.csharp\4.0.1\.nupkg.metadata +microsoft.csharp\4.3.0\.nupkg.metadata +microsoft.csharp\4.5.0\.nupkg.metadata +microsoft.data.edm\5.8.2\.nupkg.metadata +microsoft.data.odata\5.8.2\.nupkg.metadata +microsoft.data.sqlite.core\2.1.0\.nupkg.metadata +microsoft.data.sqlite\2.1.0\.nupkg.metadata +microsoft.dotnet.platformabstractions\2.1.0\.nupkg.metadata +microsoft.entityframeworkcore.abstractions\2.1.1\.nupkg.metadata +microsoft.entityframeworkcore.analyzers\2.1.1\.nupkg.metadata +microsoft.entityframeworkcore.design\2.1.1\.nupkg.metadata +microsoft.entityframeworkcore.inmemory\2.1.1\.nupkg.metadata +microsoft.entityframeworkcore.relational\2.1.1\.nupkg.metadata +microsoft.entityframeworkcore.sqlite.core\2.1.1\.nupkg.metadata +microsoft.entityframeworkcore.sqlite\2.1.1\.nupkg.metadata +microsoft.entityframeworkcore.sqlserver\2.1.1\.nupkg.metadata +microsoft.entityframeworkcore.tools\2.1.1\.nupkg.metadata +microsoft.entityframeworkcore\2.1.1\.nupkg.metadata +microsoft.extensions.caching.abstractions\2.1.1\.nupkg.metadata +microsoft.extensions.caching.memory\2.1.1\.nupkg.metadata +microsoft.extensions.caching.redis\2.1.1\.nupkg.metadata +microsoft.extensions.caching.sqlserver\2.1.1\.nupkg.metadata +microsoft.extensions.configuration.abstractions\2.1.1\.nupkg.metadata +microsoft.extensions.configuration.azurekeyvault\2.1.1\.nupkg.metadata +microsoft.extensions.configuration.binder\2.1.1\.nupkg.metadata +microsoft.extensions.configuration.commandline\2.1.1\.nupkg.metadata +microsoft.extensions.configuration.environmentvariables\2.1.1\.nupkg.metadata +microsoft.extensions.configuration.fileextensions\2.1.1\.nupkg.metadata +microsoft.extensions.configuration.ini\2.1.1\.nupkg.metadata +microsoft.extensions.configuration.json\2.1.1\.nupkg.metadata +microsoft.extensions.configuration.keyperfile\2.1.1\.nupkg.metadata +microsoft.extensions.configuration.usersecrets\2.1.1\.nupkg.metadata +microsoft.extensions.configuration.xml\2.1.1\.nupkg.metadata +microsoft.extensions.configuration\2.1.1\.nupkg.metadata +microsoft.extensions.dependencyinjection.abstractions\2.1.1\.nupkg.metadata +microsoft.extensions.dependencyinjection\2.1.1\.nupkg.metadata +microsoft.extensions.dependencymodel\2.1.0\.nupkg.metadata +microsoft.extensions.diagnosticadapter\2.1.0\.nupkg.metadata +microsoft.extensions.fileproviders.abstractions\2.1.1\.nupkg.metadata +microsoft.extensions.fileproviders.composite\2.1.1\.nupkg.metadata +microsoft.extensions.fileproviders.embedded\2.1.1\.nupkg.metadata +microsoft.extensions.fileproviders.physical\2.1.1\.nupkg.metadata +microsoft.extensions.filesystemglobbing\2.1.1\.nupkg.metadata +microsoft.extensions.hosting.abstractions\2.1.1\.nupkg.metadata +microsoft.extensions.hosting\2.1.1\.nupkg.metadata +microsoft.extensions.http\2.1.1\.nupkg.metadata +microsoft.extensions.identity.core\2.1.1\.nupkg.metadata +microsoft.extensions.identity.stores\2.1.1\.nupkg.metadata +microsoft.extensions.localization.abstractions\2.1.1\.nupkg.metadata +microsoft.extensions.localization\2.1.1\.nupkg.metadata +microsoft.extensions.logging.abstractions\2.1.1\.nupkg.metadata +microsoft.extensions.logging.azureappservices\2.1.1\.nupkg.metadata +microsoft.extensions.logging.configuration\2.1.1\.nupkg.metadata +microsoft.extensions.logging.console\2.1.1\.nupkg.metadata +microsoft.extensions.logging.debug\2.1.1\.nupkg.metadata +microsoft.extensions.logging.eventsource\2.1.1\.nupkg.metadata +microsoft.extensions.logging.tracesource\2.1.1\.nupkg.metadata +microsoft.extensions.logging\2.1.1\.nupkg.metadata +microsoft.extensions.objectpool\2.1.1\.nupkg.metadata +microsoft.extensions.options.configurationextensions\2.1.1\.nupkg.metadata +microsoft.extensions.options\2.1.1\.nupkg.metadata +microsoft.extensions.platformabstractions\1.1.0\.nupkg.metadata +microsoft.extensions.primitives\2.1.1\.nupkg.metadata +microsoft.extensions.webencoders\2.1.1\.nupkg.metadata +microsoft.identitymodel.clients.activedirectory\3.14.2\.nupkg.metadata +microsoft.identitymodel.clients.activedirectory\3.14.2\.signature.p7s +microsoft.identitymodel.logging\5.2.0\.nupkg.metadata +microsoft.identitymodel.logging\5.2.0\.signature.p7s +microsoft.identitymodel.protocols.openidconnect\5.2.0\.nupkg.metadata +microsoft.identitymodel.protocols.openidconnect\5.2.0\.signature.p7s +microsoft.identitymodel.protocols.wsfederation\5.2.0\.nupkg.metadata +microsoft.identitymodel.protocols.wsfederation\5.2.0\.signature.p7s +microsoft.identitymodel.protocols\5.2.0\.nupkg.metadata +microsoft.identitymodel.protocols\5.2.0\.signature.p7s +microsoft.identitymodel.tokens.saml\5.2.0\.nupkg.metadata +microsoft.identitymodel.tokens.saml\5.2.0\.signature.p7s +microsoft.identitymodel.tokens\5.2.0\.nupkg.metadata +microsoft.identitymodel.tokens\5.2.0\.signature.p7s +microsoft.identitymodel.xml\5.2.0\.nupkg.metadata +microsoft.identitymodel.xml\5.2.0\.signature.p7s +microsoft.net.http.headers\2.1.1\.nupkg.metadata +microsoft.netcore.app\2.1.0\.nupkg.metadata +microsoft.netcore.dotnetapphost\2.1.0\.nupkg.metadata +microsoft.netcore.dotnethostpolicy\2.1.0\.nupkg.metadata +microsoft.netcore.dotnethostresolver\2.1.0\.nupkg.metadata +microsoft.netcore.platforms\1.0.1\.nupkg.metadata +microsoft.netcore.platforms\1.0.1\.signature.p7s +microsoft.netcore.platforms\1.0.2\.nupkg.metadata +microsoft.netcore.platforms\1.0.2\.signature.p7s +microsoft.netcore.platforms\1.1.0\.nupkg.metadata +microsoft.netcore.platforms\1.1.0\.signature.p7s +microsoft.netcore.platforms\2.0.0\.nupkg.metadata +microsoft.netcore.platforms\2.0.0\.signature.p7s +microsoft.netcore.platforms\2.1.0\.nupkg.metadata +microsoft.netcore.targets\1.0.1\.nupkg.metadata +microsoft.netcore.targets\1.1.0\.nupkg.metadata +microsoft.netcore.targets\2.1.0\.nupkg.metadata +microsoft.rest.clientruntime.azure\3.3.7\.nupkg.metadata +microsoft.rest.clientruntime.azure\3.3.7\.signature.p7s +microsoft.rest.clientruntime\2.3.8\.nupkg.metadata +microsoft.rest.clientruntime\2.3.8\.signature.p7s +microsoft.visualstudio.web.browserlink\2.1.1\.nupkg.metadata +microsoft.visualstudio.web.codegeneration.contracts\2.1.6\.nupkg.metadata +microsoft.visualstudio.web.codegeneration.contracts\2.1.6\.signature.p7s +microsoft.visualstudio.web.codegeneration.contracts\2.1.6\lib\netstandard2.0\Microsoft.VisualStudio.Web.CodeGeneration.Contracts.dll +microsoft.visualstudio.web.codegeneration.contracts\2.1.6\microsoft.visualstudio.web.codegeneration.contracts.2.1.6.nupkg +microsoft.visualstudio.web.codegeneration.contracts\2.1.6\microsoft.visualstudio.web.codegeneration.contracts.2.1.6.nupkg.sha512 +microsoft.visualstudio.web.codegeneration.contracts\2.1.6\microsoft.visualstudio.web.codegeneration.contracts.nuspec +microsoft.visualstudio.web.codegeneration.core\2.1.6\.nupkg.metadata +microsoft.visualstudio.web.codegeneration.core\2.1.6\.signature.p7s +microsoft.visualstudio.web.codegeneration.core\2.1.6\lib\netstandard2.0\Microsoft.VisualStudio.Web.CodeGeneration.Core.dll +microsoft.visualstudio.web.codegeneration.core\2.1.6\microsoft.visualstudio.web.codegeneration.core.2.1.6.nupkg +microsoft.visualstudio.web.codegeneration.core\2.1.6\microsoft.visualstudio.web.codegeneration.core.2.1.6.nupkg.sha512 +microsoft.visualstudio.web.codegeneration.core\2.1.6\microsoft.visualstudio.web.codegeneration.core.nuspec +microsoft.visualstudio.web.codegeneration.design\2.1.6\.nupkg.metadata +microsoft.visualstudio.web.codegeneration.design\2.1.6\.signature.p7s +microsoft.visualstudio.web.codegeneration.design\2.1.6\lib\net461\dotnet-aspnet-codegenerator-design.exe +microsoft.visualstudio.web.codegeneration.design\2.1.6\lib\netstandard2.0\dotnet-aspnet-codegenerator-design.dll +microsoft.visualstudio.web.codegeneration.design\2.1.6\microsoft.visualstudio.web.codegeneration.design.2.1.6.nupkg +microsoft.visualstudio.web.codegeneration.design\2.1.6\microsoft.visualstudio.web.codegeneration.design.2.1.6.nupkg.sha512 +microsoft.visualstudio.web.codegeneration.design\2.1.6\microsoft.visualstudio.web.codegeneration.design.nuspec +microsoft.visualstudio.web.codegeneration.design\2.1.6\runtimes\win7-x64\lib\net461\dotnet-aspnet-codegenerator-design.exe +microsoft.visualstudio.web.codegeneration.design\2.1.6\runtimes\win7-x86\lib\net461\dotnet-aspnet-codegenerator-design.exe +microsoft.visualstudio.web.codegeneration.design\2.1.6\runtimes\win-arm\lib\net461\dotnet-aspnet-codegenerator-design.exe +microsoft.visualstudio.web.codegeneration.design\2.1.6\runtimes\win-arm64\lib\net461\dotnet-aspnet-codegenerator-design.exe +microsoft.visualstudio.web.codegeneration.entityframeworkcore\2.1.6\.nupkg.metadata +microsoft.visualstudio.web.codegeneration.entityframeworkcore\2.1.6\.signature.p7s +microsoft.visualstudio.web.codegeneration.entityframeworkcore\2.1.6\lib\netstandard2.0\Microsoft.VisualStudio.Web.CodeGeneration.EntityFrameworkCore.dll +microsoft.visualstudio.web.codegeneration.entityframeworkcore\2.1.6\microsoft.visualstudio.web.codegeneration.entityframeworkcore.2.1.6.nupkg +microsoft.visualstudio.web.codegeneration.entityframeworkcore\2.1.6\microsoft.visualstudio.web.codegeneration.entityframeworkcore.2.1.6.nupkg.sha512 +microsoft.visualstudio.web.codegeneration.entityframeworkcore\2.1.6\microsoft.visualstudio.web.codegeneration.entityframeworkcore.nuspec +microsoft.visualstudio.web.codegeneration.entityframeworkcore\2.1.6\Templates\DbContext\NewLocalDbContext.cshtml +microsoft.visualstudio.web.codegeneration.templating\2.1.6\.nupkg.metadata +microsoft.visualstudio.web.codegeneration.templating\2.1.6\.signature.p7s +microsoft.visualstudio.web.codegeneration.templating\2.1.6\lib\netstandard2.0\Microsoft.VisualStudio.Web.CodeGeneration.Templating.dll +microsoft.visualstudio.web.codegeneration.templating\2.1.6\microsoft.visualstudio.web.codegeneration.templating.2.1.6.nupkg +microsoft.visualstudio.web.codegeneration.templating\2.1.6\microsoft.visualstudio.web.codegeneration.templating.2.1.6.nupkg.sha512 +microsoft.visualstudio.web.codegeneration.templating\2.1.6\microsoft.visualstudio.web.codegeneration.templating.nuspec +microsoft.visualstudio.web.codegeneration.utils\2.1.6\.nupkg.metadata +microsoft.visualstudio.web.codegeneration.utils\2.1.6\.signature.p7s +microsoft.visualstudio.web.codegeneration.utils\2.1.6\lib\netstandard2.0\Microsoft.VisualStudio.Web.CodeGeneration.Utils.dll +microsoft.visualstudio.web.codegeneration.utils\2.1.6\microsoft.visualstudio.web.codegeneration.utils.2.1.6.nupkg +microsoft.visualstudio.web.codegeneration.utils\2.1.6\microsoft.visualstudio.web.codegeneration.utils.2.1.6.nupkg.sha512 +microsoft.visualstudio.web.codegeneration.utils\2.1.6\microsoft.visualstudio.web.codegeneration.utils.nuspec +microsoft.visualstudio.web.codegeneration\2.1.6\.nupkg.metadata +microsoft.visualstudio.web.codegeneration\2.1.6\.signature.p7s +microsoft.visualstudio.web.codegeneration\2.1.6\lib\netstandard2.0\Microsoft.VisualStudio.Web.CodeGeneration.dll +microsoft.visualstudio.web.codegeneration\2.1.6\microsoft.visualstudio.web.codegeneration.2.1.6.nupkg +microsoft.visualstudio.web.codegeneration\2.1.6\microsoft.visualstudio.web.codegeneration.2.1.6.nupkg.sha512 +microsoft.visualstudio.web.codegeneration\2.1.6\microsoft.visualstudio.web.codegeneration.nuspec +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\.nupkg.metadata +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\.signature.p7s +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Generators\ParameterDefinitions\area.json +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Generators\ParameterDefinitions\controller.json +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Generators\ParameterDefinitions\identity.json +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Generators\ParameterDefinitions\razorpage.json +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Generators\ParameterDefinitions\view.json +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\lib\netstandard2.0\identitygeneratorfilesconfig.json +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\lib\netstandard2.0\Microsoft.VisualStudio.Web.CodeGenerators.Mvc.dll +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\microsoft.visualstudio.web.codegenerators.mvc.2.1.6.nupkg +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\microsoft.visualstudio.web.codegenerators.mvc.2.1.6.nupkg.sha512 +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\microsoft.visualstudio.web.codegenerators.mvc.nuspec +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\ControllerGenerator\ApiControllerWithActions.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\ControllerGenerator\ApiControllerWithContext.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\ControllerGenerator\ApiEmptyController.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\ControllerGenerator\ControllerWithActions.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\ControllerGenerator\EmptyController.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\ControllerGenerator\MvcControllerWithContext.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\_LoginPartial.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Data\ApplicationDbContext.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Data\ApplicationUser.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\IdentityHostingStartup.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\_Layout.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\_ValidationScriptsPartial.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\_ViewImports.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\_ViewStart.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Account._ViewImports.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Account.AccessDenied.cs.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Account.AccessDenied.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Account.ConfirmEmail.cs.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Account.ConfirmEmail.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Account.ExternalLogin.cs.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Account.ExternalLogin.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Account.ForgotPassword.cs.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Account.ForgotPassword.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Account.ForgotPasswordConfirmation.cs.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Account.ForgotPasswordConfirmation.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Account.Lockout.cs.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Account.Lockout.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Account.Login.cs.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Account.Login.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Account.LoginWith2fa.cs.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Account.LoginWith2fa.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Account.LoginWithRecoveryCode.cs.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Account.LoginWithRecoveryCode.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Account.Logout.cs.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Account.Logout.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Account.Register.cs.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Account.Register.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Account.ResetPassword.cs.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Account.ResetPassword.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Account.ResetPasswordConfirmation.cs.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Account.ResetPasswordConfirmation.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Manage\Account.Manage._Layout.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Manage\Account.Manage._ManageNav.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Manage\Account.Manage._StatusMessage.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Manage\Account.Manage._ViewImports.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Manage\Account.Manage.ChangePassword.cs.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Manage\Account.Manage.ChangePassword.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Manage\Account.Manage.DeletePersonalData.cs.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Manage\Account.Manage.DeletePersonalData.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Manage\Account.Manage.Disable2fa.cs.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Manage\Account.Manage.Disable2fa.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Manage\Account.Manage.DownloadPersonalData.cs.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Manage\Account.Manage.DownloadPersonalData.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Manage\Account.Manage.EnableAuthenticator.cs.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Manage\Account.Manage.EnableAuthenticator.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Manage\Account.Manage.ExternalLogins.cs.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Manage\Account.Manage.ExternalLogins.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Manage\Account.Manage.GenerateRecoveryCodes.cs.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Manage\Account.Manage.GenerateRecoveryCodes.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Manage\Account.Manage.Index.cs.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Manage\Account.Manage.Index.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Manage\Account.Manage.ManageNavPages.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Manage\Account.Manage.PersonalData.cs.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Manage\Account.Manage.PersonalData.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Manage\Account.Manage.ResetAuthenticator.cs.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Manage\Account.Manage.ResetAuthenticator.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Manage\Account.Manage.SetPassword.cs.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Manage\Account.Manage.SetPassword.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Manage\Account.Manage.ShowRecoveryCodes.cs.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Manage\Account.Manage.ShowRecoveryCodes.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Manage\Account.Manage.TwoFactorAuthentication.cs.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Manage\Account.Manage.TwoFactorAuthentication.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Error.cs.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Error.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\ScaffoldingReadme.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\SupportPages._CookieConsentPartial.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\SupportPages._ViewImports.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\SupportPages._ViewStart.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\css\site.css +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\css\site.min.css +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\favicon.ico +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\images\banner1.svg +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\images\banner2.svg +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\images\banner3.svg +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\js\site.js +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\js\site.min.js +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\lib\bootstrap\.bower.json +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\lib\bootstrap\dist\css\bootstrap.css +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\lib\bootstrap\dist\css\bootstrap.css.map +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\lib\bootstrap\dist\css\bootstrap.min.css +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\lib\bootstrap\dist\css\bootstrap.min.css.map +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\lib\bootstrap\dist\css\bootstrap-theme.css +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\lib\bootstrap\dist\css\bootstrap-theme.css.map +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\lib\bootstrap\dist\css\bootstrap-theme.min.css +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\lib\bootstrap\dist\css\bootstrap-theme.min.css.map +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\lib\bootstrap\dist\fonts\glyphicons-halflings-regular.eot +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\lib\bootstrap\dist\fonts\glyphicons-halflings-regular.svg +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\lib\bootstrap\dist\fonts\glyphicons-halflings-regular.ttf +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\lib\bootstrap\dist\fonts\glyphicons-halflings-regular.woff +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\lib\bootstrap\dist\fonts\glyphicons-halflings-regular.woff2 +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\lib\bootstrap\dist\js\bootstrap.js +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\lib\bootstrap\dist\js\bootstrap.min.js +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\lib\bootstrap\dist\js\npm.js +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\lib\bootstrap\LICENSE +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\lib\jquery\.bower.json +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\lib\jquery\dist\jquery.js +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\lib\jquery\dist\jquery.min.js +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\lib\jquery\dist\jquery.min.map +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\lib\jquery\LICENSE.txt +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\lib\jquery-validation\.bower.json +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\lib\jquery-validation\dist\additional-methods.js +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\lib\jquery-validation\dist\additional-methods.min.js +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\lib\jquery-validation\dist\jquery.validate.js +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\lib\jquery-validation\dist\jquery.validate.min.js +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\lib\jquery-validation\LICENSE.md +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\lib\jquery-validation-unobtrusive\.bower.json +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\lib\jquery-validation-unobtrusive\jquery.validate.unobtrusive.js +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\lib\jquery-validation-unobtrusive\jquery.validate.unobtrusive.min.js +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\lib\jquery-validation-unobtrusive\LICENSE.txt +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\MvcLayout\_Layout.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\MvcLayout\Error.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\RazorPageGenerator\_ValidationScriptsPartial.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\RazorPageGenerator\Create.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\RazorPageGenerator\CreatePageModel.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\RazorPageGenerator\Delete.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\RazorPageGenerator\DeletePageModel.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\RazorPageGenerator\Details.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\RazorPageGenerator\DetailsPageModel.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\RazorPageGenerator\Edit.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\RazorPageGenerator\EditPageModel.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\RazorPageGenerator\Empty.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\RazorPageGenerator\EmptyPageModel.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\RazorPageGenerator\List.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\RazorPageGenerator\ListPageModel.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Startup\ReadMe.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Startup\Startup.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\ViewGenerator\_ValidationScriptsPartial.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\ViewGenerator\Create.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\ViewGenerator\Delete.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\ViewGenerator\Details.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\ViewGenerator\Edit.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\ViewGenerator\Empty.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\ViewGenerator\List.cshtml +microsoft.win32.primitives\4.0.1\.nupkg.metadata +microsoft.win32.primitives\4.3.0\.nupkg.metadata +microsoft.win32.registry\4.3.0\.nupkg.metadata +microsoft.win32.registry\4.5.0\.nupkg.metadata +netstandard.library\1.6.0\.nupkg.metadata +netstandard.library\1.6.1\.nupkg.metadata +netstandard.library\2.0.3\.nupkg.metadata +newtonsoft.json.bson\1.0.1\.nupkg.metadata +newtonsoft.json.bson\1.0.1\.signature.p7s +newtonsoft.json\10.0.1\.nupkg.metadata +newtonsoft.json\10.0.1\.signature.p7s +newtonsoft.json\11.0.2\.nupkg.metadata +newtonsoft.json\11.0.2\.signature.p7s +newtonsoft.json\9.0.1\.nupkg.metadata +nuget.frameworks\4.7.0\.nupkg.metadata +remotion.linq\2.2.0\.nupkg.metadata +remotion.linq\2.2.0\.signature.p7s +runtime.debian.8-x64.runtime.native.system.security.cryptography.openssl\4.3.0\.nupkg.metadata +runtime.fedora.23-x64.runtime.native.system.security.cryptography.openssl\4.3.0\.nupkg.metadata +runtime.fedora.24-x64.runtime.native.system.security.cryptography.openssl\4.3.0\.nupkg.metadata +runtime.native.system.data.sqlclient.sni\4.4.0\.nupkg.metadata +runtime.native.system.io.compression\4.1.0\.nupkg.metadata +runtime.native.system.io.compression\4.3.0\.nupkg.metadata +runtime.native.system.net.http\4.0.1\.nupkg.metadata +runtime.native.system.net.http\4.3.0\.nupkg.metadata +runtime.native.system.net.security\4.3.0\.nupkg.metadata +runtime.native.system.security.cryptography.apple\4.3.0\.nupkg.metadata +runtime.native.system.security.cryptography.openssl\4.3.0\.nupkg.metadata +runtime.native.system.security.cryptography\4.0.0\.nupkg.metadata +runtime.native.system\4.0.0\.nupkg.metadata +runtime.native.system\4.3.0\.nupkg.metadata +runtime.opensuse.13.2-x64.runtime.native.system.security.cryptography.openssl\4.3.0\.nupkg.metadata +runtime.opensuse.42.1-x64.runtime.native.system.security.cryptography.openssl\4.3.0\.nupkg.metadata +runtime.osx.10.10-x64.runtime.native.system.security.cryptography.apple\4.3.0\.nupkg.metadata +runtime.osx.10.10-x64.runtime.native.system.security.cryptography.openssl\4.3.0\.nupkg.metadata +runtime.rhel.7-x64.runtime.native.system.security.cryptography.openssl\4.3.0\.nupkg.metadata +runtime.ubuntu.14.04-x64.runtime.native.system.security.cryptography.openssl\4.3.0\.nupkg.metadata +runtime.ubuntu.16.04-x64.runtime.native.system.security.cryptography.openssl\4.3.0\.nupkg.metadata +runtime.ubuntu.16.10-x64.runtime.native.system.security.cryptography.openssl\4.3.0\.nupkg.metadata +runtime.win-arm64.runtime.native.system.data.sqlclient.sni\4.4.0\.nupkg.metadata +runtime.win-x64.runtime.native.system.data.sqlclient.sni\4.4.0\.nupkg.metadata +runtime.win-x86.runtime.native.system.data.sqlclient.sni\4.4.0\.nupkg.metadata +sqlitepclraw.bundle_green\1.1.11\.nupkg.metadata +sqlitepclraw.bundle_green\1.1.11\.signature.p7s +sqlitepclraw.core\1.1.11\.nupkg.metadata +sqlitepclraw.core\1.1.11\.signature.p7s +sqlitepclraw.lib.e_sqlite3.linux\1.1.11\.nupkg.metadata +sqlitepclraw.lib.e_sqlite3.linux\1.1.11\.signature.p7s +sqlitepclraw.lib.e_sqlite3.osx\1.1.11\.nupkg.metadata +sqlitepclraw.lib.e_sqlite3.osx\1.1.11\.signature.p7s +sqlitepclraw.lib.e_sqlite3.v110_xp\1.1.11\.nupkg.metadata +sqlitepclraw.lib.e_sqlite3.v110_xp\1.1.11\.signature.p7s +sqlitepclraw.provider.e_sqlite3.netstandard11\1.1.11\.nupkg.metadata +sqlitepclraw.provider.e_sqlite3.netstandard11\1.1.11\.signature.p7s +stackexchange.redis.strongname\1.2.4\.nupkg.metadata +stackexchange.redis.strongname\1.2.4\.signature.p7s +system.appcontext\4.1.0\.nupkg.metadata +system.appcontext\4.3.0\.nupkg.metadata +system.buffers\4.0.0\.nupkg.metadata +system.buffers\4.3.0\.nupkg.metadata +system.buffers\4.5.0\.nupkg.metadata +system.collections.concurrent\4.0.12\.nupkg.metadata +system.collections.concurrent\4.3.0\.nupkg.metadata +system.collections.immutable\1.3.0\.nupkg.metadata +system.collections.immutable\1.3.1\.nupkg.metadata +system.collections.immutable\1.5.0\.nupkg.metadata +system.collections.nongeneric\4.3.0\.nupkg.metadata +system.collections.specialized\4.3.0\.nupkg.metadata +system.collections\4.0.11\.nupkg.metadata +system.collections\4.3.0\.nupkg.metadata +system.componentmodel.annotations\4.5.0\.nupkg.metadata +system.componentmodel.primitives\4.3.0\.nupkg.metadata +system.componentmodel.typeconverter\4.3.0\.nupkg.metadata +system.componentmodel\4.3.0\.nupkg.metadata +system.composition.attributedmodel\1.0.31\.nupkg.metadata +system.composition.convention\1.0.31\.nupkg.metadata +system.composition.hosting\1.0.31\.nupkg.metadata +system.composition.runtime\1.0.31\.nupkg.metadata +system.composition.typedparts\1.0.31\.nupkg.metadata +system.composition\1.0.31\.nupkg.metadata +system.console\4.0.0\.nupkg.metadata +system.console\4.3.0\.nupkg.metadata +system.data.sqlclient\4.5.1\.nupkg.metadata +system.diagnostics.contracts\4.3.0\.nupkg.metadata +system.diagnostics.debug\4.0.11\.nupkg.metadata +system.diagnostics.debug\4.3.0\.nupkg.metadata +system.diagnostics.diagnosticsource\4.0.0\.nupkg.metadata +system.diagnostics.diagnosticsource\4.3.0\.nupkg.metadata +system.diagnostics.diagnosticsource\4.4.0\.nupkg.metadata +system.diagnostics.diagnosticsource\4.4.0\.signature.p7s +system.diagnostics.diagnosticsource\4.5.0\.nupkg.metadata +system.diagnostics.fileversioninfo\4.3.0\.nupkg.metadata +system.diagnostics.process\4.3.0\.nupkg.metadata +system.diagnostics.stacktrace\4.3.0\.nupkg.metadata +system.diagnostics.tools\4.0.1\.nupkg.metadata +system.diagnostics.tools\4.3.0\.nupkg.metadata +system.diagnostics.tracing\4.1.0\.nupkg.metadata +system.diagnostics.tracing\4.3.0\.nupkg.metadata +system.dynamic.runtime\4.0.11\.nupkg.metadata +system.dynamic.runtime\4.3.0\.nupkg.metadata +system.globalization.calendars\4.0.1\.nupkg.metadata +system.globalization.calendars\4.3.0\.nupkg.metadata +system.globalization.extensions\4.0.1\.nupkg.metadata +system.globalization.extensions\4.3.0\.nupkg.metadata +system.globalization\4.0.11\.nupkg.metadata +system.globalization\4.3.0\.nupkg.metadata +system.identitymodel.tokens.jwt\5.2.0\.nupkg.metadata +system.identitymodel.tokens.jwt\5.2.0\.signature.p7s +system.interactive.async\3.1.1\.nupkg.metadata +system.interactive.async\3.1.1\.signature.p7s +system.io.compression.zipfile\4.0.1\.nupkg.metadata +system.io.compression.zipfile\4.3.0\.nupkg.metadata +system.io.compression\4.1.0\.nupkg.metadata +system.io.compression\4.3.0\.nupkg.metadata +system.io.filesystem.primitives\4.0.1\.nupkg.metadata +system.io.filesystem.primitives\4.3.0\.nupkg.metadata +system.io.filesystem\4.0.1\.nupkg.metadata +system.io.filesystem\4.3.0\.nupkg.metadata +system.io.pipelines\4.5.0\.nupkg.metadata +system.io\4.1.0\.nupkg.metadata +system.io\4.3.0\.nupkg.metadata +system.linq.expressions\4.1.0\.nupkg.metadata +system.linq.expressions\4.3.0\.nupkg.metadata +system.linq.parallel\4.3.0\.nupkg.metadata +system.linq.queryable\4.0.1\.nupkg.metadata +system.linq\4.1.0\.nupkg.metadata +system.linq\4.3.0\.nupkg.metadata +system.memory\4.5.1\.nupkg.metadata +system.net.http\4.1.0\.nupkg.metadata +system.net.http\4.1.0\.signature.p7s +system.net.http\4.3.0\.nupkg.metadata +system.net.http\4.3.0\.signature.p7s +system.net.nameresolution\4.3.0\.nupkg.metadata +system.net.primitives\4.0.11\.nupkg.metadata +system.net.primitives\4.3.0\.nupkg.metadata +system.net.security\4.3.0\.nupkg.metadata +system.net.sockets\4.1.0\.nupkg.metadata +system.net.sockets\4.3.0\.nupkg.metadata +system.net.websockets.websocketprotocol\4.5.1\.nupkg.metadata +system.numerics.vectors\4.5.0\.nupkg.metadata +system.objectmodel\4.0.12\.nupkg.metadata +system.objectmodel\4.3.0\.nupkg.metadata +system.private.datacontractserialization\4.1.1\.nupkg.metadata +system.private.datacontractserialization\4.3.0\.nupkg.metadata +system.reflection.emit.ilgeneration\4.0.1\.nupkg.metadata +system.reflection.emit.ilgeneration\4.3.0\.nupkg.metadata +system.reflection.emit.lightweight\4.0.1\.nupkg.metadata +system.reflection.emit.lightweight\4.3.0\.nupkg.metadata +system.reflection.emit\4.0.1\.nupkg.metadata +system.reflection.emit\4.3.0\.nupkg.metadata +system.reflection.extensions\4.0.1\.nupkg.metadata +system.reflection.extensions\4.3.0\.nupkg.metadata +system.reflection.metadata\1.4.1\.nupkg.metadata +system.reflection.metadata\1.4.2\.nupkg.metadata +system.reflection.metadata\1.6.0\.nupkg.metadata +system.reflection.primitives\4.0.1\.nupkg.metadata +system.reflection.primitives\4.3.0\.nupkg.metadata +system.reflection.typeextensions\4.1.0\.nupkg.metadata +system.reflection.typeextensions\4.3.0\.nupkg.metadata +system.reflection\4.1.0\.nupkg.metadata +system.reflection\4.3.0\.nupkg.metadata +system.resources.resourcemanager\4.0.1\.nupkg.metadata +system.resources.resourcemanager\4.3.0\.nupkg.metadata +system.runtime.compilerservices.unsafe\4.5.0\.nupkg.metadata +system.runtime.compilerservices.unsafe\4.5.1\.nupkg.metadata +system.runtime.extensions\4.1.0\.nupkg.metadata +system.runtime.extensions\4.3.0\.nupkg.metadata +system.runtime.handles\4.0.1\.nupkg.metadata +system.runtime.handles\4.3.0\.nupkg.metadata +system.runtime.interopservices.runtimeinformation\4.0.0\.nupkg.metadata +system.runtime.interopservices.runtimeinformation\4.3.0\.nupkg.metadata +system.runtime.interopservices\4.1.0\.nupkg.metadata +system.runtime.interopservices\4.3.0\.nupkg.metadata +system.runtime.numerics\4.0.1\.nupkg.metadata +system.runtime.numerics\4.3.0\.nupkg.metadata +system.runtime.serialization.formatters\4.3.0\.nupkg.metadata +system.runtime.serialization.json\4.0.2\.nupkg.metadata +system.runtime.serialization.primitives\4.1.1\.nupkg.metadata +system.runtime.serialization.primitives\4.3.0\.nupkg.metadata +system.runtime.serialization.xml\4.3.0\.nupkg.metadata +system.runtime\4.1.0\.nupkg.metadata +system.runtime\4.3.0\.nupkg.metadata +system.security.accesscontrol\4.5.0\.nupkg.metadata +system.security.claims\4.3.0\.nupkg.metadata +system.security.cryptography.algorithms\4.2.0\.nupkg.metadata +system.security.cryptography.algorithms\4.3.0\.nupkg.metadata +system.security.cryptography.cng\4.2.0\.nupkg.metadata +system.security.cryptography.cng\4.3.0\.nupkg.metadata +system.security.cryptography.cng\4.5.0\.nupkg.metadata +system.security.cryptography.csp\4.0.0\.nupkg.metadata +system.security.cryptography.csp\4.3.0\.nupkg.metadata +system.security.cryptography.encoding\4.0.0\.nupkg.metadata +system.security.cryptography.encoding\4.3.0\.nupkg.metadata +system.security.cryptography.openssl\4.0.0\.nupkg.metadata +system.security.cryptography.openssl\4.3.0\.nupkg.metadata +system.security.cryptography.pkcs\4.5.0\.nupkg.metadata +system.security.cryptography.primitives\4.0.0\.nupkg.metadata +system.security.cryptography.primitives\4.3.0\.nupkg.metadata +system.security.cryptography.x509certificates\4.1.0\.nupkg.metadata +system.security.cryptography.x509certificates\4.3.0\.nupkg.metadata +system.security.cryptography.xml\4.5.0\.nupkg.metadata +system.security.permissions\4.5.0\.nupkg.metadata +system.security.principal.windows\4.3.0\.nupkg.metadata +system.security.principal.windows\4.5.0\.nupkg.metadata +system.security.principal\4.3.0\.nupkg.metadata +system.spatial\5.8.2\.nupkg.metadata +system.text.encoding.codepages\4.3.0\.nupkg.metadata +system.text.encoding.codepages\4.5.0\.nupkg.metadata +system.text.encoding.extensions\4.0.11\.nupkg.metadata +system.text.encoding.extensions\4.3.0\.nupkg.metadata +system.text.encoding\4.0.11\.nupkg.metadata +system.text.encoding\4.3.0\.nupkg.metadata +system.text.encodings.web\4.3.1\.nupkg.metadata +system.text.encodings.web\4.3.1\.signature.p7s +system.text.encodings.web\4.5.0\.nupkg.metadata +system.text.regularexpressions\4.1.0\.nupkg.metadata +system.text.regularexpressions\4.3.0\.nupkg.metadata +system.threading.channels\4.5.0\.nupkg.metadata +system.threading.tasks.extensions\4.0.0\.nupkg.metadata +system.threading.tasks.extensions\4.3.0\.nupkg.metadata +system.threading.tasks.extensions\4.5.1\.nupkg.metadata +system.threading.tasks.parallel\4.3.0\.nupkg.metadata +system.threading.tasks\4.0.11\.nupkg.metadata +system.threading.tasks\4.3.0\.nupkg.metadata +system.threading.thread\4.3.0\.nupkg.metadata +system.threading.threadpool\4.3.0\.nupkg.metadata +system.threading.timer\4.0.1\.nupkg.metadata +system.threading.timer\4.3.0\.nupkg.metadata +system.threading\4.0.11\.nupkg.metadata +system.threading\4.3.0\.nupkg.metadata +system.valuetuple\4.3.0\.nupkg.metadata +system.valuetuple\4.5.0\.nupkg.metadata +system.xml.readerwriter\4.0.11\.nupkg.metadata +system.xml.readerwriter\4.3.0\.nupkg.metadata +system.xml.xdocument\4.0.11\.nupkg.metadata +system.xml.xdocument\4.3.0\.nupkg.metadata +system.xml.xmldocument\4.0.1\.nupkg.metadata +system.xml.xmldocument\4.3.0\.nupkg.metadata +system.xml.xmlserializer\4.0.11\.nupkg.metadata +system.xml.xmlserializer\4.3.0\.nupkg.metadata +system.xml.xpath.xdocument\4.3.0\.nupkg.metadata +system.xml.xpath\4.3.0\.nupkg.metadata +windowsazure.storage\8.1.4\.nupkg.metadata +windowsazure.storage\8.1.4\.signature.p7s diff --git a/src/PackageArchive/Archive.CiServer.Patch/ArchiveBaseline.2.1.6.txt b/src/PackageArchive/Archive.CiServer.Patch/ArchiveBaseline.2.1.6.txt new file mode 100644 index 0000000000..ba2033ed81 --- /dev/null +++ b/src/PackageArchive/Archive.CiServer.Patch/ArchiveBaseline.2.1.6.txt @@ -0,0 +1,667 @@ +libuv\1.10.0\.nupkg.metadata +libuv\1.10.0\.signature.p7s +messagepack\1.7.3.4\.nupkg.metadata +messagepack\1.7.3.4\.signature.p7s +microsoft.applicationinsights.aspnetcore\2.1.1\.nupkg.metadata +microsoft.applicationinsights.aspnetcore\2.1.1\.signature.p7s +microsoft.applicationinsights.dependencycollector\2.4.1\.nupkg.metadata +microsoft.applicationinsights.dependencycollector\2.4.1\.signature.p7s +microsoft.applicationinsights\2.4.0\.nupkg.metadata +microsoft.applicationinsights\2.4.0\.signature.p7s +microsoft.aspnet.webapi.client\5.2.6\.nupkg.metadata +microsoft.aspnetcore.all\2.1.1\.nupkg.metadata +microsoft.aspnetcore.antiforgery\2.1.1\.nupkg.metadata +microsoft.aspnetcore.app\2.1.1\.nupkg.metadata +microsoft.aspnetcore.applicationinsights.hostingstartup\2.1.1\.nupkg.metadata +microsoft.aspnetcore.authentication.abstractions\2.1.1\.nupkg.metadata +microsoft.aspnetcore.authentication.azuread.ui\2.1.1\.nupkg.metadata +microsoft.aspnetcore.authentication.azureadb2c.ui\2.1.1\.nupkg.metadata +microsoft.aspnetcore.authentication.cookies\2.1.1\.nupkg.metadata +microsoft.aspnetcore.authentication.core\2.1.1\.nupkg.metadata +microsoft.aspnetcore.authentication.facebook\2.1.1\.nupkg.metadata +microsoft.aspnetcore.authentication.google\2.1.1\.nupkg.metadata +microsoft.aspnetcore.authentication.jwtbearer\2.1.1\.nupkg.metadata +microsoft.aspnetcore.authentication.microsoftaccount\2.1.1\.nupkg.metadata +microsoft.aspnetcore.authentication.oauth\2.1.1\.nupkg.metadata +microsoft.aspnetcore.authentication.openidconnect\2.1.1\.nupkg.metadata +microsoft.aspnetcore.authentication.twitter\2.1.1\.nupkg.metadata +microsoft.aspnetcore.authentication.wsfederation\2.1.1\.nupkg.metadata +microsoft.aspnetcore.authentication\2.1.1\.nupkg.metadata +microsoft.aspnetcore.authorization.policy\2.1.1\.nupkg.metadata +microsoft.aspnetcore.authorization\2.1.1\.nupkg.metadata +microsoft.aspnetcore.azureappservices.hostingstartup\2.1.1\.nupkg.metadata +microsoft.aspnetcore.azureappservicesintegration\2.1.1\.nupkg.metadata +microsoft.aspnetcore.connections.abstractions\2.1.1\.nupkg.metadata +microsoft.aspnetcore.cookiepolicy\2.1.1\.nupkg.metadata +microsoft.aspnetcore.cors\2.1.1\.nupkg.metadata +microsoft.aspnetcore.cryptography.internal\2.1.1\.nupkg.metadata +microsoft.aspnetcore.cryptography.keyderivation\2.1.1\.nupkg.metadata +microsoft.aspnetcore.dataprotection.abstractions\2.1.1\.nupkg.metadata +microsoft.aspnetcore.dataprotection.azurekeyvault\2.1.1\.nupkg.metadata +microsoft.aspnetcore.dataprotection.azurestorage\2.1.1\.nupkg.metadata +microsoft.aspnetcore.dataprotection.extensions\2.1.1\.nupkg.metadata +microsoft.aspnetcore.dataprotection\2.1.1\.nupkg.metadata +microsoft.aspnetcore.diagnostics.abstractions\2.1.1\.nupkg.metadata +microsoft.aspnetcore.diagnostics.entityframeworkcore\2.1.1\.nupkg.metadata +microsoft.aspnetcore.diagnostics\2.1.1\.nupkg.metadata +microsoft.aspnetcore.hostfiltering\2.1.1\.nupkg.metadata +microsoft.aspnetcore.hosting.abstractions\2.1.1\.nupkg.metadata +microsoft.aspnetcore.hosting.server.abstractions\2.1.1\.nupkg.metadata +microsoft.aspnetcore.hosting\2.1.1\.nupkg.metadata +microsoft.aspnetcore.html.abstractions\2.1.1\.nupkg.metadata +microsoft.aspnetcore.http.abstractions\2.1.1\.nupkg.metadata +microsoft.aspnetcore.http.connections.common\1.0.1\.nupkg.metadata +microsoft.aspnetcore.http.connections\1.0.1\.nupkg.metadata +microsoft.aspnetcore.http.extensions\2.1.1\.nupkg.metadata +microsoft.aspnetcore.http.features\2.1.1\.nupkg.metadata +microsoft.aspnetcore.http\2.1.1\.nupkg.metadata +microsoft.aspnetcore.httpoverrides\2.1.1\.nupkg.metadata +microsoft.aspnetcore.httpspolicy\2.1.1\.nupkg.metadata +microsoft.aspnetcore.identity.entityframeworkcore\2.1.1\.nupkg.metadata +microsoft.aspnetcore.identity.ui\2.1.1\.nupkg.metadata +microsoft.aspnetcore.identity\2.1.1\.nupkg.metadata +microsoft.aspnetcore.jsonpatch\2.1.1\.nupkg.metadata +microsoft.aspnetcore.localization.routing\2.1.1\.nupkg.metadata +microsoft.aspnetcore.localization\2.1.1\.nupkg.metadata +microsoft.aspnetcore.middlewareanalysis\2.1.1\.nupkg.metadata +microsoft.aspnetcore.mvc.abstractions\2.1.1\.nupkg.metadata +microsoft.aspnetcore.mvc.analyzers\2.1.1\.nupkg.metadata +microsoft.aspnetcore.mvc.apiexplorer\2.1.1\.nupkg.metadata +microsoft.aspnetcore.mvc.core\2.1.1\.nupkg.metadata +microsoft.aspnetcore.mvc.cors\2.1.1\.nupkg.metadata +microsoft.aspnetcore.mvc.dataannotations\2.1.1\.nupkg.metadata +microsoft.aspnetcore.mvc.formatters.json\2.1.1\.nupkg.metadata +microsoft.aspnetcore.mvc.formatters.xml\2.1.1\.nupkg.metadata +microsoft.aspnetcore.mvc.localization\2.1.1\.nupkg.metadata +microsoft.aspnetcore.mvc.razor.extensions\2.1.1\.nupkg.metadata +microsoft.aspnetcore.mvc.razor.viewcompilation\2.1.1\.nupkg.metadata +microsoft.aspnetcore.mvc.razor\2.1.1\.nupkg.metadata +microsoft.aspnetcore.mvc.razorpages\2.1.1\.nupkg.metadata +microsoft.aspnetcore.mvc.taghelpers\2.1.1\.nupkg.metadata +microsoft.aspnetcore.mvc.viewfeatures\2.1.1\.nupkg.metadata +microsoft.aspnetcore.mvc\2.1.1\.nupkg.metadata +microsoft.aspnetcore.nodeservices\2.1.1\.nupkg.metadata +microsoft.aspnetcore.owin\2.1.1\.nupkg.metadata +microsoft.aspnetcore.razor.design\2.1.2\.nupkg.metadata +microsoft.aspnetcore.razor.language\2.1.1\.nupkg.metadata +microsoft.aspnetcore.razor.runtime\2.1.1\.nupkg.metadata +microsoft.aspnetcore.razor\2.1.1\.nupkg.metadata +microsoft.aspnetcore.responsecaching.abstractions\2.1.1\.nupkg.metadata +microsoft.aspnetcore.responsecaching\2.1.1\.nupkg.metadata +microsoft.aspnetcore.responsecompression\2.1.1\.nupkg.metadata +microsoft.aspnetcore.rewrite\2.1.1\.nupkg.metadata +microsoft.aspnetcore.routing.abstractions\2.1.1\.nupkg.metadata +microsoft.aspnetcore.routing\2.1.1\.nupkg.metadata +microsoft.aspnetcore.server.httpsys\2.1.1\.nupkg.metadata +microsoft.aspnetcore.server.iisintegration\2.1.1\.nupkg.metadata +microsoft.aspnetcore.server.kestrel.core\2.1.1\.nupkg.metadata +microsoft.aspnetcore.server.kestrel.https\2.1.1\.nupkg.metadata +microsoft.aspnetcore.server.kestrel.transport.abstractions\2.1.1\.nupkg.metadata +microsoft.aspnetcore.server.kestrel.transport.libuv\2.1.1\.nupkg.metadata +microsoft.aspnetcore.server.kestrel.transport.sockets\2.1.1\.nupkg.metadata +microsoft.aspnetcore.server.kestrel\2.1.1\.nupkg.metadata +microsoft.aspnetcore.session\2.1.1\.nupkg.metadata +microsoft.aspnetcore.signalr.common\1.0.1\.nupkg.metadata +microsoft.aspnetcore.signalr.core\1.0.1\.nupkg.metadata +microsoft.aspnetcore.signalr.protocols.json\1.0.1\.nupkg.metadata +microsoft.aspnetcore.signalr.redis\1.0.1\.nupkg.metadata +microsoft.aspnetcore.signalr\1.0.1\.nupkg.metadata +microsoft.aspnetcore.spaservices.extensions\2.1.1\.nupkg.metadata +microsoft.aspnetcore.spaservices\2.1.1\.nupkg.metadata +microsoft.aspnetcore.staticfiles\2.1.1\.nupkg.metadata +microsoft.aspnetcore.websockets\2.1.1\.nupkg.metadata +microsoft.aspnetcore.webutilities\2.1.1\.nupkg.metadata +microsoft.aspnetcore\2.1.1\.nupkg.metadata +microsoft.azure.keyvault.webkey\2.0.7\.nupkg.metadata +microsoft.azure.keyvault.webkey\2.0.7\.signature.p7s +microsoft.azure.keyvault\2.3.2\.nupkg.metadata +microsoft.azure.keyvault\2.3.2\.signature.p7s +microsoft.azure.services.appauthentication\1.0.1\.nupkg.metadata +microsoft.azure.services.appauthentication\1.0.1\.signature.p7s +microsoft.codeanalysis.analyzers\1.1.0\.nupkg.metadata +microsoft.codeanalysis.common\2.8.0\.nupkg.metadata +microsoft.codeanalysis.csharp.workspaces\2.8.0\.nupkg.metadata +microsoft.codeanalysis.csharp\2.8.0\.nupkg.metadata +microsoft.codeanalysis.razor\2.1.1\.nupkg.metadata +microsoft.codeanalysis.workspaces.common\2.8.0\.nupkg.metadata +microsoft.csharp\4.0.1\.nupkg.metadata +microsoft.csharp\4.3.0\.nupkg.metadata +microsoft.csharp\4.5.0\.nupkg.metadata +microsoft.data.edm\5.8.2\.nupkg.metadata +microsoft.data.odata\5.8.2\.nupkg.metadata +microsoft.data.sqlite.core\2.1.0\.nupkg.metadata +microsoft.data.sqlite\2.1.0\.nupkg.metadata +microsoft.dotnet.platformabstractions\2.1.0\.nupkg.metadata +microsoft.entityframeworkcore.abstractions\2.1.1\.nupkg.metadata +microsoft.entityframeworkcore.analyzers\2.1.1\.nupkg.metadata +microsoft.entityframeworkcore.design\2.1.1\.nupkg.metadata +microsoft.entityframeworkcore.inmemory\2.1.1\.nupkg.metadata +microsoft.entityframeworkcore.relational\2.1.1\.nupkg.metadata +microsoft.entityframeworkcore.sqlite.core\2.1.1\.nupkg.metadata +microsoft.entityframeworkcore.sqlite\2.1.1\.nupkg.metadata +microsoft.entityframeworkcore.sqlserver\2.1.1\.nupkg.metadata +microsoft.entityframeworkcore.tools\2.1.1\.nupkg.metadata +microsoft.entityframeworkcore\2.1.1\.nupkg.metadata +microsoft.extensions.caching.abstractions\2.1.1\.nupkg.metadata +microsoft.extensions.caching.memory\2.1.1\.nupkg.metadata +microsoft.extensions.caching.redis\2.1.1\.nupkg.metadata +microsoft.extensions.caching.sqlserver\2.1.1\.nupkg.metadata +microsoft.extensions.configuration.abstractions\2.1.1\.nupkg.metadata +microsoft.extensions.configuration.azurekeyvault\2.1.1\.nupkg.metadata +microsoft.extensions.configuration.binder\2.1.1\.nupkg.metadata +microsoft.extensions.configuration.commandline\2.1.1\.nupkg.metadata +microsoft.extensions.configuration.environmentvariables\2.1.1\.nupkg.metadata +microsoft.extensions.configuration.fileextensions\2.1.1\.nupkg.metadata +microsoft.extensions.configuration.ini\2.1.1\.nupkg.metadata +microsoft.extensions.configuration.json\2.1.1\.nupkg.metadata +microsoft.extensions.configuration.keyperfile\2.1.1\.nupkg.metadata +microsoft.extensions.configuration.usersecrets\2.1.1\.nupkg.metadata +microsoft.extensions.configuration.xml\2.1.1\.nupkg.metadata +microsoft.extensions.configuration\2.1.1\.nupkg.metadata +microsoft.extensions.dependencyinjection.abstractions\2.1.1\.nupkg.metadata +microsoft.extensions.dependencyinjection\2.1.1\.nupkg.metadata +microsoft.extensions.dependencymodel\2.1.0\.nupkg.metadata +microsoft.extensions.diagnosticadapter\2.1.0\.nupkg.metadata +microsoft.extensions.fileproviders.abstractions\2.1.1\.nupkg.metadata +microsoft.extensions.fileproviders.composite\2.1.1\.nupkg.metadata +microsoft.extensions.fileproviders.embedded\2.1.1\.nupkg.metadata +microsoft.extensions.fileproviders.physical\2.1.1\.nupkg.metadata +microsoft.extensions.filesystemglobbing\2.1.1\.nupkg.metadata +microsoft.extensions.hosting.abstractions\2.1.1\.nupkg.metadata +microsoft.extensions.hosting\2.1.1\.nupkg.metadata +microsoft.extensions.http\2.1.1\.nupkg.metadata +microsoft.extensions.identity.core\2.1.1\.nupkg.metadata +microsoft.extensions.identity.stores\2.1.1\.nupkg.metadata +microsoft.extensions.localization.abstractions\2.1.1\.nupkg.metadata +microsoft.extensions.localization\2.1.1\.nupkg.metadata +microsoft.extensions.logging.abstractions\2.1.1\.nupkg.metadata +microsoft.extensions.logging.azureappservices\2.1.1\.nupkg.metadata +microsoft.extensions.logging.configuration\2.1.1\.nupkg.metadata +microsoft.extensions.logging.console\2.1.1\.nupkg.metadata +microsoft.extensions.logging.debug\2.1.1\.nupkg.metadata +microsoft.extensions.logging.eventsource\2.1.1\.nupkg.metadata +microsoft.extensions.logging.tracesource\2.1.1\.nupkg.metadata +microsoft.extensions.logging\2.1.1\.nupkg.metadata +microsoft.extensions.objectpool\2.1.1\.nupkg.metadata +microsoft.extensions.options.configurationextensions\2.1.1\.nupkg.metadata +microsoft.extensions.options\2.1.1\.nupkg.metadata +microsoft.extensions.platformabstractions\1.1.0\.nupkg.metadata +microsoft.extensions.primitives\2.1.1\.nupkg.metadata +microsoft.extensions.webencoders\2.1.1\.nupkg.metadata +microsoft.identitymodel.clients.activedirectory\3.14.2\.nupkg.metadata +microsoft.identitymodel.clients.activedirectory\3.14.2\.signature.p7s +microsoft.identitymodel.logging\5.2.0\.nupkg.metadata +microsoft.identitymodel.logging\5.2.0\.signature.p7s +microsoft.identitymodel.protocols.openidconnect\5.2.0\.nupkg.metadata +microsoft.identitymodel.protocols.openidconnect\5.2.0\.signature.p7s +microsoft.identitymodel.protocols.wsfederation\5.2.0\.nupkg.metadata +microsoft.identitymodel.protocols.wsfederation\5.2.0\.signature.p7s +microsoft.identitymodel.protocols\5.2.0\.nupkg.metadata +microsoft.identitymodel.protocols\5.2.0\.signature.p7s +microsoft.identitymodel.tokens.saml\5.2.0\.nupkg.metadata +microsoft.identitymodel.tokens.saml\5.2.0\.signature.p7s +microsoft.identitymodel.tokens\5.2.0\.nupkg.metadata +microsoft.identitymodel.tokens\5.2.0\.signature.p7s +microsoft.identitymodel.xml\5.2.0\.nupkg.metadata +microsoft.identitymodel.xml\5.2.0\.signature.p7s +microsoft.net.http.headers\2.1.1\.nupkg.metadata +microsoft.netcore.app\2.1.0\.nupkg.metadata +microsoft.netcore.dotnetapphost\2.1.0\.nupkg.metadata +microsoft.netcore.dotnethostpolicy\2.1.0\.nupkg.metadata +microsoft.netcore.dotnethostresolver\2.1.0\.nupkg.metadata +microsoft.netcore.platforms\1.0.1\.nupkg.metadata +microsoft.netcore.platforms\1.0.1\.signature.p7s +microsoft.netcore.platforms\1.0.2\.nupkg.metadata +microsoft.netcore.platforms\1.0.2\.signature.p7s +microsoft.netcore.platforms\1.1.0\.nupkg.metadata +microsoft.netcore.platforms\1.1.0\.signature.p7s +microsoft.netcore.platforms\2.0.0\.nupkg.metadata +microsoft.netcore.platforms\2.0.0\.signature.p7s +microsoft.netcore.platforms\2.1.0\.nupkg.metadata +microsoft.netcore.targets\1.0.1\.nupkg.metadata +microsoft.netcore.targets\1.1.0\.nupkg.metadata +microsoft.netcore.targets\2.1.0\.nupkg.metadata +microsoft.rest.clientruntime.azure\3.3.7\.nupkg.metadata +microsoft.rest.clientruntime.azure\3.3.7\.signature.p7s +microsoft.rest.clientruntime\2.3.8\.nupkg.metadata +microsoft.rest.clientruntime\2.3.8\.signature.p7s +microsoft.visualstudio.web.browserlink\2.1.1\.nupkg.metadata +microsoft.visualstudio.web.codegeneration.contracts\2.1.6\.nupkg.metadata +microsoft.visualstudio.web.codegeneration.contracts\2.1.6\.signature.p7s +microsoft.visualstudio.web.codegeneration.contracts\2.1.6\lib\netstandard2.0\Microsoft.VisualStudio.Web.CodeGeneration.Contracts.dll +microsoft.visualstudio.web.codegeneration.contracts\2.1.6\microsoft.visualstudio.web.codegeneration.contracts.2.1.6.nupkg.sha512 +microsoft.visualstudio.web.codegeneration.contracts\2.1.6\microsoft.visualstudio.web.codegeneration.contracts.nuspec +microsoft.visualstudio.web.codegeneration.core\2.1.6\.nupkg.metadata +microsoft.visualstudio.web.codegeneration.core\2.1.6\.signature.p7s +microsoft.visualstudio.web.codegeneration.core\2.1.6\lib\netstandard2.0\Microsoft.VisualStudio.Web.CodeGeneration.Core.dll +microsoft.visualstudio.web.codegeneration.core\2.1.6\microsoft.visualstudio.web.codegeneration.core.2.1.6.nupkg.sha512 +microsoft.visualstudio.web.codegeneration.core\2.1.6\microsoft.visualstudio.web.codegeneration.core.nuspec +microsoft.visualstudio.web.codegeneration.design\2.1.6\.nupkg.metadata +microsoft.visualstudio.web.codegeneration.design\2.1.6\.signature.p7s +microsoft.visualstudio.web.codegeneration.design\2.1.6\lib\net461\dotnet-aspnet-codegenerator-design.exe +microsoft.visualstudio.web.codegeneration.design\2.1.6\lib\netstandard2.0\dotnet-aspnet-codegenerator-design.dll +microsoft.visualstudio.web.codegeneration.design\2.1.6\microsoft.visualstudio.web.codegeneration.design.2.1.6.nupkg.sha512 +microsoft.visualstudio.web.codegeneration.design\2.1.6\microsoft.visualstudio.web.codegeneration.design.nuspec +microsoft.visualstudio.web.codegeneration.design\2.1.6\runtimes\win7-x64\lib\net461\dotnet-aspnet-codegenerator-design.exe +microsoft.visualstudio.web.codegeneration.design\2.1.6\runtimes\win7-x86\lib\net461\dotnet-aspnet-codegenerator-design.exe +microsoft.visualstudio.web.codegeneration.design\2.1.6\runtimes\win-arm\lib\net461\dotnet-aspnet-codegenerator-design.exe +microsoft.visualstudio.web.codegeneration.design\2.1.6\runtimes\win-arm64\lib\net461\dotnet-aspnet-codegenerator-design.exe +microsoft.visualstudio.web.codegeneration.entityframeworkcore\2.1.6\.nupkg.metadata +microsoft.visualstudio.web.codegeneration.entityframeworkcore\2.1.6\.signature.p7s +microsoft.visualstudio.web.codegeneration.entityframeworkcore\2.1.6\lib\netstandard2.0\Microsoft.VisualStudio.Web.CodeGeneration.EntityFrameworkCore.dll +microsoft.visualstudio.web.codegeneration.entityframeworkcore\2.1.6\microsoft.visualstudio.web.codegeneration.entityframeworkcore.2.1.6.nupkg.sha512 +microsoft.visualstudio.web.codegeneration.entityframeworkcore\2.1.6\microsoft.visualstudio.web.codegeneration.entityframeworkcore.nuspec +microsoft.visualstudio.web.codegeneration.entityframeworkcore\2.1.6\Templates\DbContext\NewLocalDbContext.cshtml +microsoft.visualstudio.web.codegeneration.templating\2.1.6\.nupkg.metadata +microsoft.visualstudio.web.codegeneration.templating\2.1.6\.signature.p7s +microsoft.visualstudio.web.codegeneration.templating\2.1.6\lib\netstandard2.0\Microsoft.VisualStudio.Web.CodeGeneration.Templating.dll +microsoft.visualstudio.web.codegeneration.templating\2.1.6\microsoft.visualstudio.web.codegeneration.templating.2.1.6.nupkg.sha512 +microsoft.visualstudio.web.codegeneration.templating\2.1.6\microsoft.visualstudio.web.codegeneration.templating.nuspec +microsoft.visualstudio.web.codegeneration.utils\2.1.6\.nupkg.metadata +microsoft.visualstudio.web.codegeneration.utils\2.1.6\.signature.p7s +microsoft.visualstudio.web.codegeneration.utils\2.1.6\lib\netstandard2.0\Microsoft.VisualStudio.Web.CodeGeneration.Utils.dll +microsoft.visualstudio.web.codegeneration.utils\2.1.6\microsoft.visualstudio.web.codegeneration.utils.2.1.6.nupkg.sha512 +microsoft.visualstudio.web.codegeneration.utils\2.1.6\microsoft.visualstudio.web.codegeneration.utils.nuspec +microsoft.visualstudio.web.codegeneration\2.1.6\.nupkg.metadata +microsoft.visualstudio.web.codegeneration\2.1.6\.signature.p7s +microsoft.visualstudio.web.codegeneration\2.1.6\lib\netstandard2.0\Microsoft.VisualStudio.Web.CodeGeneration.dll +microsoft.visualstudio.web.codegeneration\2.1.6\microsoft.visualstudio.web.codegeneration.2.1.6.nupkg.sha512 +microsoft.visualstudio.web.codegeneration\2.1.6\microsoft.visualstudio.web.codegeneration.nuspec +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\.nupkg.metadata +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\.signature.p7s +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Generators\ParameterDefinitions\area.json +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Generators\ParameterDefinitions\controller.json +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Generators\ParameterDefinitions\identity.json +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Generators\ParameterDefinitions\razorpage.json +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Generators\ParameterDefinitions\view.json +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\lib\netstandard2.0\identitygeneratorfilesconfig.json +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\lib\netstandard2.0\Microsoft.VisualStudio.Web.CodeGenerators.Mvc.dll +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\microsoft.visualstudio.web.codegenerators.mvc.2.1.6.nupkg.sha512 +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\microsoft.visualstudio.web.codegenerators.mvc.nuspec +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\ControllerGenerator\ApiControllerWithActions.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\ControllerGenerator\ApiControllerWithContext.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\ControllerGenerator\ApiEmptyController.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\ControllerGenerator\ControllerWithActions.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\ControllerGenerator\EmptyController.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\ControllerGenerator\MvcControllerWithContext.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\_LoginPartial.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Data\ApplicationDbContext.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Data\ApplicationUser.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\IdentityHostingStartup.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\_Layout.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\_ValidationScriptsPartial.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\_ViewImports.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\_ViewStart.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Account._ViewImports.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Account.AccessDenied.cs.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Account.AccessDenied.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Account.ConfirmEmail.cs.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Account.ConfirmEmail.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Account.ExternalLogin.cs.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Account.ExternalLogin.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Account.ForgotPassword.cs.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Account.ForgotPassword.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Account.ForgotPasswordConfirmation.cs.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Account.ForgotPasswordConfirmation.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Account.Lockout.cs.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Account.Lockout.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Account.Login.cs.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Account.Login.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Account.LoginWith2fa.cs.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Account.LoginWith2fa.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Account.LoginWithRecoveryCode.cs.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Account.LoginWithRecoveryCode.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Account.Logout.cs.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Account.Logout.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Account.Register.cs.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Account.Register.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Account.ResetPassword.cs.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Account.ResetPassword.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Account.ResetPasswordConfirmation.cs.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Account.ResetPasswordConfirmation.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Manage\Account.Manage._Layout.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Manage\Account.Manage._ManageNav.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Manage\Account.Manage._StatusMessage.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Manage\Account.Manage._ViewImports.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Manage\Account.Manage.ChangePassword.cs.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Manage\Account.Manage.ChangePassword.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Manage\Account.Manage.DeletePersonalData.cs.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Manage\Account.Manage.DeletePersonalData.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Manage\Account.Manage.Disable2fa.cs.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Manage\Account.Manage.Disable2fa.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Manage\Account.Manage.DownloadPersonalData.cs.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Manage\Account.Manage.DownloadPersonalData.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Manage\Account.Manage.EnableAuthenticator.cs.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Manage\Account.Manage.EnableAuthenticator.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Manage\Account.Manage.ExternalLogins.cs.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Manage\Account.Manage.ExternalLogins.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Manage\Account.Manage.GenerateRecoveryCodes.cs.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Manage\Account.Manage.GenerateRecoveryCodes.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Manage\Account.Manage.Index.cs.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Manage\Account.Manage.Index.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Manage\Account.Manage.ManageNavPages.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Manage\Account.Manage.PersonalData.cs.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Manage\Account.Manage.PersonalData.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Manage\Account.Manage.ResetAuthenticator.cs.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Manage\Account.Manage.ResetAuthenticator.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Manage\Account.Manage.SetPassword.cs.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Manage\Account.Manage.SetPassword.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Manage\Account.Manage.ShowRecoveryCodes.cs.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Manage\Account.Manage.ShowRecoveryCodes.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Manage\Account.Manage.TwoFactorAuthentication.cs.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Account\Manage\Account.Manage.TwoFactorAuthentication.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Error.cs.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\Pages\Error.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\ScaffoldingReadme.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\SupportPages._CookieConsentPartial.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\SupportPages._ViewImports.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\SupportPages._ViewStart.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\css\site.css +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\css\site.min.css +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\favicon.ico +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\images\banner1.svg +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\images\banner2.svg +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\images\banner3.svg +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\js\site.js +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\js\site.min.js +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\lib\bootstrap\.bower.json +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\lib\bootstrap\dist\css\bootstrap.css +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\lib\bootstrap\dist\css\bootstrap.css.map +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\lib\bootstrap\dist\css\bootstrap.min.css +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\lib\bootstrap\dist\css\bootstrap.min.css.map +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\lib\bootstrap\dist\css\bootstrap-theme.css +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\lib\bootstrap\dist\css\bootstrap-theme.css.map +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\lib\bootstrap\dist\css\bootstrap-theme.min.css +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\lib\bootstrap\dist\css\bootstrap-theme.min.css.map +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\lib\bootstrap\dist\fonts\glyphicons-halflings-regular.eot +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\lib\bootstrap\dist\fonts\glyphicons-halflings-regular.svg +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\lib\bootstrap\dist\fonts\glyphicons-halflings-regular.ttf +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\lib\bootstrap\dist\fonts\glyphicons-halflings-regular.woff +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\lib\bootstrap\dist\fonts\glyphicons-halflings-regular.woff2 +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\lib\bootstrap\dist\js\bootstrap.js +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\lib\bootstrap\dist\js\bootstrap.min.js +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\lib\bootstrap\dist\js\npm.js +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\lib\bootstrap\LICENSE +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\lib\jquery\.bower.json +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\lib\jquery\dist\jquery.js +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\lib\jquery\dist\jquery.min.js +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\lib\jquery\dist\jquery.min.map +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\lib\jquery\LICENSE.txt +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\lib\jquery-validation\.bower.json +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\lib\jquery-validation\dist\additional-methods.js +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\lib\jquery-validation\dist\additional-methods.min.js +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\lib\jquery-validation\dist\jquery.validate.js +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\lib\jquery-validation\dist\jquery.validate.min.js +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\lib\jquery-validation\LICENSE.md +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\lib\jquery-validation-unobtrusive\.bower.json +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\lib\jquery-validation-unobtrusive\jquery.validate.unobtrusive.js +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\lib\jquery-validation-unobtrusive\jquery.validate.unobtrusive.min.js +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Identity\wwwroot\lib\jquery-validation-unobtrusive\LICENSE.txt +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\MvcLayout\_Layout.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\MvcLayout\Error.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\RazorPageGenerator\_ValidationScriptsPartial.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\RazorPageGenerator\Create.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\RazorPageGenerator\CreatePageModel.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\RazorPageGenerator\Delete.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\RazorPageGenerator\DeletePageModel.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\RazorPageGenerator\Details.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\RazorPageGenerator\DetailsPageModel.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\RazorPageGenerator\Edit.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\RazorPageGenerator\EditPageModel.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\RazorPageGenerator\Empty.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\RazorPageGenerator\EmptyPageModel.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\RazorPageGenerator\List.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\RazorPageGenerator\ListPageModel.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Startup\ReadMe.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\Startup\Startup.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\ViewGenerator\_ValidationScriptsPartial.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\ViewGenerator\Create.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\ViewGenerator\Delete.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\ViewGenerator\Details.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\ViewGenerator\Edit.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\ViewGenerator\Empty.cshtml +microsoft.visualstudio.web.codegenerators.mvc\2.1.6\Templates\ViewGenerator\List.cshtml +microsoft.win32.primitives\4.0.1\.nupkg.metadata +microsoft.win32.primitives\4.3.0\.nupkg.metadata +microsoft.win32.registry\4.3.0\.nupkg.metadata +microsoft.win32.registry\4.5.0\.nupkg.metadata +netstandard.library\1.6.0\.nupkg.metadata +netstandard.library\1.6.1\.nupkg.metadata +netstandard.library\2.0.3\.nupkg.metadata +newtonsoft.json.bson\1.0.1\.nupkg.metadata +newtonsoft.json.bson\1.0.1\.signature.p7s +newtonsoft.json\10.0.1\.nupkg.metadata +newtonsoft.json\10.0.1\.signature.p7s +newtonsoft.json\11.0.2\.nupkg.metadata +newtonsoft.json\11.0.2\.signature.p7s +newtonsoft.json\9.0.1\.nupkg.metadata +nuget.frameworks\4.7.0\.nupkg.metadata +remotion.linq\2.2.0\.nupkg.metadata +remotion.linq\2.2.0\.signature.p7s +runtime.debian.8-x64.runtime.native.system.security.cryptography.openssl\4.3.0\.nupkg.metadata +runtime.fedora.23-x64.runtime.native.system.security.cryptography.openssl\4.3.0\.nupkg.metadata +runtime.fedora.24-x64.runtime.native.system.security.cryptography.openssl\4.3.0\.nupkg.metadata +runtime.native.system.data.sqlclient.sni\4.4.0\.nupkg.metadata +runtime.native.system.io.compression\4.1.0\.nupkg.metadata +runtime.native.system.io.compression\4.3.0\.nupkg.metadata +runtime.native.system.net.http\4.0.1\.nupkg.metadata +runtime.native.system.net.http\4.3.0\.nupkg.metadata +runtime.native.system.net.security\4.3.0\.nupkg.metadata +runtime.native.system.security.cryptography.apple\4.3.0\.nupkg.metadata +runtime.native.system.security.cryptography.openssl\4.3.0\.nupkg.metadata +runtime.native.system.security.cryptography\4.0.0\.nupkg.metadata +runtime.native.system\4.0.0\.nupkg.metadata +runtime.native.system\4.3.0\.nupkg.metadata +runtime.opensuse.13.2-x64.runtime.native.system.security.cryptography.openssl\4.3.0\.nupkg.metadata +runtime.opensuse.42.1-x64.runtime.native.system.security.cryptography.openssl\4.3.0\.nupkg.metadata +runtime.osx.10.10-x64.runtime.native.system.security.cryptography.apple\4.3.0\.nupkg.metadata +runtime.osx.10.10-x64.runtime.native.system.security.cryptography.openssl\4.3.0\.nupkg.metadata +runtime.rhel.7-x64.runtime.native.system.security.cryptography.openssl\4.3.0\.nupkg.metadata +runtime.ubuntu.14.04-x64.runtime.native.system.security.cryptography.openssl\4.3.0\.nupkg.metadata +runtime.ubuntu.16.04-x64.runtime.native.system.security.cryptography.openssl\4.3.0\.nupkg.metadata +runtime.ubuntu.16.10-x64.runtime.native.system.security.cryptography.openssl\4.3.0\.nupkg.metadata +runtime.win-arm64.runtime.native.system.data.sqlclient.sni\4.4.0\.nupkg.metadata +runtime.win-x64.runtime.native.system.data.sqlclient.sni\4.4.0\.nupkg.metadata +runtime.win-x86.runtime.native.system.data.sqlclient.sni\4.4.0\.nupkg.metadata +sqlitepclraw.bundle_green\1.1.11\.nupkg.metadata +sqlitepclraw.bundle_green\1.1.11\.signature.p7s +sqlitepclraw.core\1.1.11\.nupkg.metadata +sqlitepclraw.core\1.1.11\.signature.p7s +sqlitepclraw.lib.e_sqlite3.linux\1.1.11\.nupkg.metadata +sqlitepclraw.lib.e_sqlite3.linux\1.1.11\.signature.p7s +sqlitepclraw.lib.e_sqlite3.osx\1.1.11\.nupkg.metadata +sqlitepclraw.lib.e_sqlite3.osx\1.1.11\.signature.p7s +sqlitepclraw.lib.e_sqlite3.v110_xp\1.1.11\.nupkg.metadata +sqlitepclraw.lib.e_sqlite3.v110_xp\1.1.11\.signature.p7s +sqlitepclraw.provider.e_sqlite3.netstandard11\1.1.11\.nupkg.metadata +sqlitepclraw.provider.e_sqlite3.netstandard11\1.1.11\.signature.p7s +stackexchange.redis.strongname\1.2.4\.nupkg.metadata +stackexchange.redis.strongname\1.2.4\.signature.p7s +system.appcontext\4.1.0\.nupkg.metadata +system.appcontext\4.3.0\.nupkg.metadata +system.buffers\4.0.0\.nupkg.metadata +system.buffers\4.3.0\.nupkg.metadata +system.buffers\4.5.0\.nupkg.metadata +system.collections.concurrent\4.0.12\.nupkg.metadata +system.collections.concurrent\4.3.0\.nupkg.metadata +system.collections.immutable\1.3.0\.nupkg.metadata +system.collections.immutable\1.3.1\.nupkg.metadata +system.collections.immutable\1.5.0\.nupkg.metadata +system.collections.nongeneric\4.3.0\.nupkg.metadata +system.collections.specialized\4.3.0\.nupkg.metadata +system.collections\4.0.11\.nupkg.metadata +system.collections\4.3.0\.nupkg.metadata +system.componentmodel.annotations\4.5.0\.nupkg.metadata +system.componentmodel.primitives\4.3.0\.nupkg.metadata +system.componentmodel.typeconverter\4.3.0\.nupkg.metadata +system.componentmodel\4.3.0\.nupkg.metadata +system.composition.attributedmodel\1.0.31\.nupkg.metadata +system.composition.convention\1.0.31\.nupkg.metadata +system.composition.hosting\1.0.31\.nupkg.metadata +system.composition.runtime\1.0.31\.nupkg.metadata +system.composition.typedparts\1.0.31\.nupkg.metadata +system.composition\1.0.31\.nupkg.metadata +system.console\4.0.0\.nupkg.metadata +system.console\4.3.0\.nupkg.metadata +system.data.sqlclient\4.5.1\.nupkg.metadata +system.diagnostics.contracts\4.3.0\.nupkg.metadata +system.diagnostics.debug\4.0.11\.nupkg.metadata +system.diagnostics.debug\4.3.0\.nupkg.metadata +system.diagnostics.diagnosticsource\4.0.0\.nupkg.metadata +system.diagnostics.diagnosticsource\4.3.0\.nupkg.metadata +system.diagnostics.diagnosticsource\4.4.0\.nupkg.metadata +system.diagnostics.diagnosticsource\4.4.0\.signature.p7s +system.diagnostics.diagnosticsource\4.5.0\.nupkg.metadata +system.diagnostics.fileversioninfo\4.3.0\.nupkg.metadata +system.diagnostics.process\4.3.0\.nupkg.metadata +system.diagnostics.stacktrace\4.3.0\.nupkg.metadata +system.diagnostics.tools\4.0.1\.nupkg.metadata +system.diagnostics.tools\4.3.0\.nupkg.metadata +system.diagnostics.tracing\4.1.0\.nupkg.metadata +system.diagnostics.tracing\4.3.0\.nupkg.metadata +system.dynamic.runtime\4.0.11\.nupkg.metadata +system.dynamic.runtime\4.3.0\.nupkg.metadata +system.globalization.calendars\4.0.1\.nupkg.metadata +system.globalization.calendars\4.3.0\.nupkg.metadata +system.globalization.extensions\4.0.1\.nupkg.metadata +system.globalization.extensions\4.3.0\.nupkg.metadata +system.globalization\4.0.11\.nupkg.metadata +system.globalization\4.3.0\.nupkg.metadata +system.identitymodel.tokens.jwt\5.2.0\.nupkg.metadata +system.identitymodel.tokens.jwt\5.2.0\.signature.p7s +system.interactive.async\3.1.1\.nupkg.metadata +system.interactive.async\3.1.1\.signature.p7s +system.io.compression.zipfile\4.0.1\.nupkg.metadata +system.io.compression.zipfile\4.3.0\.nupkg.metadata +system.io.compression\4.1.0\.nupkg.metadata +system.io.compression\4.3.0\.nupkg.metadata +system.io.filesystem.primitives\4.0.1\.nupkg.metadata +system.io.filesystem.primitives\4.3.0\.nupkg.metadata +system.io.filesystem\4.0.1\.nupkg.metadata +system.io.filesystem\4.3.0\.nupkg.metadata +system.io.pipelines\4.5.0\.nupkg.metadata +system.io\4.1.0\.nupkg.metadata +system.io\4.3.0\.nupkg.metadata +system.linq.expressions\4.1.0\.nupkg.metadata +system.linq.expressions\4.3.0\.nupkg.metadata +system.linq.parallel\4.3.0\.nupkg.metadata +system.linq.queryable\4.0.1\.nupkg.metadata +system.linq\4.1.0\.nupkg.metadata +system.linq\4.3.0\.nupkg.metadata +system.memory\4.5.1\.nupkg.metadata +system.net.http\4.1.0\.nupkg.metadata +system.net.http\4.1.0\.signature.p7s +system.net.http\4.3.0\.nupkg.metadata +system.net.http\4.3.0\.signature.p7s +system.net.nameresolution\4.3.0\.nupkg.metadata +system.net.primitives\4.0.11\.nupkg.metadata +system.net.primitives\4.3.0\.nupkg.metadata +system.net.security\4.3.0\.nupkg.metadata +system.net.sockets\4.1.0\.nupkg.metadata +system.net.sockets\4.3.0\.nupkg.metadata +system.net.websockets.websocketprotocol\4.5.1\.nupkg.metadata +system.numerics.vectors\4.5.0\.nupkg.metadata +system.objectmodel\4.0.12\.nupkg.metadata +system.objectmodel\4.3.0\.nupkg.metadata +system.private.datacontractserialization\4.1.1\.nupkg.metadata +system.private.datacontractserialization\4.3.0\.nupkg.metadata +system.reflection.emit.ilgeneration\4.0.1\.nupkg.metadata +system.reflection.emit.ilgeneration\4.3.0\.nupkg.metadata +system.reflection.emit.lightweight\4.0.1\.nupkg.metadata +system.reflection.emit.lightweight\4.3.0\.nupkg.metadata +system.reflection.emit\4.0.1\.nupkg.metadata +system.reflection.emit\4.3.0\.nupkg.metadata +system.reflection.extensions\4.0.1\.nupkg.metadata +system.reflection.extensions\4.3.0\.nupkg.metadata +system.reflection.metadata\1.4.1\.nupkg.metadata +system.reflection.metadata\1.4.2\.nupkg.metadata +system.reflection.metadata\1.6.0\.nupkg.metadata +system.reflection.primitives\4.0.1\.nupkg.metadata +system.reflection.primitives\4.3.0\.nupkg.metadata +system.reflection.typeextensions\4.1.0\.nupkg.metadata +system.reflection.typeextensions\4.3.0\.nupkg.metadata +system.reflection\4.1.0\.nupkg.metadata +system.reflection\4.3.0\.nupkg.metadata +system.resources.resourcemanager\4.0.1\.nupkg.metadata +system.resources.resourcemanager\4.3.0\.nupkg.metadata +system.runtime.compilerservices.unsafe\4.5.0\.nupkg.metadata +system.runtime.compilerservices.unsafe\4.5.1\.nupkg.metadata +system.runtime.extensions\4.1.0\.nupkg.metadata +system.runtime.extensions\4.3.0\.nupkg.metadata +system.runtime.handles\4.0.1\.nupkg.metadata +system.runtime.handles\4.3.0\.nupkg.metadata +system.runtime.interopservices.runtimeinformation\4.0.0\.nupkg.metadata +system.runtime.interopservices.runtimeinformation\4.3.0\.nupkg.metadata +system.runtime.interopservices\4.1.0\.nupkg.metadata +system.runtime.interopservices\4.3.0\.nupkg.metadata +system.runtime.numerics\4.0.1\.nupkg.metadata +system.runtime.numerics\4.3.0\.nupkg.metadata +system.runtime.serialization.formatters\4.3.0\.nupkg.metadata +system.runtime.serialization.json\4.0.2\.nupkg.metadata +system.runtime.serialization.primitives\4.1.1\.nupkg.metadata +system.runtime.serialization.primitives\4.3.0\.nupkg.metadata +system.runtime.serialization.xml\4.3.0\.nupkg.metadata +system.runtime\4.1.0\.nupkg.metadata +system.runtime\4.3.0\.nupkg.metadata +system.security.accesscontrol\4.5.0\.nupkg.metadata +system.security.claims\4.3.0\.nupkg.metadata +system.security.cryptography.algorithms\4.2.0\.nupkg.metadata +system.security.cryptography.algorithms\4.3.0\.nupkg.metadata +system.security.cryptography.cng\4.2.0\.nupkg.metadata +system.security.cryptography.cng\4.3.0\.nupkg.metadata +system.security.cryptography.cng\4.5.0\.nupkg.metadata +system.security.cryptography.csp\4.0.0\.nupkg.metadata +system.security.cryptography.csp\4.3.0\.nupkg.metadata +system.security.cryptography.encoding\4.0.0\.nupkg.metadata +system.security.cryptography.encoding\4.3.0\.nupkg.metadata +system.security.cryptography.openssl\4.0.0\.nupkg.metadata +system.security.cryptography.openssl\4.3.0\.nupkg.metadata +system.security.cryptography.pkcs\4.5.0\.nupkg.metadata +system.security.cryptography.primitives\4.0.0\.nupkg.metadata +system.security.cryptography.primitives\4.3.0\.nupkg.metadata +system.security.cryptography.x509certificates\4.1.0\.nupkg.metadata +system.security.cryptography.x509certificates\4.3.0\.nupkg.metadata +system.security.cryptography.xml\4.5.0\.nupkg.metadata +system.security.permissions\4.5.0\.nupkg.metadata +system.security.principal.windows\4.3.0\.nupkg.metadata +system.security.principal.windows\4.5.0\.nupkg.metadata +system.security.principal\4.3.0\.nupkg.metadata +system.spatial\5.8.2\.nupkg.metadata +system.text.encoding.codepages\4.3.0\.nupkg.metadata +system.text.encoding.codepages\4.5.0\.nupkg.metadata +system.text.encoding.extensions\4.0.11\.nupkg.metadata +system.text.encoding.extensions\4.3.0\.nupkg.metadata +system.text.encoding\4.0.11\.nupkg.metadata +system.text.encoding\4.3.0\.nupkg.metadata +system.text.encodings.web\4.3.1\.nupkg.metadata +system.text.encodings.web\4.3.1\.signature.p7s +system.text.encodings.web\4.5.0\.nupkg.metadata +system.text.regularexpressions\4.1.0\.nupkg.metadata +system.text.regularexpressions\4.3.0\.nupkg.metadata +system.threading.channels\4.5.0\.nupkg.metadata +system.threading.tasks.extensions\4.0.0\.nupkg.metadata +system.threading.tasks.extensions\4.3.0\.nupkg.metadata +system.threading.tasks.extensions\4.5.1\.nupkg.metadata +system.threading.tasks.parallel\4.3.0\.nupkg.metadata +system.threading.tasks\4.0.11\.nupkg.metadata +system.threading.tasks\4.3.0\.nupkg.metadata +system.threading.thread\4.3.0\.nupkg.metadata +system.threading.threadpool\4.3.0\.nupkg.metadata +system.threading.timer\4.0.1\.nupkg.metadata +system.threading.timer\4.3.0\.nupkg.metadata +system.threading\4.0.11\.nupkg.metadata +system.threading\4.3.0\.nupkg.metadata +system.valuetuple\4.3.0\.nupkg.metadata +system.valuetuple\4.5.0\.nupkg.metadata +system.xml.readerwriter\4.0.11\.nupkg.metadata +system.xml.readerwriter\4.3.0\.nupkg.metadata +system.xml.xdocument\4.0.11\.nupkg.metadata +system.xml.xdocument\4.3.0\.nupkg.metadata +system.xml.xmldocument\4.0.1\.nupkg.metadata +system.xml.xmldocument\4.3.0\.nupkg.metadata +system.xml.xmlserializer\4.0.11\.nupkg.metadata +system.xml.xmlserializer\4.3.0\.nupkg.metadata +system.xml.xpath.xdocument\4.3.0\.nupkg.metadata +system.xml.xpath\4.3.0\.nupkg.metadata +windowsazure.storage\8.1.4\.nupkg.metadata +windowsazure.storage\8.1.4\.signature.p7s diff --git a/src/SiteExtensions/build/dependencies.props b/src/SiteExtensions/build/dependencies.props index e20e907408..721f8428ed 100644 --- a/src/SiteExtensions/build/dependencies.props +++ b/src/SiteExtensions/build/dependencies.props @@ -4,7 +4,7 @@ - 2.2.0-preview2-20181004.6 + 2.2.1-build-20181114.3 2.1.1-rtm-31076 2.2.0-rtm-35515 15.6.1 diff --git a/src/WebSockets/.gitignore b/src/WebSockets/.gitignore deleted file mode 100644 index 0e10f4aa54..0000000000 --- a/src/WebSockets/.gitignore +++ /dev/null @@ -1,31 +0,0 @@ -[Oo]bj/ -[Bb]in/ -TestResults/ -.nuget/ -_ReSharper.*/ -packages/ -artifacts/ -PublishProfiles/ -*.user -*.suo -*.cache -*.docstates -_ReSharper.* -nuget.exe -*net45.csproj -*k10.csproj -*.psess -*.vsp -*.pidb -*.userprefs -*DS_Store -*.ncrunchsolution -*.*sdf -*.ipch -*.sln.ide -/.vs/ -.testPublish/ -.build/ -autobahnreports/ -.vscode/ -global.json diff --git a/src/WebSockets/Directory.Build.props b/src/WebSockets/Directory.Build.props deleted file mode 100644 index 97bc856e11..0000000000 --- a/src/WebSockets/Directory.Build.props +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/src/WebSockets/NuGetPackageVerifier.json b/src/WebSockets/NuGetPackageVerifier.json deleted file mode 100644 index b153ab1515..0000000000 --- a/src/WebSockets/NuGetPackageVerifier.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "Default": { - "rules": [ - "DefaultCompositeRule" - ] - } -} \ No newline at end of file diff --git a/src/WebSockets/README.md b/src/WebSockets/README.md deleted file mode 100644 index c7c69ee89b..0000000000 --- a/src/WebSockets/README.md +++ /dev/null @@ -1,19 +0,0 @@ -WebSockets -================ - -AppVeyor: [![AppVeyor](https://ci.appveyor.com/api/projects/status/lk5hyg6gki03hdqe/branch/dev?svg=true)](https://ci.appveyor.com/project/aspnetci/WebSockets/branch/dev) - -Travis: [![Travis](https://travis-ci.org/aspnet/WebSockets.svg?branch=dev)](https://travis-ci.org/aspnet/WebSockets) - -Contains a managed implementation of the WebSocket protocol, along with server integration components. - -This project is part of ASP.NET Core. You can find samples, documentation and getting started instructions for ASP.NET Core at the [Home](https://github.com/aspnet/home) repo. - - -## System Requirements - -This repo has a few special system requirements/prerequisites. - -1. Windows IIS Express tests require IIS Express 10 and Windows 8 for WebSockets support -2. HttpListener/ASP.NET 4.6 samples require at least Windows 8 -3. Autobahn Test Suite requires special installation see the README.md in test/AutobahnTestApp diff --git a/src/WebSockets/WebSockets.sln b/src/WebSockets/WebSockets.sln deleted file mode 100644 index 6977d2812e..0000000000 --- a/src/WebSockets/WebSockets.sln +++ /dev/null @@ -1,145 +0,0 @@ -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.26730.10 -MinimumVisualStudioVersion = 15.0.26730.03 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{2C7947A5-9FBD-4267-97C1-2D726D7B3BAF}" - ProjectSection(SolutionItems) = preProject - src\Directory.Build.props = src\Directory.Build.props - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{C45106D0-76C8-4776-A140-F7DD83CA2958}" - ProjectSection(SolutionItems) = preProject - test\Directory.Build.props = test\Directory.Build.props - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{9E55FC5B-FD9C-4266-AB24-F3AA649D7C8B}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestServer", "samples\TestServer\TestServer.csproj", "{4E5F5FCC-172C-44D9-BEA0-39098A79CD0B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "scripts", "scripts", "{19595D64-E42E-46FD-AB2E-BDC870724EE7}" - ProjectSection(SolutionItems) = preProject - scripts\UpdateCoreFxCode.ps1 = scripts\UpdateCoreFxCode.ps1 - EndProjectSection -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.WebSockets", "src\Microsoft.AspNetCore.WebSockets\Microsoft.AspNetCore.WebSockets.csproj", "{CDE16880-0374-46FA-8896-99F1B90B4B6F}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.WebSockets.Test", "test\Microsoft.AspNetCore.WebSockets.Test\Microsoft.AspNetCore.WebSockets.Test.csproj", "{5AFA74F5-9B1D-4FC5-815F-EF471F5AC1EF}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.WebSockets.ConformanceTest", "test\Microsoft.AspNetCore.WebSockets.ConformanceTest\Microsoft.AspNetCore.WebSockets.ConformanceTest.csproj", "{74F45408-1959-4FEE-9511-25D40F4913FD}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EchoApp", "samples\EchoApp\EchoApp.csproj", "{421954B0-5C6B-4092-8D4D-EACA4CE60AFB}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AutobahnTestApp", "test\AutobahnTestApp\AutobahnTestApp.csproj", "{150DF5A8-87C6-42F7-8886-CE07BFD02FD2}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{92CE12E6-E127-433B-96D3-164C0113EA17}" - ProjectSection(SolutionItems) = preProject - build\dependencies.props = build\dependencies.props - build\Key.snk = build\Key.snk - build\repo.props = build\repo.props - build\repo.targets = build\repo.targets - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{7A963B09-471B-4D67-B5C0-6039AF0C39EE}" - ProjectSection(SolutionItems) = preProject - Directory.Build.props = Directory.Build.props - Directory.Build.targets = Directory.Build.targets - EndProjectSection -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Release|Any CPU = Release|Any CPU - Release|x64 = Release|x64 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {4E5F5FCC-172C-44D9-BEA0-39098A79CD0B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {4E5F5FCC-172C-44D9-BEA0-39098A79CD0B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4E5F5FCC-172C-44D9-BEA0-39098A79CD0B}.Debug|x64.ActiveCfg = Debug|Any CPU - {4E5F5FCC-172C-44D9-BEA0-39098A79CD0B}.Debug|x64.Build.0 = Debug|Any CPU - {4E5F5FCC-172C-44D9-BEA0-39098A79CD0B}.Debug|x86.ActiveCfg = Debug|Any CPU - {4E5F5FCC-172C-44D9-BEA0-39098A79CD0B}.Debug|x86.Build.0 = Debug|Any CPU - {4E5F5FCC-172C-44D9-BEA0-39098A79CD0B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {4E5F5FCC-172C-44D9-BEA0-39098A79CD0B}.Release|Any CPU.Build.0 = Release|Any CPU - {4E5F5FCC-172C-44D9-BEA0-39098A79CD0B}.Release|x64.ActiveCfg = Release|Any CPU - {4E5F5FCC-172C-44D9-BEA0-39098A79CD0B}.Release|x64.Build.0 = Release|Any CPU - {4E5F5FCC-172C-44D9-BEA0-39098A79CD0B}.Release|x86.ActiveCfg = Release|Any CPU - {4E5F5FCC-172C-44D9-BEA0-39098A79CD0B}.Release|x86.Build.0 = Release|Any CPU - {CDE16880-0374-46FA-8896-99F1B90B4B6F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {CDE16880-0374-46FA-8896-99F1B90B4B6F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {CDE16880-0374-46FA-8896-99F1B90B4B6F}.Debug|x64.ActiveCfg = Debug|Any CPU - {CDE16880-0374-46FA-8896-99F1B90B4B6F}.Debug|x64.Build.0 = Debug|Any CPU - {CDE16880-0374-46FA-8896-99F1B90B4B6F}.Debug|x86.ActiveCfg = Debug|Any CPU - {CDE16880-0374-46FA-8896-99F1B90B4B6F}.Debug|x86.Build.0 = Debug|Any CPU - {CDE16880-0374-46FA-8896-99F1B90B4B6F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {CDE16880-0374-46FA-8896-99F1B90B4B6F}.Release|Any CPU.Build.0 = Release|Any CPU - {CDE16880-0374-46FA-8896-99F1B90B4B6F}.Release|x64.ActiveCfg = Release|Any CPU - {CDE16880-0374-46FA-8896-99F1B90B4B6F}.Release|x64.Build.0 = Release|Any CPU - {CDE16880-0374-46FA-8896-99F1B90B4B6F}.Release|x86.ActiveCfg = Release|Any CPU - {CDE16880-0374-46FA-8896-99F1B90B4B6F}.Release|x86.Build.0 = Release|Any CPU - {5AFA74F5-9B1D-4FC5-815F-EF471F5AC1EF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {5AFA74F5-9B1D-4FC5-815F-EF471F5AC1EF}.Debug|Any CPU.Build.0 = Debug|Any CPU - {5AFA74F5-9B1D-4FC5-815F-EF471F5AC1EF}.Debug|x64.ActiveCfg = Debug|Any CPU - {5AFA74F5-9B1D-4FC5-815F-EF471F5AC1EF}.Debug|x64.Build.0 = Debug|Any CPU - {5AFA74F5-9B1D-4FC5-815F-EF471F5AC1EF}.Debug|x86.ActiveCfg = Debug|Any CPU - {5AFA74F5-9B1D-4FC5-815F-EF471F5AC1EF}.Debug|x86.Build.0 = Debug|Any CPU - {5AFA74F5-9B1D-4FC5-815F-EF471F5AC1EF}.Release|Any CPU.ActiveCfg = Release|Any CPU - {5AFA74F5-9B1D-4FC5-815F-EF471F5AC1EF}.Release|Any CPU.Build.0 = Release|Any CPU - {5AFA74F5-9B1D-4FC5-815F-EF471F5AC1EF}.Release|x64.ActiveCfg = Release|Any CPU - {5AFA74F5-9B1D-4FC5-815F-EF471F5AC1EF}.Release|x64.Build.0 = Release|Any CPU - {5AFA74F5-9B1D-4FC5-815F-EF471F5AC1EF}.Release|x86.ActiveCfg = Release|Any CPU - {5AFA74F5-9B1D-4FC5-815F-EF471F5AC1EF}.Release|x86.Build.0 = Release|Any CPU - {74F45408-1959-4FEE-9511-25D40F4913FD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {74F45408-1959-4FEE-9511-25D40F4913FD}.Debug|Any CPU.Build.0 = Debug|Any CPU - {74F45408-1959-4FEE-9511-25D40F4913FD}.Debug|x64.ActiveCfg = Debug|Any CPU - {74F45408-1959-4FEE-9511-25D40F4913FD}.Debug|x64.Build.0 = Debug|Any CPU - {74F45408-1959-4FEE-9511-25D40F4913FD}.Debug|x86.ActiveCfg = Debug|Any CPU - {74F45408-1959-4FEE-9511-25D40F4913FD}.Debug|x86.Build.0 = Debug|Any CPU - {74F45408-1959-4FEE-9511-25D40F4913FD}.Release|Any CPU.ActiveCfg = Release|Any CPU - {74F45408-1959-4FEE-9511-25D40F4913FD}.Release|Any CPU.Build.0 = Release|Any CPU - {74F45408-1959-4FEE-9511-25D40F4913FD}.Release|x64.ActiveCfg = Release|Any CPU - {74F45408-1959-4FEE-9511-25D40F4913FD}.Release|x64.Build.0 = Release|Any CPU - {74F45408-1959-4FEE-9511-25D40F4913FD}.Release|x86.ActiveCfg = Release|Any CPU - {74F45408-1959-4FEE-9511-25D40F4913FD}.Release|x86.Build.0 = Release|Any CPU - {421954B0-5C6B-4092-8D4D-EACA4CE60AFB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {421954B0-5C6B-4092-8D4D-EACA4CE60AFB}.Debug|Any CPU.Build.0 = Debug|Any CPU - {421954B0-5C6B-4092-8D4D-EACA4CE60AFB}.Debug|x64.ActiveCfg = Debug|Any CPU - {421954B0-5C6B-4092-8D4D-EACA4CE60AFB}.Debug|x64.Build.0 = Debug|Any CPU - {421954B0-5C6B-4092-8D4D-EACA4CE60AFB}.Debug|x86.ActiveCfg = Debug|Any CPU - {421954B0-5C6B-4092-8D4D-EACA4CE60AFB}.Debug|x86.Build.0 = Debug|Any CPU - {421954B0-5C6B-4092-8D4D-EACA4CE60AFB}.Release|Any CPU.ActiveCfg = Release|Any CPU - {421954B0-5C6B-4092-8D4D-EACA4CE60AFB}.Release|Any CPU.Build.0 = Release|Any CPU - {421954B0-5C6B-4092-8D4D-EACA4CE60AFB}.Release|x64.ActiveCfg = Release|Any CPU - {421954B0-5C6B-4092-8D4D-EACA4CE60AFB}.Release|x64.Build.0 = Release|Any CPU - {421954B0-5C6B-4092-8D4D-EACA4CE60AFB}.Release|x86.ActiveCfg = Release|Any CPU - {421954B0-5C6B-4092-8D4D-EACA4CE60AFB}.Release|x86.Build.0 = Release|Any CPU - {150DF5A8-87C6-42F7-8886-CE07BFD02FD2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {150DF5A8-87C6-42F7-8886-CE07BFD02FD2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {150DF5A8-87C6-42F7-8886-CE07BFD02FD2}.Debug|x64.ActiveCfg = Debug|Any CPU - {150DF5A8-87C6-42F7-8886-CE07BFD02FD2}.Debug|x64.Build.0 = Debug|Any CPU - {150DF5A8-87C6-42F7-8886-CE07BFD02FD2}.Debug|x86.ActiveCfg = Debug|Any CPU - {150DF5A8-87C6-42F7-8886-CE07BFD02FD2}.Debug|x86.Build.0 = Debug|Any CPU - {150DF5A8-87C6-42F7-8886-CE07BFD02FD2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {150DF5A8-87C6-42F7-8886-CE07BFD02FD2}.Release|Any CPU.Build.0 = Release|Any CPU - {150DF5A8-87C6-42F7-8886-CE07BFD02FD2}.Release|x64.ActiveCfg = Release|Any CPU - {150DF5A8-87C6-42F7-8886-CE07BFD02FD2}.Release|x64.Build.0 = Release|Any CPU - {150DF5A8-87C6-42F7-8886-CE07BFD02FD2}.Release|x86.ActiveCfg = Release|Any CPU - {150DF5A8-87C6-42F7-8886-CE07BFD02FD2}.Release|x86.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {4E5F5FCC-172C-44D9-BEA0-39098A79CD0B} = {9E55FC5B-FD9C-4266-AB24-F3AA649D7C8B} - {CDE16880-0374-46FA-8896-99F1B90B4B6F} = {2C7947A5-9FBD-4267-97C1-2D726D7B3BAF} - {5AFA74F5-9B1D-4FC5-815F-EF471F5AC1EF} = {C45106D0-76C8-4776-A140-F7DD83CA2958} - {74F45408-1959-4FEE-9511-25D40F4913FD} = {C45106D0-76C8-4776-A140-F7DD83CA2958} - {421954B0-5C6B-4092-8D4D-EACA4CE60AFB} = {9E55FC5B-FD9C-4266-AB24-F3AA649D7C8B} - {150DF5A8-87C6-42F7-8886-CE07BFD02FD2} = {C45106D0-76C8-4776-A140-F7DD83CA2958} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {D3542868-F8C6-401B-8071-37FE3C981604} - EndGlobalSection -EndGlobal diff --git a/src/WebSockets/build.cmd b/src/WebSockets/build.cmd deleted file mode 100644 index f4169ea5e4..0000000000 --- a/src/WebSockets/build.cmd +++ /dev/null @@ -1,3 +0,0 @@ -@ECHO OFF -SET RepoRoot="%~dp0..\.." -%RepoRoot%\build.cmd -LockFile %RepoRoot%\korebuild-lock.txt -Path %~dp0 %* diff --git a/src/WebSockets/build.sh b/src/WebSockets/build.sh deleted file mode 100755 index d5bb0cf631..0000000000 --- a/src/WebSockets/build.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -repo_root="$DIR/../.." -"$repo_root/build.sh" --path "$DIR" --lockfile "$repo_root/korebuild-lock.txt" "$@" diff --git a/src/WebSockets/build/Key.snk b/src/WebSockets/build/Key.snk deleted file mode 100644 index e10e4889c1..0000000000 Binary files a/src/WebSockets/build/Key.snk and /dev/null differ diff --git a/src/WebSockets/build/repo.props b/src/WebSockets/build/repo.props deleted file mode 100644 index ad95e1570f..0000000000 --- a/src/WebSockets/build/repo.props +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - true - - - - - - diff --git a/src/WebSockets/build/repo.targets b/src/WebSockets/build/repo.targets deleted file mode 100644 index 1767331014..0000000000 --- a/src/WebSockets/build/repo.targets +++ /dev/null @@ -1,9 +0,0 @@ - - - $(ArtifactsDir)autobahnreports\ - - - - - - diff --git a/src/WebSockets/samples/EchoApp/EchoApp.csproj b/src/WebSockets/samples/EchoApp/EchoApp.csproj deleted file mode 100644 index fc19ef1f0c..0000000000 --- a/src/WebSockets/samples/EchoApp/EchoApp.csproj +++ /dev/null @@ -1,19 +0,0 @@ - - - - netcoreapp2.2;net461 - - - - - - - - - - - - - - - diff --git a/src/WebSockets/src/Directory.Build.props b/src/WebSockets/src/Directory.Build.props deleted file mode 100644 index b1162b313a..0000000000 --- a/src/WebSockets/src/Directory.Build.props +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/src/WebSockets/src/Microsoft.AspNetCore.WebSockets/Microsoft.AspNetCore.WebSockets.csproj b/src/WebSockets/src/Microsoft.AspNetCore.WebSockets/Microsoft.AspNetCore.WebSockets.csproj deleted file mode 100644 index 277c0cc9fe..0000000000 --- a/src/WebSockets/src/Microsoft.AspNetCore.WebSockets/Microsoft.AspNetCore.WebSockets.csproj +++ /dev/null @@ -1,19 +0,0 @@ - - - - ASP.NET Core web socket middleware for use on top of opaque servers. - netstandard2.0 - $(NoWarn);CS1591 - true - true - aspnetcore - - - - - - - - - - diff --git a/src/WebSockets/test/Directory.Build.props b/src/WebSockets/test/Directory.Build.props deleted file mode 100644 index b7071b5a9d..0000000000 --- a/src/WebSockets/test/Directory.Build.props +++ /dev/null @@ -1,22 +0,0 @@ - - - - - netcoreapp2.2 - $(DeveloperBuildTestTfms) - - $(StandardTestTfms);net461 - - - - - true - - - - - - diff --git a/src/WebSockets/test/Microsoft.AspNetCore.WebSockets.ConformanceTest/Microsoft.AspNetCore.WebSockets.ConformanceTest.csproj b/src/WebSockets/test/Microsoft.AspNetCore.WebSockets.ConformanceTest/Microsoft.AspNetCore.WebSockets.ConformanceTest.csproj deleted file mode 100644 index 539a46f002..0000000000 --- a/src/WebSockets/test/Microsoft.AspNetCore.WebSockets.ConformanceTest/Microsoft.AspNetCore.WebSockets.ConformanceTest.csproj +++ /dev/null @@ -1,17 +0,0 @@ - - - - netcoreapp2.2 - - - - - - - - - - - - - diff --git a/src/WebSockets/test/Microsoft.AspNetCore.WebSockets.Test/Microsoft.AspNetCore.WebSockets.Test.csproj b/src/WebSockets/test/Microsoft.AspNetCore.WebSockets.Test/Microsoft.AspNetCore.WebSockets.Test.csproj deleted file mode 100644 index 5e7f2eb67e..0000000000 --- a/src/WebSockets/test/Microsoft.AspNetCore.WebSockets.Test/Microsoft.AspNetCore.WebSockets.Test.csproj +++ /dev/null @@ -1,20 +0,0 @@ - - - - $(StandardTestTfms) - - - - - - - - - - - - - - - - diff --git a/src/WebSockets/version.props b/src/WebSockets/version.props deleted file mode 100644 index 28bb08494b..0000000000 --- a/src/WebSockets/version.props +++ /dev/null @@ -1,7 +0,0 @@ - - - - 2.2.0 - 2.2.0 - - diff --git a/version.props b/version.props index b56e745b10..1deb9905cf 100644 --- a/version.props +++ b/version.props @@ -11,6 +11,8 @@ $(PreReleaseLabel)-$(BuildNumber) $(PreReleaseBrandingLabel) Build $(BuildNumber) + + true false true @@ -34,6 +36,9 @@ $(VersionSuffix)+$(VersionMetadata) release/$(AspNetCoreMajorVersion).$(AspNetCoreMinorVersion) + + + $(AspNetCoreMajorVersion).$(AspNetCoreMinorVersion).$([MSBuild]::Subtract($(AspNetCorePatchVersion), 1))