diff --git a/MLS.Agent.Tests/ApiViaHttpTests.cs b/MLS.Agent.Tests/ApiViaHttpTests.cs index 2a9fe48d3..71def98ba 100644 --- a/MLS.Agent.Tests/ApiViaHttpTests.cs +++ b/MLS.Agent.Tests/ApiViaHttpTests.cs @@ -633,7 +633,7 @@ public static IEnumerable Fibonacci() public async Task When_aspnet_webapi_workspace_request_succeeds_then_output_shows_web_response() { var workspace = new Workspace(workspaceType: "aspnet.webapi", buffers: new[] { new Buffer("empty.cs", "") }); - var request = new WorkspaceRequest(workspace, httpRequest: new HttpRequest("/api/values", "get"), requestId: "TestRun"); + var request = new WorkspaceRequest(workspace, httpRequest: new HttpRequest("/custom/values", "get"), requestId: "TestRun"); var json = request.ToJson(); @@ -667,7 +667,7 @@ public async Task When_aspnet_webapi_workspace_request_succeeds_then_standard_ou await package.CreateRoslynWorkspaceForRunAsync(new TimeBudget(10.Minutes())); var workspace = WorkspaceFactory.CreateWorkspaceFromDirectory(package.Directory, package.Directory.Name); - var request = new WorkspaceRequest(workspace, httpRequest: new HttpRequest("/api/values", "get"), requestId: "TestRun"); + var request = new WorkspaceRequest(workspace, httpRequest: new HttpRequest("/custom/values", "get"), requestId: "TestRun"); var response = await CallRun(request.ToJson(), 30000); @@ -693,7 +693,7 @@ public async Task When_aspnet_webapi_workspace_request_fails_then_diagnostics_ar files: workspace.Files.ToArray(), workspaceType: workspace.WorkspaceType); - var request = new WorkspaceRequest(workspace, httpRequest: new HttpRequest("/api/values", "get"), requestId: "TestRun"); + var request = new WorkspaceRequest(workspace, httpRequest: new HttpRequest("/custom/values", "get"), requestId: "TestRun"); var response = await CallRun(request.ToJson(), null); diff --git a/MLS.Agent.Tools/AsyncLazy{T}.cs b/MLS.Agent.Tools/AsyncLazy{T}.cs index b3e819b14..3305aa446 100644 --- a/MLS.Agent.Tools/AsyncLazy{T}.cs +++ b/MLS.Agent.Tools/AsyncLazy{T}.cs @@ -8,7 +8,7 @@ namespace MLS.Agent.Tools { public class AsyncLazy { - private readonly Lazy> lazy; + private readonly Lazy> _lazy; public AsyncLazy(Func> initialize) { @@ -17,9 +17,9 @@ public AsyncLazy(Func> initialize) throw new ArgumentNullException(nameof(initialize)); } - lazy = new Lazy>(initialize); + _lazy = new Lazy>(initialize); } - public Task ValueAsync() => lazy.Value; + public Task ValueAsync() => _lazy.Value; } } diff --git a/MLS.Agent/CommandLine/CommandLineParser.cs b/MLS.Agent/CommandLine/CommandLineParser.cs index d83930abf..718b97f34 100644 --- a/MLS.Agent/CommandLine/CommandLineParser.cs +++ b/MLS.Agent/CommandLine/CommandLineParser.cs @@ -57,7 +57,7 @@ public delegate Task Jupyter( InvocationContext context = null); public static Parser Create( - IServiceCollection services, + IServiceCollection services, StartServer startServer = null, Demo demo = null, TryGitHub tryGithub = null, @@ -100,7 +100,7 @@ public static Parser Create( install = install ?? InstallCommand.Do; - + var dirArgument = new Argument { @@ -146,63 +146,63 @@ RootCommand StartInTryMode() command.AddOption(new Option( "--add-package-source", "Specify an additional NuGet package source") - { - Argument = new Argument(() => new PackageSource(Directory.GetCurrentDirectory())) - { - Name = "NuGet source" - } - }); + { + Argument = new Argument(() => new PackageSource(Directory.GetCurrentDirectory())) + { + Name = "NuGet source" + } + }); command.AddOption(new Option( "--package", "Specify a Try .NET package or path to a .csproj to run code samples with") - { - Argument = new Argument - { - Name = "name or .csproj" - } - }); + { + Argument = new Argument + { + Name = "name or .csproj" + } + }); command.AddOption(new Option( "--package-version", "Specify a Try .NET package version to use with the --package option") - { - Argument = new Argument - { - Name = "version" - } - }); + { + Argument = new Argument + { + Name = "version" + } + }); command.AddOption(new Option( "--uri", "Specify a URL or a relative path to a Markdown file") - { - Argument = new Argument() - }); + { + Argument = new Argument() + }); command.AddOption(new Option( "--enable-preview-features", "Enable preview features") - { - Argument = new Argument() - }); + { + Argument = new Argument() + }); command.AddOption(new Option( "--log-path", "Enable file logging to the specified directory") - { - Argument = new Argument - { - Name = "dir" - } - }); + { + Argument = new Argument + { + Name = "dir" + } + }); command.AddOption(new Option( "--verbose", "Enable verbose logging to the console") - { - Argument = new Argument() - }); + { + Argument = new Argument() + }); var portArgument = new Argument(); @@ -221,9 +221,9 @@ RootCommand StartInTryMode() command.AddOption(new Option( "--port", "Specify the port for dotnet try to listen on") - { - Argument = portArgument - }); + { + Argument = portArgument + }); command.Handler = CommandHandler.Create((context, options) => { diff --git a/MLS.Agent/CommandLine/PackCommand.cs b/MLS.Agent/CommandLine/PackCommand.cs index 71d58d32b..aedbc092f 100644 --- a/MLS.Agent/CommandLine/PackCommand.cs +++ b/MLS.Agent/CommandLine/PackCommand.cs @@ -31,7 +31,7 @@ public static async Task Do(PackOptions options, IConsole console) if (options.EnableWasm) { - string runnerDirectoryName = $"wasm"; + string runnerDirectoryName = "wasm"; var temp_projects_wasm = temp_projects.CreateSubdirectory(runnerDirectoryName); var temp_mlsblazor = temp.CreateSubdirectory("MLS.Blazor"); await AddBlazorProject(temp_mlsblazor, GetProjectFile(temp_projects_build), name, temp_projects_wasm); @@ -73,7 +73,7 @@ await File.WriteAllTextAsync( private static async Task AddBlazorProject(DirectoryInfo blazorTargetDirectory, FileInfo projectToReference, string name, DirectoryInfo wasmLocation) { - var initializer = new BlazorPackageInitializer(name, new System.Collections.Generic.List()); + var initializer = new BlazorPackageInitializer(name, new System.Collections.Generic.List<(string,string)>()); await initializer.Initialize(blazorTargetDirectory); await AddReference(blazorTargetDirectory, projectToReference); diff --git a/MLS.Agent/MLS.Agent.csproj b/MLS.Agent/MLS.Agent.csproj index 8731528c6..bf3bdbf99 100644 --- a/MLS.Agent/MLS.Agent.csproj +++ b/MLS.Agent/MLS.Agent.csproj @@ -155,7 +155,7 @@ - + @@ -172,7 +172,7 @@ <_TryDotNetMinJsExists Condition="Exists('$(TryDotNetJsFile)')">true - + @@ -191,7 +191,7 @@ <_TryDotNetClientExists Condition="Exists('$(ClientOutputFile)')">true - + diff --git a/MLS.Agent/Startup.cs b/MLS.Agent/Startup.cs index 461a1fc26..83e1917f8 100644 --- a/MLS.Agent/Startup.cs +++ b/MLS.Agent/Startup.cs @@ -234,7 +234,10 @@ private void LaunchBrowser(IBrowserLauncher browserLauncher, IDirectoryAccessor RelativeFilePath FindReadmeFileAtRoot() { - var files = directoryAccessor.GetAllFilesRecursively().Where(f => (StringComparer.InvariantCultureIgnoreCase.Compare(f.FileName, "readme.md") == 0) && IsRoot(f.Directory)).ToList(); + var files = directoryAccessor + .GetAllFilesRecursively() + .Where(f => (StringComparer.InvariantCultureIgnoreCase.Compare(f.FileName, "readme.md") == 0) && IsRoot(f.Directory)) + .ToList(); return files.FirstOrDefault(); } diff --git a/Microsoft.DotNet.Try.Client/package-lock.json b/Microsoft.DotNet.Try.Client/package-lock.json index ae6558cdc..b8202479a 100644 --- a/Microsoft.DotNet.Try.Client/package-lock.json +++ b/Microsoft.DotNet.Try.Client/package-lock.json @@ -6148,9 +6148,9 @@ } }, "lodash": { - "version": "4.17.11", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", - "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", + "version": "4.17.14", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.14.tgz", + "integrity": "sha512-mmKYbW3GLuJeX+iGP+Y7Gp1AiGHGbXHCOh/jZmrawMmsE7MS4znI3RL2FsjbqOyMayHInjOeykW7PEajUk1/xw==", "dev": true }, "lodash-es": { @@ -6443,9 +6443,9 @@ } }, "mixin-deep": { - "version": "1.3.1", - "resolved": "https://msazure.pkgs.visualstudio.com/_packaging/MLS/npm/registry/mixin-deep/-/mixin-deep-1.3.1.tgz", - "integrity": "sha1-pJ5yaNzhoNlpjkUybFYm3zVD0P4=", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", "dev": true, "requires": { "for-in": "^1.0.2", @@ -6454,8 +6454,8 @@ "dependencies": { "is-extendable": { "version": "1.0.1", - "resolved": "https://msazure.pkgs.visualstudio.com/_packaging/MLS/npm/registry/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha1-p0cPnkJnM9gb2B4RVSZOOjUHyrQ=", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", "dev": true, "requires": { "is-plain-object": "^2.0.4" @@ -6718,72 +6718,13 @@ } }, "mocha-trx-reporter": { - "version": "3.2.0", - "resolved": "https://msazure.pkgs.visualstudio.com/_packaging/MLS/npm/registry/mocha-trx-reporter/-/mocha-trx-reporter-3.2.0.tgz", - "integrity": "sha1-9cmNu3INjEGAHJw3+JoW13XfW0E=", + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/mocha-trx-reporter/-/mocha-trx-reporter-3.2.3.tgz", + "integrity": "sha512-UYgc72sjmGK/ztXlECE6ptiPcSSPuwYsspJF0wQELQvVETRmOrs9a2cu6sjSsjbuoddnVxz7znfhJczyoKULzA==", "dev": true, "requires": { - "mocha": "^5.0.0", - "node-trx": "^0.8.0" - }, - "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "glob": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "he": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", - "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", - "dev": true - }, - "mocha": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.2.0.tgz", - "integrity": "sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==", - "dev": true, - "requires": { - "browser-stdout": "1.3.1", - "commander": "2.15.1", - "debug": "3.1.0", - "diff": "3.5.0", - "escape-string-regexp": "1.0.5", - "glob": "7.1.2", - "growl": "1.10.5", - "he": "1.1.1", - "minimatch": "3.0.4", - "mkdirp": "0.5.1", - "supports-color": "5.4.0" - } - }, - "supports-color": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", - "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } + "mocha": "^6.0.0", + "node-trx": "^0.9.0" } }, "mocha-typescript": { @@ -7275,13 +7216,13 @@ } }, "node-trx": { - "version": "0.8.0", - "resolved": "https://msazure.pkgs.visualstudio.com/_packaging/MLS/npm/registry/node-trx/-/node-trx-0.8.0.tgz", - "integrity": "sha1-cadaFGOgc5orgMDr9thH4K9aC28=", + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/node-trx/-/node-trx-0.9.0.tgz", + "integrity": "sha512-Jq9bgcO3P5AkYhpSh+DfOfbwPmf66Hhr783uGPh9MpqG/nVj0XTlpRq9L231NT809WLa+gnQTbZuTONgKLWYhQ==", "dev": true, "requires": { - "uuid": "^3.0.0", - "xmlbuilder": "^2.6.5" + "uuid": "^3.3.2", + "xmlbuilder": "^13.0.2" } }, "nodent-runtime": { @@ -8956,9 +8897,9 @@ "dev": true }, "set-value": { - "version": "2.0.0", - "resolved": "https://msazure.pkgs.visualstudio.com/_packaging/MLS/npm/registry/set-value/-/set-value-2.0.0.tgz", - "integrity": "sha1-ca5KiPD+77v1LR6mBPP7MV67YnQ=", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", "dev": true, "requires": { "extend-shallow": "^2.0.1", @@ -9940,38 +9881,15 @@ } }, "union-value": { - "version": "1.0.0", - "resolved": "https://msazure.pkgs.visualstudio.com/_packaging/MLS/npm/registry/union-value/-/union-value-1.0.0.tgz", - "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", "dev": true, "requires": { "arr-union": "^3.1.0", "get-value": "^2.0.6", "is-extendable": "^0.1.1", - "set-value": "^0.4.3" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://msazure.pkgs.visualstudio.com/_packaging/MLS/npm/registry/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "set-value": { - "version": "0.4.3", - "resolved": "https://msazure.pkgs.visualstudio.com/_packaging/MLS/npm/registry/set-value/-/set-value-0.4.3.tgz", - "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.1", - "to-object-path": "^0.3.0" - } - } + "set-value": "^2.0.1" } }, "unique-filename": { @@ -10592,21 +10510,10 @@ "dev": true }, "xmlbuilder": { - "version": "2.6.5", - "resolved": "https://msazure.pkgs.visualstudio.com/_packaging/MLS/npm/registry/xmlbuilder/-/xmlbuilder-2.6.5.tgz", - "integrity": "sha1-b/etYPty0idk8AehZLd/K/FABSY=", - "dev": true, - "requires": { - "lodash": "^3.5.0" - }, - "dependencies": { - "lodash": { - "version": "3.10.1", - "resolved": "https://msazure.pkgs.visualstudio.com/_packaging/MLS/npm/registry/lodash/-/lodash-3.10.1.tgz", - "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=", - "dev": true - } - } + "version": "13.0.2", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-13.0.2.tgz", + "integrity": "sha512-Eux0i2QdDYKbdbA6AM6xE4m6ZTZr4G4xF9kahI2ukSEMCzwce2eX9WlTI5J3s+NU7hpasFsr8hWIONae7LluAQ==", + "dev": true }, "xmlchars": { "version": "1.3.1", diff --git a/Microsoft.DotNet.Try.Client/package.json b/Microsoft.DotNet.Try.Client/package.json index c135d86b5..c63dcb3dc 100644 --- a/Microsoft.DotNet.Try.Client/package.json +++ b/Microsoft.DotNet.Try.Client/package.json @@ -88,12 +88,12 @@ "js-cookie": "2.2.0", "jsdom": "12.0.0", "jsdom-global": "3.0.2", - "lodash": "4.17.11", + "lodash": "4.17.14", "mini-css-extract-plugin": "0.4.2", "mkdirp": "0.5.1", "mocha": "6.1.4", "mocha-multi-reporters": "1.1.7", - "mocha-trx-reporter": "3.2.0", + "mocha-trx-reporter": "3.2.3", "mocha-typescript": "1.1.17", "mochapack": "1.1.1", "mock-http-server": "1.0.0", diff --git a/Microsoft.DotNet.Try.Jupyter.Tests/CompleteRequestHandlerTests.cs b/Microsoft.DotNet.Try.Jupyter.Tests/CompleteRequestHandlerTests.cs new file mode 100644 index 000000000..ee2873563 --- /dev/null +++ b/Microsoft.DotNet.Try.Jupyter.Tests/CompleteRequestHandlerTests.cs @@ -0,0 +1,41 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Threading.Tasks; +using FluentAssertions; +using Microsoft.DotNet.Try.Jupyter.Protocol; +using WorkspaceServer.Kernel; +using Xunit; + +namespace Microsoft.DotNet.Try.Jupyter.Tests +{ + public class CompleteRequestHandlerTests + { + private readonly MessageSender _ioPubChannel; + private readonly MessageSender _serverChannel; + private readonly RecordingSocket _serverRecordingSocket; + private readonly RecordingSocket _ioRecordingSocket; + private readonly KernelStatus _kernelStatus; + + public CompleteRequestHandlerTests() + { + var signatureValidator = new SignatureValidator("key", "HMACSHA256"); + _serverRecordingSocket = new RecordingSocket(); + _serverChannel = new MessageSender(_serverRecordingSocket, signatureValidator); + _ioRecordingSocket = new RecordingSocket(); + _ioPubChannel = new MessageSender(_ioRecordingSocket, signatureValidator); + _kernelStatus = new KernelStatus(); + } + + [Fact] + public void cannot_handle_requests_that_are_not_executeRequest() + { + var kernel = new CSharpRepl(); + var handler = new CompleteRequestHandler(kernel); + var request = Message.Create(new DisplayData(), null); + Func messageHandling = () => handler.Handle(new JupyterRequestContext(_serverChannel, _ioPubChannel, request, _kernelStatus)); + messageHandling.Should().ThrowExactly(); + } + } +} \ No newline at end of file diff --git a/Microsoft.DotNet.Try.Jupyter/CompleteRequestHandler.cs b/Microsoft.DotNet.Try.Jupyter/CompleteRequestHandler.cs new file mode 100644 index 000000000..3685fd5cc --- /dev/null +++ b/Microsoft.DotNet.Try.Jupyter/CompleteRequestHandler.cs @@ -0,0 +1,32 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Reactive.Disposables; +using System.Threading.Tasks; +using Microsoft.DotNet.Try.Jupyter.Protocol; +using WorkspaceServer.Kernel; + +namespace Microsoft.DotNet.Try.Jupyter +{ + public class CompleteRequestHandler: RequestHandlerBase + { + + + public CompleteRequestHandler(IKernel kernel) : base(kernel) + { + + } + + public Task Handle(JupyterRequestContext context) + { + var completeRequest = GetRequest(context); + + context.RequestHandlerStatus.SetAsBusy(); + context.RequestHandlerStatus.SetAsIdle(); + throw new NotImplementedException(); + } + + + } +} \ No newline at end of file diff --git a/Microsoft.DotNet.Try.Jupyter/ExecuteRequestHandler.cs b/Microsoft.DotNet.Try.Jupyter/ExecuteRequestHandler.cs index 435d5e9c3..e79109e37 100644 --- a/Microsoft.DotNet.Try.Jupyter/ExecuteRequestHandler.cs +++ b/Microsoft.DotNet.Try.Jupyter/ExecuteRequestHandler.cs @@ -5,7 +5,6 @@ using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; -using System.Reactive.Disposables; using System.Threading; using System.Threading.Tasks; using Microsoft.DotNet.Try.Jupyter.Protocol; @@ -14,46 +13,13 @@ namespace Microsoft.DotNet.Try.Jupyter { - public class ExecuteRequestHandler : IDisposable + public class ExecuteRequestHandler : RequestHandlerBase { - private readonly IKernel _kernel; private readonly RenderingEngine _renderingEngine; - private readonly ConcurrentDictionary _openRequests = new ConcurrentDictionary(); private int _executionCount; - private readonly CompositeDisposable _disposables = new CompositeDisposable(); - - private class OpenRequest : IDisposable - { - private readonly CompositeDisposable _disposables = new CompositeDisposable(); - public Guid Id { get; } - public Dictionary Transient { get; } - public JupyterRequestContext Context { get; } - public ExecuteRequest ExecuteRequest { get; } - public int ExecutionCount { get; } - - public OpenRequest(JupyterRequestContext context, ExecuteRequest executeRequest, int executionCount, Guid id, Dictionary transient) - { - Context = context; - ExecuteRequest = executeRequest; - ExecutionCount = executionCount; - Id = id; - Transient = transient; - } - - public void AddDisposable(IDisposable disposable) - { - _disposables.Add(disposable); - } - - public void Dispose() - { - _disposables.Dispose(); - } - } - - public ExecuteRequestHandler(IKernel kernel) + + public ExecuteRequestHandler(IKernel kernel) :base(kernel) { - _kernel = kernel; _renderingEngine = new RenderingEngine(new DefaultRenderer(), new PlainTextRendering("")); _renderingEngine.RegisterRenderer(typeof(string), new DefaultRenderer()); _renderingEngine.RegisterRenderer(typeof(IDictionary), new DictionaryRenderer()); @@ -63,26 +29,27 @@ public ExecuteRequestHandler(IKernel kernel) public async Task Handle(JupyterRequestContext context) { - var executeRequest = context.GetRequestContent() ?? throw new InvalidOperationException($"Request Content must be a not null {typeof(ExecuteRequest).Name}"); + var executeRequest = GetRequest(context); + context.RequestHandlerStatus.SetAsBusy(); var executionCount = executeRequest.Silent ? _executionCount : Interlocked.Increment(ref _executionCount); - - try - { - var command = new SubmitCode(executeRequest.Code, "csharp"); - var id = Guid.NewGuid(); + var command = new SubmitCode(executeRequest.Code, "csharp"); + var id = Guid.NewGuid(); + var transient = new Dictionary { { "display_id", id.ToString() } }; + var openRequest = new OpenRequest(context, executeRequest, executionCount, transient); - var transient = new Dictionary { { "display_id", id.ToString() } }; - - var openRequest = new OpenRequest(context, executeRequest, executionCount, id, transient); - _openRequests[command] = openRequest; + OpenRequests[command] = openRequest; - var kernelResult = await _kernel.SendAsync(command); + try + { + var kernelResult = await Kernel.SendAsync(command); openRequest.AddDisposable(kernelResult.KernelEvents.Subscribe(OnKernelResultEvent)); } catch (Exception e) { + OpenRequests.TryRemove(command, out _); + var errorContent = new Error( eName: "Unhandled Exception", eValue: $"{e.Message}" @@ -122,13 +89,13 @@ void OnKernelResultEvent(IKernelEvent value) switch (value) { case ValueProduced valueProduced: - OnValueProduced(valueProduced, _openRequests, _renderingEngine); + OnValueProduced(valueProduced, OpenRequests, _renderingEngine); break; case CodeSubmissionEvaluated codeSubmissionEvaluated: - OnCodeSubmissionEvaluated(codeSubmissionEvaluated, _openRequests); + OnCodeSubmissionEvaluated(codeSubmissionEvaluated, OpenRequests); break; case CodeSubmissionEvaluationFailed codeSubmissionEvaluationFailed: - OnCodeSubmissionEvaluatedFailed(codeSubmissionEvaluationFailed, _openRequests); + OnCodeSubmissionEvaluatedFailed(codeSubmissionEvaluationFailed, OpenRequests); break; case CodeSubmissionReceived _: case IncompleteCodeSubmissionReceived _: @@ -141,14 +108,14 @@ void OnKernelResultEvent(IKernelEvent value) private static void OnCodeSubmissionEvaluatedFailed(CodeSubmissionEvaluationFailed codeSubmissionEvaluationFailed, ConcurrentDictionary openRequests) { - var openRequest = openRequests[codeSubmissionEvaluationFailed.Command]; + openRequests.TryRemove(codeSubmissionEvaluationFailed.Command, out var openRequest); var errorContent = new Error( eName: "Unhandled Exception", eValue: $"{codeSubmissionEvaluationFailed.Message}" ); - if (!openRequest.ExecuteRequest.Silent) + if (!openRequest.Request.Silent) { // send on io var error = Message.Create( @@ -175,12 +142,18 @@ private static void OnCodeSubmissionEvaluatedFailed(CodeSubmissionEvaluationFail openRequest.Context.ServerChannel.Send(executeReply); openRequest.Context.RequestHandlerStatus.SetAsIdle(); + openRequest.Dispose(); } private static void OnValueProduced(ValueProduced valueProduced, ConcurrentDictionary openRequests, RenderingEngine renderingEngine) { - var openRequest = openRequests[valueProduced.Command]; + openRequests.TryGetValue(valueProduced.Command, out var openRequest); + if (openRequest == null) + { + return; + } + try { var rendering = renderingEngine.Render(valueProduced.Value); @@ -194,7 +167,7 @@ private static void OnValueProduced(ValueProduced valueProduced, { rendering.MimeType, rendering.Content } }); - if (!openRequest.ExecuteRequest.Silent) + if (!openRequest.Request.Silent) { // send on io var executeResultMessage = Message.Create( @@ -210,7 +183,7 @@ private static void OnValueProduced(ValueProduced valueProduced, eValue: $"{e.Message}" ); - if (!openRequest.ExecuteRequest.Silent) + if (!openRequest.Request.Silent) { // send on io var error = Message.Create( @@ -231,7 +204,7 @@ private static void OnValueProduced(ValueProduced valueProduced, private static void OnCodeSubmissionEvaluated(CodeSubmissionEvaluated codeSubmissionEvaluated, ConcurrentDictionary openRequests) { - var openRequest = openRequests[codeSubmissionEvaluated.Command]; + openRequests.TryRemove(codeSubmissionEvaluated.Command, out var openRequest); // reply ok var executeReplyPayload = new ExecuteReplyOk(executionCount: openRequest.ExecutionCount); @@ -242,11 +215,7 @@ private static void OnCodeSubmissionEvaluated(CodeSubmissionEvaluated codeSubmis openRequest.Context.ServerChannel.Send(executeReply); openRequest.Context.RequestHandlerStatus.SetAsIdle(); - } - - public void Dispose() - { - _disposables.Dispose(); + openRequest.Dispose(); } } } \ No newline at end of file diff --git a/Microsoft.DotNet.Try.Jupyter/JupyterRequestContextHandler.cs b/Microsoft.DotNet.Try.Jupyter/JupyterRequestContextHandler.cs index af0b7b9e3..d42ba1b75 100644 --- a/Microsoft.DotNet.Try.Jupyter/JupyterRequestContextHandler.cs +++ b/Microsoft.DotNet.Try.Jupyter/JupyterRequestContextHandler.cs @@ -2,7 +2,6 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; -using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; using System.Threading.Tasks; @@ -19,15 +18,17 @@ namespace Microsoft.DotNet.Try.Jupyter public class JupyterRequestContextHandler : ICommandHandler { private static readonly Regex _lastToken = new Regex(@"(?\S+)$", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Multiline); - private int _executionCount; + private readonly WorkspaceServerMultiplexer _server; private readonly ExecuteRequestHandler _executeHandler; + private readonly CompleteRequestHandler _completeHandler; public JupyterRequestContextHandler( PackageRegistry packageRegistry, IKernel kernel) { _executeHandler = new ExecuteRequestHandler(kernel); + _completeHandler = new CompleteRequestHandler(kernel); if (packageRegistry == null) { @@ -43,9 +44,9 @@ public async Task Handle( { case MessageTypeValues.ExecuteRequest: await _executeHandler.Handle(delivery.Command); - // await HandleExecuteRequest(delivery); break; case MessageTypeValues.CompleteRequest: + await _completeHandler.Handle(delivery.Command); delivery.Command.RequestHandlerStatus.SetAsBusy(); await HandleCompleteRequest(delivery); delivery.Command.RequestHandlerStatus.SetAsIdle(); @@ -105,108 +106,6 @@ private static int ComputeReplacementStartPosition(string code, int cursorPositi return pos; } - private async Task HandleExecuteRequest(ICommandDelivery delivery) - { - var ioPubChannel = delivery.Command.IoPubChannel; - var serverChannel = delivery.Command.ServerChannel; - - var transient = new Dictionary { { "display_id", Guid.NewGuid().ToString() } }; - - var executeRequest = delivery.Command.GetRequestContent(); - - var code = executeRequest.Code; - - var workspace = CreateScaffoldWorkspace(code); - - var workspaceRequest = new WorkspaceRequest(workspace); - - var result = await _server.Run(workspaceRequest); - - if (!executeRequest.Silent) - { - _executionCount++; - - var executeInput = Message.Create( - new ExecuteInput(code: code, executionCount: _executionCount), - delivery.Command.Request.Header); - - ioPubChannel.Send(executeInput); - } - - // execute result - var output = string.Join("\n", result.Output); - - - // executeResult data - var executeResultData = new ExecuteResult( - _executionCount, - transient: transient, - data: new Dictionary { - { "text/html", output}, - { "text/plain", output} - }); - - - var resultSucceeded = result.Succeeded && - result.Exception == null; - - if (resultSucceeded) - { - // reply ok - var executeReplyPayload = new ExecuteReplyOk(executionCount: _executionCount); - - - // send to server - var executeReply = Message.CreateResponse( - executeReplyPayload, - delivery.Command.Request); - - serverChannel.Send(executeReply); - } - else - { - var errorContent = new Error( - eName: string.IsNullOrWhiteSpace(result.Exception) ? "Compile Error" : "Unhandled Exception", - eValue: output - ); - - // reply Error - var executeReplyPayload = new ExecuteReplyError(errorContent, executionCount: _executionCount); - - // send to server - var executeReply = Message.CreateResponse( - executeReplyPayload, - delivery.Command.Request); - - serverChannel.Send(executeReply); - - if (!executeRequest.Silent) - { - // send on io - var error = Message.Create( - errorContent, - delivery.Command.Request.Header); - ioPubChannel.Send(error); - - // send on stderr - var stdErr = new StdErrStream(errorContent.EValue); - var stream = Message.Create( - stdErr, - delivery.Command.Request.Header); - ioPubChannel.Send(stream); - } - } - - if (!executeRequest.Silent && resultSucceeded) - { - // send on io - var executeResultMessage = Message.Create( - executeResultData, - delivery.Command.Request.Header); - ioPubChannel.Send(executeResultMessage); - } - } - private static Workspace CreateScaffoldWorkspace(string code, int cursorPosition = 0) { var workspace = CreateCsharpScaffold(code, cursorPosition); diff --git a/Microsoft.DotNet.Try.Jupyter/RequestHandlerBase.cs b/Microsoft.DotNet.Try.Jupyter/RequestHandlerBase.cs new file mode 100644 index 000000000..f5e7873b8 --- /dev/null +++ b/Microsoft.DotNet.Try.Jupyter/RequestHandlerBase.cs @@ -0,0 +1,69 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Reactive.Disposables; +using Microsoft.DotNet.Try.Jupyter.Protocol; +using WorkspaceServer.Kernel; + +namespace Microsoft.DotNet.Try.Jupyter +{ + public abstract class RequestHandlerBase : IDisposable + where T : JupyterMessageContent + { + private readonly CompositeDisposable _disposables = new CompositeDisposable(); + + protected RequestHandlerBase(IKernel kernel) + { + Kernel = kernel ?? throw new ArgumentNullException(nameof(kernel)); + } + + protected static T GetRequest(JupyterRequestContext context) + { + var request = context.GetRequestContent() ?? + throw new InvalidOperationException( + $"Request Content must be a not null {typeof(T).Name}"); + return request; + } + + protected IKernel Kernel { get; } + + protected ConcurrentDictionary OpenRequests { get; } = new ConcurrentDictionary(); + + public void Dispose() + { + _disposables.Dispose(); + } + + protected class OpenRequest : IDisposable + { + private readonly CompositeDisposable _disposables = new CompositeDisposable(); + public Dictionary Transient { get; } + public JupyterRequestContext Context { get; } + public T Request { get; } + public int ExecutionCount { get; } + + public OpenRequest(JupyterRequestContext context, T request, int executionCount, + Dictionary transient) + { + Context = context; + Request = request; + ExecutionCount = executionCount; + Transient = transient; + } + + public void AddDisposable(IDisposable disposable) + { + _disposables.Add(disposable); + } + public void Dispose() + { + _disposables.Dispose(); + } + } + + + } +} \ No newline at end of file diff --git a/Microsoft.DotNet.Try.Styles/package-lock.json b/Microsoft.DotNet.Try.Styles/package-lock.json index 203b3e5e1..17eddfc9d 100644 --- a/Microsoft.DotNet.Try.Styles/package-lock.json +++ b/Microsoft.DotNet.Try.Styles/package-lock.json @@ -662,9 +662,9 @@ } }, "lodash": { - "version": "4.17.11", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", - "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", + "version": "4.17.14", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.14.tgz", + "integrity": "sha512-mmKYbW3GLuJeX+iGP+Y7Gp1AiGHGbXHCOh/jZmrawMmsE7MS4znI3RL2FsjbqOyMayHInjOeykW7PEajUk1/xw==", "dev": true }, "loud-rejection": { diff --git a/Microsoft.DotNet.Try.Styles/package.json b/Microsoft.DotNet.Try.Styles/package.json index 9ef3972e9..a8e8f4dd3 100644 --- a/Microsoft.DotNet.Try.Styles/package.json +++ b/Microsoft.DotNet.Try.Styles/package.json @@ -17,6 +17,7 @@ "@mdi/font": "3.5.95", "fstream": "1.0.12", "github-markdown-css": "3.0.1", + "lodash": "4.17.14", "mkdirp": "0.5.1", "node-sass": "4.12.0", "tar": "4.4.8", diff --git a/Microsoft.DotNet.Try.js/package-lock.json b/Microsoft.DotNet.Try.js/package-lock.json index 32e72fbf3..7bc87cbcc 100644 --- a/Microsoft.DotNet.Try.js/package-lock.json +++ b/Microsoft.DotNet.Try.js/package-lock.json @@ -3190,9 +3190,9 @@ } }, "lodash": { - "version": "4.17.11", - "resolved": "https://msazure.pkgs.visualstudio.com/_packaging/MLS/npm/registry/lodash/-/lodash-4.17.11.tgz", - "integrity": "sha1-s56mIp72B+zYniyN8SU2iRysm40=", + "version": "4.17.14", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.14.tgz", + "integrity": "sha512-mmKYbW3GLuJeX+iGP+Y7Gp1AiGHGbXHCOh/jZmrawMmsE7MS4znI3RL2FsjbqOyMayHInjOeykW7PEajUk1/xw==", "dev": true }, "lodash.sortby": { @@ -3345,9 +3345,9 @@ "dev": true }, "mixin-deep": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", - "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", "dev": true, "requires": { "for-in": "^1.0.2", @@ -3662,58 +3662,13 @@ } }, "mocha-trx-reporter": { - "version": "3.2.0", - "resolved": "https://msazure.pkgs.visualstudio.com/_packaging/MLS/npm/registry/mocha-trx-reporter/-/mocha-trx-reporter-3.2.0.tgz", - "integrity": "sha1-9cmNu3INjEGAHJw3+JoW13XfW0E=", + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/mocha-trx-reporter/-/mocha-trx-reporter-3.2.3.tgz", + "integrity": "sha512-UYgc72sjmGK/ztXlECE6ptiPcSSPuwYsspJF0wQELQvVETRmOrs9a2cu6sjSsjbuoddnVxz7znfhJczyoKULzA==", "dev": true, "requires": { - "mocha": "^5.0.0", - "node-trx": "^0.8.0" - }, - "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "he": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", - "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", - "dev": true - }, - "mocha": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.2.0.tgz", - "integrity": "sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==", - "dev": true, - "requires": { - "browser-stdout": "1.3.1", - "commander": "2.15.1", - "debug": "3.1.0", - "diff": "3.5.0", - "escape-string-regexp": "1.0.5", - "glob": "7.1.2", - "growl": "1.10.5", - "he": "1.1.1", - "minimatch": "3.0.4", - "mkdirp": "0.5.1", - "supports-color": "5.4.0" - } - }, - "supports-color": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", - "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } + "mocha": "^6.0.0", + "node-trx": "^0.9.0" } }, "mocha-typescript": { @@ -3841,13 +3796,13 @@ } }, "node-trx": { - "version": "0.8.0", - "resolved": "https://msazure.pkgs.visualstudio.com/_packaging/MLS/npm/registry/node-trx/-/node-trx-0.8.0.tgz", - "integrity": "sha1-cadaFGOgc5orgMDr9thH4K9aC28=", + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/node-trx/-/node-trx-0.9.0.tgz", + "integrity": "sha512-Jq9bgcO3P5AkYhpSh+DfOfbwPmf66Hhr783uGPh9MpqG/nVj0XTlpRq9L231NT809WLa+gnQTbZuTONgKLWYhQ==", "dev": true, "requires": { - "uuid": "^3.0.0", - "xmlbuilder": "^2.6.5" + "uuid": "^3.3.2", + "xmlbuilder": "^13.0.2" } }, "npm-run-path": { @@ -5119,9 +5074,9 @@ "dev": true }, "set-value": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", - "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", "dev": true, "requires": { "extend-shallow": "^2.0.1", @@ -5837,38 +5792,15 @@ } }, "union-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", - "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", "dev": true, "requires": { "arr-union": "^3.1.0", "get-value": "^2.0.6", "is-extendable": "^0.1.1", - "set-value": "^0.4.3" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "set-value": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz", - "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.1", - "to-object-path": "^0.3.0" - } - } + "set-value": "^2.0.1" } }, "universalify": { @@ -6106,21 +6038,10 @@ "dev": true }, "xmlbuilder": { - "version": "2.6.5", - "resolved": "https://msazure.pkgs.visualstudio.com/_packaging/MLS/npm/registry/xmlbuilder/-/xmlbuilder-2.6.5.tgz", - "integrity": "sha1-b/etYPty0idk8AehZLd/K/FABSY=", - "dev": true, - "requires": { - "lodash": "^3.5.0" - }, - "dependencies": { - "lodash": { - "version": "3.10.1", - "resolved": "https://msazure.pkgs.visualstudio.com/_packaging/MLS/npm/registry/lodash/-/lodash-3.10.1.tgz", - "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=", - "dev": true - } - } + "version": "13.0.2", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-13.0.2.tgz", + "integrity": "sha512-Eux0i2QdDYKbdbA6AM6xE4m6ZTZr4G4xF9kahI2ukSEMCzwce2eX9WlTI5J3s+NU7hpasFsr8hWIONae7LluAQ==", + "dev": true }, "xmlchars": { "version": "1.3.1", diff --git a/Microsoft.DotNet.Try.js/package.json b/Microsoft.DotNet.Try.js/package.json index 05b0f97fd..e6ada684f 100644 --- a/Microsoft.DotNet.Try.js/package.json +++ b/Microsoft.DotNet.Try.js/package.json @@ -30,11 +30,11 @@ "chai": "4.2.0", "chai-as-promised": "7.1.1", "jsdom": "13.2.0", - "lodash": "4.17.11", + "lodash": "4.17.14", "mkdirp": "0.5.1", "mocha": "6.1.4", "mocha-multi-reporters": "1.1.7", - "mocha-trx-reporter": "3.2.0", + "mocha-trx-reporter": "3.2.3", "mocha-typescript": "1.1.17", "rollup": "1.1.2", "rollup-plugin-commonjs": "9.2.0", diff --git a/WorkspaceServer.Tests/AspNetWorkspaceTests.cs b/WorkspaceServer.Tests/AspNetWorkspaceTests.cs index d9c3af752..6a68f10bf 100644 --- a/WorkspaceServer.Tests/AspNetWorkspaceTests.cs +++ b/WorkspaceServer.Tests/AspNetWorkspaceTests.cs @@ -3,7 +3,6 @@ using System; using System.Net.Http; -using System.Runtime.CompilerServices; using System.Threading.Tasks; using Clockwise; using FluentAssertions; @@ -14,7 +13,6 @@ using WorkspaceServer.Models.Execution; using WorkspaceServer.Servers.Roslyn; using WorkspaceServer.Features; -using WorkspaceServer.Packaging; using Xunit; using Xunit.Abstractions; using Package = WorkspaceServer.Packaging.Package; @@ -47,7 +45,7 @@ public async Task Run_starts_the_kestrel_server_and_provides_a_WebServer_feature { var webServer = runResult.GetFeature(); - var response = await webServer.SendAsync(new HttpRequestMessage(HttpMethod.Get, "/api/values")).CancelIfExceeds(new TimeBudget(35.Seconds())); + var response = await webServer.SendAsync(new HttpRequestMessage(HttpMethod.Get, "/custom/values")).CancelIfExceeds(new TimeBudget(35.Seconds())); var result = await response.EnsureSuccess() .DeserializeAs(); diff --git a/WorkspaceServer.Tests/BlazorPackageInitializerTests.cs b/WorkspaceServer.Tests/BlazorPackageInitializerTests.cs index 1ffceace3..9d5896ec5 100644 --- a/WorkspaceServer.Tests/BlazorPackageInitializerTests.cs +++ b/WorkspaceServer.Tests/BlazorPackageInitializerTests.cs @@ -22,7 +22,7 @@ public void WORKAROUND_Requires_MLS_Blazor_directory_because_of_aspnet_AspNetCor var initializer = new BlazorPackageInitializer( "blazor-test", - new List()); + new List<(string,string)>()); Func initialize = async () => await initializer.Initialize(empty.Directory); @@ -40,7 +40,7 @@ public async Task Initializes_project_with_right_files() var name = "blazor-test"; var initializer = new BlazorPackageInitializer( name, - new List()); + new List<(string, string)>()); await initializer.Initialize(dir); @@ -73,9 +73,9 @@ public async Task Adds_packages() var name = "blazor-test"; var initializer = new BlazorPackageInitializer( name, - new List() + new List<(string, string)> { - "Microsoft.Extensions.Logging::3.0.0-preview6.19304.6" + ("Microsoft.Extensions.Logging", "3.0.0-preview6.19304.6") }); await initializer.Initialize(dir); diff --git a/WorkspaceServer.Tests/Default.cs b/WorkspaceServer.Tests/Default.cs index ee379bbb3..b5d297248 100644 --- a/WorkspaceServer.Tests/Default.cs +++ b/WorkspaceServer.Tests/Default.cs @@ -1,9 +1,7 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System; using System.CommandLine; -using System.IO; using System.Linq; using System.Threading.Tasks; using Clockwise; @@ -58,6 +56,24 @@ public class Default { var packageBuilder = new PackageBuilder("aspnet.webapi"); packageBuilder.CreateUsingDotnet("webapi"); + packageBuilder.WriteFile("Controllers/CustomController.cs", @"using System.Collections.Generic; +using Microsoft.AspNetCore.Mvc; + +namespace aspnet.webapi.Controllers +{ + [ApiController] + [Route(""[controller]"")] + public class CustomController : ControllerBase + { + public CustomController() { } + + [HttpGet(""values"")] + public IEnumerable Values() + { + return new[] { ""value1"", ""value2"" }; + } + } +}"); packageBuilder.TrySetLanguageVersion("8.0"); var package = packageBuilder.GetPackage() as Package; await package.CreateRoslynWorkspaceForRunAsync(new Budget()); diff --git a/WorkspaceServer.Tests/Kernel/CSharpReplTests.cs b/WorkspaceServer.Tests/Kernel/CSharpReplTests.cs index 20340a329..a91d30632 100644 --- a/WorkspaceServer.Tests/Kernel/CSharpReplTests.cs +++ b/WorkspaceServer.Tests/Kernel/CSharpReplTests.cs @@ -184,6 +184,22 @@ public async Task it_aggregates_multiple_submissions() .Be(3); } + + [Fact(Skip = "requires support for cs8 in roslyn scripting")] + public async Task it_supports_csharp_8() + { + var repl = await CreateKernelAsync(); + + await repl.SendAsync(new SubmitCode("var text = \"meow? meow!\";", "csharp")); + await repl.SendAsync(new SubmitCode("text[^5..^0]", "csharp")); + + KernelEvents.OfType() + .Last() + .Value + .Should() + .Be("meow!"); + } + [Fact] public async Task it_can_load_assembly_references_using_r_directive() { diff --git a/WorkspaceServer.Tests/WebServerTests.cs b/WorkspaceServer.Tests/WebServerTests.cs index 5c10dd74d..9405a04b3 100644 --- a/WorkspaceServer.Tests/WebServerTests.cs +++ b/WorkspaceServer.Tests/WebServerTests.cs @@ -2,17 +2,13 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; -using System.Collections.Generic; using System.Net.Http; using System.Text; using System.Threading.Tasks; -using Clockwise; using FluentAssertions; -using FluentAssertions.Extensions; using Pocket; using Recipes; using WorkspaceServer.Features; -using WorkspaceServer.Packaging; using WorkspaceServer.Tests.Packaging; using Xunit; using Xunit.Abstractions; @@ -37,8 +33,8 @@ public async Task Multiple_WebServer_instances_can_be_run_concurrently_in_the_sa using (var webServer1 = new WebServer(workspace)) using (var webServer2 = new WebServer(workspace)) { - var response1 = await webServer1.SendAsync(new HttpRequestMessage(HttpMethod.Get, "/api/values")); - var response2 = await webServer2.SendAsync(new HttpRequestMessage(HttpMethod.Get, "/api/values")); + var response1 = await webServer1.SendAsync(new HttpRequestMessage(HttpMethod.Get, "/custom/values")); + var response2 = await webServer2.SendAsync(new HttpRequestMessage(HttpMethod.Get, "/custom/values")); response1.EnsureSuccess(); response2.EnsureSuccess(); diff --git a/WorkspaceServer/Kernel/CSharpRepl.cs b/WorkspaceServer/Kernel/CSharpRepl.cs index 19f5be42d..85b188d4e 100644 --- a/WorkspaceServer/Kernel/CSharpRepl.cs +++ b/WorkspaceServer/Kernel/CSharpRepl.cs @@ -23,7 +23,7 @@ public class CSharpRepl : KernelBase private ScriptState _scriptState; - protected CSharpParseOptions ParseOptions = new CSharpParseOptions(LanguageVersion.Latest, kind: SourceCodeKind.Script); + protected CSharpParseOptions ParseOptions = new CSharpParseOptions(LanguageVersion.Default, kind: SourceCodeKind.Script); protected ScriptOptions ScriptOptions; private StringBuilder _inputBuffer = new StringBuilder(); diff --git a/WorkspaceServer/Packaging/BlazorPackageInitializer.cs b/WorkspaceServer/Packaging/BlazorPackageInitializer.cs index 56c825964..28347598e 100644 --- a/WorkspaceServer/Packaging/BlazorPackageInitializer.cs +++ b/WorkspaceServer/Packaging/BlazorPackageInitializer.cs @@ -14,20 +14,21 @@ namespace WorkspaceServer.Packaging public class BlazorPackageInitializer : PackageInitializer { private readonly string _name; - private readonly List _addPackages; + private readonly List<(string packageName, string packageVersion)> _addPackages; - public BlazorPackageInitializer(string name, List addPackages) : + public BlazorPackageInitializer(string name, List<(string packageName, string packageVersion)> addPackages) : base("blazor", "MLS.Blazor") { _name = name; - _addPackages = addPackages ?? throw new ArgumentNullException(nameof(addPackages)); + var packages = addPackages ?? throw new ArgumentNullException(nameof(addPackages)); - var requiredPackages = new List + var requiredPackages = new List<(string packageName, string packageVersion)> { - "Newtonsoft.Json" + ("Newtonsoft.Json", "12.0.02"), + ("system.commandline.experimental", "0.3.0-alpha.19317.1") }; - _addPackages = addPackages.Concat(requiredPackages).Distinct().ToList(); + _addPackages = packages.Concat(requiredPackages).Distinct().ToList(); } public override async Task Initialize(DirectoryInfo directory, Budget budget = null) @@ -53,10 +54,14 @@ private async Task MakeBlazorProject(DirectoryInfo directory, Budget budget) foreach (var packageId in _addPackages) { - await dotnet.AddPackage(packageId); - } + var addPackageResult = await dotnet.AddPackage(packageId.packageName, packageId.packageVersion); + var addPackageResultMessage = string.Concat( + string.Join("\n", addPackageResult.Output), + string.Join("\n", addPackageResult.Error)); - await dotnet.AddPackage("System.CommandLine.Experimental", "0.3.0-alpha.19317.1"); + addPackageResult.ThrowOnFailure(addPackageResultMessage); + } + var result = await dotnet.Build("-o runtime /bl", budget: budget); var stuff = string.Concat(string.Join("\n", result.Output), (string.Join("\n", result.Error))); @@ -113,8 +118,8 @@ private void WriteResourcesToLocation(string[] resources, string targetDirectory private void WriteResource(string resourceName, string targetDirectory) { - var text = this.GetType().ReadManifestResource($"WorkspaceServer.{resourceName}"); - System.IO.Directory.CreateDirectory(targetDirectory); + var text = GetType().ReadManifestResource($"WorkspaceServer.{resourceName}"); + Directory.CreateDirectory(targetDirectory); var path = Path.Combine(targetDirectory, resourceName); File.WriteAllText(path, text); } diff --git a/WorkspaceServer/Packaging/PackageBuilder.cs b/WorkspaceServer/Packaging/PackageBuilder.cs index c85226e91..7293f1b28 100644 --- a/WorkspaceServer/Packaging/PackageBuilder.cs +++ b/WorkspaceServer/Packaging/PackageBuilder.cs @@ -13,7 +13,7 @@ public class PackageBuilder { private PackageBase _packageBase; private readonly List> _afterCreateActions = new List>(); - private readonly List _addPackages = new List(); + private readonly List<(string packageName, string packageVersion)> _addPackages = new List<(string packageName, string packageVersion)>(); private string _languageVersion = "8.0"; public PackageBuilder(string packageName, IPackageInitializer packageInitializer = null) @@ -48,7 +48,7 @@ public void CreateUsingDotnet(string template, string projectName = null, string public void AddPackageReference(string packageId, string version = null) { - _addPackages.Add(packageId); + _addPackages.Add((packageId, version)); _afterCreateActions.Add(async (package, budget) => { Func action = async () => @@ -127,7 +127,20 @@ public void DeleteFile(string relativePath) { await Task.Yield(); var filePath = Path.Combine(workspace.Directory.FullName, relativePath); - File.Delete(filePath); + if (File.Exists(filePath)) + { + File.Delete(filePath); + } + }); + } + + public void WriteFile(string relativePath, string content) + { + _afterCreateActions.Add(async (workspace, budget) => + { + await Task.Yield(); + var filePath = Path.Combine(workspace.Directory.FullName, relativePath); + File.WriteAllText(filePath,content); }); } diff --git a/WorkspaceServer/WorkspaceFeatures/WebServer.cs b/WorkspaceServer/WorkspaceFeatures/WebServer.cs index e31f0aebb..42b3bef84 100644 --- a/WorkspaceServer/WorkspaceFeatures/WebServer.cs +++ b/WorkspaceServer/WorkspaceFeatures/WebServer.cs @@ -51,7 +51,7 @@ private async Task RunKestrel() StandardOutput.OnNext, StandardError.OnNext, ("ASPNETCORE_DETAILEDERRORS", "1"), - ("ASPNETCORE_URLS", $"http://127.0.0.1:0"), + ("ASPNETCORE_URLS", "http://127.0.0.1:0"), ("ASPNETCORE_PORT", null)); _disposables.Add(() =>