microsoft/TypeAgent

Public

mirrored fromhttps://github.com/microsoft/TypeAgentAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
d4944c6517c9a96a3c419f827769f518913f1b75

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

dotnet/autoShell/ActionDispatcher.cs

164lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4using System;
5using System.Collections.Generic;
6using System.IO;
7using System.Text.Json;
8using autoShell.Handlers;
9using autoShell.Handlers.Settings;
10using autoShell.Logging;
11using autoShell.Services;
12
13namespace autoShell;
14
15/// <summary>
16/// Routes incoming JSON actions to the appropriate handler via a direct dictionary lookup.
17/// </summary>
18internal class ActionDispatcher
19{
20 private readonly Dictionary<string, IActionHandler> _handlers = new(StringComparer.OrdinalIgnoreCase);
21 private readonly ILogger _logger;
22
23 public ActionDispatcher(ILogger logger)
24 {
25 _logger = logger;
26 }
27
28 /// <summary>
29 /// Gets the names of all registered actions.
30 /// </summary>
31 public IEnumerable<string> RegisteredActions => _handlers.Keys;
32
33 /// <summary>
34 /// Creates a <see cref="ActionDispatcher"/> with all production services and handlers registered.
35 /// </summary>
36 public static ActionDispatcher Create(ILogger logger)
37 {
38 return Create(
39 logger,
40 new WindowsRegistryService(),
41 new WindowsSystemParametersService(),
42 new WindowsProcessService(),
43 new WindowsAudioService(logger),
44 new WindowsAppRegistry(logger),
45 new WindowsDebuggerService(),
46 new WindowsBrightnessService(logger),
47 new WindowsDisplayService(logger),
48 new WindowsWindowService(logger),
49 new WindowsNetworkService(logger),
50 new WindowsVirtualDesktopService(logger)
51 );
52 }
53
54 /// <summary>
55 /// Creates a <see cref="ActionDispatcher"/> with the specified services, enabling integration testing
56 /// with mock services while exercising real handler wiring.
57 /// </summary>
58 internal static ActionDispatcher Create(
59 ILogger logger,
60 IRegistryService registry,
61 ISystemParametersService systemParams,
62 IProcessService process,
63 IAudioService audio,
64 IAppRegistry appRegistry,
65 IDebuggerService debugger,
66 IBrightnessService brightness,
67 IDisplayService display,
68 IWindowService window,
69 INetworkService network,
70 IVirtualDesktopService virtualDesktop)
71 {
72 var dispatcher = new ActionDispatcher(logger);
73
74 dispatcher.Register(
75 new AudioActionHandler(audio),
76 new AppActionHandler(appRegistry, process, window, logger),
77 new WindowActionHandler(appRegistry, window),
78 new ThemeActionHandler(registry, process, systemParams),
79 new VirtualDesktopActionHandler(appRegistry, window, virtualDesktop, logger),
80 new NetworkActionHandler(network, process, logger),
81 new DisplayActionHandler(display, logger),
82 new TaskbarSettingsHandler(registry, process),
83 new DisplaySettingsHandler(registry, process, brightness, logger),
84 new PersonalizationSettingsHandler(registry, process),
85 new MouseSettingsHandler(registry, process, systemParams, logger),
86 new AccessibilitySettingsHandler(registry, process, systemParams),
87 new PowerSettingsHandler(registry, process),
88 new FileExplorerSettingsHandler(registry),
89 new PrivacySettingsHandler(registry),
90 new SystemSettingsHandler(registry, process),
91 new SystemActionHandler(process, debugger)
92 );
93
94 var validator = new SchemaValidator(logger);
95 var schemaDir = Path.Combine(AppContext.BaseDirectory, SchemaValidator.DefaultSchemaRelativePath);
96 var schemaActions = validator.LoadActionNames(schemaDir);
97 if (schemaActions.Count > 0)
98 {
99 validator.ValidateWiring(schemaActions, dispatcher.RegisteredActions);
100 }
101
102 return dispatcher;
103 }
104
105 /// <summary>
106 /// Registers one or more handlers with the dispatcher.
107 /// Throws if an action name is already registered.
108 /// </summary>
109 public void Register(params IActionHandler[] handlers)
110 {
111 foreach (var handler in handlers)
112 {
113 foreach (string action in handler.SupportedActions)
114 {
115 if (!_handlers.TryAdd(action, handler))
116 {
117 throw new InvalidOperationException(
118 $"Action '{action}' is already registered by {_handlers[action].GetType().Name}. " +
119 $"Cannot register again from {handler.GetType().Name}.");
120 }
121 }
122 }
123 }
124
125 /// <summary>
126 /// Dispatches an action in the format <c>{"actionName":"Volume","parameters":{"targetVolume":50}}</c>
127 /// to the appropriate handler.
128 /// </summary>
129 /// <returns>
130 /// A <see cref="ActionResult"/> for the executed action. Check <see cref="ActionResult.IsQuit"/>
131 /// to determine if the caller should exit the interactive loop.
132 /// </returns>
133 public ActionResult Dispatch(JsonElement root)
134 {
135 string actionName = root.TryGetProperty("actionName", out JsonElement actionNameElement)
136 ? actionNameElement.GetString()
137 : null;
138 if (string.IsNullOrEmpty(actionName))
139 {
140 return ActionResult.Fail("Missing actionName in action JSON");
141 }
142
143 if (string.Equals(actionName, "quit", StringComparison.OrdinalIgnoreCase))
144 {
145 return ActionResult.Quit();
146 }
147
148 JsonElement parameters = root.TryGetProperty("parameters", out JsonElement p)
149 ? p
150 : JsonDocument.Parse("{}").RootElement.Clone();
151
152 try
153 {
154 return _handlers.TryGetValue(actionName, out IActionHandler handler)
155 ? handler.Handle(actionName, parameters)
156 : ActionResult.Fail($"Unknown action: {actionName}");
157 }
158 catch (Exception ex)
159 {
160 _logger.Error(ex);
161 return ActionResult.Fail($"Error executing {actionName}: {ex.Message}");
162 }
163 }
164}
165