diff --git a/src/.editorconfig b/.editorconfig similarity index 99% rename from src/.editorconfig rename to .editorconfig index cffde480..9d2e9100 100644 --- a/src/.editorconfig +++ b/.editorconfig @@ -14,4 +14,3 @@ trim_trailing_whitespace = false [*.{razor,cshtml}] charset = utf-8-bom - diff --git a/.gitignore b/.gitignore index 07f9ca24..130f4ede 100644 --- a/.gitignore +++ b/.gitignore @@ -52,7 +52,6 @@ BenchmarkDotNet.Artifacts/ project.lock.json project.fragment.lock.json artifacts/ -**/Properties/launchSettings.json # optional local settings appsettings.Local.json @@ -225,7 +224,7 @@ ClientBin/ *.publishsettings orleans.codegen.cs -# Including strong name files can present a security risk +# Including strong name files can present a security risk # (https://github.com/github/gitignore/pull/2483#issue-259490424) #*.snk @@ -321,7 +320,7 @@ __pycache__/ # OpenCover UI analysis results OpenCover/ -# Azure Stream Analytics local run output +# Azure Stream Analytics local run output ASALocalRun/ # MSBuild Binary and Structured Log @@ -330,7 +329,7 @@ ASALocalRun/ # NVidia Nsight GPU debugger configuration file *.nvuser -# MFractors (Xamarin productivity tool) working folder +# MFractors (Xamarin productivity tool) working folder .mfractor/ *.DS_Store @@ -348,4 +347,4 @@ src/Apps/Web/Blink.Apps.WebApp/global.json NuGetScratch/ VBCSCompiler/ .dotnet/ -dist/ \ No newline at end of file +dist/ diff --git a/Directory.Build.props b/Directory.Build.props new file mode 100644 index 00000000..828b1338 --- /dev/null +++ b/Directory.Build.props @@ -0,0 +1,13 @@ + + + net6.0 + net6.0 + latest + True + Enable + True + https://github.com/tareqimbasher/netpad + https://github.com/tareqimbasher/netpad + Tareq Imbasher + + diff --git a/global.json b/global.json new file mode 100644 index 00000000..bad9dc3b --- /dev/null +++ b/global.json @@ -0,0 +1,6 @@ +{ + "sdk": { + "version": "8.0.0", + "rollForward": "latestMinor" + } +} diff --git a/src/Apps/NetPad.Apps.App/App/src/core/@common/utils/system.ts b/src/Apps/NetPad.Apps.App/App/src/core/@common/utils/system.ts index d7747dfd..fe03f885 100644 --- a/src/Apps/NetPad.Apps.App/App/src/core/@common/utils/system.ts +++ b/src/Apps/NetPad.Apps.App/App/src/core/@common/utils/system.ts @@ -1,5 +1,3 @@ -import * as process from "process"; - export class System { /** * Opens a URL in system-configured default browser. @@ -15,9 +13,14 @@ export class System { } public static getPlatform() { - try { - return process.platform; - } catch { + if (this.isRunningInElectron()) { + try { + // eslint-disable-next-line @typescript-eslint/no-var-requires + return require("process").platform; + } catch { + return undefined; + } + } else { return undefined; } } diff --git a/src/Apps/NetPad.Apps.App/App/src/core/@domain/api.ts b/src/Apps/NetPad.Apps.App/App/src/core/@domain/api.ts index 68d6a949..36a86303 100644 --- a/src/Apps/NetPad.Apps.App/App/src/core/@domain/api.ts +++ b/src/Apps/NetPad.Apps.App/App/src/core/@domain/api.ts @@ -1324,7 +1324,7 @@ export interface IScriptsApiClient { save(id: string, signal?: AbortSignal | undefined): Promise; - run(id: string, dto: RunOptionsDto, signal?: AbortSignal | undefined): Promise; + run(id: string, options: RunOptions, signal?: AbortSignal | undefined): Promise; stop(id: string, signal?: AbortSignal | undefined): Promise; @@ -1541,14 +1541,14 @@ export class ScriptsApiClient extends ApiClientBase implements IScriptsApiClient return Promise.resolve(null); } - run(id: string, dto: RunOptionsDto, signal?: AbortSignal | undefined): Promise { + run(id: string, options: RunOptions, signal?: AbortSignal | undefined): Promise { let url_ = this.baseUrl + "/scripts/{id}/run"; if (id === undefined || id === null) throw new Error("The parameter 'id' must be defined."); url_ = url_.replace("{id}", encodeURIComponent("" + id)); url_ = url_.replace(/[?&]$/, ""); - const content_ = JSON.stringify(dto); + const content_ = JSON.stringify(options); let options_ = { body: content_, @@ -2249,7 +2249,7 @@ export interface ISettingsApiClient { get(signal?: AbortSignal | undefined): Promise; - update(settings: Settings, signal?: AbortSignal | undefined): Promise; + update(update: Settings, signal?: AbortSignal | undefined): Promise; openSettingsWindow(tab: string | null | undefined, signal?: AbortSignal | undefined): Promise; @@ -2302,11 +2302,11 @@ export class SettingsApiClient extends ApiClientBase implements ISettingsApiClie return Promise.resolve(null); } - update(settings: Settings, signal?: AbortSignal | undefined): Promise { + update(update: Settings, signal?: AbortSignal | undefined): Promise { let url_ = this.baseUrl + "/settings"; url_ = url_.replace(/[?&]$/, ""); - const content_ = JSON.stringify(settings); + const content_ = JSON.stringify(update); let options_ = { body: content_, @@ -2669,12 +2669,19 @@ export interface IAppDependencyCheckResult { isSupportedDotNetEfToolInstalled: boolean; } +/** An implementation of semantic versioning (https://semver.org) */ export class SemanticVersion implements ISemanticVersion { + /** The major version number, never negative. */ major!: number; + /** The minor version number, never negative. */ minor!: number; + /** The patch version, -1 if not specified. */ patch!: number; + /** PreReleaseLabel position in the SymVer string 'major.minor.patch-PreReleaseLabel+BuildLabel'. */ preReleaseLabel?: string | undefined; + /** BuildLabel position in the SymVer string 'major.minor.patch-PreReleaseLabel+BuildLabel'. */ buildLabel?: string | undefined; + /** String representation. */ string!: string; constructor(data?: ISemanticVersion) { @@ -2723,12 +2730,19 @@ export class SemanticVersion implements ISemanticVersion { } } +/** An implementation of semantic versioning (https://semver.org) */ export interface ISemanticVersion { + /** The major version number, never negative. */ major: number; + /** The minor version number, never negative. */ minor: number; + /** The patch version, -1 if not specified. */ patch: number; + /** PreReleaseLabel position in the SymVer string 'major.minor.patch-PreReleaseLabel+BuildLabel'. */ preReleaseLabel?: string | undefined; + /** BuildLabel position in the SymVer string 'major.minor.patch-PreReleaseLabel+BuildLabel'. */ buildLabel?: string | undefined; + /** String representation. */ string: string; } @@ -3188,6 +3202,7 @@ export class SyntaxNodeOrTokenSlim implements ISyntaxNodeOrTokenSlim { isToken!: boolean; isNode!: boolean; kind!: SyntaxKind; + type!: string; span!: LinePositionSpan; isMissing!: boolean; valueText?: string | undefined; @@ -3215,6 +3230,7 @@ export class SyntaxNodeOrTokenSlim implements ISyntaxNodeOrTokenSlim { this.isToken = _data["isToken"]; this.isNode = _data["isNode"]; this.kind = _data["kind"]; + this.type = _data["type"]; this.span = _data["span"] ? LinePositionSpan.fromJS(_data["span"]) : new LinePositionSpan(); this.isMissing = _data["isMissing"]; this.valueText = _data["valueText"]; @@ -3248,6 +3264,7 @@ export class SyntaxNodeOrTokenSlim implements ISyntaxNodeOrTokenSlim { data["isToken"] = this.isToken; data["isNode"] = this.isNode; data["kind"] = this.kind; + data["type"] = this.type; data["span"] = this.span ? this.span.toJSON() : undefined; data["isMissing"] = this.isMissing; data["valueText"] = this.valueText; @@ -3281,6 +3298,7 @@ export interface ISyntaxNodeOrTokenSlim { isToken: boolean; isNode: boolean; kind: SyntaxKind; + type: string; span: LinePositionSpan; isMissing: boolean; valueText?: string | undefined; @@ -4234,7 +4252,7 @@ export interface IPackageIdentity { version: string; } -export type DotNetFrameworkVersion = "DotNet2" | "DotNet3" | "DotNet5" | "DotNet6" | "DotNet7" | "DotNet8" | "DotNet9"; +export type DotNetFrameworkVersion = "DotNet5" | "DotNet6" | "DotNet7" | "DotNet8" | "DotNet9"; export class ScriptSummary implements IScriptSummary { id!: string; @@ -4344,11 +4362,13 @@ export interface ICreateScriptDto { runImmediately: boolean; } -export class RunOptionsDto implements IRunOptionsDto { +/** Options that configure the running of a script. */ +export class RunOptions implements IRunOptions { + /** If not null, this code will run instead of script code. Typically used to only run code that user has +highlighted in the editor. */ specificCodeToRun?: string | undefined; - additionalCode?: SourceCodeDto[] | undefined; - constructor(data?: IRunOptionsDto) { + constructor(data?: IRunOptions) { if (data) { for (var property in data) { if (data.hasOwnProperty(property)) @@ -4360,17 +4380,12 @@ export class RunOptionsDto implements IRunOptionsDto { init(_data?: any) { if (_data) { this.specificCodeToRun = _data["specificCodeToRun"]; - if (Array.isArray(_data["additionalCode"])) { - this.additionalCode = [] as any; - for (let item of _data["additionalCode"]) - this.additionalCode!.push(SourceCodeDto.fromJS(item)); - } } } - static fromJS(data: any): RunOptionsDto { + static fromJS(data: any): RunOptions { data = typeof data === 'object' ? data : {}; - let result = new RunOptionsDto(); + let result = new RunOptions(); result.init(data); return result; } @@ -4378,80 +4393,22 @@ export class RunOptionsDto implements IRunOptionsDto { toJSON(data?: any) { data = typeof data === 'object' ? data : {}; data["specificCodeToRun"] = this.specificCodeToRun; - if (Array.isArray(this.additionalCode)) { - data["additionalCode"] = []; - for (let item of this.additionalCode) - data["additionalCode"].push(item.toJSON()); - } return data; } - clone(): RunOptionsDto { + clone(): RunOptions { const json = this.toJSON(); - let result = new RunOptionsDto(); + let result = new RunOptions(); result.init(json); return result; } } -export interface IRunOptionsDto { +/** Options that configure the running of a script. */ +export interface IRunOptions { + /** If not null, this code will run instead of script code. Typically used to only run code that user has +highlighted in the editor. */ specificCodeToRun?: string | undefined; - additionalCode?: SourceCodeDto[] | undefined; -} - -export class SourceCodeDto implements ISourceCodeDto { - usings?: string[] | undefined; - code?: string | undefined; - - constructor(data?: ISourceCodeDto) { - if (data) { - for (var property in data) { - if (data.hasOwnProperty(property)) - (this)[property] = (data)[property]; - } - } - } - - init(_data?: any) { - if (_data) { - if (Array.isArray(_data["usings"])) { - this.usings = [] as any; - for (let item of _data["usings"]) - this.usings!.push(item); - } - this.code = _data["code"]; - } - } - - static fromJS(data: any): SourceCodeDto { - data = typeof data === 'object' ? data : {}; - let result = new SourceCodeDto(); - result.init(data); - return result; - } - - toJSON(data?: any) { - data = typeof data === 'object' ? data : {}; - if (Array.isArray(this.usings)) { - data["usings"] = []; - for (let item of this.usings) - data["usings"].push(item); - } - data["code"] = this.code; - return data; - } - - clone(): SourceCodeDto { - const json = this.toJSON(); - let result = new SourceCodeDto(); - result.init(json); - return result; - } -} - -export interface ISourceCodeDto { - usings?: string[] | undefined; - code?: string | undefined; } export type OptimizationLevel = "Debug" | "Release"; @@ -5163,6 +5120,7 @@ export interface IKeyboardShortcutConfiguration { export type KeyCode = "Unknown" | "Backspace" | "Tab" | "Enter" | "ShiftLeft" | "ShiftRight" | "ControlLeft" | "ControlRight" | "AltLeft" | "AltRight" | "Pause" | "CapsLock" | "Escape" | "Space" | "PageUp" | "PageDown" | "End" | "Home" | "ArrowLeft" | "ArrowUp" | "ArrowRight" | "ArrowDown" | "PrintScreen" | "Insert" | "Delete" | "Digit0" | "Digit1" | "Digit2" | "Digit3" | "Digit4" | "Digit5" | "Digit6" | "Digit7" | "Digit8" | "Digit9" | "KeyA" | "KeyB" | "KeyC" | "KeyD" | "KeyE" | "KeyF" | "KeyG" | "KeyH" | "KeyI" | "KeyJ" | "KeyK" | "KeyL" | "KeyM" | "KeyN" | "KeyO" | "KeyP" | "KeyQ" | "KeyR" | "KeyS" | "KeyT" | "KeyU" | "KeyV" | "KeyW" | "KeyX" | "KeyY" | "KeyZ" | "MetaLeft" | "MetaRight" | "ContextMenu" | "Numpad0" | "Numpad1" | "Numpad2" | "Numpad3" | "Numpad4" | "Numpad5" | "Numpad6" | "Numpad7" | "Numpad8" | "Numpad9" | "NumpadMultiply" | "NumpadAdd" | "NumpadSubtract" | "NumpadDecimal" | "NumpadDivide" | "F1" | "F2" | "F3" | "F4" | "F5" | "F6" | "F7" | "F8" | "F9" | "F10" | "F11" | "F12" | "NumLock" | "ScrollLock" | "Semicolon" | "Equal" | "Comma" | "Minus" | "Period" | "Slash" | "Backquote" | "BracketLeft" | "Backslash" | "BracketRight" | "Quote"; +/** This should be moved to the OmniSharp plugin */ export class OmniSharpOptions implements IOmniSharpOptions { enabled!: boolean; executablePath?: string | undefined; @@ -5227,6 +5185,7 @@ export class OmniSharpOptions implements IOmniSharpOptions { } } +/** This should be moved to the OmniSharp plugin */ export interface IOmniSharpOptions { enabled: boolean; executablePath?: string | undefined; @@ -5713,8 +5672,11 @@ export interface IErrorResult { details?: string | undefined; } +/** A base class for all script output */ export abstract class ScriptOutput implements IScriptOutput { + /** The body of the output. */ body?: any | undefined; + /** The order this output was emitted. A value of 0 indicates no order. */ order!: number; constructor(data?: IScriptOutput) { @@ -5750,11 +5712,15 @@ export abstract class ScriptOutput implements IScriptOutput { } } +/** A base class for all script output */ export interface IScriptOutput { + /** The body of the output. */ body?: any | undefined; + /** The order this output was emitted. A value of 0 indicates no order. */ order: number; } +/** A base class for script output with an HTML-formatted string as the body. */ export abstract class HtmlScriptOutput extends ScriptOutput implements IHtmlScriptOutput { body?: string | undefined; @@ -5786,10 +5752,12 @@ export abstract class HtmlScriptOutput extends ScriptOutput implements IHtmlScri } } +/** A base class for script output with an HTML-formatted string as the body. */ export interface IHtmlScriptOutput extends IScriptOutput { body?: string | undefined; } +/** Results script output represented as HTML. */ export class HtmlResultsScriptOutput extends HtmlScriptOutput implements IHtmlResultsScriptOutput { constructor(data?: IHtmlResultsScriptOutput) { @@ -5821,9 +5789,11 @@ export class HtmlResultsScriptOutput extends HtmlScriptOutput implements IHtmlRe } } +/** Results script output represented as HTML. */ export interface IHtmlResultsScriptOutput extends IHtmlScriptOutput { } +/** Error script output represented as HTML. */ export class HtmlErrorScriptOutput extends HtmlScriptOutput implements IHtmlErrorScriptOutput { constructor(data?: IHtmlErrorScriptOutput) { @@ -5855,9 +5825,11 @@ export class HtmlErrorScriptOutput extends HtmlScriptOutput implements IHtmlErro } } +/** Error script output represented as HTML. */ export interface IHtmlErrorScriptOutput extends IHtmlScriptOutput { } +/** Raw script output represented as HTML. */ export class HtmlRawScriptOutput extends HtmlScriptOutput implements IHtmlRawScriptOutput { constructor(data?: IHtmlRawScriptOutput) { @@ -5889,9 +5861,11 @@ export class HtmlRawScriptOutput extends HtmlScriptOutput implements IHtmlRawScr } } +/** Raw script output represented as HTML. */ export interface IHtmlRawScriptOutput extends IHtmlScriptOutput { } +/** SQL script output represented as HTML. */ export class HtmlSqlScriptOutput extends HtmlScriptOutput implements IHtmlSqlScriptOutput { constructor(data?: IHtmlSqlScriptOutput) { @@ -5923,6 +5897,7 @@ export class HtmlSqlScriptOutput extends HtmlScriptOutput implements IHtmlSqlScr } } +/** SQL script output represented as HTML. */ export interface IHtmlSqlScriptOutput extends IHtmlScriptOutput { } @@ -6018,11 +5993,17 @@ export interface IAppStatusMessagePublishedEvent { message: AppStatusMessage; } +/** Represents a status change in the application. */ export class AppStatusMessage implements IAppStatusMessage { + /** The ID of the script this message relates to. */ scriptId?: string | undefined; + /** The text of this message. */ text!: string; + /** The priority of this message. */ priority!: AppStatusMessagePriority; + /** Whether this status message should be persistant or it should clear out after a timeout. */ persistant!: boolean; + /** The DateTime of when this message was created. */ createdDate!: Date; constructor(data?: IAppStatusMessage) { @@ -6069,11 +6050,17 @@ export class AppStatusMessage implements IAppStatusMessage { } } +/** Represents a status change in the application. */ export interface IAppStatusMessage { + /** The ID of the script this message relates to. */ scriptId?: string | undefined; + /** The text of this message. */ text: string; + /** The priority of this message. */ priority: AppStatusMessagePriority; + /** Whether this status message should be persistant or it should clear out after a timeout. */ persistant: boolean; + /** The DateTime of when this message was created. */ createdDate: Date; } diff --git a/src/Apps/NetPad.Apps.App/App/src/windows/main/panes/code-pane/syntax-tree-view/syntax-tree-view.html b/src/Apps/NetPad.Apps.App/App/src/windows/main/panes/code-pane/syntax-tree-view/syntax-tree-view.html index b5ebb80a..f4455e78 100644 --- a/src/Apps/NetPad.Apps.App/App/src/windows/main/panes/code-pane/syntax-tree-view/syntax-tree-view.html +++ b/src/Apps/NetPad.Apps.App/App/src/windows/main/panes/code-pane/syntax-tree-view/syntax-tree-view.html @@ -37,7 +37,10 @@ - ${syntaxNode.kind} + + ${syntaxNode.kind} + { const document = viewable.textDocument; - const runOptions = new RunOptionsDto(); + const runOptions = new RunOptions(); if (document.selection && !document.selection.isEmpty()) { runOptions.specificCodeToRun = document.textModel.getValueInRange(document.selection); diff --git a/src/Apps/NetPad.Apps.App/App/src/windows/settings/about/about.html b/src/Apps/NetPad.Apps.App/App/src/windows/settings/about/about.html index feadca34..a2c86402 100644 --- a/src/Apps/NetPad.Apps.App/App/src/windows/settings/about/about.html +++ b/src/Apps/NetPad.Apps.App/App/src/windows/settings/about/about.html @@ -19,8 +19,4 @@ GitHub - -
- Build: v${appId.productVersion} -
diff --git a/src/Apps/NetPad.Apps.App/App/webpack.config.js b/src/Apps/NetPad.Apps.App/App/webpack.config.js index 91c1427d..c6c5798c 100644 --- a/src/Apps/NetPad.Apps.App/App/webpack.config.js +++ b/src/Apps/NetPad.Apps.App/App/webpack.config.js @@ -58,6 +58,7 @@ module.exports = function (env, {analyze}) { ], fallback: { "fs": false, + "process": false, "path": require.resolve("path-browserify"), }, alias: { diff --git a/src/Apps/NetPad.Apps.App/BackgroundServices/AppSetupAndCleanupBackgroundService.cs b/src/Apps/NetPad.Apps.App/BackgroundServices/AppSetupAndCleanupBackgroundService.cs index 95c5b747..06586247 100644 --- a/src/Apps/NetPad.Apps.App/BackgroundServices/AppSetupAndCleanupBackgroundService.cs +++ b/src/Apps/NetPad.Apps.App/BackgroundServices/AppSetupAndCleanupBackgroundService.cs @@ -1,54 +1,40 @@ using System.Linq; -using System.Threading; -using System.Threading.Tasks; using Microsoft.Extensions.Logging; using NetPad.Application; +using NetPad.Apps.Plugins; using NetPad.Configuration; -using NetPad.Plugins; using NetPad.Scripts; using NetPad.Sessions; namespace NetPad.BackgroundServices; -public class AppSetupAndCleanupBackgroundService : BackgroundService +public class AppSetupAndCleanupBackgroundService( + ISession session, + IAutoSaveScriptRepository autoSaveScriptRepository, + IPluginManager pluginManager, + IAppStatusMessagePublisher appStatusMessagePublisher, + ILoggerFactory loggerFactory) + : BackgroundService(loggerFactory) { - private readonly ISession _session; - private readonly IAutoSaveScriptRepository _autoSaveScriptRepository; - private readonly IPluginManager _pluginManager; - private readonly IAppStatusMessagePublisher _appStatusMessagePublisher; - - public AppSetupAndCleanupBackgroundService( - ISession session, - IAutoSaveScriptRepository autoSaveScriptRepository, - IPluginManager pluginManager, - IAppStatusMessagePublisher appStatusMessagePublisher, - ILoggerFactory loggerFactory) : base(loggerFactory) - { - _session = session; - _autoSaveScriptRepository = autoSaveScriptRepository; - _pluginManager = pluginManager; - _appStatusMessagePublisher = appStatusMessagePublisher; - } - protected override async Task ExecuteAsync(CancellationToken stoppingToken) { - var autoSavedScripts = await _autoSaveScriptRepository.GetScriptsAsync(); + var autoSavedScripts = await autoSaveScriptRepository.GetScriptsAsync(); - await _session.OpenAsync(autoSavedScripts); + await session.OpenAsync(autoSavedScripts); } protected override async Task StoppingAsync(CancellationToken cancellationToken) { - await _appStatusMessagePublisher.PublishAsync("Stopping...", AppStatusMessagePriority.Normal, true); + await appStatusMessagePublisher.PublishAsync("Stopping...", AppStatusMessagePriority.Normal, true); - var environments = _session.Environments; + var environments = session.Environments; - await _session.CloseAsync(environments.Select(e => e.Script.Id).ToArray()); + await session.CloseAsync(environments.Select(e => e.Script.Id).ToArray()); AppDataProvider.ExternalProcessesDirectoryPath.DeleteIfExists(); AppDataProvider.TypedDataContextTempDirectoryPath.DeleteIfExists(); - foreach (var registration in _pluginManager.PluginRegistrations) + foreach (var registration in pluginManager.PluginRegistrations) { await registration.Plugin.CleaupAsync(); } diff --git a/src/Apps/NetPad.Apps.App/BackgroundServices/BackgroundService.cs b/src/Apps/NetPad.Apps.App/BackgroundServices/BackgroundService.cs index 75f04b93..a6030962 100644 --- a/src/Apps/NetPad.Apps.App/BackgroundServices/BackgroundService.cs +++ b/src/Apps/NetPad.Apps.App/BackgroundServices/BackgroundService.cs @@ -1,6 +1,3 @@ -using System; -using System.Threading; -using System.Threading.Tasks; using Microsoft.Extensions.Logging; namespace NetPad.BackgroundServices; diff --git a/src/Apps/NetPad.Apps.App/BackgroundServices/DebugAssemblyUnloadBackgroundService.cs b/src/Apps/NetPad.Apps.App/BackgroundServices/DebugAssemblyUnloadBackgroundService.cs index f874c42a..62c30715 100644 --- a/src/Apps/NetPad.Apps.App/BackgroundServices/DebugAssemblyUnloadBackgroundService.cs +++ b/src/Apps/NetPad.Apps.App/BackgroundServices/DebugAssemblyUnloadBackgroundService.cs @@ -1,21 +1,14 @@ -using System; using System.Linq; -using System.Threading; -using System.Threading.Tasks; using Microsoft.Extensions.Logging; namespace NetPad.BackgroundServices; /// /// Periodically outputs the compiled script assemblies loaded by the program into memory. This is -/// mainly used to debug assembly unloading after script execution (when using InMemoryScriptRuntime only). +/// mainly used to debug assembly unloading after script execution (when using InMemoryScriptRunner only). /// -public class DebugAssemblyUnloadBackgroundService : BackgroundService +public class DebugAssemblyUnloadBackgroundService(ILoggerFactory loggerFactory) : BackgroundService(loggerFactory) { - public DebugAssemblyUnloadBackgroundService(ILoggerFactory loggerFactory) : base(loggerFactory) - { - } - protected override Task ExecuteAsync(CancellationToken stoppingToken) { Task.Run(async () => diff --git a/src/Apps/NetPad.Apps.App/BackgroundServices/EventForwardToIpcBackgroundService.cs b/src/Apps/NetPad.Apps.App/BackgroundServices/EventForwardToIpcBackgroundService.cs index 0805fe08..dc6030bf 100644 --- a/src/Apps/NetPad.Apps.App/BackgroundServices/EventForwardToIpcBackgroundService.cs +++ b/src/Apps/NetPad.Apps.App/BackgroundServices/EventForwardToIpcBackgroundService.cs @@ -1,30 +1,26 @@ -using System; using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; using Microsoft.Extensions.Logging; +using NetPad.Application.Events; +using NetPad.Apps.UiInterop; +using NetPad.Configuration.Events; +using NetPad.Data.Events; using NetPad.Events; using NetPad.Scripts; -using NetPad.UiInterop; +using NetPad.Scripts.Events; +using NetPad.Sessions.Events; namespace NetPad.BackgroundServices; /// /// Forwards specific events to IPC clients. /// -public class EventForwardToIpcBackgroundService : BackgroundService +public class EventForwardToIpcBackgroundService( + IEventBus eventBus, + IIpcService ipcService, + ILoggerFactory loggerFactory) + : BackgroundService(loggerFactory) { - private readonly IEventBus _eventBus; - private readonly IIpcService _ipcService; - private readonly Dictionary> _environmentSubscriptionTokens; - - public EventForwardToIpcBackgroundService(IEventBus eventBus, IIpcService ipcService, ILoggerFactory loggerFactory) - : base(loggerFactory) - { - _eventBus = eventBus; - _ipcService = ipcService; - _environmentSubscriptionTokens = new Dictionary>(); - } + private readonly Dictionary> _environmentSubscriptionTokens = new(); protected override Task ExecuteAsync(CancellationToken stoppingToken) { @@ -52,7 +48,7 @@ private void ForwardApplicationLevelEvents() private void ForwardEnvironmentLevelEvents() { - _eventBus.Subscribe(ev => + eventBus.Subscribe(ev => { foreach (var environment in ev.Environments) { @@ -78,7 +74,7 @@ private void ForwardEnvironmentLevelEvents() return Task.CompletedTask; }); - _eventBus.Subscribe(ev => + eventBus.Subscribe(ev => { Unsubscribe(ev.Environments); return Task.CompletedTask; @@ -87,20 +83,20 @@ private void ForwardEnvironmentLevelEvents() private void SubscribeAndForwardToIpc() where TEvent : class, IEvent { - _eventBus.Subscribe(async ev => { await _ipcService.SendAsync(ev); }); + eventBus.Subscribe(async ev => { await ipcService.SendAsync(ev); }); } private void SubscribeAndForwardToIpc(ScriptEnvironment environment, Func? predicate = null) where TEvent : class, IScriptEvent { - var token = _eventBus.Subscribe(async ev => + var token = eventBus.Subscribe(async ev => { if (predicate != null && !predicate(ev)) { return; } - await _ipcService.SendAsync(ev); + await ipcService.SendAsync(ev); }); AddEnvironmentEventToken(environment, token); @@ -110,7 +106,7 @@ private void AddEnvironmentEventToken(ScriptEnvironment environment, EventSubscr { if (!_environmentSubscriptionTokens.TryGetValue(environment.Script.Id, out var tokens)) { - tokens = new List(); + tokens = []; _environmentSubscriptionTokens.Add(environment.Script.Id, tokens); } @@ -128,7 +124,7 @@ private void Unsubscribe(ScriptEnvironment[] environments) foreach (var token in tokens) { - _eventBus.Unsubscribe(token); + eventBus.Unsubscribe(token); } _environmentSubscriptionTokens.Remove(environment.Script.Id); diff --git a/src/Apps/NetPad.Apps.App/BackgroundServices/EventHandlerBackgroundService.cs b/src/Apps/NetPad.Apps.App/BackgroundServices/EventHandlerBackgroundService.cs index f91343ba..3b445308 100644 --- a/src/Apps/NetPad.Apps.App/BackgroundServices/EventHandlerBackgroundService.cs +++ b/src/Apps/NetPad.Apps.App/BackgroundServices/EventHandlerBackgroundService.cs @@ -1,23 +1,16 @@ -using System.Threading; -using System.Threading.Tasks; using Microsoft.Extensions.Logging; +using NetPad.Configuration.Events; using NetPad.Events; using NetPad.Presentation.Html; namespace NetPad.BackgroundServices; -public class EventHandlerBackgroundService : BackgroundService +public class EventHandlerBackgroundService(IEventBus eventBus, ILoggerFactory loggerFactory) + : BackgroundService(loggerFactory) { - private readonly IEventBus _eventBus; - - public EventHandlerBackgroundService(IEventBus eventBus, ILoggerFactory loggerFactory) : base(loggerFactory) - { - _eventBus = eventBus; - } - protected override Task ExecuteAsync(CancellationToken stoppingToken) { - _eventBus.Subscribe(ev => + eventBus.Subscribe(ev => { HtmlPresenter.UpdateSerializerSettings(ev.Settings.Results.MaxSerializationDepth, ev.Settings.Results.MaxCollectionSerializeLength); diff --git a/src/Apps/NetPad.Apps.App/BackgroundServices/ScriptEnvironmentBackgroundService.cs b/src/Apps/NetPad.Apps.App/BackgroundServices/ScriptEnvironmentBackgroundService.cs index 99d3c52d..54b38a1f 100644 --- a/src/Apps/NetPad.Apps.App/BackgroundServices/ScriptEnvironmentBackgroundService.cs +++ b/src/Apps/NetPad.Apps.App/BackgroundServices/ScriptEnvironmentBackgroundService.cs @@ -1,41 +1,29 @@ -using System; using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; using Microsoft.Extensions.Logging; -using NetPad.CQs; +using NetPad.Apps.CQs; +using NetPad.Apps.UiInterop; using NetPad.Events; using NetPad.IO; using NetPad.Scripts; +using NetPad.Scripts.Events; using NetPad.Services; -using NetPad.UiInterop; +using NetPad.Sessions.Events; namespace NetPad.BackgroundServices; /// /// Handles automations that occur when a script environment is added to removed from the session. /// -public class ScriptEnvironmentBackgroundService : BackgroundService +public class ScriptEnvironmentBackgroundService( + IEventBus eventBus, + IIpcService ipcService, + IAutoSaveScriptRepository autoSaveScriptRepository, + ILoggerFactory loggerFactory) + : BackgroundService(loggerFactory) { - private readonly IEventBus _eventBus; - private readonly IIpcService _ipcService; - private readonly IAutoSaveScriptRepository _autoSaveScriptRepository; - private readonly ILoggerFactory _loggerFactory; - - private readonly Dictionary> _environmentSubscriptionTokens; - - public ScriptEnvironmentBackgroundService( - IEventBus eventBus, - IIpcService ipcService, - IAutoSaveScriptRepository autoSaveScriptRepository, - ILoggerFactory loggerFactory) : base(loggerFactory) - { - _eventBus = eventBus; - _ipcService = ipcService; - _autoSaveScriptRepository = autoSaveScriptRepository; - _loggerFactory = loggerFactory; - _environmentSubscriptionTokens = new Dictionary>(); - } + private readonly ILoggerFactory _loggerFactory = loggerFactory; + + private readonly Dictionary> _environmentSubscriptionTokens = new(); protected override Task ExecuteAsync(CancellationToken stoppingToken) { @@ -46,7 +34,7 @@ protected override Task ExecuteAsync(CancellationToken stoppingToken) private void ListenToEnvironmentsChanges() { - _eventBus.Subscribe(ev => + eventBus.Subscribe(ev => { foreach (var environment in ev.Environments) { @@ -54,12 +42,12 @@ private void ListenToEnvironmentsChanges() var inputReader = new AsyncActionInputReader( // TODO There should be a way to cancel the wait when the environment stops using a CancellationToken - async () => await _ipcService.SendAndReceiveAsync(new PromptUserForInputCommand(environment.Script.Id))); + async () => await ipcService.SendAndReceiveAsync(new PromptUserForInputCommand(environment.Script.Id))); var outputWriter = new ScriptEnvironmentIpcOutputWriter( environment, - _ipcService, - _eventBus, + ipcService, + eventBus, _loggerFactory.CreateLogger()); environment.SetIO(inputReader, outputWriter); @@ -70,7 +58,7 @@ private void ListenToEnvironmentsChanges() return Task.CompletedTask; }); - _eventBus.Subscribe(ev => + eventBus.Subscribe(ev => { foreach (var environment in ev.Environments) { @@ -88,17 +76,17 @@ private void AutoSaveScriptChanges(ScriptEnvironment environment) if (scriptId != environment.Script.Id || !environment.Script.IsDirty) return; - await _autoSaveScriptRepository.SaveAsync(environment.Script); + await autoSaveScriptRepository.SaveAsync(environment.Script); }).DebounceAsync(3000); - var scriptPropChangeToken = _eventBus.Subscribe(ev => + var scriptPropChangeToken = eventBus.Subscribe(ev => { autoSave(ev.ScriptId); return Task.CompletedTask; }); AddEnvironmentEventToken(environment, scriptPropChangeToken); - var scriptConfigPropChangeToken = _eventBus.Subscribe(ev => + var scriptConfigPropChangeToken = eventBus.Subscribe(ev => { autoSave(ev.ScriptId); return Task.CompletedTask; @@ -110,7 +98,7 @@ private void AddEnvironmentEventToken(ScriptEnvironment environment, IDisposable { if (!_environmentSubscriptionTokens.TryGetValue(environment.Script.Id, out var tokens)) { - tokens = new List(); + tokens = []; _environmentSubscriptionTokens.Add(environment.Script.Id, tokens); } diff --git a/src/Apps/NetPad.Apps.App/BackgroundServices/ScriptsFileWatcherBackgroundService.cs b/src/Apps/NetPad.Apps.App/BackgroundServices/ScriptsFileWatcherBackgroundService.cs index e9400450..55ff1480 100644 --- a/src/Apps/NetPad.Apps.App/BackgroundServices/ScriptsFileWatcherBackgroundService.cs +++ b/src/Apps/NetPad.Apps.App/BackgroundServices/ScriptsFileWatcherBackgroundService.cs @@ -1,12 +1,9 @@ -using System; using System.IO; -using System.Threading; -using System.Threading.Tasks; using Microsoft.Extensions.Logging; +using NetPad.Apps.UiInterop; using NetPad.Configuration; -using NetPad.Events; using NetPad.Scripts; -using NetPad.UiInterop; +using NetPad.Scripts.Events; namespace NetPad.BackgroundServices; diff --git a/src/Apps/NetPad.Apps.App/Controllers/AppController.cs b/src/Apps/NetPad.Apps.App/Controllers/AppController.cs index 2d9753f9..ff93a36a 100644 --- a/src/Apps/NetPad.Apps.App/Controllers/AppController.cs +++ b/src/Apps/NetPad.Apps.App/Controllers/AppController.cs @@ -1,33 +1,24 @@ -using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Net.Http; using System.Text.Json; -using System.Threading.Tasks; using MediatR; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using NetPad.Application; +using NetPad.Apps.CQs; +using NetPad.Apps.UiInterop; using NetPad.Configuration; -using NetPad.CQs; using NetPad.Filters; -using NetPad.UiInterop; namespace NetPad.Controllers; [ApiController] [Route("app")] -public class AppController : ControllerBase +public class AppController(ILogger logger) : ControllerBase { - private readonly ILogger _logger; - - public AppController(ILogger logger) - { - _logger = logger; - } - [HttpGet("identifier")] public AppIdentifier GetIdentifier([FromServices] AppIdentifier appIdentifier) { @@ -67,7 +58,7 @@ public AppIdentifier GetIdentifier([FromServices] AppIdentifier appIdentifier) } catch (Exception ex) { - _logger.LogError(ex, "Error getting latest version"); + logger.LogError(ex, "Error getting latest version"); } return null; @@ -155,7 +146,7 @@ public void OpenPackageCacheFolder([FromServices] Settings settings) { var message = log.Message ?? string.Empty; - foreach (var logOptionalParam in log.OptionalParams ??= Array.Empty()) + foreach (var logOptionalParam in log.OptionalParams ??= []) { message += $" {logOptionalParam}"; } diff --git a/src/Apps/NetPad.Apps.App/Controllers/AssembliesController.cs b/src/Apps/NetPad.Apps.App/Controllers/AssembliesController.cs index 95e2c9b1..41df94ff 100644 --- a/src/Apps/NetPad.Apps.App/Controllers/AssembliesController.cs +++ b/src/Apps/NetPad.Apps.App/Controllers/AssembliesController.cs @@ -1,7 +1,5 @@ -using System; using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using NetPad.Assemblies; using NetPad.Common; @@ -12,15 +10,8 @@ namespace NetPad.Controllers; [ApiController] [Route("assemblies")] -public class AssembliesController : ControllerBase +public class AssembliesController(IPackageProvider packageProvider) : ControllerBase { - private readonly IPackageProvider _packageProvider; - - public AssembliesController(IPackageProvider packageProvider) - { - _packageProvider = packageProvider; - } - [HttpPatch("namespaces")] public async Task> GetNamespaces([FromBody] Reference reference) { @@ -36,7 +27,7 @@ public async Task> GetNamespaces([FromBody] Reference ref if (reference is PackageReference packageReference) { - var assets = await _packageProvider.GetCachedPackageAssetsAsync( + var assets = await packageProvider.GetCachedPackageAssetsAsync( packageReference.PackageId, packageReference.Version, GlobalConsts.AppDotNetFrameworkVersion); diff --git a/src/Apps/NetPad.Apps.App/Controllers/CodeController.cs b/src/Apps/NetPad.Apps.App/Controllers/CodeController.cs index c14e2288..85b2ef4a 100644 --- a/src/Apps/NetPad.Apps.App/Controllers/CodeController.cs +++ b/src/Apps/NetPad.Apps.App/Controllers/CodeController.cs @@ -1,4 +1,3 @@ -using System; using System.Text.Json; using Microsoft.AspNetCore.Mvc; using NetPad.CodeAnalysis; diff --git a/src/Apps/NetPad.Apps.App/Controllers/DataConnectionsController.cs b/src/Apps/NetPad.Apps.App/Controllers/DataConnectionsController.cs index a328fcc8..57c05287 100644 --- a/src/Apps/NetPad.Apps.App/Controllers/DataConnectionsController.cs +++ b/src/Apps/NetPad.Apps.App/Controllers/DataConnectionsController.cs @@ -1,28 +1,19 @@ -using System; using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; using MediatR; using Microsoft.AspNetCore.Mvc; +using NetPad.Apps.CQs; +using NetPad.Apps.Data.EntityFrameworkCore.DataConnections; +using NetPad.Apps.UiInterop; using NetPad.Common; -using NetPad.CQs; using NetPad.Data; -using NetPad.Data.EntityFrameworkCore.DataConnections; -using NetPad.UiInterop; namespace NetPad.Controllers; [ApiController] [Route("data-connections")] -public class DataConnectionsController : ControllerBase +public class DataConnectionsController(IMediator mediator) : ControllerBase { - private readonly IMediator _mediator; - - public DataConnectionsController(IMediator mediator) - { - _mediator = mediator; - } - [HttpPatch("open")] public async Task OpenDataConnectionWindow([FromServices] IUiWindowService uiWindowService, [FromQuery] Guid? dataConnectionId = null, [FromQuery] bool copy = false) { @@ -30,12 +21,12 @@ public async Task OpenDataConnectionWindow([FromServices] IUiWindowService uiWin } [HttpGet] - public async Task> GetAll() => await _mediator.Send(new GetDataConnectionsQuery()); + public async Task> GetAll() => await mediator.Send(new GetDataConnectionsQuery()); [HttpGet("{id:guid}")] public async Task> Get(Guid id) { - var connection = await _mediator.Send(new GetDataConnectionQuery(id)); + var connection = await mediator.Send(new GetDataConnectionQuery(id)); if (connection == null) return connection; @@ -60,13 +51,13 @@ public async Task> GetAllNames() } [HttpPut] - public async Task Save(DataConnection dataConnection) => await _mediator.Send(new SaveDataConnectionCommand(dataConnection)); + public async Task Save(DataConnection dataConnection) => await mediator.Send(new SaveDataConnectionCommand(dataConnection)); [HttpPatch("{id:guid}/refresh")] - public async Task Refresh(Guid id) => await _mediator.Send(new RefreshDataConnectionCommand(id)); + public async Task Refresh(Guid id) => await mediator.Send(new RefreshDataConnectionCommand(id)); [HttpDelete("{id:guid}")] - public async Task Delete(Guid id) => await _mediator.Send(new DeleteDataConnectionCommand(id)); + public async Task Delete(Guid id) => await mediator.Send(new DeleteDataConnectionCommand(id)); [HttpPost("connection-string")] public string GetConnectionString( @@ -102,13 +93,13 @@ public async Task> GetAllNames() [HttpPatch("{id:guid}/database-structure")] public async Task GetDatabaseStructure(Guid id) { - var dataConnection = await _mediator.Send(new GetDataConnectionQuery(id)); + var dataConnection = await mediator.Send(new GetDataConnectionQuery(id)); if (dataConnection is not DatabaseConnection databaseConnection) { throw new InvalidOperationException($"Cannot get database structure except on connections of type {nameof(DatabaseConnection)}."); } - return await _mediator.Send(new GetDatabaseConnectionStructureQuery(databaseConnection)); + return await mediator.Send(new GetDatabaseConnectionStructureQuery(databaseConnection)); } } diff --git a/src/Apps/NetPad.Apps.App/Controllers/FilesController.cs b/src/Apps/NetPad.Apps.App/Controllers/FilesController.cs index 370a3c15..f11a6f29 100644 --- a/src/Apps/NetPad.Apps.App/Controllers/FilesController.cs +++ b/src/Apps/NetPad.Apps.App/Controllers/FilesController.cs @@ -1,4 +1,3 @@ -using System; using System.IO; using Microsoft.AspNetCore.Mvc; diff --git a/src/Apps/NetPad.Apps.App/Controllers/PackagesController.cs b/src/Apps/NetPad.Apps.App/Controllers/PackagesController.cs index 647e38a8..aaee9802 100644 --- a/src/Apps/NetPad.Apps.App/Controllers/PackagesController.cs +++ b/src/Apps/NetPad.Apps.App/Controllers/PackagesController.cs @@ -1,7 +1,5 @@ using System.Collections.Generic; using System.Linq; -using System.Threading; -using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using NetPad.Common; using NetPad.DotNet; @@ -11,25 +9,18 @@ namespace NetPad.Controllers; [ApiController] [Route("packages")] -public class PackagesController : ControllerBase +public class PackagesController(IPackageProvider packageProvider) : ControllerBase { - private readonly IPackageProvider _packageProvider; - - public PackagesController(IPackageProvider packageProvider) - { - _packageProvider = packageProvider; - } - [HttpGet("cache")] public async Task>> GetCachedPackages([FromQuery] bool loadMetadata) { - return Ok(await _packageProvider.GetCachedPackagesAsync(loadMetadata)); + return Ok(await packageProvider.GetCachedPackagesAsync(loadMetadata)); } [HttpGet("cache/explicitly-installed")] public async Task>> GetExplicitlyInstalledCachedPackages([FromQuery] bool loadMetadata) { - return Ok(await _packageProvider.GetExplicitlyInstalledCachedPackagesAsync(loadMetadata)); + return Ok(await packageProvider.GetExplicitlyInstalledCachedPackagesAsync(loadMetadata)); } [HttpDelete("cache")] @@ -40,27 +31,27 @@ public async Task DeleteCachedPackage([FromQuery] string packageI if (string.IsNullOrWhiteSpace(packageVersion)) return BadRequest($"{nameof(packageVersion)} is required."); - await _packageProvider.DeleteCachedPackageAsync(packageId, packageVersion); + await packageProvider.DeleteCachedPackageAsync(packageId, packageVersion); return Ok(); } [HttpPatch("cache/purge")] public async Task PurgePackageCache() { - await _packageProvider.PurgePackageCacheAsync(); + await packageProvider.PurgePackageCacheAsync(); return Ok(); } [HttpGet("versions")] public async Task GetPackageVersionsAsync([FromQuery] string packageId, [FromQuery] bool includePrerelease = false) { - return await _packageProvider.GetPackageVersionsAsync(packageId, includePrerelease); + return await packageProvider.GetPackageVersionsAsync(packageId, includePrerelease); } [HttpPost("metadata")] public async Task GetPackageMetadata([FromBody] PackageIdentity[] packages, CancellationToken cancellationToken) { - return (await _packageProvider.GetExtendedMetadataAsync(packages, cancellationToken)) + return (await packageProvider.GetExtendedMetadataAsync(packages, cancellationToken)) .Values .Where(x => x != null) .ToArray()!; @@ -73,7 +64,7 @@ public async Task GetPackageMetadata([FromBody] PackageIdenti [FromQuery] int? take = null, [FromQuery] bool? includePrerelease = null) { - var packages = await _packageProvider.SearchPackagesAsync( + var packages = await packageProvider.SearchPackagesAsync( term, skip ?? 0, take ?? 30, @@ -95,11 +86,11 @@ public async Task GetPackageMetadata([FromBody] PackageIdenti if (string.IsNullOrWhiteSpace(packageVersion)) return BadRequest($"{nameof(packageVersion)} is required."); - var installInfo = await _packageProvider.GetPackageInstallInfoAsync(packageId, packageVersion); + var installInfo = await packageProvider.GetPackageInstallInfoAsync(packageId, packageVersion); if (installInfo?.InstallReason == PackageInstallReason.Explicit) return Ok(); - await _packageProvider.InstallPackageAsync(packageId, packageVersion, dotNetFrameworkVersion ?? GlobalConsts.AppDotNetFrameworkVersion); + await packageProvider.InstallPackageAsync(packageId, packageVersion, dotNetFrameworkVersion ?? GlobalConsts.AppDotNetFrameworkVersion); return Ok(); } } diff --git a/src/Apps/NetPad.Apps.App/Controllers/ScriptsController.cs b/src/Apps/NetPad.Apps.App/Controllers/ScriptsController.cs index bcbdffa6..ffbc6d31 100644 --- a/src/Apps/NetPad.Apps.App/Controllers/ScriptsController.cs +++ b/src/Apps/NetPad.Apps.App/Controllers/ScriptsController.cs @@ -1,59 +1,51 @@ -using System; using System.Collections.Generic; -using System.Threading.Tasks; using MediatR; using Microsoft.AspNetCore.Mvc; using Microsoft.CodeAnalysis; -using NetPad.CQs; +using NetPad.Apps.CQs; +using NetPad.Apps.UiInterop; using NetPad.Data; using NetPad.DotNet; using NetPad.Dtos; using NetPad.Exceptions; -using NetPad.Runtimes; +using NetPad.ExecutionModel; using NetPad.Scripts; -using NetPad.UiInterop; +using NetPad.Services; namespace NetPad.Controllers; [ApiController] [Route("scripts")] -public class ScriptsController : ControllerBase +public class ScriptsController(IMediator mediator) : ControllerBase { - private readonly IMediator _mediator; - - public ScriptsController(IMediator mediator) - { - _mediator = mediator; - } - [HttpGet] public async Task> GetScripts() { - return await _mediator.Send(new GetAllScriptsQuery()); + return await mediator.Send(new GetAllScriptsQuery()); } [HttpPatch("create")] public async Task Create([FromBody] CreateScriptDto dto, [FromServices] IDataConnectionRepository dataConnectionRepository) { - var script = await _mediator.Send(new CreateScriptCommand()); + var script = await mediator.Send(new CreateScriptCommand()); bool hasSeedCode = !string.IsNullOrWhiteSpace(dto.Code); if (hasSeedCode) { - await _mediator.Send(new UpdateScriptCodeCommand(script, dto.Code)); + await mediator.Send(new UpdateScriptCodeCommand(script, dto.Code)); } if (dto.DataConnectionId != null) { var dataConnection = await dataConnectionRepository.GetAsync(dto.DataConnectionId.Value); - await _mediator.Send(new SetScriptDataConnectionCommand(script, dataConnection)); + await mediator.Send(new SetScriptDataConnectionCommand(script, dataConnection)); } - await _mediator.Send(new OpenScriptCommand(script)); + await mediator.Send(new OpenScriptCommand(script)); if (hasSeedCode && dto.RunImmediately) { - await _mediator.Send(new RunScriptCommand(script.Id, new RunOptions())); + await mediator.Send(new RunScriptCommand(script.Id, new RunOptions())); } } @@ -61,41 +53,40 @@ public async Task Create([FromBody] CreateScriptDto dto, [FromServices] IDataCon public async Task Rename(Guid id, [FromBody] string newName) { var environment = await GetScriptEnvironmentAsync(id); - await _mediator.Send(new RenameScriptCommand(environment.Script, newName)); + await mediator.Send(new RenameScriptCommand(environment.Script, newName)); } [HttpPatch("{id:guid}/duplicate")] public async Task Duplicate(Guid id) { var environment = await GetScriptEnvironmentAsync(id); - var script = await _mediator.Send(new DuplicateScriptCommand(environment.Script)); - await _mediator.Send(new OpenScriptCommand(script)); + var script = await mediator.Send(new DuplicateScriptCommand(environment.Script)); + await mediator.Send(new OpenScriptCommand(script)); } [HttpPatch("{id:guid}/save")] - public async Task Save(Guid id) + public async Task Save(Guid id, [FromServices] ScriptService scriptService) { - var environment = await GetScriptEnvironmentAsync(id); - await _mediator.Send(new SaveScriptCommand(environment.Script)); + await scriptService.SaveScriptAsync(id); } [HttpPatch("{id:guid}/run")] - public async Task Run(Guid id, [FromBody] RunOptionsDto dto) + public async Task Run(Guid id, [FromBody] RunOptions options) { - await _mediator.Send(new RunScriptCommand(id, dto.ToRunOptions())); + await mediator.Send(new RunScriptCommand(id, options)); } [HttpPatch("{id:guid}/stop")] public async Task Stop(Guid id) { - await _mediator.Send(new StopScriptCommand(id)); + await mediator.Send(new StopScriptCommand(id)); } [HttpPut("{id:guid}/code")] public async Task UpdateCode(Guid id, [FromBody] string code) { var environment = await GetScriptEnvironmentAsync(id); - await _mediator.Send(new UpdateScriptCodeCommand(environment.Script, code)); + await mediator.Send(new UpdateScriptCodeCommand(environment.Script, code)); } [HttpPatch("{id:guid}/open-config")] @@ -111,7 +102,7 @@ public async Task SetScriptNamespaces(Guid id, [FromBody] IEnumer { var environment = await GetScriptEnvironmentAsync(id); - await _mediator.Send(new UpdateScriptNamespacesCommand(environment.Script, namespaces)); + await mediator.Send(new UpdateScriptNamespacesCommand(environment.Script, namespaces)); return NoContent(); } @@ -121,7 +112,7 @@ public async Task SetReferences(Guid id, [FromBody] IEnumerable SetTargetFrameworkVersion(Guid id, [FromBody] D { var environment = await GetScriptEnvironmentAsync(id); - await _mediator.Send(new UpdateScriptTargetFrameworkCommand(environment.Script, targetFrameworkVersion)); + await mediator.Send(new UpdateScriptTargetFrameworkCommand(environment.Script, targetFrameworkVersion)); return NoContent(); } @@ -149,7 +140,7 @@ public async Task SetOptimizationLevel(Guid id, [FromBody] Optimi { var environment = await GetScriptEnvironmentAsync(id); - await _mediator.Send(new UpdateScriptOptimizationLevelCommand(environment.Script, optimizationLevel)); + await mediator.Send(new UpdateScriptOptimizationLevelCommand(environment.Script, optimizationLevel)); return NoContent(); } @@ -159,7 +150,7 @@ public async Task SetUseAspNet(Guid id, [FromBody] bool useAspNet { var environment = await GetScriptEnvironmentAsync(id); - await _mediator.Send(new UpdateScriptUseAspNetCommand(environment.Script, useAspNet)); + await mediator.Send(new UpdateScriptUseAspNetCommand(environment.Script, useAspNet)); return NoContent(); } @@ -179,13 +170,13 @@ public async Task SetUseAspNet(Guid id, [FromBody] bool useAspNet dataConnection = await dataConnectionRepository.GetAsync(dataConnectionId.Value); } - await _mediator.Send(new SetScriptDataConnectionCommand(environment.Script, dataConnection)); + await mediator.Send(new SetScriptDataConnectionCommand(environment.Script, dataConnection)); return NoContent(); } private async Task GetScriptEnvironmentAsync(Guid id) { - return await _mediator.Send(new GetOpenedScriptEnvironmentQuery(id, true)) + return await mediator.Send(new GetOpenedScriptEnvironmentQuery(id, true)) ?? throw new ScriptNotFoundException(id); } } diff --git a/src/Apps/NetPad.Apps.App/Controllers/SessionController.cs b/src/Apps/NetPad.Apps.App/Controllers/SessionController.cs index 8950aa38..a62a46b9 100644 --- a/src/Apps/NetPad.Apps.App/Controllers/SessionController.cs +++ b/src/Apps/NetPad.Apps.App/Controllers/SessionController.cs @@ -1,66 +1,58 @@ -using System; using System.Collections.Generic; -using System.Threading.Tasks; using MediatR; using Microsoft.AspNetCore.Mvc; -using NetPad.CQs; +using NetPad.Apps.CQs; using NetPad.Exceptions; using NetPad.Scripts; +using NetPad.Services; namespace NetPad.Controllers; [ApiController] [Route("session")] -public class SessionController : ControllerBase +public class SessionController(IMediator mediator) : ControllerBase { - private readonly IMediator _mediator; - - public SessionController(IMediator mediator) - { - _mediator = mediator; - } - [HttpGet("environments/{scriptId:guid}")] public async Task GetEnvironment(Guid scriptId) { - return await _mediator.Send(new GetOpenedScriptEnvironmentQuery(scriptId)) + return await mediator.Send(new GetOpenedScriptEnvironmentQuery(scriptId)) ?? throw new EnvironmentNotFoundException(scriptId); } [HttpGet("environments")] public async Task> GetEnvironments() { - return await _mediator.Send(new GetOpenedScriptEnvironmentsQuery()); + return await mediator.Send(new GetOpenedScriptEnvironmentsQuery()); } [HttpPatch("open/path")] public async Task OpenByPath([FromBody] string scriptPath) { - await _mediator.Send(new OpenScriptCommand(scriptPath)); + await mediator.Send(new OpenScriptCommand(scriptPath)); } [HttpPatch("{scriptId:guid}/close")] - public async Task Close(Guid scriptId) + public async Task Close(Guid scriptId, [FromServices] ScriptService scriptService) { - await _mediator.Send(new CloseScriptCommand(scriptId)); + await scriptService.CloseScriptAsync(scriptId); } [HttpGet("active")] public async Task GetActive() { - var active = await _mediator.Send(new GetActiveScriptEnvironmentQuery()); + var active = await mediator.Send(new GetActiveScriptEnvironmentQuery()); return active?.Script.Id; } [HttpPatch("{scriptId:guid}/activate")] public async Task Activate(Guid scriptId) { - await _mediator.Send(new ActivateScriptCommand(scriptId)); + await mediator.Send(new ActivateScriptCommand(scriptId)); } [HttpPatch("activate-last-active")] public async Task ActivateLastActive() { - await _mediator.Send(new ActivateLastActiveScriptCommand()); + await mediator.Send(new ActivateLastActiveScriptCommand()); } } diff --git a/src/Apps/NetPad.Apps.App/Controllers/SettingsController.cs b/src/Apps/NetPad.Apps.App/Controllers/SettingsController.cs index 05895817..bd873d57 100644 --- a/src/Apps/NetPad.Apps.App/Controllers/SettingsController.cs +++ b/src/Apps/NetPad.Apps.App/Controllers/SettingsController.cs @@ -1,37 +1,26 @@ using System.IO; -using System.Threading.Tasks; using MediatR; using Microsoft.AspNetCore.Mvc; +using NetPad.Apps.CQs; +using NetPad.Apps.UiInterop; using NetPad.Configuration; -using NetPad.CQs; -using NetPad.UiInterop; namespace NetPad.Controllers; [ApiController] [Route("settings")] -public class SettingsController : ControllerBase +public class SettingsController(Settings settings, IMediator mediator) : ControllerBase { - private readonly Settings _settings; - private readonly IMediator _mediator; - - public SettingsController(Settings settings, IMediator mediator) - { - _settings = settings; - _mediator = mediator; - } - [HttpGet] public Settings Get() { - return _settings; + return settings; } [HttpPut] - public async Task Update([FromBody] Settings settings) + public async Task Update([FromBody] Settings update) { - await _mediator.Send(new UpdateSettingsCommand(settings)); - + await mediator.Send(new UpdateSettingsCommand(update)); return NoContent(); } diff --git a/src/Apps/NetPad.Apps.App/Controllers/TypesController.cs b/src/Apps/NetPad.Apps.App/Controllers/TypesController.cs index 04c9f1f8..16ea8712 100644 --- a/src/Apps/NetPad.Apps.App/Controllers/TypesController.cs +++ b/src/Apps/NetPad.Apps.App/Controllers/TypesController.cs @@ -1,11 +1,15 @@ using Microsoft.AspNetCore.Mvc; -using NetPad.CQs; -using NetPad.Data.EntityFrameworkCore.DataConnections; +using NetPad.Application.Events; +using NetPad.Apps.CQs; +using NetPad.Apps.Data.EntityFrameworkCore.DataConnections; +using NetPad.Apps.UiInterop; +using NetPad.Configuration.Events; +using NetPad.Data.Events; using NetPad.Dtos; -using NetPad.Events; -using NetPad.Runtimes; +using NetPad.Presentation; using NetPad.Scripts; -using NetPad.UiInterop; +using NetPad.Scripts.Events; +using NetPad.Sessions.Events; namespace NetPad.Controllers; diff --git a/src/Apps/NetPad.Apps.App/Controllers/WindowController.cs b/src/Apps/NetPad.Apps.App/Controllers/WindowController.cs index 79bf5cda..ff4130c8 100644 --- a/src/Apps/NetPad.Apps.App/Controllers/WindowController.cs +++ b/src/Apps/NetPad.Apps.App/Controllers/WindowController.cs @@ -1,29 +1,21 @@ -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc; -using NetPad.UiInterop; +using Microsoft.AspNetCore.Mvc; +using NetPad.Apps.UiInterop; namespace NetPad.Controllers; [ApiController] [Route("window")] -public class WindowController : ControllerBase +public class WindowController(IUiWindowService uiWindowService) : ControllerBase { - private readonly IUiWindowService _uiWindowService; - - public WindowController(IUiWindowService uiWindowService) - { - _uiWindowService = uiWindowService; - } - [HttpPatch("open-output-window")] public async Task OpenOutputWindow() { - await _uiWindowService.OpenOutputWindowAsync(); + await uiWindowService.OpenOutputWindowAsync(); } [HttpPatch("open-code-window")] public async Task OpenCodeWindow() { - await _uiWindowService.OpenCodeWindowAsync(); + await uiWindowService.OpenCodeWindowAsync(); } } diff --git a/src/Apps/NetPad.Apps.App/Dtos/CreateScriptDto.cs b/src/Apps/NetPad.Apps.App/Dtos/CreateScriptDto.cs index c42bc464..f1d6ee44 100644 --- a/src/Apps/NetPad.Apps.App/Dtos/CreateScriptDto.cs +++ b/src/Apps/NetPad.Apps.App/Dtos/CreateScriptDto.cs @@ -1,5 +1,3 @@ -using System; - namespace NetPad.Dtos; public class CreateScriptDto diff --git a/src/Apps/NetPad.Apps.App/Dtos/ErrorResult.cs b/src/Apps/NetPad.Apps.App/Dtos/ErrorResult.cs index 14ac6b9a..35cb6857 100644 --- a/src/Apps/NetPad.Apps.App/Dtos/ErrorResult.cs +++ b/src/Apps/NetPad.Apps.App/Dtos/ErrorResult.cs @@ -1,13 +1,7 @@ namespace NetPad.Dtos; -public class ErrorResult +public class ErrorResult(string message, string? details = null) { - public ErrorResult(string message, string? details = null) - { - Message = message; - Details = details; - } - - public string Message { get; } - public string? Details { get; } + public string Message { get; } = message; + public string? Details { get; } = details; } diff --git a/src/Apps/NetPad.Apps.App/Dtos/RunOptionsDto.cs b/src/Apps/NetPad.Apps.App/Dtos/RunOptionsDto.cs deleted file mode 100644 index 6a360350..00000000 --- a/src/Apps/NetPad.Apps.App/Dtos/RunOptionsDto.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using NetPad.DotNet; -using NetPad.Runtimes; - -namespace NetPad.Dtos; - -public class RunOptionsDto -{ - public string? SpecificCodeToRun { get; set; } - public SourceCodeDto[]? AdditionalCode { get; set; } - - public RunOptions ToRunOptions() - { - AdditionalCode ??= Array.Empty(); - - var runOptions = new RunOptions(SpecificCodeToRun); - - runOptions.AdditionalCode.AddRange(AdditionalCode.Select(c => new SourceCode(c.Code, c.Usings))); - - return runOptions; - } - - public class SourceCodeDto - { - public HashSet? Usings { get; set; } - public string? Code { get; set; } - } -} diff --git a/src/Apps/NetPad.Apps.App/GlobalUsings.cs b/src/Apps/NetPad.Apps.App/GlobalUsings.cs index caec0c26..be2adcdb 100644 --- a/src/Apps/NetPad.Apps.App/GlobalUsings.cs +++ b/src/Apps/NetPad.Apps.App/GlobalUsings.cs @@ -1 +1,4 @@ +global using System; +global using System.Threading; +global using System.Threading.Tasks; global using NetPad.Utilities; diff --git a/src/Apps/NetPad.Apps.App/Middlewares/ExceptionHandlerMiddleware.cs b/src/Apps/NetPad.Apps.App/Middlewares/ExceptionHandlerMiddleware.cs index 17cb7f2c..cde39a29 100644 --- a/src/Apps/NetPad.Apps.App/Middlewares/ExceptionHandlerMiddleware.cs +++ b/src/Apps/NetPad.Apps.App/Middlewares/ExceptionHandlerMiddleware.cs @@ -1,6 +1,4 @@ -using System; using System.Net; -using System.Threading.Tasks; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Hosting; @@ -10,30 +8,22 @@ namespace NetPad.Middlewares; -public class ExceptionHandlerMiddleware +public class ExceptionHandlerMiddleware( + RequestDelegate next, + IWebHostEnvironment webHostEnvironment, + ILogger logger) { - private readonly RequestDelegate _next; - private readonly IWebHostEnvironment _webHostEnvironment; - private readonly ILogger _logger; - - public ExceptionHandlerMiddleware(RequestDelegate next, IWebHostEnvironment webHostEnvironment, ILogger logger) - { - _next = next; - _webHostEnvironment = webHostEnvironment; - _logger = logger; - } - public async Task InvokeAsync(HttpContext httpContext) { try { - await _next(httpContext); + await next(httpContext); } catch (Exception ex) { - _logger.LogError(ex, "An error occurred in request pipeline"); + logger.LogError(ex, "An error occurred in request pipeline"); - var result = new ErrorResult(ex.Message, _webHostEnvironment.IsProduction() ? null : ex.ToString()); + var result = new ErrorResult(ex.Message, webHostEnvironment.IsProduction() ? null : ex.ToString()); httpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError; httpContext.Response.ContentType = "application/json"; diff --git a/src/Apps/NetPad.Apps.App/NetPad.Apps.App.csproj b/src/Apps/NetPad.Apps.App/NetPad.Apps.App.csproj index 2bc67a83..f7048306 100644 --- a/src/Apps/NetPad.Apps.App/NetPad.Apps.App.csproj +++ b/src/Apps/NetPad.Apps.App/NetPad.Apps.App.csproj @@ -1,20 +1,17 @@  - net6.0 - true - Latest + $(DefaultTargetFramework) + NetPad + 0.7.1 + 0.7.1 + 0.7.1 false App\ $(DefaultItemExcludes);$(SpaRoot)node_modules\** - + true + Latest false Linux - NetPad - enable - 0.7.1 - 0.7.1 - 0.7.1 - 11 @@ -31,8 +28,8 @@ - + PreserveNewest @@ -73,15 +70,12 @@ - - - - + - - - + + + diff --git a/src/Apps/NetPad.Apps.App/Pages/Error.cshtml.cs b/src/Apps/NetPad.Apps.App/Pages/Error.cshtml.cs index 99d6f7a5..c48e5367 100644 --- a/src/Apps/NetPad.Apps.App/Pages/Error.cshtml.cs +++ b/src/Apps/NetPad.Apps.App/Pages/Error.cshtml.cs @@ -6,14 +6,9 @@ namespace NetPad.Pages; [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] -public class ErrorModel : PageModel +public class ErrorModel(ILogger logger) : PageModel { - private readonly ILogger _logger; - - public ErrorModel(ILogger logger) - { - _logger = logger; - } + private readonly ILogger _logger = logger; public string? RequestId { get; set; } diff --git a/src/Apps/NetPad.Apps.App/Program.cs b/src/Apps/NetPad.Apps.App/Program.cs index 6aec2d19..f81724b0 100644 --- a/src/Apps/NetPad.Apps.App/Program.cs +++ b/src/Apps/NetPad.Apps.App/Program.cs @@ -1,34 +1,96 @@ -using System; using System.IO; using System.Linq; +using System.Net.Http; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using NetPad.Application; +using NetPad.Apps; +using NetPad.Apps.Shells; +using NetPad.Apps.Shells.Electron; +using NetPad.Apps.Shells.Web; using NetPad.Configuration; -using NetPad.Electron; -using NetPad.Web; +using NetPad.Swagger; using Serilog; namespace NetPad; public static class Program { - internal static IApplicationConfigurator ApplicationConfigurator { get; private set; } = null!; + internal static IShell Shell { get; private set; } = null!; + private static bool _isSwaggerCodeGenMode; - public static int Main(string[] args) + public static async Task Main(string[] args) + { + if (args.Contains("--swagger")) + { + _isSwaggerCodeGenMode = true; + return await GenerateSwaggerClientCodeAsync(args); + } + + return RunApp(args); + } + + private static async Task GenerateSwaggerClientCodeAsync(string[] args) + { + try + { + Console.WriteLine("Starting host..."); + var host = CreateHostBuilder(args).Build(); + _ = host.RunAsync(); + + var hostInfo = host.Services.GetRequiredService(); + + string[] docs = + [ + "/swagger/NetPad/swagger.json", + "/swagger/netpad.plugins.omnisharp/swagger.json" + ]; + + var maxLength = docs.Select(x => x.Length).Max(); + + using var client = new HttpClient(); + + foreach (var doc in docs) + { + var url = $"{hostInfo.HostUrl}{doc}"; + + Console.Write($"* Generating client code for: {doc.PadRight(maxLength)} ... "); + var response = await client.GetAsync(url); + var success = response.IsSuccessStatusCode; + Console.WriteLine(success ? "DONE" : "FAIL"); + + if (!success) + { + var content = await response.Content.ReadAsStringAsync(); + Console.WriteLine(content); + return 1; + } + } + + await host.StopAsync(); + return 0; + } + catch (Exception e) + { + Console.WriteLine(e); + return 1; + } + } + + private static int RunApp(string[] args) { try { - // Configure as an Electron app or a web app - ApplicationConfigurator = args.Any(a => a.ContainsIgnoreCase("/ELECTRONPORT")) - ? new NetPadElectronConfigurator() - : new NetPadWebConfigurator(); + // Select a shell + Shell = args.Any(a => a.ContainsIgnoreCase("/ELECTRONPORT")) + ? new ElectronShell() + : new WebBrowserShell(); var host = CreateHostBuilder(args).Build(); - Console.WriteLine($"Starting NetPad. App type: {host.Services.GetRequiredService().Type}"); + Console.WriteLine($"Starting app. Shell: {Shell.GetType().Name}"); host.Run(); return 0; @@ -36,7 +98,7 @@ public static int Main(string[] args) catch (IOException ioException) when (ioException.Message.ContainsIgnoreCase("address already in use")) { Console.WriteLine($"Another instance is already running. {ioException.Message}"); - ApplicationConfigurator.ShowErrorDialog( + Shell.ShowErrorDialog( $"{AppIdentifier.AppName} Already Running", $"{AppIdentifier.AppName} is already running. You cannot open multiple instances of {AppIdentifier.AppName}."); return 1; @@ -59,8 +121,15 @@ public static int Main(string[] args) .UseSerilog((ctx, config) => { ConfigureLogging(config, ctx.Configuration); }) .ConfigureWebHostDefaults(webBuilder => { - ApplicationConfigurator.ConfigureWebHost(webBuilder, args); - webBuilder.UseStartup(); + if (_isSwaggerCodeGenMode) + { + webBuilder.UseStartup(); + } + else + { + Shell.ConfigureWebHost(webBuilder, args); + webBuilder.UseStartup(); + } }); private static void ConfigureLogging(LoggerConfiguration serilogConfig, IConfiguration appConfig) diff --git a/src/Apps/NetPad.Apps.App/Properties/launchSettings.json b/src/Apps/NetPad.Apps.App/Properties/launchSettings.json new file mode 100644 index 00000000..b3c0bc98 --- /dev/null +++ b/src/Apps/NetPad.Apps.App/Properties/launchSettings.json @@ -0,0 +1,40 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:18454", + "sslPort": 44349 + } + }, + "profiles": { + "Electron.NET App": { + "commandName": "Executable", + "executablePath": "electronize", + "commandLineArgs": "start", + "workingDirectory": "." + }, + "ASP.NET Core App (no Electron)": { + "commandName": "Project", + "launchBrowser": false, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "Generate Swagger Client Code": { + "commandName": "Project", + "commandLineArgs": "--swagger", + "launchBrowser": false, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/src/Apps/NetPad.Apps.App/Services/Data/DataConnectionResourcesGeneratorFactory.cs b/src/Apps/NetPad.Apps.App/Services/Data/DataConnectionResourcesGeneratorFactory.cs deleted file mode 100644 index dc41ce95..00000000 --- a/src/Apps/NetPad.Apps.App/Services/Data/DataConnectionResourcesGeneratorFactory.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using Microsoft.Extensions.DependencyInjection; -using NetPad.Data; -using NetPad.Data.EntityFrameworkCore; -using NetPad.Data.EntityFrameworkCore.DataConnections; - -namespace NetPad.Services.Data; - -public class DataConnectionResourcesGeneratorFactory : IDataConnectionResourcesGeneratorFactory -{ - private readonly IServiceProvider _serviceProvider; - - public DataConnectionResourcesGeneratorFactory(IServiceProvider serviceProvider) - { - _serviceProvider = serviceProvider; - } - - public IDataConnectionResourcesGenerator Create(DataConnection dataConnection) - { - if (dataConnection is EntityFrameworkDatabaseConnection) - { - return _serviceProvider.GetRequiredService(); - } - - throw new NotImplementedException("Only EntityFramework data connections are supported at this time."); - } -} diff --git a/src/Apps/NetPad.Apps.App/Services/Data/DatabaseConnectionMetadataProviderFactory.cs b/src/Apps/NetPad.Apps.App/Services/Data/DatabaseConnectionMetadataProviderFactory.cs deleted file mode 100644 index e256f953..00000000 --- a/src/Apps/NetPad.Apps.App/Services/Data/DatabaseConnectionMetadataProviderFactory.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using Microsoft.Extensions.DependencyInjection; -using NetPad.Data; -using NetPad.Data.EntityFrameworkCore; -using NetPad.Data.EntityFrameworkCore.DataConnections; - -namespace NetPad.Services.Data; - -public class DatabaseConnectionMetadataProviderFactory : IDatabaseConnectionMetadataProviderFactory -{ - private readonly IServiceProvider _serviceProvider; - - public DatabaseConnectionMetadataProviderFactory(IServiceProvider serviceProvider) - { - _serviceProvider = serviceProvider; - } - - public IDatabaseConnectionMetadataProvider Create(DatabaseConnection databaseConnection) - { - if (databaseConnection is EntityFrameworkDatabaseConnection) - { - return _serviceProvider.GetRequiredService(); - } - - throw new NotImplementedException("Only EntityFramework database connections are supported at this time."); - } -} diff --git a/src/Core/NetPad.Application/Services/MediatorRequestPipeline.cs b/src/Apps/NetPad.Apps.App/Services/MediatorRequestPipeline.cs similarity index 97% rename from src/Core/NetPad.Application/Services/MediatorRequestPipeline.cs rename to src/Apps/NetPad.Apps.App/Services/MediatorRequestPipeline.cs index 3fad0950..bf1d3549 100644 --- a/src/Core/NetPad.Application/Services/MediatorRequestPipeline.cs +++ b/src/Apps/NetPad.Apps.App/Services/MediatorRequestPipeline.cs @@ -1,5 +1,5 @@ using MediatR; -using NetPad.CQs; +using NetPad.Apps.CQs; namespace NetPad.Services; diff --git a/src/Apps/NetPad.Apps.App/Services/ScriptEnvironmentIpcOutputWriter.cs b/src/Apps/NetPad.Apps.App/Services/ScriptEnvironmentIpcOutputWriter.cs index b9d7faff..ce91cf59 100644 --- a/src/Apps/NetPad.Apps.App/Services/ScriptEnvironmentIpcOutputWriter.cs +++ b/src/Apps/NetPad.Apps.App/Services/ScriptEnvironmentIpcOutputWriter.cs @@ -1,17 +1,14 @@ -using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; -using System.Threading; -using System.Threading.Tasks; using Microsoft.Extensions.Logging; +using NetPad.Apps.UiInterop; using NetPad.Events; using NetPad.IO; using NetPad.Presentation; using NetPad.Presentation.Html; -using NetPad.Runtimes; using NetPad.Scripts; -using NetPad.UiInterop; +using NetPad.Scripts.Events; using O2Html; using Timer = System.Timers.Timer; @@ -31,9 +28,9 @@ public sealed record ScriptEnvironmentIpcOutputWriter : IOutputWriter, I private readonly Accessor _ctsAccessor; private readonly ConcurrentQueue _sendMessageQueue; private readonly Timer _sendMessageQueueTimer; - private const int _sendMessageQueueBatchSize = 1000; - private const int _processSendMessageQueueEveryMs = 50; - private const int _maxUserOutputMessagesPerRun = 10100; + private const int SendMessageQueueBatchSize = 1000; + private const int ProcessSendMessageQueueEveryMs = 50; + private const int MaxUserOutputMessagesPerRun = 10100; private int _userOutputMessagesSentThisRun; private bool _outputLimitReachedMessageSent; private readonly object _outputLimitReachedMessageSendLock = new(); @@ -47,19 +44,19 @@ public sealed record ScriptEnvironmentIpcOutputWriter : IOutputWriter, I _scriptEnvironment = scriptEnvironment; _ipcService = ipcService; _logger = logger; - _disposables = new List(); + _disposables = []; _ctsAccessor = new Accessor(new CancellationTokenSource()); _sendMessageQueue = new(); _sendMessageQueueTimer = new Timer() { - Interval = _processSendMessageQueueEveryMs, + Interval = ProcessSendMessageQueueEveryMs, AutoReset = false, Enabled = false }; - _sendMessageQueueTimer.Elapsed += async (_, _) => await ProcessSendMessageQueue(_sendMessageQueueBatchSize); + _sendMessageQueueTimer.Elapsed += async (_, _) => await ProcessSendMessageQueue(SendMessageQueueBatchSize); _disposables.Add(eventBus.Subscribe(msg => { @@ -196,7 +193,7 @@ public async Task WriteAsync(object? output, string? title = null, CancellationT private bool HasReachedUserOutputMessageLimitForThisRun() { - return _userOutputMessagesSentThisRun >= _maxUserOutputMessagesPerRun; + return _userOutputMessagesSentThisRun >= MaxUserOutputMessagesPerRun; } private void QueueMessage(ScriptOutput output, bool isCancellable) diff --git a/src/Apps/NetPad.Apps.App/Services/ScriptService.cs b/src/Apps/NetPad.Apps.App/Services/ScriptService.cs new file mode 100644 index 00000000..02fb9cc0 --- /dev/null +++ b/src/Apps/NetPad.Apps.App/Services/ScriptService.cs @@ -0,0 +1,64 @@ +using MediatR; +using NetPad.Apps.CQs; +using NetPad.Apps.UiInterop; +using NetPad.Exceptions; +using NetPad.Sessions; + +namespace NetPad.Services; + +public class ScriptService(ISession session, IUiDialogService uiDialogService, IMediator mediator) +{ + public async Task CloseScriptAsync(Guid scriptId) + { + var scriptEnvironment = session.Get(scriptId) ?? throw new ScriptNotFoundException(scriptId); + var script = scriptEnvironment.Script; + + bool shouldAskUserToSave = script.IsDirty; + if (script.IsNew && string.IsNullOrEmpty(script.Code)) + { + shouldAskUserToSave = false; + } + + if (shouldAskUserToSave) + { + var response = await uiDialogService.AskUserIfTheyWantToSave(script); + if (response == YesNoCancel.Cancel) + { + return; + } + + if (response == YesNoCancel.Yes) + { + bool saved = await SaveScriptAsync(scriptId); + if (!saved) + { + return; + } + } + } + + await mediator.Send(new CloseScriptCommand(scriptId)); + } + + public async Task SaveScriptAsync(Guid scriptId) + { + var scriptEnvironment = session.Get(scriptId) ?? throw new ScriptNotFoundException(scriptId); + var script = scriptEnvironment.Script; + + if (script.IsNew) + { + var path = await uiDialogService.AskUserForSaveLocation(script); + + if (string.IsNullOrWhiteSpace(path)) + { + return false; + } + + script.SetPath(path); + } + + await mediator.Send(new SaveScriptCommand(script)); + + return true; + } +} diff --git a/src/Apps/NetPad.Apps.App/Startup.cs b/src/Apps/NetPad.Apps.App/Startup.cs index 3db48724..5e491678 100644 --- a/src/Apps/NetPad.Apps.App/Startup.cs +++ b/src/Apps/NetPad.Apps.App/Startup.cs @@ -1,59 +1,55 @@ -using System; using System.Collections.Generic; using System.Linq; using System.Net.Http; using System.Reflection; using MediatR; using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.DataProtection; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting.Server.Features; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -using NetPad.Application; +using NetPad.Apps; +using NetPad.Apps.Configuration; +using NetPad.Apps.CQs; +using NetPad.Apps.Data; +using NetPad.Apps.Data.EntityFrameworkCore; +using NetPad.Apps.Plugins; +using NetPad.Apps.Resources; +using NetPad.Apps.Scripts; +using NetPad.Apps.UiInterop; using NetPad.Assemblies; using NetPad.BackgroundServices; -using NetPad.CodeAnalysis; using NetPad.Common; -using NetPad.Compilation; -using NetPad.Compilation.CSharp; using NetPad.Configuration; -using NetPad.CQs; using NetPad.Data; -using NetPad.Data.EntityFrameworkCore; -using NetPad.Data.EntityFrameworkCore.DataConnections; -using NetPad.DotNet; -using NetPad.Events; +using NetPad.ExecutionModel; using NetPad.Middlewares; using NetPad.Packages; -using NetPad.Plugins; +using NetPad.Packages.NuGet; using NetPad.Plugins.OmniSharp; -using NetPad.Resources; -using NetPad.Runtimes; using NetPad.Scripts; using NetPad.Services; -using NetPad.Services.Data; using NetPad.Sessions; using NetPad.Swagger; -using NetPad.UiInterop; namespace NetPad; public class Startup { private readonly Assembly[] _pluginAssemblies = - { + [ typeof(Plugin).Assembly - }; + ]; public Startup(IConfiguration configuration, IWebHostEnvironment webHostEnvironment) { Configuration = configuration; WebHostEnvironment = webHostEnvironment; - Console.WriteLine($".NET Version: {Environment.Version.ToString()}"); - Console.WriteLine($"Environment: {webHostEnvironment.EnvironmentName}"); - Console.WriteLine($"WebRootPath: {webHostEnvironment.WebRootPath}"); - Console.WriteLine($"ContentRootPath: {webHostEnvironment.ContentRootPath}"); + Console.WriteLine("Configuration:"); + Console.WriteLine($" - .NET Runtime Version: {Environment.Version.ToString()}"); + Console.WriteLine($" - Environment: {webHostEnvironment.EnvironmentName}"); + Console.WriteLine($" - WebRootPath: {webHostEnvironment.WebRootPath}"); + Console.WriteLine($" - ContentRootPath: {webHostEnvironment.ContentRootPath}"); } public IConfiguration Configuration { get; } @@ -61,51 +57,36 @@ public Startup(IConfiguration configuration, IWebHostEnvironment webHostEnvironm public void ConfigureServices(IServiceCollection services) { - services.AddSingleton(); + services.AddCoreServices(); + services.AddSingleton(); - services.AddSingleton(_ => - { - var httpClient = new HttpClient(); - httpClient.Timeout = TimeSpan.FromMinutes(1); - return httpClient; - }); services.AddTransient(); services.AddSingleton(sp => sp.GetRequiredService().GetSettingsAsync().Result); services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddTransient(); services.AddTransient(); - services.AddTransient(); services.AddTransient(); - services.AddSingleton(); services.AddSingleton(); - // Scripts + // Script services + services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddSingleton(); - services.AddTransient(); - // Select how we will run scripts, using an external process or in-memory - // NOTE: A different app, ex. a CLI version of NetPad, could use AddInMemoryScriptRuntime() - services.AddExternalProcessScriptRuntime(); + // Script execution mechanism + services.AddExternalExecutionModel(options => + { + options.ProcessCliArgs = ["-html"]; + options.RedirectIo = true; + }); // Data connections - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddSingleton(); - services.AddSingleton(sp => new Lazy(sp.GetRequiredService())); - services.AddTransient(s => - new DataProtector(s.GetRequiredService(), "DataConnectionPasswords")); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); - services.AddTransient(); + services + .AddDataConnectionFeature< + FileSystemDataConnectionRepository, + FileSystemDataConnectionResourcesRepository, + FileSystemDataConnectionResourcesCache>() + .AddEntityFrameworkCoreDataConnectionDriver(); // Package management services.AddTransient(); @@ -122,10 +103,6 @@ public void ConfigureServices(IServiceCollection services) services.AddHostedService(); services.AddHostedService(); -#if DEBUG - //services.AddHostedService(); -#endif - // Should be the last hosted service so it runs last on app start services.AddHostedService(); @@ -158,6 +135,14 @@ public void ConfigureServices(IServiceCollection services) mvcBuilder.AddApplicationPart(registration.Assembly); } + // HttpClient + services.AddSingleton(_ => + { + var httpClient = new HttpClient(); + httpClient.Timeout = TimeSpan.FromMinutes(1); + return httpClient; + }); + // SignalR services.AddSignalR() .AddJsonProtocol(options => { JsonSerializer.Configure(options.PayloadSerializerOptions); }); @@ -167,24 +152,19 @@ public void ConfigureServices(IServiceCollection services) // Mediator services.AddTransient(typeof(IPipelineBehavior<,>), typeof(MediatorRequestPipeline<,>)); - services.AddMediatR(new[] { typeof(Command).Assembly }.Union(pluginRegistrations.Select(pr => pr.Assembly)).ToArray()); + services.AddMediatR(new[] { typeof(Command).Assembly }.Union(pluginRegistrations.Select(pr => pr.Assembly)) + .ToArray()); // Swagger #if DEBUG SwaggerSetup.AddSwagger(services, WebHostEnvironment, pluginRegistrations); #endif - services.AddDataProtection(options => - { - // A built-in string that identifies NetPad - options.ApplicationDiscriminator = "NETPAD_8C94D5EA-9510-4493-AA43-CADE372ED853"; - }); - - // Allow ApplicationConfigurator to add/modify any service registrations it needs - Program.ApplicationConfigurator.ConfigureServices(services); + // Allow Shell to add/modify any service registrations it needs + Program.Shell.ConfigureServices(services); // We want to always use SignalR for IPC, overriding Electron's IPC service - // This should come after the ApplicationConfigurator adds its services so it overrides it + // This should come after the Shell adds its services so it overrides it services.AddTransient(); } @@ -256,7 +236,6 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) #endif }); - // Allow ApplicationConfigurator to run any configuration - Program.ApplicationConfigurator.Configure(app, env); + Program.Shell.Initialize(app, env); } } diff --git a/src/Apps/NetPad.Apps.App/Swagger/OperationProcessors.cs b/src/Apps/NetPad.Apps.App/Swagger/OperationProcessors.cs index a6e8350b..73cdbdb9 100644 --- a/src/Apps/NetPad.Apps.App/Swagger/OperationProcessors.cs +++ b/src/Apps/NetPad.Apps.App/Swagger/OperationProcessors.cs @@ -5,32 +5,18 @@ namespace NetPad.Swagger; -internal class IncludeControllersInAssemblies : IOperationProcessor +internal class IncludeControllersInAssemblies(params Assembly[] assemblies) : IOperationProcessor { - private readonly Assembly[] _assemblies; - - public IncludeControllersInAssemblies(params Assembly[] assemblies) - { - _assemblies = assemblies; - } - public bool Process(OperationProcessorContext context) { - return _assemblies.Contains(context.ControllerType.Assembly); + return assemblies.Contains(context.ControllerType.Assembly); } } -internal class ExcludeControllersInAssemblies : IOperationProcessor +internal class ExcludeControllersInAssemblies(params Assembly[] assemblies) : IOperationProcessor { - private readonly Assembly[] _assemblies; - - public ExcludeControllersInAssemblies(params Assembly[] assemblies) - { - _assemblies = assemblies; - } - public bool Process(OperationProcessorContext context) { - return !_assemblies.Contains(context.ControllerType.Assembly); + return !assemblies.Contains(context.ControllerType.Assembly); } } diff --git a/src/Apps/NetPad.Apps.App/Swagger/SwaggerCodeGenerationStartup.cs b/src/Apps/NetPad.Apps.App/Swagger/SwaggerCodeGenerationStartup.cs new file mode 100644 index 00000000..a84d4140 --- /dev/null +++ b/src/Apps/NetPad.Apps.App/Swagger/SwaggerCodeGenerationStartup.cs @@ -0,0 +1,104 @@ +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Hosting.Server.Features; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using NetPad.Apps; +using NetPad.Apps.Configuration; +using NetPad.Apps.Plugins; +using NetPad.Common; +using NetPad.Configuration; +using NetPad.Plugins.OmniSharp; + +namespace NetPad.Swagger; + +public class SwaggerCodeGenerationStartup(IConfiguration configuration, IWebHostEnvironment webHostEnvironment) +{ + private readonly Assembly[] _pluginAssemblies = + [ + typeof(Plugin).Assembly + ]; + + public IConfiguration Configuration { get; } = configuration; + public IWebHostEnvironment WebHostEnvironment { get; } = webHostEnvironment; + + public void ConfigureServices(IServiceCollection services) + { + services.AddCoreServices(); + + services.AddSingleton(); + services.AddTransient(); + services.AddSingleton(sp => sp.GetRequiredService().GetSettingsAsync().Result); + + // Plugins + var pluginInitialization = new PluginInitialization(Configuration, WebHostEnvironment); + IPluginManager pluginManager = new PluginManager(pluginInitialization); + services.AddSingleton(pluginInitialization); + services.AddSingleton(pluginManager); + + var pluginRegistrations = new List(); + + // Register plugins + var emptyServices = new ServiceCollection(); // We don't care about plugin service registrations in this scenario + foreach (var pluginAssembly in _pluginAssemblies) + { + try + { + var registration = pluginManager.RegisterPlugin(pluginAssembly, emptyServices); + pluginRegistrations.Add(registration); + + Console.WriteLine("Registered plugin '{0}' from '{1}'", + registration.Plugin.Name, + registration.Assembly.FullName); + } + catch (Exception ex) + { + Console.Error.WriteLine("Could not register plugin: '{0}'. {1}", pluginAssembly.FullName, ex); + } + } + + // MVC + var mvcBuilder = services.AddControllersWithViews() + .AddJsonOptions(options => JsonSerializer.Configure(options.JsonSerializerOptions)); + + foreach (var registration in pluginRegistrations) + { + mvcBuilder.AddApplicationPart(registration.Assembly); + } + + SwaggerSetup.AddSwagger(services, WebHostEnvironment, pluginRegistrations); + } + + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + var services = app.ApplicationServices; + app.UseDeveloperExceptionPage(); + + app.UseOpenApi(); + app.UseSwaggerUi3(); + + // Set host url + var hostInfo = services.GetRequiredService(); + hostInfo.SetWorkingDirectory(env.ContentRootPath); + + var serverAddresses = app.ServerFeatures.Get()?.Addresses; + + if (serverAddresses == null || !serverAddresses.Any()) + { + throw new Exception("No server urls specified. Specify the url with the '--urls' parameter"); + } + + var url = serverAddresses.FirstOrDefault(a => a.StartsWith("https:")) ?? + serverAddresses.FirstOrDefault(a => a.StartsWith("http:")); + + if (url == null) + { + throw new Exception("No server urls specified that start with 'http' or 'https'"); + } + + hostInfo.SetHostUrl(url); + } +} diff --git a/src/Apps/NetPad.Apps.App/Swagger/SwaggerSetup.cs b/src/Apps/NetPad.Apps.App/Swagger/SwaggerSetup.cs index 2ee22429..61254df6 100644 --- a/src/Apps/NetPad.Apps.App/Swagger/SwaggerSetup.cs +++ b/src/Apps/NetPad.Apps.App/Swagger/SwaggerSetup.cs @@ -1,10 +1,9 @@ -using System; using System.Collections.Generic; using System.IO; using System.Linq; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; -using NetPad.Plugins; +using NetPad.Apps.Plugins; using NJsonSchema.CodeGeneration.TypeScript; using NSwag; using NSwag.CodeGeneration.TypeScript; @@ -17,7 +16,7 @@ public static void AddSwagger(IServiceCollection services, IWebHostEnvironment w { services.AddSwaggerDocument(config => { - config.Title = "NetPad"; + config.Title = "NetPad HTTP Interface"; config.DocumentName = "NetPad"; config.OperationProcessors.Insert(0, new ExcludeControllersInAssemblies(pluginRegistrations.Select(p => p.Assembly).ToArray())); @@ -29,8 +28,8 @@ public static void AddSwagger(IServiceCollection services, IWebHostEnvironment w { services.AddSwaggerDocument(config => { - config.Title = $"NetPad Plugin - {pluginRegistration.Plugin.Name}"; - config.DocumentName = config.Title; + config.Title = $"Plugin - {pluginRegistration.Plugin.Name}"; + config.DocumentName = pluginRegistration.Plugin.Id; config.OperationProcessors.Insert(0, new IncludeControllersInAssemblies(pluginRegistration.Assembly)); string pluginDirName = pluginRegistration.Plugin.Name.Replace(" ", "-"); diff --git a/src/Apps/NetPad.Apps.App/Swagger/TypeScriptClientCodeTransform.cs b/src/Apps/NetPad.Apps.App/Swagger/TypeScriptClientCodeTransform.cs index 50447801..1e2a439e 100644 --- a/src/Apps/NetPad.Apps.App/Swagger/TypeScriptClientCodeTransform.cs +++ b/src/Apps/NetPad.Apps.App/Swagger/TypeScriptClientCodeTransform.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Generic; using System.Linq; diff --git a/src/Apps/NetPad.Apps.App/global.json b/src/Apps/NetPad.Apps.App/global.json deleted file mode 100644 index 38e49604..00000000 --- a/src/Apps/NetPad.Apps.App/global.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "sdk": { - "version": "7.0.0", - "rollForward": "latestMinor", - "allowPrerelease": true - } -} diff --git a/src/Apps/NetPad.Apps.Common/CQs/ActivateLastActiveScriptCommand.cs b/src/Apps/NetPad.Apps.Common/CQs/ActivateLastActiveScriptCommand.cs new file mode 100644 index 00000000..16c30d4c --- /dev/null +++ b/src/Apps/NetPad.Apps.Common/CQs/ActivateLastActiveScriptCommand.cs @@ -0,0 +1,16 @@ +using MediatR; +using NetPad.Sessions; + +namespace NetPad.Apps.CQs; + +public class ActivateLastActiveScriptCommand : Command +{ + public class Handler(ISession session) : IRequestHandler + { + public async Task Handle(ActivateLastActiveScriptCommand request, CancellationToken cancellationToken) + { + await session.ActivateLastActiveScriptAsync(); + return Unit.Value; + } + } +} diff --git a/src/Apps/NetPad.Apps.Common/CQs/ActivateScriptCommand.cs b/src/Apps/NetPad.Apps.Common/CQs/ActivateScriptCommand.cs new file mode 100644 index 00000000..4d4a6ea6 --- /dev/null +++ b/src/Apps/NetPad.Apps.Common/CQs/ActivateScriptCommand.cs @@ -0,0 +1,18 @@ +using MediatR; +using NetPad.Sessions; + +namespace NetPad.Apps.CQs; + +public class ActivateScriptCommand(Guid scriptId) : Command +{ + public Guid ScriptId { get; } = scriptId; + + public class Handler(ISession session) : IRequestHandler + { + public async Task Handle(ActivateScriptCommand request, CancellationToken cancellationToken) + { + await session.ActivateAsync(request.ScriptId); + return Unit.Value; + } + } +} diff --git a/src/Apps/NetPad.Apps.Common/CQs/AlertUserAboutMissingAppDependencies.cs b/src/Apps/NetPad.Apps.Common/CQs/AlertUserAboutMissingAppDependencies.cs new file mode 100644 index 00000000..a1bb3d0a --- /dev/null +++ b/src/Apps/NetPad.Apps.Common/CQs/AlertUserAboutMissingAppDependencies.cs @@ -0,0 +1,8 @@ +using NetPad.Application; + +namespace NetPad.Apps.CQs; + +public class AlertUserAboutMissingAppDependencies(AppDependencyCheckResult dependencyCheckResult) : Command +{ + public AppDependencyCheckResult DependencyCheckResult { get; } = dependencyCheckResult; +} diff --git a/src/Apps/NetPad.Apps.Common/CQs/AlertUserCommand.cs b/src/Apps/NetPad.Apps.Common/CQs/AlertUserCommand.cs new file mode 100644 index 00000000..abc424be --- /dev/null +++ b/src/Apps/NetPad.Apps.Common/CQs/AlertUserCommand.cs @@ -0,0 +1,6 @@ +namespace NetPad.Apps.CQs; + +public class AlertUserCommand(string message) : Command +{ + public string Message { get; } = message; +} diff --git a/src/Apps/NetPad.Apps.Common/CQs/Bases.cs b/src/Apps/NetPad.Apps.Common/CQs/Bases.cs new file mode 100644 index 00000000..deeea385 --- /dev/null +++ b/src/Apps/NetPad.Apps.Common/CQs/Bases.cs @@ -0,0 +1,19 @@ +using MediatR; + +namespace NetPad.Apps.CQs; + +public abstract class CommandBase +{ + public Guid Id { get; } = Guid.NewGuid(); +} + +public abstract class Command : CommandBase, IRequest; + +public abstract class Command : CommandBase, IRequest; + +public abstract class QueryBase +{ + public Guid Id { get; } = Guid.NewGuid(); +} + +public abstract class Query : QueryBase, IRequest; diff --git a/src/Core/NetPad.Application/CQs/CheckAppDependenciesQuery.cs b/src/Apps/NetPad.Apps.Common/CQs/CheckAppDependenciesQuery.cs similarity index 52% rename from src/Core/NetPad.Application/CQs/CheckAppDependenciesQuery.cs rename to src/Apps/NetPad.Apps.Common/CQs/CheckAppDependenciesQuery.cs index a9fa674c..6ec3aaf8 100644 --- a/src/Core/NetPad.Application/CQs/CheckAppDependenciesQuery.cs +++ b/src/Apps/NetPad.Apps.Common/CQs/CheckAppDependenciesQuery.cs @@ -3,21 +3,13 @@ using NetPad.Application; using NetPad.DotNet; -namespace NetPad.CQs; +namespace NetPad.Apps.CQs; public class CheckAppDependenciesQuery : Query { - public class Handler : IRequestHandler + public class Handler(IDotNetInfo dotNetInfo, ILogger logger) + : IRequestHandler { - private readonly IDotNetInfo _dotNetInfo; - private readonly ILogger _logger; - - public Handler(IDotNetInfo dotNetInfo, ILogger logger) - { - _dotNetInfo = dotNetInfo; - _logger = logger; - } - public Task Handle(CheckAppDependenciesQuery request, CancellationToken cancellationToken) { @@ -26,29 +18,29 @@ public Handler(IDotNetInfo dotNetInfo, ILogger logger) try { - dotNetSdkVersions = _dotNetInfo.GetDotNetSdkVersions(); + dotNetSdkVersions = dotNetInfo.GetDotNetSdkVersions(); } catch (Exception ex) { - _logger.LogError(ex, "Error getting .NET SDK versions"); + logger.LogError(ex, "Error getting .NET SDK versions"); } try { - var dotNetEfToolExePath = _dotNetInfo.LocateDotNetEfToolExecutable(); + var dotNetEfToolExePath = dotNetInfo.LocateDotNetEfToolExecutable(); dotNetEfToolVersion = dotNetEfToolExePath == null ? null - : _dotNetInfo.GetDotNetEfToolVersion(dotNetEfToolExePath); + : dotNetInfo.GetDotNetEfToolVersion(dotNetEfToolExePath); } catch (Exception ex) { - _logger.LogError(ex, "Error getting .NET Entity Framework Tool version"); + logger.LogError(ex, "Error getting .NET Entity Framework Tool version"); } var result = new AppDependencyCheckResult( - _dotNetInfo.GetCurrentDotNetRuntimeVersion().ToString(), - dotNetSdkVersions?.Select(v => v.Version).ToArray() ?? Array.Empty(), + dotNetInfo.GetCurrentDotNetRuntimeVersion().ToString(), + dotNetSdkVersions?.Select(v => v.Version).ToArray() ?? [], dotNetEfToolVersion ); diff --git a/src/Apps/NetPad.Apps.Common/CQs/CloseScriptCommand.cs b/src/Apps/NetPad.Apps.Common/CQs/CloseScriptCommand.cs new file mode 100644 index 00000000..19ee48a8 --- /dev/null +++ b/src/Apps/NetPad.Apps.Common/CQs/CloseScriptCommand.cs @@ -0,0 +1,35 @@ +using MediatR; +using NetPad.Events; +using NetPad.Exceptions; +using NetPad.Scripts; +using NetPad.Scripts.Events; +using NetPad.Sessions; + +namespace NetPad.Apps.CQs; + +public class CloseScriptCommand(Guid scriptId) : Command +{ + public Guid ScriptId { get; } = scriptId; + + public class Handler( + ISession session, + IAutoSaveScriptRepository autoSaveScriptRepository, + IEventBus eventBus) + : IRequestHandler + { + public async Task Handle(CloseScriptCommand request, CancellationToken cancellationToken) + { + var scriptId = request.ScriptId; + + var script = session.Get(scriptId)?.Script ?? throw new ScriptNotFoundException(scriptId); + + await session.CloseAsync(scriptId); + + await autoSaveScriptRepository.DeleteAsync(script); + + await eventBus.PublishAsync(new ScriptClosedEvent(script)); + + return Unit.Value; + } + } +} diff --git a/src/Apps/NetPad.Apps.Common/CQs/ConfirmSaveCommand.cs b/src/Apps/NetPad.Apps.Common/CQs/ConfirmSaveCommand.cs new file mode 100644 index 00000000..5d135422 --- /dev/null +++ b/src/Apps/NetPad.Apps.Common/CQs/ConfirmSaveCommand.cs @@ -0,0 +1,9 @@ +using NetPad.Apps.UiInterop; +using NetPad.Scripts; + +namespace NetPad.Apps.CQs; + +public class ConfirmSaveCommand(Script script) : Command +{ + public string Message { get; } = $"You have unsaved changes. Do you want to save '{script.Name}'?"; +} diff --git a/src/Apps/NetPad.Apps.Common/CQs/ConfirmWithUserCommand.cs b/src/Apps/NetPad.Apps.Common/CQs/ConfirmWithUserCommand.cs new file mode 100644 index 00000000..03a9db8a --- /dev/null +++ b/src/Apps/NetPad.Apps.Common/CQs/ConfirmWithUserCommand.cs @@ -0,0 +1,8 @@ +using NetPad.Apps.UiInterop; + +namespace NetPad.Apps.CQs; + +public class ConfirmWithUserCommand(string message) : Command +{ + public string Message { get; } = message; +} diff --git a/src/Apps/NetPad.Apps.Common/CQs/CreateScriptCommand.cs b/src/Apps/NetPad.Apps.Common/CQs/CreateScriptCommand.cs new file mode 100644 index 00000000..6fe91a2f --- /dev/null +++ b/src/Apps/NetPad.Apps.Common/CQs/CreateScriptCommand.cs @@ -0,0 +1,28 @@ +using MediatR; +using NetPad.Events; +using NetPad.Scripts; +using NetPad.Scripts.Events; + +namespace NetPad.Apps.CQs; + +public class CreateScriptCommand : Command