GPAL - Generally Positive Automation Library v1.0
GPAL The Fluent Automation LIbrary
Loading...
Searching...
No Matches
CDPResultsExtractor.cs
1// =============================================================================
2// GPAL - Generally Positive Automation Library
3// Copyright © 2026 Software Decisions, Inc. All rights reserved.
4//
5// This file is part of GPAL.
6// Licensed under the Business Source License 1.1
7//
8// Primary development, architecture, and vision by Michael B. Vederman,
9// CEO of Software Decisions, Inc., Texas.
10//
11// Internal development maintained privately.
12// Public releases appear on GitHub: https://github.com/SoftwareDecisionsInc/GPAL.
13//
14// See LICENSE for full terms, including Additional Use Grant.
15// =============================================================================
16
17// In CDPResultsExtractor.cs
18using System;
19using System.Collections.Generic;
20using System.Linq;
21using System.Reflection;
22using Newtonsoft.Json;
23using Newtonsoft.Json.Linq;
24using static GenerallyPositive.Enums;
25
27{
28 internal static class CdpResultExtractors
29 {
30 public static Dictionary<DevToolsMethods, Func<JObject, object>> Build(string protocolJson)
31 {
32 if (string.IsNullOrEmpty(protocolJson))
33 throw new ArgumentException("Invalid protocol JSON");
34
35 var protocol = JsonConvert.DeserializeObject<ProtocolRoot>(protocolJson);
36 if (protocol == null)
37 throw new ArgumentException("Failed to parse protocol JSON");
38
39 var commandToReturns = protocol.Domains
40 .SelectMany(d => (d.Commands ?? new List<ProtocolCommand>())
41 .Select(c => new
42 {
43 Key = $"{d.Domain}.{c.Name}",
44 ReturnNames = (c.Returns ?? new List<ProtocolReturn>())
45 .Select(r => r.Name)
46 .ToArray()
47 }))
48 .ToDictionary(x => x.Key, x => x.ReturnNames, StringComparer.OrdinalIgnoreCase);
49
50 var enumToCommand = Enum.GetValues(typeof(DevToolsMethods))
51 .Cast<DevToolsMethods>()
52 .ToDictionary(
53 m => m,
54 m =>
55 {
56 var field = m.GetType().GetField(m.ToString());
57 var attr = field?.GetCustomAttribute<CommandAttribute>();
58 return attr?.Name ?? m.ToString().Replace("_", ".");
59 },
60 EqualityComparer<DevToolsMethods>.Default);
61
62 var overrides = new Dictionary<string, Func<JObject, object>>(StringComparer.OrdinalIgnoreCase)
63 {
64 ["Page.getNavigationHistory"] = m =>
65 {
66 var idx = (int?)m["currentIndex"] ?? -1;
67 var entries = m["entries"] as JArray;
68 if (entries != null && idx >= 0 && idx < entries.Count)
69 {
70 return entries[idx]?["url"]?.Value<string>();
71 }
72 else
73 {
74 return new { CurrentIndex = m["currentIndex"]?.Value<int>(), Entries = entries };
75 }
76
77 },
78 ["DOM.querySelectorAll"] = m => m["nodeIds"]?.ToObject<int[]>() ?? new int[0],
79 ["Page.captureScreenshot"] = m => m["data"]?.Value<string>(),
80 ["Target.createTarget"] = m => m["targetId"]?.Value<string>(),
81 ["Target.attachToTarget"] = m => m["sessionId"]?.Value<string>(),
82 ["Runtime.evaluate"] = m =>
83 {
84 var result = m["result"];
85 if (result == null) return null;
86 var ro = result["result"];
87 if (ro?["objectId"] == null && ro["value"] != null && ro["type"]?.Value<string>() != "undefined")
88 return ro["value"].ToObject<object>();
89 return ro;
90 },
91 ["DOM.getDocument"] = m => m["root"],
92 ["DOM.describeNode"] = m => m["node"],
93 ["DOM.getAttributes"] = m => m["attributes"]?.ToObject<string[]>(),
94 ["DOM.getBoxModel"] = m => m["model"],
95 ["Runtime.getProperties"] = m => m["result"]?.ToObject<object[]>(),
96 ["DOM.resolveNode"] = m => m["node"],
97 ["Page.enable"] = m => null,
98 ["DOM.enable"] = m => null,
99 ["Network.enable"] = m => null,
100 ["Emulation.setAutomationOverride"] = m => null,
101 ["Input.dispatchMouseEvent"] = m => null,
102 ["Input.insertText"] = m => null
103 };
104
105 var dict = new Dictionary<DevToolsMethods, Func<JObject, object>>();
106
107 foreach (var kv in enumToCommand)
108 {
109 var method = kv.Key;
110 var commandName = kv.Value;
111
112 if (overrides.TryGetValue(commandName, out var overrideExtractor))
113 {
114 dict[method] = overrideExtractor;
115 continue;
116 }
117
118 if (!commandToReturns.TryGetValue(commandName, out var returns) || returns.Length == 0)
119 {
120 dict[method] = m => m.ToObject<object>(); // Return full result instead of null
121 continue;
122 }
123
124 if (returns.Length == 1)
125 {
126 var name = returns[0];
127 dict[method] = m => m[name]?.ToObject<object>();
128 }
129 else
130 {
131 var names = returns;
132 dict[method] = m =>
133 {
134 var result = new Dictionary<string, object>();
135 foreach (var n in names)
136 {
137 if (m[n] != null)
138 result[n] = m[n].ToObject<object>();
139 }
140 return result.Count > 0 ? result : m.ToObject<object>();
141 };
142 }
143 }
144
145 foreach (var kv in enumToCommand.Where(kv => !dict.ContainsKey(kv.Key)))
146 {
147 dict[kv.Key] = m => m.ToObject<object>();
148 }
149
150 return dict;
151 }
152
153 public class ProtocolRoot
154 {
155 [JsonProperty("domains")]
156 public List<ProtocolDomain> Domains { get; set; } = new List<ProtocolDomain>();
157 }
158
159 public class ProtocolDomain
160 {
161 [JsonProperty("domain")]
162 public string Domain { get; set; } = string.Empty;
163
164 [JsonProperty("commands")]
165 public List<ProtocolCommand> Commands { get; set; } = new List<ProtocolCommand>();
166
167 [JsonProperty("types")]
168 public List<ProtocolType> Types { get; set; } = new List<ProtocolType>();
169 }
170
171 public class ProtocolCommand
172 {
173 [JsonProperty("name")]
174 public string Name { get; set; } = string.Empty;
175
176 [JsonProperty("returns")]
177 public List<ProtocolReturn> Returns { get; set; } = new List<ProtocolReturn>();
178 }
179
180 public class ProtocolReturn
181 {
182 [JsonProperty("name")]
183 public string Name { get; set; } = string.Empty;
184
185 [JsonProperty("type")]
186 public string Type { get; set; } = string.Empty;
187
188 [JsonProperty("$ref")]
189 public string Ref { get; set; } = string.Empty;
190 }
191
192 public class ProtocolType
193 {
194 [JsonProperty("id")]
195 public string Id { get; set; } = string.Empty;
196 }
197
198 [AttributeUsage(AttributeTargets.Field)]
199 public sealed class CommandAttribute : Attribute
200 {
201 public string Name { get; }
202 public CommandAttribute(string name) => Name = name;
203 }
204 }
205}