GPAL - Generally Positive Automation Library v1.0
GPAL The Fluent Automation LIbrary
Loading...
Searching...
No Matches
RESTClient.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
17using System;
18using System.Collections.Generic;
19using System.IO;
20using System.Linq;
21using System.Net.Http;
22using System.Threading.Tasks;
23using DocumentFormat.OpenXml.Office2019.Drawing.Diagram11;
25using static GenerallyPositive.Enums;
26using static OpenCvSharp.LineIterator;
27
28namespace GenerallyPositive
29{
56 public class RESTClient : IRESTClient
57 {
58 private readonly List<Action<RESTClient>> _workflows = new List<Action<RESTClient>>();
59 private readonly List<(string Endpoint, object Parameters, string HttpMethod, string NextResultName)?> _workflowStates = new List<(string, object, string, string)?>();
60 private bool _customEndpoint = false;
61 private string _endpoint;
62 private object _parameters;
63 private string _httpMethod = "GET";
64 internal ContentEncoding _contentEncoding = ContentEncoding.Json; // Default to JSON
65 private string _name;
66 private string _saveFile { get; set; }
67 private string _nextResultName;
68 private readonly List<(string Name, object Result, int Iteration)> _executionResults = new List<(string Name, object Result, int Iteration)>();
69 private RESTHelper _helper;
70 private int _currentIteration;
71 internal Dictionary<string, string> _headers = new Dictionary<string, string>();
72 public int StatusCode
73 {
74 get => _helper.StatusCode;
75 }
76
77 internal Dictionary<string, (string[] Mandatory, string[] Optional, Func<IDictionary<string, object>, bool> CustomValidation)>
78 openApiParamRules = new Dictionary<string, (string[] Mandatory, string[] Optional, Func<IDictionary<string, object>, bool> CustomValidation)>(StringComparer.OrdinalIgnoreCase);
79
80 Dictionary<ApiEndpoint, (string[] Mandatory, string[] Optional, Func<IDictionary<string, object>, bool> CustomValidation)> paramRules = new Dictionary<ApiEndpoint, (string[] Mandatory, string[] Optional, Func<IDictionary<string, object>, bool> CustomValidation)>
81 {
82 [ApiEndpoint.Back] = (new string[] { }, new string[] { }, null),
83 [ApiEndpoint.CaptureVisibleTab] = (new string[] { }, new string[] { "windowId", "imageFormat" }, null),
84 [ApiEndpoint.CastDesktop] = (new[] { "sinkName" }, new string[] { }, null),
85 [ApiEndpoint.CastTab] = (new[] { "sinkName" }, new string[] { }, null),
86 [ApiEndpoint.CheckNetworkIdle] = (
87 Mandatory: new string[] { },
88 Optional: new[] { "maxConnections", "timeoutMs", "pruneMs", "sessionToken" },
89 CustomValidation: null
90 ),
91 [ApiEndpoint.CheckStatus] = (new string[] { }, new string[] { }, null),
92 [ApiEndpoint.ClearReferrer] = (new string[] { }, new string[] { }, null),
93 [ApiEndpoint.CloseBrowser] = (
94 Mandatory: new string[] { },
95 Optional: new string[] { },
96 CustomValidation: null
97 ),
98 [ApiEndpoint.CloseTab] = (
99 Mandatory: new string[] { },
100 Optional: new[] { "tabId", "url" },
101 CustomValidation: null
102 ),
103 [ApiEndpoint.CloseWindow] = (
104 Mandatory: new string[] { },
105 Optional: new[] { "windowId", "url" },
106 CustomValidation: null
107 ),
108 // NOTE: magic allows no storageType to delete all storage by not specifying storageType
109 // NOTE: at this point, we don't want to accidentally delete all, so we enforce storageType as mandatory, but all of a storageType can be deleted
110 // BROWSER.browsingData.remove({ origins }, {
111 // cache: true,
112 // cookies: true,
113 // localStorage: true,
114 // serviceWorkers: true
115 // });
116 [ApiEndpoint.DeleteStorage] = (new[] { "storageType" }, new[] { "domain", "path", "key", "storeName" }, null),
117 [ApiEndpoint.DragAndDrop] = (new[] { "deltaX", "deltaY" }, new[] { "css", "elementId", "offsetX", "offsetY" }, null),
118 [ApiEndpoint.Evaluate] = (new string[] { }, new string[] { "xpath", "elementId" }, dict => true == dict.ContainsKey("xpath") || true == dict.ContainsKey("elementId")),
119 [ApiEndpoint.EvaluateAll] = (new string[] { }, new string[] { "xpath", "elementId" }, dict => true == dict.ContainsKey("xpath") || true == dict.ContainsKey("elementId")),
120 [ApiEndpoint.EvaluatePersistent] = (new string[] { }, new string[] { "xpath", "elementId" }, dict => true == dict.ContainsKey("xpath") || true == dict.ContainsKey("elementId")),
121 [ApiEndpoint.EvaluateAllPersistent] = (new string[] { }, new string[] { "xpath", "elementId" }, dict => true == dict.ContainsKey("xpath") || true == dict.ContainsKey("elementId")),
122 [ApiEndpoint.ExecuteJavaScript] = (new[] { "script" }, new[] { "parameters" }, null), // parameters optional
123 [ApiEndpoint.Fetch] = (new[] { "url" }, new[] { "method", "body", "contentType", "headers", "bytes" }, null), // GET with no body when unsaid, text unless bytes were asked for
124 [ApiEndpoint.FillInAppend] = (new[] { "text" }, new string[] { "css", "elementId", "gpalElements", "typingDelay" }, dict => true == dict.ContainsKey("css") || true == dict.ContainsKey("elementId") || true == dict.ContainsKey("gpalElements")),
125 [ApiEndpoint.FillInInsert] = (new[] { "text" }, new string[] { "css", "elementId", "gpalElements", "typingDelay" }, dict => true == dict.ContainsKey("css") || true == dict.ContainsKey("elementId") || true == dict.ContainsKey("gpalElements")),
126 [ApiEndpoint.FillInOverwrite] = (new[] { "text" }, new string[] { "css", "elementId", "gpalElements", "typingDelay" }, dict => true == dict.ContainsKey("css") || true == dict.ContainsKey("elementId") || true == dict.ContainsKey("gpalElements")),
127 [ApiEndpoint.FireChangeEvent] = (new string[] { }, new string[] { "css", "elementId", "gpalElements" }, dict => true == dict.ContainsKey("css") || true == dict.ContainsKey("elementId") || true == dict.ContainsKey("gpalElements")),
128 [ApiEndpoint.Focus] = (new string[] { }, new string[] { "css", "elementId", "gpalElements" }, dict => true == dict.ContainsKey("css") || true == dict.ContainsKey("elementId") || true == dict.ContainsKey("gpalElements")),
129 [ApiEndpoint.Forward] = (new string[] { }, new string[] { }, null),
130 [ApiEndpoint.FullScreen] = (new string[] { }, new string[] { }, null),
131 [ApiEndpoint.GetAttribute] = (new[] { "attribute" }, new string[] { "css", "elementId", "gpalElements" }, dict => true == dict.ContainsKey("css") || true == dict.ContainsKey("elementId") || true == dict.ContainsKey("gpalElements")),
132 [ApiEndpoint.GetBoundingClientRect] = (new string[] { }, new string[] { "css", "elementId", "gpalElements" }, dict => true == dict.ContainsKey("css") || true == dict.ContainsKey("elementId") || true == dict.ContainsKey("gpalElements")),
133 [ApiEndpoint.ElementFromPoint] = (new[] { "x", "y" }, new string[] { "css", "elementId", "gpalElements" }, dict => true == dict.ContainsKey("css") || true == dict.ContainsKey("elementId") || true == dict.ContainsKey("gpalElements")),
134 [ApiEndpoint.GetBrowserSettings] = (new string[] { }, new string[] { }, null),
135 [ApiEndpoint.GetCssAttributes] = (new string[] { }, new string[] { "css", "elementId", "gpalElements" }, null),
136 [ApiEndpoint.GetContentAndCss] = (new string[] { }, new string[] { "elementId", "gpalElements" }, null),
137 [ApiEndpoint.GetCurrentUrl] = (new string[] { }, new string[] { }, null),
138 [ApiEndpoint.GetCurrentWindow] = (new string[] { }, new string[] { }, null),
139 [ApiEndpoint.GetDomAttributes] = (new string[] { }, new string[] { "css", "elementId", "gpalElements" }, null),
140 [ApiEndpoint.GetDomProperties] = (new string[] { }, new string[] { "css", "elementId", "gpalElements" }, null),
141 [ApiEndpoint.GetElementAttributeHash] = (new string[] { }, new string[] { "css", "elementId", "gpalElements" }, null),
142 [ApiEndpoint.GetGpalSettings] = (new string[] { }, new string[] { }, null),
143 [ApiEndpoint.GetPageSource] = (new string[] { }, new string[] { }, null),
144 [ApiEndpoint.GetParentNode] = (new string[] { }, new string[] { "css", "elementId", "gpalElements" }, null),
145 [ApiEndpoint.GetReadyStatus] = (new string[] { }, new string[] { "sessionToken" }, null),
146 [ApiEndpoint.GetSettings] = (new string[] { }, new string[] { }, null),
147 [ApiEndpoint.GetShadowRoot] = (new string[] { }, new string[] { "css", "elementId" }, null),
148 [ApiEndpoint.GetStorage] = (new[] { "storageType" }, new[] { "domain", "path", "key", "storeName" }, null),
149 [ApiEndpoint.GetLanguages] = (new string[] { }, new string[] { }, null),
150 [ApiEndpoint.CaptureCalls] = (new string[] { }, new[] { "capture", "filter", "clear" }, null), // no filter records everything
151 [ApiEndpoint.GetCapturedCalls] = (new string[] { }, new string[] { }, null),
152 [ApiEndpoint.GetUserAgent] = (new string[] { }, new string[] { }, null),
153 [ApiEndpoint.GetWorkflow] = (new string[] { }, new string[] { }, null),
154 [ApiEndpoint.GoTo] = (new[] { "url" }, new string[] { }, null),
155 [ApiEndpoint.GoToTab] = (
156 Mandatory: new string[] { },
157 Optional: new[] { "tabId", "url" },
158 CustomValidation: dict => true == dict.ContainsKey("tabId") || true == dict.ContainsKey("url")
159 ),
160 [ApiEndpoint.GoToWindow] = (
161 Mandatory: new string[] { },
162 Optional: new[] { "url", "windowId" },
163 CustomValidation: dict => dict.ContainsKey("url") != dict.ContainsKey("windowId") // Exactly one must be present
164 ),
165 [ApiEndpoint.GetWindowRectangle] = (new string[] { }, new string[] { }, null),
166 [ApiEndpoint.HideElement] = (new string[] { }, new string[] { "css", "elementId", "gpalElements" }, dict => true == dict.ContainsKey("css") || true == dict.ContainsKey("elementId") || true == dict.ContainsKey("gpalElements")),
167 [ApiEndpoint.Hover] = (new string[] { }, new string[] { "css", "elementId", "gpalElements" }, dict => true == dict.ContainsKey("css") || true == dict.ContainsKey("elementId") || true == dict.ContainsKey("gpalElements")),
168 [ApiEndpoint.InjectScript] = (new[] { "script" }, new string[] { }, null),
169 [ApiEndpoint.ClearInjectedScripts] = (new string[] { }, new string[] { }, null),
170 [ApiEndpoint.IsClickable] = (new string[] { }, new string[] { "css", "elementId", "gpalElements" }, dict => true == dict.ContainsKey("css") || true == dict.ContainsKey("elementId") || true == dict.ContainsKey("gpalElements")),
171 [ApiEndpoint.IsDisplayed] = (new string[] { }, new string[] { "css", "elementId", "gpalElements" }, dict => true == dict.ContainsKey("css") || true == dict.ContainsKey("elementId") || true == dict.ContainsKey("gpalElements")),
172 [ApiEndpoint.IsEnabled] = (new string[] { }, new string[] { "css", "elementId", "gpalElements" }, dict => true == dict.ContainsKey("css") || true == dict.ContainsKey("elementId") || true == dict.ContainsKey("gpalElements")),
173 [ApiEndpoint.IsEndOfPage] = (new string[] { }, new string[] { }, null),
174 [ApiEndpoint.IsVisibleInViewport] = (new string[] { }, new string[] { "css", "elementId", "gpalElements" }, dict => true == dict.ContainsKey("css") || true == dict.ContainsKey("elementId") || true == dict.ContainsKey("gpalElements")),
175 [ApiEndpoint.LeftClick] = (new string[] { }, new string[] { "css", "elementId", "gpalElements", "modifiers" }, dict => true == dict.ContainsKey("css") || true == dict.ContainsKey("elementId") || true == dict.ContainsKey("gpalElements") || true == dict.ContainsKey("modifiers")),
176 [ApiEndpoint.LeftClickAndDownload] = (new[] { "downloadPath" }, new string[] { "css", "elementId", "gpalElements", "modifiers" }, dict => true == dict.ContainsKey("css") || true == dict.ContainsKey("elementId") || true == dict.ContainsKey("gpalElements") || true == dict.ContainsKey("modifiers")),
177 [ApiEndpoint.LeftDoubleClick] = (new string [] { }, new string[] { "css", "elementId", "gpalElements", "modifiers" }, dict => true == dict.ContainsKey("css") || true == dict.ContainsKey("elementId") || true == dict.ContainsKey("gpalElements") || true == dict.ContainsKey("modifiers")),
178 [ApiEndpoint.Maximize] = (new string[] { }, new string[] { }, null),
179 [ApiEndpoint.MiddleClick] = (new string[] { }, new string[] { "css", "elementId", "gpalElements", "modifiers" }, dict => true == dict.ContainsKey("css") || true == dict.ContainsKey("elementId") || true == dict.ContainsKey("gpalElements") || true == dict.ContainsKey("modifiers")),
180 [ApiEndpoint.Minimize] = (new string[] { }, new string[] { }, null),
181 [ApiEndpoint.MoveTo] = (new string[] { }, new string[] { "css", "elementId", "gpalElements" }, dict => true == dict.ContainsKey("css") || true == dict.ContainsKey("elementId") || true == dict.ContainsKey("gpalElements")),
182 [ApiEndpoint.NewTab] = (new string[] { }, new[] { "url" }, null),
183 [ApiEndpoint.NextTab] = (new string[] { }, new string[] { }, null),
184 [ApiEndpoint.NextWindow] = (new string[] { }, new string[] { }, null),
185 [ApiEndpoint.Normal] = (new string[] { }, new string[] { }, null),
186 [ApiEndpoint.OpenWindow] = (new string[] { }, new[] { "url" }, null),
187 [ApiEndpoint.OverrideReferrer] = (new[] { "url" }, new string[] { }, null),
188 [ApiEndpoint.PageDown] = (new string[] { }, new string[] { "pagesToScroll" }, null),
189 [ApiEndpoint.PageEnd] = (new string[] { }, new string[] { }, null),
190 [ApiEndpoint.PageTop] = (new string[] { }, new string[] { }, null),
191 [ApiEndpoint.PageUp] = (new string[] { }, new string[] { "pagesToScroll" }, null),
192 [ApiEndpoint.PressModifierKey] = (new[] { "modifierKeys" }, new string[] { }, null),
193 [ApiEndpoint.PreviousTab] = (new string[] { }, new string[] { }, null),
194 [ApiEndpoint.PreviousWindow] = (new string[] { }, new string[] { }, null),
195 [ApiEndpoint.QuerySelector] = (new string[] { }, new string[] { "css", "elementId" }, dict => true == dict.ContainsKey("css") || true == dict.ContainsKey("elementId")),
196 [ApiEndpoint.QuerySelectors] = (new string[] { }, new string[] { "css", "elementId" }, dict => true == dict.ContainsKey("css") || true == dict.ContainsKey("elementId")),
197 [ApiEndpoint.QueryPersistentSelector] = (new string[] { }, new string[] { "css", "elementId" }, dict => true == dict.ContainsKey("css") || true == dict.ContainsKey("elementId")),
198 [ApiEndpoint.QueryPersistentSelectors] = (new string[] { }, new string[] { "css", "elementId" }, dict => true == dict.ContainsKey("css") || true == dict.ContainsKey("elementId")),
199 [ApiEndpoint.Refresh] = (new string[] { }, new string[] { }, null),
200 [ApiEndpoint.ReleaseModifierKey] = (new[] { "modifierKeys" }, new string[] { }, null),
201 [ApiEndpoint.Restore] = (new string[] { }, new string[] { }, null),
202 [ApiEndpoint.RightClick] = (new string[] { }, new string[] { "css", "elementId", "gpalElements", "modifiers" }, dict => true == dict.ContainsKey("css") || true == dict.ContainsKey("elementId") || true == dict.ContainsKey("gpalElements") || true == dict.ContainsKey("modifiers")),
203 [ApiEndpoint.RightClickAndDownload] = (new[] { "downloadPath" }, new string[] { "modifiers" }, dict => true == dict.ContainsKey("css") || true == dict.ContainsKey("elementId")),
204 [ApiEndpoint.ScrollWindowByHorizontal] = (new[] { "hPixels" }, new string[] { "vPixels" }, null),
205 [ApiEndpoint.ScrollWindowByVertical] = (new[] { "vPixels" }, new string[] { "hPixels" }, null),
206 [ApiEndpoint.ScrollElement] = (
207 Mandatory: new string[] { },
208 Optional: new string[] { },
209 CustomValidation: dict => true == (dict.ContainsKey("hPixels") || true == dict.ContainsKey("vPixels")) && (true == dict.ContainsKey("css") || true == dict.ContainsKey("elementId"))
210 ),
211 [ApiEndpoint.ScrollIntoView] = (new string[] { }, new string[] { "css", "elementId", "gpalElements" }, dict => true == dict.ContainsKey("css") || true == dict.ContainsKey("elementId") || true == dict.ContainsKey("gpalElements")),
212 [ApiEndpoint.ScrollWindow] = (
213 Mandatory: new string[] { },
214 Optional: new[] { "hPixels", "vPixels" },
215 CustomValidation: dict => true == dict.ContainsKey("hPixels") || true == dict.ContainsKey("vPixels")
216 ),
217 [ApiEndpoint.SelectClick] = ( new string[] { }, new[] { "elementId", "gpalElements", "selectIndex", "selectValue" }, dict => true == dict.ContainsKey("elementId") || true == dict.ContainsKey("gpalElements") || true == dict.ContainsKey("index") || true == dict.ContainsKey("value")),
218 [ApiEndpoint.SendKey] = (new [] { "key", "code", "vk" }, new string[] { }, null),
219 [ApiEndpoint.SendString] = (new[] { "text" }, new[] { "typingDelay" }, null),
220 [ApiEndpoint.SetAttribute] = (new[] { "attribute", "value" }, new string[] { }, dict => true == dict.ContainsKey("css") || true == dict.ContainsKey("elementId")),
221 [ApiEndpoint.SetDownloadFilename] = (new[] { "downloadPath" }, new string[] { }, null),
222 [ApiEndpoint.SetRange] = (new[] { "elementId", "rangeValue" }, new string[] { }, null),
223 [ApiEndpoint.SetStorage] = (new[] { "storageType", "key", "data" }, new[] { "domain", "path", "storeName" }, null), // local/sessionStorage are simple key/value pairs, so that is the only ones we can make mandatory
224 [ApiEndpoint.SetStorageCache] = (new[] { "storageType", "key", "data", "storeName" }, new[] { "domain", "path" }, null),
225 [ApiEndpoint.SetStorageIndexedDb] = (new[] { "storageType", "key", "data", "path", "storeName" }, new[] { "domain" }, null),
226 [ApiEndpoint.SetUserAgent] = (new[] { "userAgent" }, new string[] { }, null),
227 [ApiEndpoint.SetValueFromElement] = (new[] { "destSelector" }, new string[] { "css", "elementId", "gpalElements" }, dict => true == dict.ContainsKey("css") || true == dict.ContainsKey("elementId") || true == dict.ContainsKey("gpalElements")),
228 [ApiEndpoint.StealthOverrideReferrer] = (new string[] { }, new string[] { }, null),
229 [ApiEndpoint.StopCasting] = (new string[] { }, new string[] { }, null),
230 [ApiEndpoint.SubmitForm] = (new string[] { }, new string[] { "css", "elementId", "gpalElements" }, dict => true == dict.ContainsKey("css") || true == dict.ContainsKey("elementId") || true == dict.ContainsKey("gpalElements")),
231 [ApiEndpoint.SwitchToDefaultContent] = (new string[] { }, new string[] { }, null),
232 [ApiEndpoint.SwitchToElement] = (new string[] { }, new string[] { "css", "elementId" }, dict => true == dict.ContainsKey("css") || true == dict.ContainsKey("elementId")),
233 [ApiEndpoint.SwitchToFrame] = (new string[] { }, new string[] { "css", "elementId" }, dict => true == dict.ContainsKey("css") || true == dict.ContainsKey("elementId")),
234 [ApiEndpoint.SwitchToShadowRoot] = (new string[] { }, new string[] { "css", "elementId" }, dict => true == dict.ContainsKey("css") || true == dict.ContainsKey("elementId")),
235 [ApiEndpoint.TabCount] = (new string[] { }, new string[] { }, null),
236 [ApiEndpoint.TopBrowser] = (new string[] { }, new string[] { }, null),
237 [ApiEndpoint.MoveToPoint] = (new[] { "x", "y" }, new string[] { }, null),
238 [ApiEndpoint.ClickPoint] = (new[] { "x", "y" }, new string[] { "clickType", "modifiers" }, null),
239 [ApiEndpoint.TrustedLeftClick] = (new string[] { }, new string[] { "css", "elementId", "gpalElements", "modifiers", "holdMs" }, null),
240 [ApiEndpoint.Upload] = (new[] { "elementId" }, new string[] { "css", "elementId", "gpalElements", "modifiers", "uploadPath", "uploadPaths" }, dict => true == dict.ContainsKey("css") || true == dict.ContainsKey("elementId") || true == dict.ContainsKey("gpalElements") || true == dict.ContainsKey("modifiers")),
241 [ApiEndpoint.WindowInnerHeight] = (new string[] { }, new string[] { }, null),
242 [ApiEndpoint.WindowInnerWidth] = (new string[] { }, new string[] { }, null),
243 [ApiEndpoint.WindowOuterHeight] = (new string[] { }, new string[] { }, null),
244 [ApiEndpoint.WindowOuterWidth] = (new string[] { }, new string[] { }, null),
245 [ApiEndpoint.WindowPageOffsetX] = (new string[] { }, new string[] { }, null),
246 [ApiEndpoint.WindowPageOffsetY] = (new string[] { }, new string[] { }, null),
247 [ApiEndpoint.WindowScreenLeft] = (new string[] { }, new string[] { }, null),
248 [ApiEndpoint.WindowScreenTop] = (new string[] { }, new string[] { }, null),
249 };
250
251
252 internal RESTClient()
253 {
254 }
255
259 public string Name
260 {
261 get => _name;
262 internal set => _name = value;
263 }
264
271 {
272 _helper = new RESTHelper(url, this);
273 return this;
274 }
275
276 public IAllowRESTEndpointDetails WithAPIBase(string url, TimeSpan httpTimeout)
277 {
278 _helper = new RESTHelper(url, this, httpTimeout);
279 return this;
280 }
281
288 {
289 Reset();
290 _endpoint = endpoint;
291 _customEndpoint = true;
292 return this;
293 }
294
301 {
302 Reset();
303 if (null == _helper)
304 {
305 GPAL.PublishSimpleEvent(GPALEventType.WARNING,
306 $"{(string.IsNullOrEmpty(_name) ? "" : $"[{_name}] ")}RESTHelper not initialized. Call WithAPIBase first.",
307 null, GPALObjectType.None);
308 _endpoint = "";
309 }
310 else if (false == RESTHelper.Endpoints.ContainsKey(endpoint))
311 {
312 GPAL.PublishSimpleEvent(GPALEventType.WARNING,
313 $"{(string.IsNullOrEmpty(_name) ? "" : $"[{_name}] ")}API endpoint [{endpoint}] not found in Endpoints dictionary. Using empty string.",
314 null, GPALObjectType.None);
315 _endpoint = "";
316 }
317 else
318 {
319 _endpoint = RESTHelper.Endpoints[endpoint];
320 }
321 return this;
322 }
323
324 public IAllowRESTEndpointDetails LoadOpenAPIMap(string yamlOrUrl)
325 {
326 if (string.IsNullOrWhiteSpace(yamlOrUrl))
327 {
328 GPAL.PublishSimpleEvent(GPALEventType.ERROR, "LoadOpenAPIMap: yamlOrUrl is empty", null, GPALObjectType.Other);
329 return this;
330 }
331
332 string yamlContent;
333 string chosenBaseUrl = null;
334
335 if (yamlOrUrl.StartsWith("http", StringComparison.OrdinalIgnoreCase))
336 {
337 // It's already a direct URL to the YAML
338 try
339 {
340 using var http = new HttpClient();
341 yamlContent = http.GetStringAsync(yamlOrUrl).GetAwaiter().GetResult();
342 chosenBaseUrl = ExtractBestServerUrl(yamlContent);
343 }
344 catch (Exception ex)
345 {
346 GPAL.PublishSimpleEvent(GPALEventType.ERROR,
347 $"Failed to download OpenAPI spec from [{yamlOrUrl}]", ex.Message, GPALObjectType.Other, ex);
348 return this;
349 }
350 }
351 else
352 {
353 // Assume it's raw YAML content
354 yamlContent = yamlOrUrl;
355 chosenBaseUrl = ExtractBestServerUrl(yamlContent);
356 }
357
358 // Set the RESTHelper with the best base URL (just like WithAPIBase does)
359 if (!string.IsNullOrWhiteSpace(chosenBaseUrl))
360 {
361 _helper = new RESTHelper(chosenBaseUrl, this);
362 GPAL.PublishSimpleEvent(GPALEventType.INFO,
363 $"LoadOpenAPIMap set API base to: [{chosenBaseUrl}]", null, GPALObjectType.Other);
364 }
365 else
366 {
367 GPAL.PublishSimpleEvent(GPALEventType.WARNING,
368 "LoadOpenAPIMap could not determine API base from servers section", null, GPALObjectType.Other);
369 }
370
371 // Now load the rules
372 LoadOpenApiRulesInternal(yamlContent);
373
374 return this;
375 }
376
377 public IAllowRESTEndpointDetails LoadOpenAPIMap(GPALFile openApiMapFile)
378 {
379
380 try
381 {
382 string yamlContent = File.ReadAllText(openApiMapFile.Filename);
383 return LoadOpenAPIMap(yamlContent); // reuse the string overload
384 }
385 catch (Exception ex)
386 {
387 GPAL.PublishSimpleEvent(GPALEventType.ERROR,
388 $"Failed to read OpenAPI file [{openApiMapFile.Filename}]", ex.Message, GPALObjectType.Other, ex);
389 return this;
390 }
391 }
392
393 public void LoadOpenApiRulesInternal(string yamlContent)
394 {
395 try
396 {
397 var reader = new Microsoft.OpenApi.Readers.OpenApiStreamReader();
398 using var stream = new System.IO.MemoryStream(System.Text.Encoding.UTF8.GetBytes(yamlContent));
399 var document = reader.Read(stream, out var diagnostic);
400
401 if (diagnostic.Errors.Any())
402 {
403 GPAL.PublishSimpleEvent(GPALEventType.WARNING,
404 $"OpenAPI parse issues: [{diagnostic.Errors.Count}] [{string.Join(",",diagnostic.Errors)}]", diagnostic, GPALObjectType.Other);
405 }
406
407 openApiParamRules.Clear();
408
409 foreach (var pathItem in document.Paths)
410 {
411 foreach (var opKv in pathItem.Value.Operations)
412 {
413 var method = opKv.Key.ToString().ToUpperInvariant();
414 var path = pathItem.Key.TrimStart('/');
415 var ruleKey = $"{method} {path}"; // e.g. "GET /api/basicstore/products"
416
417 var mandatory = new List<string>();
418 var optional = new List<string>();
419
420 foreach (var param in opKv.Value.Parameters)
421 {
422 if (param.Required)
423 mandatory.Add(param.Name);
424 else
425 optional.Add(param.Name);
426 }
427
428 if (opKv.Value.RequestBody?.Required == true)
429 mandatory.Add("body"); // simple body marker
430
431 openApiParamRules[ruleKey] = (
432 Mandatory: mandatory.ToArray(),
433 Optional: optional.ToArray(),
434 CustomValidation: null
435 );
436 }
437 }
438
439 GPAL.PublishSimpleEvent(GPALEventType.INFO,
440 $"Loaded OpenAPI rules for [{openApiParamRules.Count}] operations from spec",
441 null, GPALObjectType.Other);
442 }
443 catch (Exception ex)
444 {
445 GPAL.PublishSimpleEvent(GPALEventType.EXCEPTION,
446 "Failed to load OpenAPI parameter rules", yamlContent?.Length ?? 0, GPALObjectType.Other, ex);
447 }
448 }
449
450 public IAllowRESTEndpointDetails ListOpenAPIEndPoints()
451 {
452 foreach (var key in openApiParamRules.Keys)
453 GPAL.PublishSimpleEvent(GPALEventType.INFO, key);
454
455 return this;
456 }
457
464 {
465 _nextResultName = name;
466 return this;
467 }
468
475 {
476 _parameters = new { url };
477 WithHttpMethod("POST");
478 return this;
479 }
480
487 {
488 if (0 > resultIndex || resultIndex >= _executionResults.Count)
489 {
490 GPAL.PublishSimpleEvent(GPALEventType.WARNING,
491 $"{(string.IsNullOrEmpty(_name) ? "" : $"[{_name}] ")}Result index [{resultIndex}] is out of range for WithUrlFromResult. Using empty string.",
492 null, GPALObjectType.None);
493 return WithUrl("");
494 }
495 var previousResult = _executionResults[resultIndex].Result;
496 var url = null == previousResult ? "" : previousResult.ToString();
497 return WithUrl(url);
498 }
499
506 {
507 var resultEntry = _executionResults.LastOrDefault(entry => entry.Name == name);
508 if (null == resultEntry.Result)
509 {
510 GPAL.PublishSimpleEvent(GPALEventType.WARNING,
511 $"{(string.IsNullOrEmpty(_name) ? "" : $"[{_name}] ")}Result name [{name}] not found for WithUrlFromResult. Using empty string.",
512 null, GPALObjectType.None);
513 return WithUrl("");
514 }
515 var previousResult = resultEntry.Result;
516 var url = null == previousResult ? "" : previousResult.ToString();
517 return WithUrl(url);
518 }
519
526 {
527 _parameters = new { tabId };
528 WithHttpMethod("POST");
529 return this;
530 }
531
538 {
539 if (0 > resultIndex || resultIndex >= _executionResults.Count)
540 {
541 GPAL.PublishSimpleEvent(GPALEventType.WARNING,
542 $"{(string.IsNullOrEmpty(_name) ? "" : $"[{_name}] ")}Result index [{resultIndex}] is out of range for WithTabIdFromResult. Using 0.",
543 null, GPALObjectType.None);
544 return WithTabId(0);
545 }
546 var previousResult = _executionResults[resultIndex].Result;
547 var tabId = previousResult is int i ? i : 0;
548 return WithTabId(tabId);
549 }
550
557 {
558 var resultEntry = _executionResults.LastOrDefault(entry => entry.Name == name);
559 if (null == resultEntry.Result)
560 {
561 GPAL.PublishSimpleEvent(GPALEventType.WARNING,
562 $"{(string.IsNullOrEmpty(_name) ? "" : $"[{_name}] ")}Result name [{name}] not found for WithTabIdFromResult. Using 0.",
563 null, GPALObjectType.None);
564 return WithTabId(0);
565 }
566 var previousResult = resultEntry.Result;
567 var tabId = previousResult is int i ? i : 0;
568 return WithTabId(tabId);
569 }
570
577 {
578 _parameters = new { script, parameters = _parameters };
579 WithHttpMethod("POST");
580 return this;
581 }
582
589 {
590 _parameters = null == _parameters
591 ? new { key }
592 : MergeParameters(_parameters, new { key });
593
594 WithHttpMethod("POST");
595 return this;
596 }
597
604 {
605 if (0 > resultIndex || resultIndex >= _executionResults.Count)
606 {
607 GPAL.PublishSimpleEvent(GPALEventType.WARNING,
608 $"{(string.IsNullOrEmpty(_name) ? "" : $"[{_name}] ")}Result index [{resultIndex}] is out of range for WithKeyFromResult. Using empty string.",
609 null, GPALObjectType.None);
610 return WithKey("");
611 }
612 var previousResult = _executionResults[resultIndex].Result;
613 var key = null == previousResult ? "" : previousResult.ToString();
614 return WithKey(key);
615 }
616
623 {
624 var resultEntry = _executionResults.LastOrDefault(entry => entry.Name == name);
625 if (null == resultEntry.Result)
626 {
627 GPAL.PublishSimpleEvent(GPALEventType.WARNING,
628 $"{(string.IsNullOrEmpty(_name) ? "" : $"[{_name}] ")}Result name [{name}] not found for WithKeyFromResult. Using empty string.",
629 null, GPALObjectType.None);
630 return WithKey("");
631 }
632 var previousResult = resultEntry.Result;
633 var key = null == previousResult ? "" : previousResult.ToString();
634 return WithKey(key);
635 }
636
643 {
644 _name = name;
645 return this;
646 }
647
654 {
655 _parameters = null == _parameters
656 ? new { css }
657 : MergeParameters(_parameters, new { css });
658 WithHttpMethod("POST");
659 return this;
660 }
661
668 {
669 if (0 > resultIndex || resultIndex >= _executionResults.Count)
670 {
671 GPAL.PublishSimpleEvent(GPALEventType.WARNING,
672 $"{(string.IsNullOrEmpty(_name) ? "" : $"[{_name}] ")}Result index [{resultIndex}] is out of range for WithCssFromResult. Using empty string.",
673 null, GPALObjectType.None);
674 return WithCss("");
675 }
676 var previousResult = _executionResults[resultIndex].Result;
677 var css = null == previousResult ? "" : previousResult.ToString();
678 return WithCss(css);
679 }
680
687 {
688 var resultEntry = _executionResults.LastOrDefault(entry => entry.Name == name);
689 if (null == resultEntry.Result)
690 {
691 GPAL.PublishSimpleEvent(GPALEventType.WARNING,
692 $"{(string.IsNullOrEmpty(_name) ? "" : $"[{_name}] ")}Result name [{name}] not found for WithCssFromResult. Using empty string.",
693 null, GPALObjectType.None);
694 return WithCss("");
695 }
696 var previousResult = resultEntry.Result;
697 var css = null == previousResult ? "" : previousResult.ToString();
698 return WithCss(css);
699 }
700
707 {
708 List<GPALElement> gpalElements = new List<GPALElement>() { (GPALElement)gPALElement };
709
710 _parameters = null == _parameters
711 ? new { gpalElements }
712 : MergeParameters(_parameters, new { gpalElements });
713 WithHttpMethod("POST");
714 return this;
715 }
716
723 {
724 if (0 > resultIndex || resultIndex >= _executionResults.Count)
725 {
726 GPAL.PublishSimpleEvent(GPALEventType.WARNING,
727 $"{(string.IsNullOrEmpty(_name) ? "" : $"[{_name}] ")}Result index [{resultIndex}] is out of range for WithElementFromResult. Using empty string.",
728 null, GPALObjectType.None);
729 return WithElementId("");
730 }
731 var previousResult = _executionResults[resultIndex].Result;
732 string elementId;
733
734 // NOTE: what kind of enumerable, what is envisioned? - grok generated...
735 if (previousResult is IEnumerable<object> enumerable && false == (previousResult is string))
736 {
737 elementId = null == enumerable.FirstOrDefault() ? "" : enumerable.FirstOrDefault().ToString();
738 }
739 else
740 {
741 elementId = null == previousResult ? "" : previousResult.ToString();
742 }
743 return WithElementId(elementId);
744 }
745
752 {
753 var resultEntry = _executionResults.LastOrDefault(entry => entry.Name == name);
754 if (null == resultEntry.Result)
755 {
756 GPAL.PublishSimpleEvent(GPALEventType.WARNING,
757 $"{(string.IsNullOrEmpty(_name) ? "" : $"[{_name}] ")}Result name [{name}] not found for WithElementFromResult. Using empty string.",
758 null, GPALObjectType.None);
759 return WithElementId("");
760 }
761 var previousResult = resultEntry.Result;
762 string elementId;
763
764 // NOTE: what kind of enumerable, what is envisioned? - grok generated...
765 if (previousResult is IEnumerable<object> enumerable && false == (previousResult is string))
766 {
767 elementId = null == enumerable.FirstOrDefault() ? "" : enumerable.FirstOrDefault().ToString();
768 }
769 else
770 {
771 elementId = null == previousResult ? "" : previousResult.ToString();
772 }
773 return WithElementId(elementId);
774 }
775
782 {
783 _parameters = null == _parameters
784 ? new { timeoutMs }
785 : MergeParameters(_parameters, new { timeoutMs });
786 WithHttpMethod("POST");
787 return this;
788 }
789
796 {
797 _parameters = null == _parameters
798 ? new { pruneMs }
799 : MergeParameters(_parameters, new { pruneMs });
800 WithHttpMethod("POST");
801 return this;
802 }
803
810 {
811 _parameters = null == _parameters
812 ? new { maxConnections }
813 : MergeParameters(_parameters, new { maxConnections });
814 WithHttpMethod("POST");
815 return this;
816 }
817
823 public IAllowRESTExecution WithUploadFile(string uploadPath)
824 {
825 _parameters = null == _parameters
826 ? new { uploadPath }
827 : MergeParameters(_parameters, new { uploadPath });
828 WithHttpMethod("POST");
829 return this;
830 }
831
838 {
839 _parameters = null == _parameters
840 ? new { uploadPaths }
841 : MergeParameters(_parameters, new { uploadPaths });
842 WithHttpMethod("POST");
843 return this;
844 }
845
852 {
853 _parameters = null == _parameters
854 ? new { xpath }
855 : MergeParameters(_parameters, new { xpath });
856 WithHttpMethod("POST");
857 return this;
858 }
859
866 {
867 if (0 > resultIndex || resultIndex >= _executionResults.Count)
868 {
869 GPAL.PublishSimpleEvent(GPALEventType.WARNING,
870 $"{(string.IsNullOrEmpty(_name) ? "" : $"[{_name}] ")}Result index [{resultIndex}] is out of range for WithXPathFromResult. Using empty string.",
871 null, GPALObjectType.None);
872 return WithXPath("");
873 }
874 var previousResult = _executionResults[resultIndex].Result;
875 var xpath = null == previousResult ? "" : previousResult.ToString();
876 return WithXPath(xpath);
877 }
878
885 {
886 var resultEntry = _executionResults.LastOrDefault(entry => entry.Name == name);
887 if (null == resultEntry.Result)
888 {
889 GPAL.PublishSimpleEvent(GPALEventType.WARNING,
890 $"{(string.IsNullOrEmpty(_name) ? "" : $"[{_name}] ")}Result name [{name}] not found for WithXPathFromResult. Using empty string.",
891 null, GPALObjectType.None);
892 return WithXPath("");
893 }
894 var previousResult = resultEntry.Result;
895 var xpath = null == previousResult ? "" : previousResult.ToString();
896 return WithXPath(xpath);
897 }
898
904 public IAllowRESTParametersOrExecution WithParameters(params object[] parameters)
905 {
906 _parameters = null == _parameters
907 ? parameters
908 : MergeParameters(_parameters, parameters);
909 WithHttpMethod("POST");
910 return this;
911 }
912
919 {
920 _parameters = null == _parameters
921 ? parameters
922 : MergeParameters(_parameters, parameters);
923 WithHttpMethod("POST");
924 return this;
925 }
926
933 {
934 if (0 > resultIndex || resultIndex >= _executionResults.Count)
935 {
936 GPAL.PublishSimpleEvent(GPALEventType.WARNING,
937 $"{(string.IsNullOrEmpty(_name) ? "" : $"[{_name}] ")}Result index [{resultIndex}] is out of range for WithParametersFromResult. Using null.",
938 null, GPALObjectType.None);
939 _parameters = null;
940 }
941 else
942 {
943 _parameters = _executionResults[resultIndex].Result;
944 }
945 WithHttpMethod("POST");
946 return this;
947 }
948
955 {
956 var resultEntry = _executionResults.LastOrDefault(entry => entry.Name == name);
957 if (null == resultEntry.Result)
958 {
959 GPAL.PublishSimpleEvent(GPALEventType.WARNING,
960 $"{(string.IsNullOrEmpty(_name) ? "" : $"[{_name}] ")}Result name [{name}] not found for WithParametersFromResult. Using null.",
961 null, GPALObjectType.None);
962 _parameters = null;
963 }
964 else
965 {
966 _parameters = resultEntry.Result;
967 }
968 WithHttpMethod("POST");
969 return this;
970 }
971
978 {
979 _parameters = null == _parameters
980 ? new { data }
981 : MergeParameters(_parameters, new { data });
982
983 WithHttpMethod("POST");
984 return this;
985 }
986
993 {
994 _parameters = null == _parameters
995 ? new { storeName }
996 : MergeParameters(_parameters, new { storeName });
997
998 WithHttpMethod("POST");
999 return this;
1000 }
1001
1008 {
1009 _parameters = null == _parameters
1010 ? new { domain }
1011 : MergeParameters(_parameters, new { domain });
1012
1013 WithHttpMethod("POST");
1014 return this;
1015 }
1016
1023 {
1024 _parameters = null == _parameters
1025 ? new { path }
1026 : MergeParameters(_parameters, new { path });
1027
1028 WithHttpMethod("POST");
1029 return this;
1030 }
1031
1037 public IAllowRESTStorageOptions WithStorageType(WebsiteStorageType storageType)
1038 {
1039 _parameters = null == _parameters
1040 ? new { storageType = storageType.ToString() }
1041 : MergeParameters(_parameters, new { storageType = storageType.ToString() });
1042
1043 WithHttpMethod("POST");
1044 return this;
1045 }
1046
1053 {
1054 _parameters = null == _parameters
1055 ? new { key }
1056 : MergeParameters(_parameters, new { key });
1057
1058 WithHttpMethod("POST");
1059 return this;
1060 }
1061
1062 public IAllowRESTStorageOptions WithDeleteAcrossOrigins(bool crossOrigin)
1063 {
1064 _parameters = null == _parameters
1065 ? new { crossOrigin }
1066 : MergeParameters(_parameters, new { crossOrigin });
1067
1068 WithHttpMethod("POST");
1069 return this;
1070 }
1071
1078 {
1079 _parameters = null == _parameters
1080 ? new { pixels }
1081 : MergeParameters(_parameters, new { pixels });
1082 WithHttpMethod("POST");
1083 return this;
1084 }
1085
1092 {
1093 if (0 > resultIndex || resultIndex >= _executionResults.Count)
1094 {
1095 GPAL.PublishSimpleEvent(GPALEventType.WARNING,
1096 $"{(string.IsNullOrEmpty(_name) ? "" : $"[{_name}] ")}Result index [{resultIndex}] is out of range for WithPixelsFromResult. Using 0.",
1097 null, GPALObjectType.None);
1098 return WithPixels(0);
1099 }
1100 var previousResult = _executionResults[resultIndex].Result;
1101 var pixels = previousResult is int i ? i : 0;
1102 return WithPixels(pixels);
1103 }
1104
1111 {
1112 var resultEntry = _executionResults.LastOrDefault(entry => entry.Name == name);
1113 if (null == resultEntry.Result)
1114 {
1115 GPAL.PublishSimpleEvent(GPALEventType.WARNING,
1116 $"{(string.IsNullOrEmpty(_name) ? "" : $"[{_name}] ")}Result name [{name}] not found for WithPixelsFromResult. Using 0.",
1117 null, GPALObjectType.None);
1118 return WithPixels(0);
1119 }
1120 var previousResult = resultEntry.Result;
1121 var pixels = previousResult is int i ? i : 0;
1122 return WithPixels(pixels);
1123 }
1124
1131 {
1132 _parameters = null == _parameters
1133 ? new { url }
1134 : MergeParameters(_parameters, new { url });
1135 WithHttpMethod("POST");
1136 return this;
1137 }
1138
1145 {
1146 if (0 > resultIndex || resultIndex >= _executionResults.Count)
1147 {
1148 GPAL.PublishSimpleEvent(GPALEventType.WARNING,
1149 $"{(string.IsNullOrEmpty(_name) ? "" : $"[{_name}] ")}Result index [{resultIndex}] is out of range. Using empty string.",
1150 null, GPALObjectType.None);
1151 return WithReferrer("");
1152 }
1153 var previousResult = _executionResults[resultIndex].Result;
1154 var referrer = null == previousResult ? "" : previousResult.ToString();
1155 return WithReferrer(referrer);
1156 }
1157
1164 {
1165 var resultEntry = _executionResults.LastOrDefault(entry => entry.Name == name);
1166 if (null == resultEntry.Result)
1167 {
1168 GPAL.PublishSimpleEvent(GPALEventType.WARNING,
1169 $"{(string.IsNullOrEmpty(_name) ? "" : $"[{_name}] ")}Result name [{name}] not found. Using empty string.",
1170 null, GPALObjectType.None);
1171 return WithReferrer("");
1172 }
1173 var previousResult = resultEntry.Result;
1174 var referrer = null == previousResult ? "" : previousResult.ToString();
1175 return WithReferrer(referrer);
1176 }
1177
1183 public IAllowRESTExecution WithText(string text)
1184 {
1185 _parameters = null == _parameters
1186 ? new { text }
1187 : MergeParameters(_parameters, new { text });
1188 WithHttpMethod("POST");
1189 return this;
1190 }
1191
1198 {
1199 if (0 > resultIndex || resultIndex >= _executionResults.Count)
1200 {
1201 GPAL.PublishSimpleEvent(GPALEventType.WARNING,
1202 $"{(string.IsNullOrEmpty(_name) ? "" : $"[{_name}] ")}Result index [{resultIndex}] is out of range for WithTextFromResult. Using empty string.",
1203 null, GPALObjectType.None);
1204 return WithText("");
1205 }
1206 var previousResult = _executionResults[resultIndex].Result;
1207 var text = null == previousResult ? "" : previousResult.ToString();
1208 return WithText(text);
1209 }
1210
1217 {
1218 var resultEntry = _executionResults.LastOrDefault(entry => entry.Name == name);
1219 if (null == resultEntry.Result)
1220 {
1221 GPAL.PublishSimpleEvent(GPALEventType.WARNING,
1222 $"{(string.IsNullOrEmpty(_name) ? "" : $"[{_name}] ")}Result name [{name}] not found for WithTextFromResult. Using empty string.",
1223 null, GPALObjectType.None);
1224 return WithText("");
1225 }
1226 var previousResult = resultEntry.Result;
1227 var text = null == previousResult ? "" : previousResult.ToString();
1228 return WithText(text);
1229 }
1230
1241 {
1242 if (null == downloadPath || null == downloadPath.Filenames || 0 == downloadPath.Filenames.Count)
1243 {
1244 GPAL.PublishSimpleEvent(GPALEventType.ERROR, "DownloadTo requires a valid GPALFile with at least one filename.", null, GPALObjectType.None);
1245 return this;
1246 }
1247
1248 _parameters = null == _parameters
1249 ? new { downloadPath }
1250 : MergeParameters(_parameters, new { downloadPath });
1251 WithHttpMethod("POST");
1252
1253 return this;
1254 }
1255
1262 {
1263 if (null == file || null == file.Filenames || 0 == file.Filenames.Count)
1264 {
1265 GPAL.PublishSimpleEvent(GPALEventType.ERROR, "SaveResultsTo requires a valid GPALFile with at least one filename.", null, GPALObjectType.None);
1266 return this;
1267 }
1268 _saveFile = file.Filename; // Store filename for Execute
1269
1270 return this;
1271 }
1272
1278 {
1279 _httpMethod = null == method ? "GET" : method;
1280 return this;
1281 }
1282
1288 internal IAllowRESTParametersOrExecution WithImageFormat(ImageFormat format)
1289 {
1290 string imageFormat = format.ToString();
1291 _parameters = null == _parameters
1292 ? new { imageFormat }
1293 : MergeParameters(_parameters, new { imageFormat });
1294 WithHttpMethod("POST");
1295 return this;
1296 }
1304 {
1305 _parameters = null == _parameters
1306 ? new { index }
1307 : MergeParameters(_parameters, new { index });
1308 WithHttpMethod("POST");
1309 return this;
1310 }
1311
1317 public IAllowRESTExecution WithDeviceName(string sinkName)
1318 {
1319 _parameters = null == _parameters
1320 ? new { sinkName }
1321 : MergeParameters(_parameters, new { sinkName });
1322 WithHttpMethod("POST");
1323 return this;
1324 }
1325
1330 public string Execute(bool publishEvent = true)
1331 {
1332 // no api base means nothing to call. for a browser's own client that is a call made before the
1333 // browser was launched, and saying so beats a null reference from three frames down
1334 if (null == _helper)
1335 {
1336 string noBase = $"{(string.IsNullOrEmpty(_name) ? "" : $"[{_name}] ")}No API base is set for endpoint [{_endpoint}]. A browser's own client has one once the browser is launched.";
1337
1338 GPAL.PublishSimpleEvent(GPALEventType.ERROR, noBase, this, GPALObjectType.RestClient);
1339
1340 throw new GPALException($"{GPAL.MyMethodName()}: {noBase}");
1341 }
1342
1343 GPALEventType publishToConsole = GPAL.GPALSettings.ConsoleEvents;
1344 GPALEventType publishToDebug = GPAL.GPALSettings.DebugEvents;
1345
1346 try
1347 {
1348 bool customApiPassedValidation = _customEndpoint;
1349
1350 if (true == _customEndpoint && 0 < openApiParamRules.Count())
1351 {
1352 string ruleKey = GetOpenApiRuleKey(_httpMethod, _endpoint); // e.g. "GET /api/basicstore/products"
1353 customApiPassedValidation = (false == string.IsNullOrEmpty(ruleKey) && true == openApiParamRules.ContainsKey(ruleKey) && true == ValidateOpenApiParameters(ruleKey));
1354 }
1355
1356 if (true == customApiPassedValidation || true == ValidateParameters(RESTHelper.Endpoints.FirstOrDefault(kvp => kvp.Value == _endpoint).Key))
1357 {
1358 if (false == publishEvent)
1359 {
1360 GPAL.GPALSettings.ConsoleEvents = GPALEventType.NONE;
1361 GPAL.GPALSettings.DebugEvents = GPALEventType.NONE;
1362 }
1363
1364 var result = _helper.Call(_endpoint, _parameters, _httpMethod, _contentEncoding).Result;
1365
1366 _executionResults.Add((_nextResultName, result, _currentIteration));
1367 _nextResultName = null;
1368 if (null != _saveFile)
1369 // Assume the response is a stream URL or base64 content for now
1370 SaveResponseToFile(result, _saveFile);
1371
1372 return result;
1373 }
1374 else
1375 return null;
1376 }
1377 catch (AggregateException ex)
1378 {
1379 if (true == publishEvent)
1380 GPAL.PublishSimpleEvent(GPALEventType.EXCEPTION,
1381 $"{(string.IsNullOrEmpty(_name) ? "" : $"[{_name}] ")}Execution failed synchronously for endpoint [{_endpoint}].",
1382 null, GPALObjectType.None, ex.InnerException);
1383 // a browser that cannot be reached is not a call that came back empty. the reason is
1384 // wrapped by the Result that surfaced it, and it leaves here as itself
1385 if (ex.InnerException is GPALException)
1386 throw ex.InnerException;
1387
1388 return "";
1389 }
1390 finally
1391 {
1392 GPAL.GPALSettings.ConsoleEvents = publishToConsole;
1393 GPAL.GPALSettings.DebugEvents = publishToDebug;
1394 }
1395 }
1396
1402 public T Execute<T>(bool publishEvent = true)
1403 {
1404 // no api base means nothing to call. for a browser's own client that is a call made before the
1405 // browser was launched, and saying so beats a null reference from three frames down
1406 if (null == _helper)
1407 {
1408 string noBase = $"{(string.IsNullOrEmpty(_name) ? "" : $"[{_name}] ")}No API base is set for endpoint [{_endpoint}]. A browser's own client has one once the browser is launched.";
1409
1410 GPAL.PublishSimpleEvent(GPALEventType.ERROR, noBase, this, GPALObjectType.RestClient);
1411
1412 throw new GPALException($"{GPAL.MyMethodName()}: {noBase}");
1413 }
1414
1415 GPALEventType publishToConsole = GPAL.GPALSettings.ConsoleEvents;
1416 GPALEventType publishToDebug = GPAL.GPALSettings.DebugEvents;
1417 T result = default;
1418
1419 try
1420 {
1421 bool customApiPassedValidation = _customEndpoint;
1422
1423 if (true == _customEndpoint && 0 < openApiParamRules.Count())
1424 {
1425 string ruleKey = GetOpenApiRuleKey(_httpMethod, _endpoint); // e.g. "GET /api/basicstore/products"
1426 customApiPassedValidation = (false == string.IsNullOrEmpty(ruleKey) && true == openApiParamRules.ContainsKey(ruleKey) && true == ValidateOpenApiParameters(ruleKey));
1427 }
1428
1429 if (true == customApiPassedValidation || true == ValidateParameters(RESTHelper.Endpoints.FirstOrDefault(kvp => kvp.Value == _endpoint).Key))
1430 {
1431 if (false == publishEvent)
1432 {
1433 GPAL.GPALSettings.ConsoleEvents = GPALEventType.NONE;
1434 GPAL.GPALSettings.DebugEvents = GPALEventType.NONE;
1435 }
1436
1437 int retry = 1;
1438
1439 do
1440 {
1441 try
1442 {
1443 result = _helper.CallAndDeserialize<T>(_endpoint, _parameters, _httpMethod, _contentEncoding).Result;
1444 }
1445 catch (AggregateException ex)
1446 {
1447 if (true == publishEvent)
1448 GPAL.PublishSimpleEvent(GPALEventType.EXCEPTION,
1449 $"{(string.IsNullOrEmpty(_name) ? "" : $"[{_name}] ")}Execution failed synchronously for endpoint [{_endpoint}].",
1450 null, GPALObjectType.None, ex.InnerException);
1451 // a browser that cannot be reached is not a call that came back empty. the reason is
1452 // wrapped by the Result that surfaced it, and it leaves here as itself
1453 if (ex.InnerException is GPALException)
1454 throw ex.InnerException;
1455
1456 return default;
1457 }
1458 }
1459 while (null == result && 0 < --retry);
1460
1461 _executionResults.Add((_nextResultName, result, _currentIteration));
1462 _nextResultName = null;
1463
1464 if (_saveFile != null)
1465 GPAL.Converter.WithInput(result).SaveTo(_saveFile);
1466
1467 return result;
1468 }
1469
1470 return default;
1471 }
1472 finally
1473 {
1474 GPAL.GPALSettings.ConsoleEvents = publishToConsole;
1475 GPAL.GPALSettings.DebugEvents = publishToDebug;
1476 }
1477 }
1478
1483 public IRESTClient AndThen(bool publishEvent = true)
1484 {
1485 var result = Execute(publishEvent);
1486 _executionResults.Add((_nextResultName, result, _currentIteration));
1487 Reset();
1488 return this;
1489 }
1490
1496 public IRESTClient AndThen<T>(bool publishEvent = true)
1497 {
1498 var result = Execute<T>(publishEvent);
1499 _executionResults.Add((_nextResultName, result, _currentIteration));
1500 Reset();
1501 return this;
1502 }
1503
1509 public async Task<string> ExecuteAsync(bool publishEvent = true)
1510 {
1511 // no api base means nothing to call. for a browser's own client that is a call made before the
1512 // browser was launched, and saying so beats a null reference from three frames down
1513 if (null == _helper)
1514 {
1515 string noBase = $"{(string.IsNullOrEmpty(_name) ? "" : $"[{_name}] ")}No API base is set for endpoint [{_endpoint}]. A browser's own client has one once the browser is launched.";
1516
1517 GPAL.PublishSimpleEvent(GPALEventType.ERROR, noBase, this, GPALObjectType.RestClient);
1518
1519 throw new GPALException($"{GPAL.MyMethodName()}: {noBase}");
1520 }
1521
1522 GPALEventType publishToConsole = GPAL.GPALSettings.ConsoleEvents;
1523 GPALEventType publishToDebug = GPAL.GPALSettings.DebugEvents;
1524
1525 try
1526 {
1527 bool customApiPassedValidation = _customEndpoint;
1528
1529 if (true == _customEndpoint && 0 < openApiParamRules.Count())
1530 {
1531 string ruleKey = GetOpenApiRuleKey(_httpMethod, _endpoint); // e.g. "GET /api/basicstore/products"
1532 customApiPassedValidation = (false == string.IsNullOrEmpty(ruleKey) && true == openApiParamRules.ContainsKey(ruleKey) && true == ValidateOpenApiParameters(ruleKey));
1533 }
1534
1535 if (true == customApiPassedValidation || true == ValidateParameters(RESTHelper.Endpoints.FirstOrDefault(kvp => kvp.Value == _endpoint).Key))
1536 {
1537 if (false == publishEvent)
1538 {
1539 GPAL.GPALSettings.ConsoleEvents = GPALEventType.NONE;
1540 GPAL.GPALSettings.DebugEvents = GPALEventType.NONE;
1541 }
1542
1543 var result = await _helper.Call(_endpoint, _parameters, _httpMethod, _contentEncoding);
1544 _executionResults.Add((_nextResultName, result, _currentIteration));
1545 _nextResultName = null;
1546 return result;
1547 }
1548 else
1549 return null;
1550 }
1551 catch (AggregateException ex)
1552 {
1553 if (true == publishEvent)
1554 GPAL.PublishSimpleEvent(GPALEventType.EXCEPTION,
1555 $"{(string.IsNullOrEmpty(_name) ? "" : $"[{_name}] ")}Execution failed asynchronously for endpoint [{_endpoint}].",
1556 null, GPALObjectType.None, ex.InnerException);
1557 // a browser that cannot be reached is not a call that came back empty. the reason is
1558 // wrapped by the Result that surfaced it, and it leaves here as itself
1559 if (ex.InnerException is GPALException)
1560 throw ex.InnerException;
1561
1562 return null;
1563 }
1564 finally
1565 {
1566 GPAL.GPALSettings.ConsoleEvents = publishToConsole;
1567 GPAL.GPALSettings.DebugEvents = publishToDebug;
1568 }
1569 }
1570
1576 public async Task<T> ExecuteAsync<T>(bool publishEvent = true)
1577 {
1578 // no api base means nothing to call. for a browser's own client that is a call made before the
1579 // browser was launched, and saying so beats a null reference from three frames down
1580 if (null == _helper)
1581 {
1582 string noBase = $"{(string.IsNullOrEmpty(_name) ? "" : $"[{_name}] ")}No API base is set for endpoint [{_endpoint}]. A browser's own client has one once the browser is launched.";
1583
1584 GPAL.PublishSimpleEvent(GPALEventType.ERROR, noBase, this, GPALObjectType.RestClient);
1585
1586 throw new GPALException($"{GPAL.MyMethodName()}: {noBase}");
1587 }
1588
1589 GPALEventType publishToConsole = GPAL.GPALSettings.ConsoleEvents;
1590 GPALEventType publishToDebug = GPAL.GPALSettings.DebugEvents;
1591
1592 try
1593 {
1594 bool customApiPassedValidation = _customEndpoint;
1595
1596 if (true == _customEndpoint && 0 < openApiParamRules.Count())
1597 {
1598 string ruleKey = GetOpenApiRuleKey(_httpMethod, _endpoint); // e.g. "GET /api/basicstore/products"
1599 customApiPassedValidation = (false == string.IsNullOrEmpty(ruleKey) && true == openApiParamRules.ContainsKey(ruleKey) && true == ValidateOpenApiParameters(ruleKey));
1600 }
1601
1602 if (true == customApiPassedValidation || true == ValidateParameters(RESTHelper.Endpoints.FirstOrDefault(kvp => kvp.Value == _endpoint).Key))
1603 {
1604 if (false == publishEvent)
1605 {
1606 GPAL.GPALSettings.ConsoleEvents = GPALEventType.NONE;
1607 GPAL.GPALSettings.DebugEvents = GPALEventType.NONE;
1608 }
1609
1610 T result = await _helper.CallAndDeserialize<T>(_endpoint, _parameters, _httpMethod, _contentEncoding);
1611 _executionResults.Add((_nextResultName, result, _currentIteration));
1612 _nextResultName = null;
1613
1614 if (_saveFile != null)
1615 GPAL.Converter.WithInput(result).SaveTo(_saveFile);
1616
1617 return result;
1618 }
1619
1620 return default;
1621 }
1622 catch (Exception ex) // Changed to catch base Exception instead of AggregateException for async
1623 {
1624 if (true == publishEvent)
1625 GPAL.PublishSimpleEvent(GPALEventType.EXCEPTION,
1626 $"{(string.IsNullOrEmpty(_name) ? "" : $"[{_name}] ")}Execution failed asynchronously for endpoint [{_endpoint}].",
1627 null, GPALObjectType.None, ex);
1628 return default;
1629 }
1630 finally
1631 {
1632 GPAL.GPALSettings.ConsoleEvents = publishToConsole;
1633 GPAL.GPALSettings.DebugEvents = publishToDebug;
1634 }
1635 }
1636
1642 {
1643 return this;
1644 }
1645
1651 {
1652 return new ResultCollection(_executionResults, _name);
1653 }
1654
1661 public IAllowRESTWorkflow WithWorkflow(Action<RESTClient> workflow)
1662 {
1663 if (null == workflow)
1664 {
1665 GPAL.PublishSimpleEvent(GPALEventType.WARNING,
1666 $"{(string.IsNullOrEmpty(_name) ? "" : $"[{_name}] ")}Workflow cannot be null.",
1667 null, GPALObjectType.None);
1668 return this;
1669 }
1670
1671 _workflowStates.Add((_endpoint, _parameters, _httpMethod, _nextResultName));
1672 _workflows.Add(workflow);
1673 return this;
1674 }
1675
1682 public IAllowRESTParametersOrExecution While(Func<bool> condition)
1683 {
1684 if (0 == _workflows.Count)
1685 {
1686 GPAL.PublishSimpleEvent(GPALEventType.WARNING,
1687 $"{(string.IsNullOrEmpty(_name) ? "" : $"[{_name}] ")}No workflows defined for While loop.",
1688 null, GPALObjectType.None);
1689 Reset();
1690 return this;
1691 }
1692
1693 int iteration = 0;
1694 const int maxIterations = 1000;
1695
1696 while (condition() && iteration < maxIterations)
1697 {
1698 _currentIteration = iteration;
1699 for (int i = 0; i < _workflows.Count; i++)
1700 {
1701 var initialState = _workflowStates[i];
1702 if (initialState.HasValue)
1703 {
1704 (_endpoint, _parameters, _httpMethod, _nextResultName) = initialState.Value;
1705 }
1706 else
1707 {
1708 Reset();
1709 }
1710 _workflows[i](this);
1711 }
1712 iteration++;
1713 }
1714
1715 if (maxIterations <= iteration)
1716 {
1717 GPAL.PublishSimpleEvent(GPALEventType.WARNING,
1718 $"{(string.IsNullOrEmpty(_name) ? "" : $"[{_name}] ")}While loop reached maximum iterations [{maxIterations}].",
1719 null, GPALObjectType.None);
1720 }
1721
1722 Reset();
1723 _currentIteration = 0;
1724 return this;
1725 }
1726
1732 public IAllowRESTParametersOrExecution Until(Func<bool> condition)
1733 {
1734 if (0 == _workflows.Count)
1735 {
1736 GPAL.PublishSimpleEvent(GPALEventType.WARNING,
1737 $"{(string.IsNullOrEmpty(_name) ? "" : $"[{_name}] ")}No workflows defined for Until loop.",
1738 null, GPALObjectType.None);
1739 Reset();
1740 return this;
1741 }
1742
1743 int iteration = 0;
1744 const int maxIterations = 1000;
1745
1746 while (false == condition() && iteration < maxIterations)
1747 {
1748 _currentIteration = iteration;
1749 for (int i = 0; i < _workflows.Count; i++)
1750 {
1751 var initialState = _workflowStates[i];
1752 if (initialState.HasValue)
1753 {
1754 (_endpoint, _parameters, _httpMethod, _nextResultName) = initialState.Value;
1755 }
1756 else
1757 {
1758 Reset();
1759 }
1760 _workflows[i](this);
1761 }
1762 iteration++;
1763 }
1764
1765 if (maxIterations <= iteration)
1766 {
1767 GPAL.PublishSimpleEvent(GPALEventType.WARNING,
1768 $"{(string.IsNullOrEmpty(_name) ? "" : $"[{_name}] ")}Until loop reached maximum iterations [{maxIterations}].",
1769 null, GPALObjectType.None);
1770 }
1771
1772 Reset();
1773 _currentIteration = 0;
1774 return this;
1775 }
1776
1783 {
1784 _parameters = null == _parameters
1785 ? new { elementId }
1786 : MergeParameters(_parameters, new { elementId });
1787 WithHttpMethod("POST");
1788 return this;
1789 }
1790
1796 public IAllowRESTParametersOrExecution WithEncoding(ContentEncoding encoding)
1797 {
1798 _contentEncoding = encoding;
1799 return this;
1800 }
1801 internal static string GetContentType(ContentEncoding encoding)
1802 {
1803 return encoding switch
1804 {
1805 ContentEncoding.UrlEncoded => "application/x-www-form-urlencoded",
1806 ContentEncoding.Json => "application/json",
1807 _ => "application/json" // Default for unknown encodings
1808 };
1809 }
1817 public IAllowRESTExecution WithHeader(string name, string value)
1818 {
1819 if (!string.IsNullOrEmpty(name) && !string.IsNullOrEmpty(value))
1820 {
1821 _headers[name] = value;
1822 }
1823 else
1824 GPAL.PublishSimpleEvent(GPALEventType.WARNING, $"Invalid name [{name}] or value [{value}], nothing added to the header.",null, GPALObjectType.None);
1825
1826 return this;
1827 }
1828
1835 {
1836 if (0 > resultIndex || resultIndex >= _executionResults.Count)
1837 {
1838 GPAL.PublishSimpleEvent(GPALEventType.WARNING,
1839 $"{(string.IsNullOrEmpty(_name) ? "" : $"[{_name}] ")}Result index [{resultIndex}] out of range. Using empty values.",
1840 null, GPALObjectType.None);
1841 return WithHeader("", "");
1842 }
1843 var result = _executionResults[resultIndex].Result?.ToString().Split(new[] { ':' }, 2);
1844 var name = result?.Length > 0 ? result[0] : "";
1845 var value = result?.Length > 1 ? result[1] : "";
1846 return WithHeader(name, value);
1847 }
1848
1855 {
1856 var resultEntry = _executionResults.LastOrDefault(entry => entry.Name == name);
1857 if (null == resultEntry.Result)
1858 {
1859 GPAL.PublishSimpleEvent(GPALEventType.WARNING,
1860 $"{(string.IsNullOrEmpty(_name) ? "" : $"[{_name}] ")}Result name [{name}] not found. Using empty values.",
1861 null, GPALObjectType.None);
1862 return WithHeader("", "");
1863 }
1864 var result = resultEntry.Result?.ToString().Split(new[] { ':' }, 2);
1865 var headerName = result?.Length > 0 ? result[0] : "";
1866 var headerValue = result?.Length > 1 ? result[1] : "";
1867 return WithHeader(headerName, headerValue);
1868 }
1869
1876 {
1877 _parameters = null == _parameters
1878 ? new { deltaX }
1879 : MergeParameters(_parameters, new { deltaX });
1880 WithHttpMethod("POST");
1881 return this;
1882 }
1883
1890 {
1891 _parameters = null == _parameters
1892 ? new { deltaY }
1893 : MergeParameters(_parameters, new { deltaY });
1894 WithHttpMethod("POST");
1895 return this;
1896 }
1897
1904 {
1905 _parameters = null == _parameters
1906 ? new { offsetX }
1907 : MergeParameters(_parameters, new { offsetX });
1908 WithHttpMethod("POST");
1909 return this;
1910 }
1911
1918 {
1919 _parameters = null == _parameters
1920 ? new { offsetY }
1921 : MergeParameters(_parameters, new { offsetY });
1922 WithHttpMethod("POST");
1923 return this;
1924 }
1925
1932 {
1933 _parameters = null == _parameters
1934 ? new { hPixels }
1935 : MergeParameters(_parameters, new { hPixels });
1936 WithHttpMethod("POST");
1937 return this;
1938 }
1939
1946 {
1947 if (0 > resultIndex || resultIndex >= _executionResults.Count)
1948 {
1949 GPAL.PublishSimpleEvent(GPALEventType.WARNING,
1950 $"{(string.IsNullOrEmpty(_name) ? "" : $"[{_name}] ")}Result index [{resultIndex}] is out of range for WithHPixelsFromResult. Using 0.",
1951 null, GPALObjectType.None);
1952 return WithHPixels(0);
1953 }
1954 var previousResult = _executionResults[resultIndex].Result;
1955 var hPixels = previousResult is int i ? i : 0;
1956 return WithHPixels(hPixels);
1957 }
1958
1965 {
1966 var resultEntry = _executionResults.LastOrDefault(entry => entry.Name == name);
1967 if (null == resultEntry.Result)
1968 {
1969 GPAL.PublishSimpleEvent(GPALEventType.WARNING,
1970 $"{(string.IsNullOrEmpty(_name) ? "" : $"[{_name}] ")}Result name [{name}] not found for WithHPixelsFromResult. Using 0.",
1971 null, GPALObjectType.None);
1972 return WithHPixels(0);
1973 }
1974 var previousResult = resultEntry.Result;
1975 var hPixels = previousResult is int i ? i : 0;
1976 return WithHPixels(hPixels);
1977 }
1978
1985 {
1986 _parameters = null == _parameters
1987 ? new { vPixels }
1988 : MergeParameters(_parameters, new { vPixels });
1989 WithHttpMethod("POST");
1990 return this;
1991 }
1992
1999 {
2000 if (0 > resultIndex || resultIndex >= _executionResults.Count)
2001 {
2002 GPAL.PublishSimpleEvent(GPALEventType.WARNING,
2003 $"{(string.IsNullOrEmpty(_name) ? "" : $"[{_name}] ")}Result index [{resultIndex}] is out of range for WithVPixelsFromResult. Using 0.",
2004 null, GPALObjectType.None);
2005 return WithVPixels(0);
2006 }
2007 var previousResult = _executionResults[resultIndex].Result;
2008 var vPixels = previousResult is int i ? i : 0;
2009 return WithVPixels(vPixels);
2010 }
2011
2018 {
2019 var resultEntry = _executionResults.LastOrDefault(entry => entry.Name == name);
2020 if (null == resultEntry.Result)
2021 {
2022 GPAL.PublishSimpleEvent(GPALEventType.WARNING,
2023 $"{(string.IsNullOrEmpty(_name) ? "" : $"[{_name}] ")}Result name [{name}] not found for WithVPixelsFromResult. Using 0.",
2024 null, GPALObjectType.None);
2025 return WithVPixels(0);
2026 }
2027 var previousResult = resultEntry.Result;
2028 var vPixels = previousResult is int i ? i : 0;
2029 return WithVPixels(vPixels);
2030 }
2031
2035 public IAllowRESTExecution CaptureVisibleTab(ImageFormat imageFormat = ImageFormat.JPEG)
2036 {
2037 WithImageFormat(imageFormat);
2038 WithEndpoint(ApiEndpoint.CaptureVisibleTab);
2039 return this;
2040 }
2041
2053 public IAllowRESTExecution SetDownloadFilename(string downloadPath)
2054 {
2055 WithEndpoint(ApiEndpoint.SetDownloadFilename);
2056 return WithDownloadFile(downloadPath);
2057 }
2058 public IAllowRESTExecution WithDownloadFile(string downloadPath)
2059 {
2060 // the parameter only. setting the endpoint here overwrote whatever it was chained onto, so
2061 // LeftClickAndDownload(id).WithDownloadFile(path) called set-download-filename and never clicked
2062 _parameters = null == _parameters
2063 ? new { downloadPath }
2064 : MergeParameters(_parameters, new { downloadPath });
2065 WithHttpMethod("POST");
2066 return this;
2067 }
2068
2075 public IAllowRESTExecution Evaluate(string xpath)
2076 {
2077 WithEndpoint(ApiEndpoint.Evaluate).WithXPath(xpath);
2078 return this;
2079 }
2080
2088 {
2089 WithEndpoint(ApiEndpoint.EvaluateAll).WithXPath(xpath);
2090 return this;
2091 }
2092
2100 {
2101 WithEndpoint(ApiEndpoint.EvaluatePersistent).WithXPath(xpath);
2102 return this;
2103 }
2104
2112 {
2113 WithEndpoint(ApiEndpoint.EvaluateAllPersistent).WithXPath(xpath);
2114 return this;
2115 }
2116
2123 {
2124 WithEndpoint(ApiEndpoint.ClearReferrer);
2125 return this;
2126 }
2127
2134 public IAllowRESTExecution FireChangeEvent(string elementId)
2135 {
2136 WithEndpoint(ApiEndpoint.FireChangeEvent).WithElementId(elementId);
2137 return this;
2138 }
2139
2146 public IAllowRESTExecution Focus(string elementId)
2147 {
2148 WithEndpoint(ApiEndpoint.Focus).WithElementId(elementId);
2149 return this;
2150 }
2151
2159 {
2160 WithEndpoint(ApiEndpoint.GetBoundingClientRect).WithElementId(elementId);
2161 return this;
2162 }
2163
2172 public IAllowRESTExecution ElementFromPoint(string elementId, int x, int y)
2173 {
2174 WithEndpoint(ApiEndpoint.ElementFromPoint).WithElementId(elementId);
2175
2176 _parameters = null == _parameters
2177 ? new { x, y }
2178 : MergeParameters(_parameters, new { x, y });
2179
2180 return this;
2181 }
2182 public IAllowRESTExecution GetWindowRectangle()
2183 {
2184 WithEndpoint(ApiEndpoint.GetWindowRectangle);
2185 return this;
2186 }
2187 public IAllowRESTExecution GetContentAndCss(string elementId)
2188 {
2189 WithEndpoint(ApiEndpoint.GetContentAndCss);
2190 _parameters = null == _parameters
2191 ? new { elementId }
2192 : MergeParameters(_parameters, new { elementId });
2193 WithHttpMethod("POST");
2194 return this;
2195 }
2196 public IAllowRESTExecution GetCssAttributes(string elementId)
2197 {
2198 WithEndpoint(ApiEndpoint.GetCssAttributes);
2199 _parameters = null == _parameters
2200 ? new { elementId }
2201 : MergeParameters(_parameters, new { elementId });
2202 WithHttpMethod("POST");
2203 return this;
2204 }
2205 public IAllowRESTExecution GetDomAttributes(string elementId)
2206 {
2207 WithEndpoint(ApiEndpoint.GetDomAttributes);
2208 _parameters = null == _parameters
2209 ? new { elementId }
2210 : MergeParameters(_parameters, new { elementId });
2211 WithHttpMethod("POST");
2212 return this;
2213 }
2214 public IAllowRESTExecution GetDomProperties(string elementId)
2215 {
2216 WithEndpoint(ApiEndpoint.GetDomProperties);
2217 _parameters = null == _parameters
2218 ? new { elementId }
2219 : MergeParameters(_parameters, new { elementId });
2220 WithHttpMethod("POST");
2221 return this;
2222 }
2229 {
2230 WithEndpoint(ApiEndpoint.GetElementAttributeHash).WithElementId(elementId);
2231 return this;
2232 }
2233
2239 public IAllowRESTExecution GetOptions(string elementId)
2240 {
2241 WithEndpoint(ApiEndpoint.SwitchToElement).WithElementId(elementId).AndThen().WithCss("option");
2242 return this;
2243 }
2244 public IAllowRESTExecution GetPageSource()
2245 {
2246 WithEndpoint(ApiEndpoint.GetPageSource);
2247 return this;
2248 }
2255 public IAllowRESTExecution GetParentNode(string elementId)
2256 {
2257 WithEndpoint(ApiEndpoint.GetParentNode).WithElementId(elementId);
2258 return this;
2259 }
2260
2267 public IAllowRESTExecution HideElement(string elementId)
2268 {
2269 WithEndpoint(ApiEndpoint.HideElement).WithElementId(elementId);
2270 return this;
2271 }
2272
2279 public IAllowRESTExecution IsClickable(string elementId)
2280 {
2281 WithEndpoint(ApiEndpoint.IsClickable).WithElementId(elementId);
2282 return this;
2283 }
2284
2291 public IAllowRESTExecution IsDisplayed(string elementId)
2292 {
2293 WithEndpoint(ApiEndpoint.IsDisplayed).WithElementId(elementId);
2294 return this;
2295 }
2296
2303 public IAllowRESTExecution IsEnabled(string elementId)
2304 {
2305 WithEndpoint(ApiEndpoint.IsEnabled).WithElementId(elementId);
2306 return this;
2307 }
2308
2315 {
2316 WithEndpoint(ApiEndpoint.IsEndOfPage);
2317 return this;
2318 }
2319
2327 {
2328 WithEndpoint(ApiEndpoint.IsVisibleInViewport).WithElementId(elementId);
2329 return this;
2330 }
2331
2338 public IAllowRESTExecution LeftClick(string elementId)
2339 {
2340 WithEndpoint(ApiEndpoint.LeftClick).WithElementId(elementId);
2341 return this;
2342 }
2343
2350 public IAllowRESTExecution LeftDoubleClick(string elementId)
2351 {
2352 WithEndpoint(ApiEndpoint.LeftDoubleClick).WithElementId(elementId);
2353 return this;
2354 }
2355
2361 public IAllowRESTExecution MiddleClick(string elementId)
2362 {
2363 WithEndpoint(ApiEndpoint.MiddleClick).WithElementId(elementId);
2364 return this;
2365 }
2366
2374 {
2375 WithEndpoint(ApiEndpoint.QuerySelector).WithCss(css);
2376 return this;
2377 }
2378
2386 {
2387 WithEndpoint(ApiEndpoint.QuerySelectors).WithCss(css);
2388 return this;
2389 }
2390
2398 {
2399 WithEndpoint(ApiEndpoint.QueryPersistentSelector).WithCss(css);
2400 return this;
2401 }
2402
2410 {
2411 WithEndpoint(ApiEndpoint.QueryPersistentSelectors).WithCss(css);
2412 return this;
2413 }
2414
2421 public IAllowRESTExecution RightClick(string elementId)
2422 {
2423 WithEndpoint(ApiEndpoint.RightClick).WithElementId(elementId);
2424 return this;
2425 }
2426
2433 public IAllowRESTExecution ScrollIntoView(string elementId)
2434 {
2435 WithEndpoint(ApiEndpoint.ScrollIntoView).WithElementId(elementId);
2436 return this;
2437 }
2438
2446 public IAllowRESTExecution ScrollElement(int hPixels, int vPixels)
2447 {
2448 WithEndpoint(ApiEndpoint.ScrollElement).WithHPixels(hPixels).WithVPixels(vPixels);
2449 return this;
2450 }
2451
2458 public IAllowRESTAttribute GetAttribute(string elementId)
2459 {
2460 WithEndpoint(ApiEndpoint.GetAttribute).WithElementId(elementId);
2461 return this;
2462 }
2463
2470 public IAllowRESTAttribute SetAttribute(string elementId)
2471 {
2472 WithEndpoint(ApiEndpoint.SetAttribute).WithElementId(elementId);
2473 return this;
2474 }
2475
2481 {
2482 WithEndpoint(ApiEndpoint.TopBrowser);
2483 return this;
2484 }
2485
2492 public IAllowRESTAttribute WithAttribute(string attribute)
2493 {
2494 _parameters = null == _parameters
2495 ? new { attribute }
2496 : MergeParameters(_parameters, new { attribute });
2497 WithHttpMethod("POST");
2498 return this;
2499 }
2500
2507 public IAllowRangeValue SetRange(string elementId)
2508 {
2509 WithEndpoint(ApiEndpoint.SetRange).WithElementId(elementId);
2510 return this;
2511 }
2512
2520 {
2521 _parameters = null == _parameters
2522 ? new { rangeValue = rangeValue.ToString() }
2523 : MergeParameters(_parameters, new { rangeValue = rangeValue.ToString() });
2524 WithHttpMethod("POST");
2525 return this;
2526 }
2527
2534 public IAllowRESTAttribute WithValue(string value)
2535 {
2536 _parameters = null == _parameters
2537 ? new { value }
2538 : MergeParameters(_parameters, new { value });
2539 WithHttpMethod("POST");
2540 return this;
2541 }
2542
2549 {
2550 WithEndpoint(ApiEndpoint.WindowInnerHeight);
2551 return this;
2552 }
2553
2560 {
2561 WithEndpoint(ApiEndpoint.WindowInnerWidth);
2562 return this;
2563 }
2564
2571 {
2572 WithEndpoint(ApiEndpoint.WindowOuterHeight);
2573 return this;
2574 }
2575
2582 {
2583 WithEndpoint(ApiEndpoint.WindowOuterWidth);
2584 return this;
2585 }
2586
2596 {
2597 WithEndpoint(ApiEndpoint.Fetch);
2598 _parameters = null == _parameters
2599 ? new { url }
2600 : MergeParameters(_parameters, new { url });
2601 WithHttpMethod("POST");
2602 return this;
2603 }
2604
2613 {
2614 _parameters = null == _parameters
2615 ? new { method }
2616 : MergeParameters(_parameters, new { method });
2617 WithHttpMethod("POST");
2618 return this;
2619 }
2620
2628 {
2629 _parameters = null == _parameters
2630 ? new { body }
2631 : MergeParameters(_parameters, new { body });
2632 WithHttpMethod("POST");
2633 return this;
2634 }
2635
2643 {
2644 _parameters = null == _parameters
2645 ? new { contentType }
2646 : MergeParameters(_parameters, new { contentType });
2647 WithHttpMethod("POST");
2648 return this;
2649 }
2650
2658 {
2659 _parameters = null == _parameters
2660 ? new { headers }
2661 : MergeParameters(_parameters, new { headers });
2662 WithHttpMethod("POST");
2663 return this;
2664 }
2665
2674 {
2675 _parameters = null == _parameters
2676 ? new { bytes }
2677 : MergeParameters(_parameters, new { bytes });
2678 WithHttpMethod("POST");
2679 return this;
2680 }
2681
2688 {
2689 WithEndpoint(ApiEndpoint.WindowPageOffsetX);
2690 return this;
2691 }
2692
2699 {
2700 WithEndpoint(ApiEndpoint.WindowPageOffsetY);
2701 return this;
2702 }
2703
2710 {
2711 WithEndpoint(ApiEndpoint.WindowScreenLeft);
2712 return this;
2713 }
2714
2721 {
2722 WithEndpoint(ApiEndpoint.WindowScreenTop);
2723 return this;
2724 }
2725
2732 public IAllowRESTExecution Hover(string elementId)
2733 {
2734 WithEndpoint(ApiEndpoint.Hover).WithElementId(elementId);
2735 return this;
2736 }
2737
2741 private void Reset()
2742 {
2743 _customEndpoint = false; ;
2744 _parameters = null;
2745 _httpMethod = "GET";
2746 _endpoint = null;
2747 // headers are the client's, not the call's. Authorization, Cookie and User-Agent describe who is
2748 // asking and stay true across every endpoint, and WithEndpoint calls this as its first step - so
2749 // clearing them here meant a client could never carry a credential to a second call, or to a first
2750 // one set up before the endpoint was chosen
2751 // _nextResultName is intentionally NOT cleared here: WithResultName() can be called
2752 // before the endpoint-setting method (e.g. .WithResultName("x").GetCurrentUrl()), and
2753 // WithEndpoint() calls Reset() as its first step. Execute()/Execute<T>() clear
2754 // _nextResultName themselves once they've consumed it.
2755 }
2756
2757 // grok left out a lot, so these are not in order
2764 {
2765 WithEndpoint(ApiEndpoint.Back);
2766 return this;
2767 }
2768
2775 public IAllowRESTExecution CheckNetworkIdle(int maxConnections = 0)
2776 {
2777 WithEndpoint(ApiEndpoint.CheckNetworkIdle).WithMaxConnections(maxConnections);
2778 return this;
2779 }
2780
2788 {
2789 WithEndpoint(ApiEndpoint.CloseTab).WithTabId(tabId);
2790 return this;
2791 }
2792
2800 {
2801 if (null != url)
2802 WithEndpoint(ApiEndpoint.CloseTab).WithUrl(url.Url);
2803 else
2804 WithEndpoint(ApiEndpoint.CloseTab);
2805 return this;
2806 }
2807
2815 {
2816 WithEndpoint(ApiEndpoint.ExecuteJavaScript);
2817 _parameters = null == _parameters
2818 ? new { script }
2819 : MergeParameters(_parameters, new { script });
2820 WithHttpMethod("POST");
2821 return this;
2822 }
2823
2830 {
2831 WithEndpoint(ApiEndpoint.InjectScript);
2832 _parameters = null == _parameters
2833 ? new { script }
2834 : MergeParameters(_parameters, new { script });
2835 WithHttpMethod("POST");
2836 return this;
2837 }
2838
2844 {
2845 WithEndpoint(ApiEndpoint.ClearInjectedScripts);
2846 WithHttpMethod("POST");
2847 return this;
2848 }
2849
2856 {
2857 WithEndpoint(ApiEndpoint.Forward);
2858 return this;
2859 }
2860
2867 {
2868 WithEndpoint(ApiEndpoint.FullScreen);
2869 return this;
2870 }
2871
2878 {
2879 WithEndpoint(ApiEndpoint.GetBrowserSettings);
2880 return this;
2881 }
2882
2889 {
2890 WithEndpoint(ApiEndpoint.GetCurrentUrl);
2891 return this;
2892 }
2893
2900 {
2901 WithEndpoint(ApiEndpoint.GetGpalSettings);
2902 return this;
2903 }
2904
2911 {
2912 WithEndpoint(ApiEndpoint.GetReadyStatus);
2913 return this;
2914 }
2915
2922 {
2923 WithEndpoint(ApiEndpoint.GetSettings);
2924 return this;
2925 }
2926
2934 {
2935 WithEndpoint(ApiEndpoint.GetShadowRoot).WithElementId(css);
2936 return this;
2937 }
2938
2945 {
2946 WithEndpoint(ApiEndpoint.GetLanguages);
2947 return this;
2948 }
2949
2957 public IAllowRESTCaptureOptions CaptureCalls(bool capture = true)
2958 {
2959 WithEndpoint(ApiEndpoint.CaptureCalls);
2960
2961 _parameters = null == _parameters
2962 ? new { capture }
2963 : MergeParameters(_parameters, new { capture });
2964
2965 WithHttpMethod("POST");
2966 return this;
2967 }
2968
2975 public IAllowRESTCaptureOptions WithCallFilter(string urlFragment)
2976 {
2977 _parameters = null == _parameters
2978 ? new { filter = urlFragment }
2979 : MergeParameters(_parameters, new { filter = urlFragment });
2980
2981 WithHttpMethod("POST");
2982 return this;
2983 }
2984
2991 {
2992 _parameters = null == _parameters
2993 ? new { clear }
2994 : MergeParameters(_parameters, new { clear });
2995
2996 WithHttpMethod("POST");
2997 return this;
2998 }
2999
3006 {
3007 WithEndpoint(ApiEndpoint.GetCapturedCalls);
3008 return this;
3009 }
3010
3017 {
3018 WithEndpoint(ApiEndpoint.GetUserAgent);
3019 return this;
3020 }
3021
3028 {
3029 WithEndpoint(ApiEndpoint.GetWorkflow);
3030 return this;
3031 }
3032
3040 {
3041 if (true == string.IsNullOrEmpty(url))
3042 url = new GPALUrl("google.com");
3043
3044 WithEndpoint(ApiEndpoint.GoTo).WithUrl(url.Url);
3045 WithHttpMethod("POST");
3046 return this;
3047 }
3048
3056 {
3057 WithEndpoint(ApiEndpoint.GoToTab).WithTabId(tabId);
3058 return this;
3059 }
3060
3068 {
3069 WithEndpoint(ApiEndpoint.GoToTab).WithUrl(url?.Url);
3070 return this;
3071 }
3072
3079 public IAllowRESTInputText FillInAppend(string elementId)
3080 {
3081 WithEndpoint(ApiEndpoint.FillInAppend).WithElementId(elementId);
3082 return this;
3083 }
3084
3091 public IAllowRESTInputText FillInInsert(string elementId)
3092 {
3093 WithEndpoint(ApiEndpoint.FillInInsert).WithElementId(elementId);
3094 return this;
3095 }
3096
3103 public IAllowRESTInputText FillInOverwrite(string elementId)
3104 {
3105 WithEndpoint(ApiEndpoint.FillInOverwrite).WithElementId(elementId);
3106 return this;
3107 }
3108
3116 {
3117 WithEndpoint(ApiEndpoint.LeftClickAndDownload).WithElementId(elementId);
3118 return this;
3119 }
3120
3128 {
3129 WithEndpoint(ApiEndpoint.Upload).WithElementId(elementId);
3130 return this;
3131 }
3132
3139 {
3140 WithEndpoint(ApiEndpoint.Maximize);
3141 return this;
3142 }
3143
3150 {
3151 WithEndpoint(ApiEndpoint.Minimize);
3152 return this;
3153 }
3154
3161 public IAllowRESTExecution MoveTo(string elementId)
3162 {
3163 WithEndpoint(ApiEndpoint.MoveTo).WithElementId(elementId);
3164 return this;
3165 }
3166
3174 {
3175
3176 WithEndpoint(ApiEndpoint.NewTab);
3177
3178 if (null != url)
3179 WithUrl(url?.Url);
3180
3181 return this;
3182 }
3183
3190 {
3191 WithEndpoint(ApiEndpoint.NextTab);
3192 return this;
3193 }
3194
3202 {
3203 WithEndpoint(ApiEndpoint.TabCount);
3204 return this;
3205 }
3206
3213 {
3214 WithEndpoint(ApiEndpoint.NextWindow);
3215 return this;
3216 }
3217
3224 {
3225 WithEndpoint(ApiEndpoint.Normal);
3226 return this;
3227 }
3228
3236 {
3237 WithEndpoint(ApiEndpoint.OverrideReferrer).WithReferrer(referrer);
3238 return this;
3239 }
3240
3247 public IAllowRESTExecution SetUserAgent(string userAgent)
3248 {
3249 WithEndpoint(ApiEndpoint.SetUserAgent);
3250 _parameters = null == _parameters
3251 ? new { userAgent }
3252 : MergeParameters(_parameters, new { userAgent });
3253 WithHttpMethod("POST");
3254 return this;
3255 }
3256
3263 public IAllowRESTExecution PageDown(int pagesToScroll = 1)
3264 {
3265 WithEndpoint(ApiEndpoint.PageDown);
3266 _parameters = null == _parameters
3267 ? new { pagesToScroll }
3268 : MergeParameters(_parameters, new { pagesToScroll });
3269 WithHttpMethod("POST");
3270 return this;
3271 }
3272
3279 {
3280 WithEndpoint(ApiEndpoint.PageEnd);
3281 return this;
3282 }
3283
3290 {
3291 WithEndpoint(ApiEndpoint.PageTop);
3292 return this;
3293 }
3294
3301 public IAllowRESTExecution PageUp(int pagesToScroll = 1)
3302 {
3303 WithEndpoint(ApiEndpoint.PageUp);
3304 _parameters = null == _parameters
3305 ? new { pagesToScroll }
3306 : MergeParameters(_parameters, new { pagesToScroll });
3307 WithHttpMethod("POST");
3308 return this;
3309 }
3310
3316 public IAllowRESTExecution PressModifierKey(ModifierKeys modifierKeys)
3317 {
3318 WithEndpoint(ApiEndpoint.PressModifierKey);
3319 _parameters = null == _parameters
3320 ? new { modifierKeys }
3321 : MergeParameters(_parameters, new { modifierKeys });
3322 WithHttpMethod("POST");
3323 return this;
3324 }
3325
3331 {
3332 WithEndpoint(ApiEndpoint.PreviousTab);
3333 return this;
3334 }
3335
3342 {
3343 WithEndpoint(ApiEndpoint.PreviousWindow);
3344 return this;
3345 }
3346
3353 {
3354 WithEndpoint(ApiEndpoint.Refresh);
3355 return this;
3356 }
3357
3364 public IAllowRESTExecution ReleaseModifierKey(ModifierKeys modifierKeys)
3365 {
3366 WithEndpoint(ApiEndpoint.ReleaseModifierKey);
3367 _parameters = null == _parameters
3368 ? new { modifierKeys }
3369 : MergeParameters(_parameters, new { modifierKeys });
3370 WithHttpMethod("POST");
3371 return this;
3372 }
3373
3380 {
3381 WithEndpoint(ApiEndpoint.Restore);
3382 return this;
3383 }
3384
3391 public IAllowRESTDragAndDrop DragAndDrop(string elementId)
3392 {
3393 WithEndpoint(ApiEndpoint.DragAndDrop).WithElementId(elementId);
3394 return this;
3395 }
3396
3404 {
3405 WithEndpoint(ApiEndpoint.RightClickAndDownload).WithElementId(elementId);
3406 return this;
3407 }
3408
3416 {
3417 WithEndpoint(ApiEndpoint.ScrollElement).WithElementId(elementId);
3418 return this;
3419 }
3420
3421 public IAllowRESTExecution ScrollWindow(int hPixels, int vPixels)
3422 {
3423 WithEndpoint(ApiEndpoint.ScrollWindow).WithHPixels(hPixels).WithVPixels(vPixels);
3424 return this;
3425 }
3433 {
3434 int vPixels = 0;
3435 WithEndpoint(ApiEndpoint.ScrollWindow).WithHPixels(hPixels).WithVPixels(vPixels);
3436 WithHttpMethod("POST");
3437 return this;
3438 }
3439
3447 {
3448 int hPixels = 0;
3449 WithEndpoint(ApiEndpoint.ScrollWindow).WithHPixels(hPixels).WithVPixels(vPixels);
3450 WithHttpMethod("POST");
3451 return this;
3452 }
3453
3459 public IAllowSelectOptions SelectClick(string elementId)
3460 {
3461 WithEndpoint(ApiEndpoint.SelectClick).WithElementId(elementId);
3462 WithHttpMethod("POST");
3463 return this;
3464 }
3465
3471 public IAllowRESTExecution WithSelectValue(string selectValue)
3472 {
3473 _parameters = null == _parameters
3474 ? new { selectValue }
3475 : MergeParameters(_parameters, new { selectValue });
3476 WithHttpMethod("POST");
3477 return this;
3478 }
3479 public IAllowRESTExecution WithSelectIndex(int selectIndex)
3480 {
3481 _parameters = null == _parameters
3482 ? new { selectIndex }
3483 : MergeParameters(_parameters, new { selectIndex });
3484 WithHttpMethod("POST");
3485 return this;
3486 }
3493 public IAllowRESTExecution SendKey(byte vkcode)
3494 {
3495 string key = null;
3496 string code = null;
3497
3498 if (true == VkCodeToDomKeyConverter.TryConvertVkCodeToDomKey(vkcode, out key, out code))
3499 {
3500 WithEndpoint(ApiEndpoint.SendKey);
3501 _parameters = null == _parameters
3502 ? new { key }
3503 : MergeParameters(_parameters, new { key });
3504 _parameters = MergeParameters(_parameters, new { code });
3505 _parameters = MergeParameters(_parameters, new { vk = code });
3506 WithHttpMethod("POST");
3507 }
3508 return this;
3509 }
3510
3518 public IAllowRESTExecution SendString(string text, int delayMs = 0)
3519 {
3520 WithEndpoint(ApiEndpoint.SendString);
3521 _parameters = null == _parameters
3522 ? new { text, typingDelay = delayMs }
3523 : MergeParameters(_parameters, new { text, typingDelay = delayMs });
3524 WithHttpMethod("POST");
3525 return this;
3526 }
3527
3536 public IAllowRESTExecution SetValueFrom(string destSelector)
3537 {
3538 WithEndpoint(ApiEndpoint.SetValueFromElement);
3539 _parameters = null == _parameters
3540 ? new { destSelector }
3541 : MergeParameters(_parameters, new { destSelector });
3542 WithHttpMethod("POST");
3543 return this;
3544 }
3545
3552 {
3553 WithEndpoint(ApiEndpoint.StealthOverrideReferrer);
3554 return this;
3555 }
3556
3563 public IAllowRESTExecution SubmitForm(string elementId)
3564 {
3565 WithEndpoint(ApiEndpoint.SubmitForm);
3566 _parameters = null == _parameters
3567 ? new { elementId }
3568 : MergeParameters(_parameters, new { elementId });
3569 WithHttpMethod("POST");
3570 return this;
3571 }
3572
3579 {
3580 WithEndpoint(ApiEndpoint.SwitchToDefaultContent);
3581 return this;
3582 }
3583 #region Window Handling
3591 {
3592 _parameters = new { windowId };
3593 WithHttpMethod("POST");
3594 return this;
3595 }
3596
3603 {
3604 if (resultIndex < 0 || resultIndex >= _executionResults.Count)
3605 {
3606 GPAL.PublishSimpleEvent(GPALEventType.WARNING,
3607 $"{(string.IsNullOrEmpty(_name) ? "" : $"[{_name}] ")}Result index [{resultIndex}] is out of range for WithWindowIdFromResult. Using 0.",
3608 null, GPALObjectType.None);
3609 return WithWindowId(0);
3610 }
3611 var previousResult = _executionResults[resultIndex].Result;
3612 var windowId = previousResult is int i ? i : (int.TryParse(previousResult?.ToString(), out int parsed) ? parsed : 0);
3613 return WithWindowId(windowId);
3614 }
3615
3622 {
3623 var resultEntry = _executionResults.LastOrDefault(entry => entry.Name == name);
3624 if (resultEntry.Result == null)
3625 {
3626 GPAL.PublishSimpleEvent(GPALEventType.WARNING,
3627 $"{(string.IsNullOrEmpty(_name) ? "" : $"[{_name}] ")}Result name [{name}] not found for WithWindowIdFromResult. Using 0.",
3628 null, GPALObjectType.None);
3629 return WithWindowId(0);
3630 }
3631 var previousResult = resultEntry.Result;
3632 var windowId = previousResult is int i ? i : (int.TryParse(previousResult?.ToString(), out int parsed) ? parsed : 0);
3633 return WithWindowId(windowId);
3634 }
3635
3642 {
3643 if (true == string.IsNullOrEmpty(url))
3644 url = new GPALUrl("google.com");
3645
3646 WithEndpoint(ApiEndpoint.OpenWindow).WithUrl(url.Url);
3647 return this;
3648 }
3649
3657 {
3658 WithEndpoint(ApiEndpoint.GoToWindow).WithUrl(url?.Url);
3659 return this;
3660 }
3661
3668 public IAllowRESTExecution GoToWindow(int windowId)
3669 {
3670 WithEndpoint(ApiEndpoint.GoToWindow).WithWindowId(windowId);
3671 return this;
3672 }
3673
3680 {
3681 WithEndpoint(ApiEndpoint.CloseBrowser);
3682 return this;
3683 }
3684
3692 {
3693 WithEndpoint(ApiEndpoint.CloseWindow);
3694
3695 if (null != url)
3696 WithUrl(url?.Url);
3697
3698 return this;
3699 }
3700
3708 {
3709 WithEndpoint(ApiEndpoint.CloseWindow).WithWindowId(windowId);
3710 return this;
3711 }
3712
3718 public IAllowRESTStorageOptions DeleteStorage(WebsiteStorageType storageType)
3719 {
3720 WithEndpoint(ApiEndpoint.DeleteStorage).WithStorageType(storageType);
3721
3722 return this;
3723 }
3724
3730 public IAllowRESTStorageOptions GetStorage(WebsiteStorageType storageType)
3731 {
3732 WithEndpoint(ApiEndpoint.GetStorage).WithStorageType(storageType);
3733
3734 return this;
3735 }
3736
3742 public IAllowRESTStorageOptions SetStorage(WebsiteStorageType storageType)
3743 {
3744 WithEndpoint(ApiEndpoint.SetStorage).WithStorageType(storageType);
3745
3746 return this;
3747 }
3748
3754 {
3755 WithEndpoint(ApiEndpoint.GetCurrentWindow);
3756 return this;
3757 }
3758 #endregion Window Handling
3765 public IAllowRESTExecution SwitchToElement(string elementId)
3766 {
3767 WithEndpoint(ApiEndpoint.SwitchToElement);
3768 _parameters = null == _parameters
3769 ? new { elementId }
3770 : MergeParameters(_parameters, new { elementId });
3771 WithHttpMethod("POST");
3772 return this;
3773 }
3774
3781 public IAllowRESTExecution SwitchToFrame(string elementId)
3782 {
3783 WithEndpoint(ApiEndpoint.SwitchToElement);
3784 _parameters = null == _parameters
3785 ? new { elementId }
3786 : MergeParameters(_parameters, new { elementId });
3787 WithHttpMethod("POST");
3788 return this;
3789 }
3790
3798 {
3799 WithEndpoint(ApiEndpoint.SwitchToShadowRoot);
3800 _parameters = null == _parameters
3801 ? new { elementId }
3802 : MergeParameters(_parameters, new { elementId });
3803 WithHttpMethod("POST");
3804 return this;
3805 }
3806
3813 private object MergeParameters(object existing, object additional)
3814 {
3815 var dict = existing is IDictionary<string, object> existingDict
3816 ? new Dictionary<string, object>(existingDict)
3817 : existing.GetType().GetProperties()
3818 .ToDictionary(p => p.Name, p => p.GetValue(existing));
3819
3820 if (null != additional)
3821 {
3822 var additionalDict = additional is IDictionary<string, object> addDict
3823 ? addDict
3824 : additional?.GetType().GetProperties()
3825 .ToDictionary(p => p.Name, p => p.GetValue(additional));
3826
3827 foreach (var kvp in additionalDict)
3828 {
3829 dict[kvp.Key] = kvp.Value;
3830 }
3831 }
3832 return dict;
3833 }
3834
3835
3836 internal bool ValidateParameters(ApiEndpoint endpoint, IDictionary<string, object> parmsToValidate = null)
3837 {
3838 try
3839 {
3840 var internalParms = _parameters as IDictionary<string, object>
3841 ?? (null != _parameters ? _parameters.GetType().GetProperties().ToDictionary(p => p.Name, p => p.GetValue(_parameters))
3842 : new Dictionary<string, object>());
3843
3844 var paramsDict = parmsToValidate ?? internalParms;
3845
3846 if (false == paramRules.ContainsKey(endpoint))
3847 {
3848 GPAL.PublishSimpleEvent(GPALEventType.WARNING,
3849 $"{(string.IsNullOrEmpty(_name) ? "" : $"[{_name}] ")}No validation rules defined for endpoint [{endpoint}].",
3850 null, GPALObjectType.None);
3851 return true;
3852 }
3853
3854 var (mandatory, optional, customValidation) = paramRules[endpoint];
3855
3856 if (ApiEndpoint.SetStorage == endpoint)
3857 {
3858 var storageType = paramsDict.ContainsKey("storageType") ? paramsDict["storageType"]?.ToString() : "";
3859 if (true == "cache".Equals(storageType))
3860 (mandatory, optional, customValidation) = paramRules[ApiEndpoint.SetStorageCache];
3861 else if (true == "indexedDb".Equals(storageType))
3862 (mandatory, optional, customValidation) = paramRules[ApiEndpoint.SetStorageIndexedDb];
3863 }
3864
3865 var missingMandatory = mandatory.Where(p => false == paramsDict.ContainsKey(p)).ToArray();
3866
3867 if (true == missingMandatory.Any())
3868 {
3869 GPAL.PublishSimpleEvent(GPALEventType.WARNING,
3870 $"{(string.IsNullOrEmpty(_name) ? "" : $"[{_name}] ")}Missing required parameters for [{endpoint}]: [{string.Join(", ", missingMandatory)}].",
3871 null, GPALObjectType.None);
3872 return false;
3873 }
3874
3875 if (null != customValidation && false == customValidation(paramsDict))
3876 {
3877 GPAL.PublishSimpleEvent(GPALEventType.WARNING,
3878 $"{(string.IsNullOrEmpty(_name) ? "" : $"[{_name}] ")}Custom validation failed for [{endpoint}].",
3879 null, GPALObjectType.None);
3880 return false;
3881 }
3882
3883 // Ensure only allowed parameters (mandatory + optional) are present
3884 var allowedParams = mandatory.Concat(optional).ToHashSet();
3885 var invalidParams = paramsDict.Keys.Where(k => false == allowedParams.Contains(k)).ToArray();
3886 if (true == invalidParams.Any())
3887 {
3888 GPAL.PublishSimpleEvent(GPALEventType.WARNING,
3889 $"{(string.IsNullOrEmpty(_name) ? "" : $"[{_name}] ")}Invalid parameters for [{endpoint}]: [{string.Join(", ", invalidParams)}].",
3890 null, GPALObjectType.None);
3891 return false;
3892 }
3893 }
3894 catch (Exception ex)
3895 {
3896 GPAL.PublishSimpleEvent(GPALEventType.EXCEPTION, $"Unable to validate [{endpoint}]", this, GPALObjectType.RestClient, ex);
3897 }
3898
3899 return true;
3900 }
3901
3902 internal bool ValidateOpenApiParameters(string ruleKey, IDictionary<string, object> parmsToValidate = null)
3903 {
3904 try
3905 {
3906 var internalParms = _parameters as IDictionary<string, object>
3907 ?? (null != _parameters ? _parameters.GetType().GetProperties().ToDictionary(p => p.Name, p => p.GetValue(_parameters))
3908 : new Dictionary<string, object>());
3909
3910 var paramsDict = parmsToValidate ?? internalParms;
3911
3912 if (false == openApiParamRules.ContainsKey(ruleKey))
3913 {
3914 GPAL.PublishSimpleEvent(GPALEventType.WARNING,
3915 $"{(string.IsNullOrEmpty(_name) ? "" : $"[{_name}] ")}No OpenAPI validation rules defined for [{ruleKey}].",
3916 null, GPALObjectType.None);
3917 return true;
3918 }
3919
3920 var (mandatory, optional, customValidation) = openApiParamRules[ruleKey];
3921
3922 var missingMandatory = mandatory.Where(p => false == paramsDict.ContainsKey(p)).ToArray();
3923
3924 if (true == missingMandatory.Any())
3925 {
3926 GPAL.PublishSimpleEvent(GPALEventType.WARNING,
3927 $"{(string.IsNullOrEmpty(_name) ? "" : $"[{_name}] ")}Missing required parameters for [{ruleKey}]: [{string.Join(", ", missingMandatory)}].",
3928 null, GPALObjectType.None);
3929 return false;
3930 }
3931
3932 if (null != customValidation && false == customValidation(paramsDict))
3933 {
3934 GPAL.PublishSimpleEvent(GPALEventType.WARNING,
3935 $"{(string.IsNullOrEmpty(_name) ? "" : $"[{_name}] ")}Custom validation failed for OpenAPI endpoint [{ruleKey}].",
3936 null, GPALObjectType.None);
3937 return false;
3938 }
3939
3940 // Ensure only allowed parameters are present
3941 var allowedParams = mandatory.Concat(optional).ToHashSet(StringComparer.OrdinalIgnoreCase);
3942 var invalidParams = paramsDict.Keys.Where(k => false == allowedParams.Contains(k)).ToArray();
3943
3944 if (true == invalidParams.Any())
3945 {
3946 GPAL.PublishSimpleEvent(GPALEventType.WARNING,
3947 $"{(string.IsNullOrEmpty(_name) ? "" : $"[{_name}] ")}Invalid parameters for [{ruleKey}]: [{string.Join(", ", invalidParams)}].",
3948 null, GPALObjectType.None);
3949 return false;
3950 }
3951 }
3952 catch (Exception ex)
3953 {
3954 GPAL.PublishSimpleEvent(GPALEventType.EXCEPTION,
3955 $"Unable to validate OpenAPI endpoint [{ruleKey}]", this, GPALObjectType.RestClient, ex);
3956 }
3957
3958 return true;
3959 }
3966 private string GetOpenApiRuleKey(string httpMethod, string endpoint)
3967 {
3968 if (string.IsNullOrWhiteSpace(httpMethod) || string.IsNullOrWhiteSpace(endpoint))
3969 return null;
3970
3971 var path = endpoint.TrimStart('/');
3972 return $"{httpMethod.ToUpperInvariant()} {path}";
3973 }
3974
3980 private string ExtractBestServerUrl(string yamlContent)
3981 {
3982 try
3983 {
3984 var reader = new Microsoft.OpenApi.Readers.OpenApiStreamReader();
3985 using var stream = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(yamlContent));
3986 var document = reader.Read(stream, out _);
3987
3988 if (document.Servers == null || document.Servers.Count == 0)
3989 return null;
3990
3991 // 1. Prefer remote (https) server
3992 var remote = document.Servers.FirstOrDefault(s =>
3993 s.Url.StartsWith("https://", StringComparison.OrdinalIgnoreCase));
3994
3995 if (remote != null)
3996 return remote.Url.TrimEnd('/');
3997
3998 // 2. Otherwise take the first one
3999 return document.Servers[0].Url.TrimEnd('/');
4000 }
4001 catch
4002 {
4003 return null;
4004 }
4005 }
4006 internal static void SaveResponseToFile(string response, string filePath)
4007 {
4008 try
4009 {
4010 // Handle Data URI (e.g., base64-encoded image) or direct HTTP URL
4011 if (response.StartsWith("data:"))
4012 {
4013 // Handle base64-encoded data URI
4014 var base64Part = response.Substring(response.IndexOf(",") + 1);
4015 var bytes = Convert.FromBase64String(base64Part);
4016 System.IO.File.WriteAllBytes(filePath, bytes);
4017 GPAL.PublishSimpleEvent(GPALEventType.INFO, $"Saved base64 response to [{filePath}]", null, GPALObjectType.None);
4018 }
4019 else if (Uri.TryCreate(response, UriKind.Absolute, out var uri))
4020 {
4021 // Handle direct HTTP(S) URL
4022 using (var client = new HttpClient())
4023 using (var stream = client.GetStreamAsync(uri).Result)
4024 using (var fileStream = System.IO.File.Create(filePath))
4025 {
4026 stream.CopyTo(fileStream);
4027 }
4028 GPAL.PublishSimpleEvent(GPALEventType.INFO, $"Downloaded response to [{filePath}]", null, GPALObjectType.None);
4029 }
4030 else
4031 {
4032 GPAL.PublishSimpleEvent(GPALEventType.WARNING, $"Unsupported response format for saving to [{filePath}]", null, GPALObjectType.None);
4033 }
4034 }
4035 catch (Exception ex)
4036 {
4037 GPAL.PublishSimpleEvent(GPALEventType.EXCEPTION, $"Failed to save response to [{filePath}]", null, GPALObjectType.None, ex);
4038 }
4039 }
4040 }
4041}
Represents a URL with optional pre-navigation storage cleanup / inspection actions....
Definition GPALUrl.cs:53
string Url
Gets the target URL string.
Definition GPALUrl.cs:69
Pseudo element used in Applications and Browser workflows for image matching and unified automation....
Thrown where GPAL deliberately ends the workflow, such as a CallIf handler returning CallIfStatus....
GPAL File object instantied with GPAL.File Used to load tokens into a GPALGrid [rows/columns].
Definition GPALFile.cs:36
List< string > Filenames
Get the list of filenames.
Definition GPALFile.cs:536
string Filename
We have only one file, accessing it.
Definition GPALFile.cs:474
Everything starts here, all the GPAL controls and global settings using fluent syntax are here....
Definition GPAL.cs:49
static IAllowConverterInput Converter
New GPAL Convertor.
Definition GPAL.cs:560
static void PublishSimpleEvent(GPALEventType gPALEventType, string msg, dynamic gPALObject=null, Enums.GPALObjectType gPALObjectType=GPALObjectType.None, Exception ex=null)
Publish a message to either the information channel or exception channel (if exception passed in) Pub...
Definition GPAL.cs:2406
IAllowRESTExecution SetValueFrom(string destSelector)
Sets the value of the destination element identified by destSelector from the source element identif...
IAllowRESTParametersOrExecution WithWindowIdFromResult(string name)
Sets the window ID from a result by name client.GoToWindow().WithWindowIdFromResult("windowResult")....
IAllowRESTExecution WindowScreenLeft()
Gets the window screen left position client.WindowScreenLeft().Execute<int>();.
IAllowRESTParametersOrExecution WithUrl(string url)
Sets the URL parameter.
IAllowRESTStorageOptions WithStoragePath(string path)
Sets the storage path parameter.
IAllowRESTParametersOrExecution WithKeyFromResult(int resultIndex)
Sets the key from a result by index.
IAllowRESTStorageOptions DeleteStorage(WebsiteStorageType storageType)
Delete the specified storage type.
IAllowRESTExecution ScrollIntoView(string elementId)
Scrolls an element into view by ID client.ScrollIntoView("element123").Execute();.
IAllowRESTParametersOrExecution WithResultName(string name)
Sets the result name for the next execution.
IAllowRESTParametersOrExecution WithElementIdFromResult(int resultIndex)
Sets the element from a result by index.
IAllowRESTParametersOrExecution WithCss(string css)
Sets the CSS parameter.
IAllowRESTParametersOrExecution WithXPath(string xpath)
Sets the XPath parameter.
IAllowRESTExecution WindowInnerWidth()
Gets the window inner width client.WindowInnerWidth().Execute<int>();.
IAllowRESTExecution Back()
Navigates browser history back client.Back().Execute();.
IAllowRESTExecution SubmitForm(string elementId)
Submits a form client.SubmitForm("form123").Execute();.
IAllowRESTExecution HideElement(string elementId)
Hides an element by ID client.HideElement("element123").Execute();.
IAllowRESTCaptureOptions WithClearCalls(bool clear=true)
Forgets what was already recorded before starting, so a second capture is not read as the first.
IAllowRESTParametersOrExecution WithCssFromResult(string name)
Sets the CSS from a result by name.
IAllowRESTParametersOrExecution WithKeyFromResult(string name)
Sets the key from a result by name.
IAllowRESTExecution WithTextFromResult(string name)
Sets the text from a result by name.
IAllowRESTParametersOrExecution WithUrlFromResult(string name)
Sets the URL from a result by name.
IAllowRESTExecution SwitchToElement(string elementId)
Switches to an element to search for selectors contained client.SwitchToElement("div123")....
IAllowRESTExecution GetBrowserSettings()
Gets browser settings client.GetBrowserSettings().Execute();.
IAllowRESTExecution Maximize()
Maximizes the browser window client.Maximize().Execute();.
IAllowRESTExecution CloseTab(int tabId)
Closes a tab by tab ID client.CloseTab(1).Execute();.
IAllowRESTParametersOrExecution WithEncoding(ContentEncoding encoding)
Specifiy the encoiding type for posted parameters: json or urlencoded.
IAllowRESTParametersOrExecution WithBytes(bool bytes)
True to bring the fetched response body back base64 encoded, which is what a file needs....
IAllowRESTParametersOrExecution WithCssFromResult(int resultIndex)
Sets the CSS from a result by index.
IAllowRESTExecution WithRangeValue(int rangeValue)
Specifies the value for SetRange operation client.SetRange("element123").WithRangeValue(63)....
IAllowRESTExecution PreviousWindow()
Switches to the next window client.NextTab().Execute();.
IAllowRESTScrollElement ScrollElement(string elementId)
Starts scrolling an element by ID client.ScrollElement("element123").WithHPixels(100)....
IAllowRESTExecution IsVisibleInViewport(string elementId)
Checks if an element is visible in viewport by ID client.IsVisibleInViewport("element123")....
IAllowRESTExecution ScrollElement(int hPixels, int vPixels)
Scrolls an element by horizontal and vertical pixels client.ScrollElement(100, 200)....
IAllowRESTExecution CloseWindow(int windowId)
Closes a window by window ID, optionally switching to the main window client.CloseWindow(123)....
IAllowRESTExecution GetLanguages()
Gets the browser's preferred languages, most preferred first client.GetLanguages()....
IAllowRESTParametersOrExecution WithTabIdFromResult(string name)
Sets the tab ID from a result by name.
IAllowRESTParametersOrExecution WithParametersFromResult(int resultIndex)
Sets parameters from a result by index.
IAllowRESTStorageOptions WithData(string data)
Sets the set storage data parameter.
IAllowRESTParametersOrExecution WithTabIdFromResult(int resultIndex)
Sets the tab ID from a result by index.
IAllowRESTExecution RightClick(string elementId)
Performs a right click on an element by ID client.RightClick("element123").Execute();.
IAllowRESTStorageOptions SetStorage(WebsiteStorageType storageType)
Set data for the specified storage type.
IAllowRESTExecution NextTab()
Switches to the next tab client.NextTab().Execute();.
IAllowCheckNetworkIdleOrExecution WithPruneMs(int pruneMs)
Sets the prune interval in milliseconds.
IAllowRESTStorageOptions WithStorageDomain(string domain)
Sets the storage domain parameter.
IAllowRESTExecution WithHttpMethod(string method)
Sets the HTTP method.
string Execute(bool publishEvent=true)
Executes the request synchronously.
IAllowRESTExecution LeftDoubleClick(string elementId)
Performs a left double click on an element by ID client.LeftDoubleClick("element123")....
IAllowRESTExecution GetUserAgent()
Gets the user agent client.GetUserAgent().Execute<string>();.
IAllowRESTParametersOrExecution WithReferrerFromResult(int resultIndex)
Sets the referrer from a result by index.
IAllowRESTInputText FillInInsert(string elementId)
Appends text to an element client.FillInAppend("input123").WithText("Hello").Execute();.
IAllowRESTDragAndDrop WithOffsetX(int offsetX)
Sets the horizontal grab offset within the element for a drag-and-drop operation.
IAllowRESTExecution SwitchToDefaultContent()
Switches to default content client.SwitchToDefaultContent().Execute();.
IAllowRESTScrollElement WithVPixelsFromResult(string name)
Sets the vertical pixels from a result by name.
IAllowRESTExecution SwitchToFrame(string elementId)
Switches to a frame client.SwitchToFrame("frame123").Execute();.
IAllowRESTExecution GoTo(GPALUrl url)
Navigates to a URL client.Goto("https://example.com").Execute();.
IAllowRESTExecution CloseTab(GPALUrl url=null)
Closes a tab by URL client.CloseTab("https://example.com").Execute();.
IAllowRESTExecution WindowOuterHeight()
Gets the window outer height client.WindowOuterHeight().Execute<int>();.
IAllowRESTParametersOrExecution WithXPathFromResult(string name)
Sets the XPath from a result by name.
async Task< string > ExecuteAsync(bool publishEvent=true)
Executes the request asynchronously and returns the raw response await client.ExecuteAsync();.
IAllowRESTExecution WindowInnerHeight()
Gets the window inner height client.WindowInnerHeight().Execute<int>();.
IAllowRESTExecution QuerySelectors(string css)
Queries multiple elements using a CSS selector client.QuerySelectors(".class").Execute<string[]>();.
IAllowRESTWithDownloadFilename RightClickAndDownload(string elementId)
Performs a right click and download on an element client.RightClickAndDownload("element123")....
IAllowRESTStorageOptions WithStorageType(WebsiteStorageType storageType)
Sets the storage path parameter.
IAllowRESTExecution IsEndOfPage()
Checks if the page is at the end client.IsEndOfPage().Execute<bool>();.
IAllowRESTScrollElement WithVPixelsFromResult(int resultIndex)
Sets the vertical pixels from a result by index.
IAllowRESTExecution IsClickable(string elementId)
Checks if an element is clickable by ID client.IsClickAble("element123").Execute<bool>();.
IAllowRESTParametersOrExecution WithParametersFromResult(string name)
Sets parameters from a result by name.
IRESTClient AndThen< T >(bool publishEvent=true)
Executes and chains the request with type.
IAllowRESTExecution ScrollWindowByHorizontal(int hPixels)
Scrolls the window horizontally by pixels client.ScrollWindowByHorizontal(100).Execute();.
IAllowRESTExecution EvaluatePersistent(string xpath)
Evaluates an XPath expression client.Evaluate("//div").Execute();.
IAllowRESTExecution OpenWindow(GPALUrl url)
Opens a new window with the specified URL client.OpenWindow("https://example.com")....
IAllowRESTWithUploadFilename LeftClickAndUpload(string elementId)
Purely headless mode, attaches the file to the input[type=file] client.LeftClickAndUpload("element123...
IAllowRESTExecution GetBoundingClientRect(string elementId)
Gets the bounding client rect of an element by ID client.GetBoundingClientRect("element123")....
T Execute< T >(bool publishEvent=true)
Executes the request synchronously with type.
IAllowRESTExecution GetElementAttributeHash(string elementId)
Gets the attribute hash of an element client.GetElementAttributeHash().Execute<string>();.
IAllowRESTParametersOrExecution WithBody(string body)
Sets the body of the request being fetched. client.Fetch(url).WithVerb("POST").WithBody(json)....
IAllowRESTExecution WithHeaderFromResult(string name)
Sets the header from a result by name.
IAllowRESTParametersOrExecution WithParameters(params object[] parameters)
Sets parameters as a variable argument list.
IAllowRESTAttribute WithValue(string value)
Specifies the value for set operation client.SetAttribute("element123").WithAttribute("disabled")....
IAllowRESTExecution PreviousTab()
Switches to the previous tab client.PreviousTab().Execute();.
IAllowRESTExecution WithUploadFiles(GPALFile uploadPaths)
Sets the timeout in milliseconds.
IAllowRESTParametersOrExecution While(Func< bool > condition)
Executes workflows while condition holds client.While(() => someCondition).Execute();.
IAllowRESTParametersOrExecution Fetch(string url)
Issue an API request from inside the page, so it carries the session the browser has already earned: ...
IAllowRESTParametersOrExecution WithWindowIdFromResult(int resultIndex)
Sets the window ID from a result by index client.GoToWindow().WithWindowIdFromResult(0)....
IAllowRESTParametersOrExecution WithScript(string script)
Sets the script parameter.
IAllowRESTExecution GoToWindow(int windowId)
Switches to a window by window ID client.GoToWindow(123).Execute<WindowTuple>();.
IAllowRESTExecution GetCurrentWindow()
Gets the current window's ID and URL client.GetCurrentWindow().Execute<WindowTuple>();.
IAllowRESTExecution Focus(string elementId)
Focuses an element by ID client.Focus("element123").Execute();.
IAllowCheckNetworkIdleOrExecution WithTimeoutMs(int timeoutMs)
Sets the timeout in milliseconds.
IAllowRESTParametersOrExecution WithHeaders(string[] headers)
Sets extra headers for the request being fetched, flattened to name, value, name, value....
IAllowRESTParametersOrExecution WithUrlFromResult(int resultIndex)
Sets the URL from a result by index.
IAllowRESTExecution ReleaseModifierKey(ModifierKeys modifierKeys)
Press the modifier keys specified. Keep pressed until a release is sent.
IAllowRESTParameters ClearInjectedScripts()
Removes all scripts previously registered via InjectScript(string).
ResultCollection GetExecutionResults()
Gets the execution results.
IAllowRESTExecution PageUp(int pagesToScroll=1)
Scrolls the page up by pages client.PageUp(1).Execute();.
IAllowRESTExecution NewTab(GPALUrl url)
Opens a new tab with optional URL client.NewTab("https://example.com").Execute();.
string Name
Gets or sets the client name.
IAllowRESTExecution GetCapturedCalls()
Everything recorded so far, oldest first, as the json the extension keeps. string json = client....
IAllowRESTAttribute SetAttribute(string elementId)
Starts setting an attribute of an element by ID client.SetAttribute("element123")....
IAllowRESTExecution WithHeaderFromResult(int resultIndex)
Sets the header from a result by index.
IAllowRESTExecution ClearReferrer()
Clears the referrer client.ClearReferrer().Execute();.
IAllowRESTExecution IsEnabled(string elementId)
Checks if an element is enabled by ID client.IsEnabled("element123").Execute<bool>();.
IAllowRESTExecution PageEnd()
Scrolls to the page end client.PageEnd().Execute();.
IAllowRESTExecution Normal()
Restores the browser window to normal client.Normal().Execute();.
IAllowRESTExecution Restore()
Restores the browser window client.Restore().Execute();.
IAllowRESTExecution QueryPersistentSelectors(string css)
Queries multiple elements using a CSS selector client.QuerySelectors(".class").Execute<string[]>();.
IAllowRESTExecution TabCount()
How many tabs the browser actually has open, which is not the same as how many GPAL opened....
IAllowRESTEndpointDetails WithName(string name)
Sets the client name.
IAllowRESTExecution GetOptions(string elementId)
Get the options in a Select manue.
IAllowRESTExecution StealthOverrideReferrer()
Overrides referrer stealthily - sets to google.com client.StealthOverrideReferrer()....
IAllowRESTExecution GetReadyStatus()
Gets the ready status client.GetReadyStatus().Execute<string>();.
async Task< T > ExecuteAsync< T >(bool publishEvent=true)
Executes the request asynchronously with type.
IAllowRESTExecution GoToTab(int tabId)
Navigates to a tab by tab ID client.GotoTab(1).Execute();.
IAllowRESTExecution GetParentNode(string elementId)
Gets the parent node of an element by ID client.GetParentNode("element123").Execute<string>();.
IAllowRESTAttribute WithAttribute(string attribute)
Specifies the attribute name for get or set operation client.GetAttribute("element123")....
IAllowRESTExecution SwitchToShadowRoot(string elementId)
Switches to a shadow root client.SwitchToShadowRoot("shadow123").Execute();.
IAllowRESTExecution MoveTo(string elementId)
Moves to an element client.MoveTo("element123").Execute();.
IAllowRESTInputText FillInAppend(string elementId)
Appends text to an element client.FillInAppend("input123").WithText("Hello").Execute();.
IAllowRESTParametersOrExecution DownloadTo(GPALFile downloadPath)
Where a file the browser downloads should end up. Not SaveTo, which is GPAL writing a file it alread...
IAllowRESTParametersOrExecution WithEndpoint(ApiEndpoint endpoint)
Sets the endpoint from an ApiEndpoint enum.
IAllowRESTExecution PressModifierKey(ModifierKeys modifierKeys)
Press the modifier keys specified. Keep pressed until a release is sent.
IAllowRESTParametersOrExecution WithReferrerFromResult(string name)
Sets the referrer from a result by name.
IAllowRESTParameters ExecuteJavaScript(string script)
Executes JavaScript code client.ExecuteJavaScript("return document.title").Execute<string>();.
IAllowRESTParametersOrExecution WithElementId(string elementId)
Sets the element ID parameter.
IAllowRESTStorageOptions WithStorageStoreName(string storeName)
Sets the storage domain parameter.
IAllowRESTExecution GetSettings()
Gets settings client.GetSettings().Execute();.
IAllowRESTExecution WithHeader(string name, string value)
Adds an HTTP header to the request client.WithHeader("Authorization", "Bearer YOUR_ACCESS_TOKEN")....
IAllowRESTParametersOrExecution WithPixelsFromResult(int resultIndex)
Sets the pixels from a result by index.
IAllowRESTParametersOrExecution WithKey(string key)
Sets the key parameter.
IAllowRESTParametersOrExecution WithElementIdFromResult(string name)
Sets the element from a result by name.
IAllowRESTParametersOrExecution WithElement(IGPALElement gPALElement)
Sets the element parameter.
IAllowRESTExecution EvaluateAll(string xpath)
Evaluates an XPath expression for multiple elements client.EvaluateAll("//div").Execute<string[]>();.
IAllowRESTCaptureOptions WithCallFilter(string urlFragment)
Records only the calls whose url holds this, so a page that talks to a dozen places narrows to the on...
IAllowRESTExecution WithTextFromResult(int resultIndex)
Sets the text from a result by index.
IAllowSelectOptions SelectClick(string elementId)
Click a Select menu option.
IAllowRESTExecution CaptureVisibleTab(ImageFormat imageFormat=ImageFormat.JPEG)
Get a screenshot of the current tab visble contents.
IAllowRESTAttribute GetAttribute(string elementId)
Starts getting an attribute of an element by ID client.GetAttribute("element123")....
IAllowRESTExecution CloseWindow(GPALUrl url)
Closes a window by URL, optionally switching to the main window client.CloseWindow("https://example....
IAllowRESTParametersOrExecution WithXPathFromResult(int resultIndex)
Sets the XPath from a result by index.
IAllowRESTExecution ElementFromPoint(string elementId, int x, int y)
Asks whether a click at this viewport point would reach the element, rather than land on whatever is ...
IRESTClient AndThen(bool publishEvent=true)
Executes and chains the request.
IAllowRangeValue SetRange(string elementId)
Starts setting an attribute of an element by ID client.SetAttribute("element123")....
IAllowRESTExecution WithUploadFile(string uploadPath)
Sets the timeout in milliseconds.
IAllowRESTEndpointDetails WithAPIBase(string url)
Sets the API base URL with download path.
IAllowRESTExecution WithText(string text)
Sets the text parameter.
IAllowRESTExecution Refresh()
Refreshes the current page client.Refresh().Execute();.
IAllowRESTExecution FullScreen()
Sets browser to full screen client.FullScreen().Execute();.
IAllowRESTExecution CheckNetworkIdle(int maxConnections=0)
Checks if network is idle with max connections client.CheckNetworkIdle(5).Execute<bool>();.
IAllowRESTExecution SendString(string text, int delayMs=0)
Sends a string client.SendString("Hello").Execute();.
IAllowRESTParametersOrExecution WithPixels(int pixels)
Sets the pixels parameter.
IAllowRESTExecution NextWindow()
Switches to the next window client.NextTab().Execute();.
IAllowRESTExecution MiddleClick(string elementId)
Performs a left click on an element by ID client.LeftClick("element123").Execute();.
IAllowRESTExecution PageDown(int pagesToScroll=1)
Scrolls the page down by pages client.PageDown(1).Execute();.
IRESTClient ToGPALObject()
Converts the client to a GPAL object.
IAllowRESTExecution GetShadowRoot(string css)
Gets the shadow root for a CSS selector client.GetShadowRoot(".class").Execute();.
IAllowRESTExecution EvaluateAllPersistent(string xpath)
Evaluates an XPath expression for multiple elements client.EvaluateAll("//div").Execute<string[]>();.
IAllowRESTExecution TopBrowser()
Top the browser, bring it into focus. Used before performing any actions on the browser.
IAllowRESTExecution GetWorkflow()
Gets the workflow client.GetWorkflow().Execute();.
IAllowRESTScrollElement WithVPixels(int vPixels)
Sets the vertical pixels parameter.
IAllowRESTExecution WindowOuterWidth()
Gets the window outer width client.WindowOuterWidth().Execute<int>();.
IAllowRESTExecution GetGpalSettings()
Gets GPAL settings client.GetGpalSettings().Execute();.
IAllowRESTExecution Minimize()
Minimizes the browser window client.Minimize().Execute();.
IAllowRESTParametersOrExecution SaveResultsTo(GPALFile file)
Saves the response to a file if the endpoint returns a downloadable content client....
IAllowRESTParametersOrExecution WithReferrer(string url)
Sets the referrer parameter.
IAllowRESTParametersOrExecution WithParameters(object parameters)
Sets parameters as a single object.
IAllowRESTParametersOrExecution Until(Func< bool > condition)
Executes workflows until condition holds.
IAllowRESTExecution Evaluate(string xpath)
Evaluates an XPath expression client.Evaluate("//div").Execute();.
IAllowRESTStorageOptions WithStorageKey(string key)
Sets the storage key parameter.
IAllowRESTDragAndDrop WithOffsetY(int offsetY)
Sets the vertical grab offset within the element for a drag-and-drop operation.
IAllowRESTDragAndDrop WithDeltaX(int deltaX)
Sets the horizontal drop offset for a drag-and-drop operation.
IAllowRESTScrollElement WithHPixels(int hPixels)
Sets the horizontal pixels parameter.
IAllowRESTExecution PageTop()
Scrolls to the page top client.PageTop().Execute();.
IAllowRESTParametersOrExecution WithTabId(int tabId)
Sets the tab ID parameter.
IAllowRESTExecution CloseBrowser()
Closes every window the browser has, which is how it is asked to quit client.CloseBrowser()....
IAllowRESTExecution SetUserAgent(string userAgent)
Overrides the browser's user agent string client.SetUserAgent("Mozilla/5.0 ...").Execute();.
IAllowRESTExecution Hover(string elementId)
Hovers over an element by ID client.Hover("element123").Execute();.
IAllowRESTExecution WindowScreenTop()
Gets the window screen top position client.WindowScreenTop().Execute<int>();.
IAllowRESTExecution GetCurrentUrl()
Gets the current URL client.GetCurrentUrl().Execute<string>();.
IAllowRESTDragAndDrop DragAndDrop(string elementId)
Drags an element from its current position by the given offset and drops it client....
IAllowRESTParametersOrExecution WithVerb(string method)
Sets the verb of the request being fetched. Not .WithHttpMethod, which is the verb of the REST call i...
IAllowRESTExecution FireChangeEvent(string elementId)
Fires a change event on an element by ID client.FireChangeEvent("element123").Execute();.
IAllowRESTExecution WindowPageOffsetY()
Gets the window page offset Y client.WindowPageOffsetY().Execute<int>();.
IAllowRESTExecution WithSelectValue(string selectValue)
Set the value to choose from the Select menu.
IAllowRESTParametersOrExecution WithWindowId(int windowId)
Sets the window ID parameter client.CloseWindow(123).Execute<WindowTuple>();.
IAllowRESTExecution Forward()
Navigates browser history forward client.Forward().Execute();.
IAllowRESTExecution WithDeviceName(string sinkName)
Sets the CSS parameter.
IAllowRESTExecution SendKey(byte vkcode)
Sends a key client.SendKey("Enter").Execute();.
IAllowRESTExecution ScrollWindowByVertical(int vPixels)
Scrolls the window vertically by pixels client.ScrollWindowByVertical(100).Execute();.
IAllowRESTScrollElement WithHPixelsFromResult(int resultIndex)
Sets the horizontal pixels from a result by index.
IAllowRESTExecution QuerySelector(string css)
Queries a single element using a CSS selector client.QuerySelector(".class").Execute();.
IAllowRESTExecution GoToTab(GPALUrl url)
Navigates to a tab by URL client.GotoTab("https://example.com").Execute();.
IAllowRESTInputText FillInOverwrite(string elementId)
Appends text to an element client.FillInAppend("input123").WithText("Hello").Execute();.
IAllowRESTExecution GoToWindow(GPALUrl url)
Switches to a window by URL client.GoToWindow("https://example.com").Execute<WindowTuple>();.
IAllowRESTStorageOptions GetStorage(WebsiteStorageType storageType)
Get the specified storage type.
IAllowRESTParametersOrExecution WithContentType(string contentType)
Sets the content type of the body of the request being fetched. client.Fetch(url)....
IAllowRESTExecution LeftClick(string elementId)
Performs a left click on an element by ID client.LeftClick("element123").Execute();.
IAllowRESTParameters InjectScript(string script)
Registers a script to run on every new document load. Persists until ClearInjectedScripts is called.
IAllowRESTWithDownloadFilename LeftClickAndDownload(string elementId)
Performs a left click and download on an element client.LeftClickAndDownload("element123")....
IAllowRESTCaptureOptions CaptureCalls(bool capture=true)
Starts or stops recording what the page asks for. Narrow it with WithCallFilter and start from nothin...
IAllowRESTExecution OverrideReferrer(string referrer)
Overrides the referrer URL client.OverrideReferrr("https://google.com").Execute();.
IAllowCheckNetworkIdleOrExecution WithMaxConnections(int maxConnections)
Sets the maximum connections.
IAllowRESTParametersOrExecution WithPixelsFromResult(string name)
Sets the pixels from a result by name.
IAllowRESTWorkflow WithWorkflow(Action< RESTClient > workflow)
Adds a workflow definition client.WithWorkflow(c => c.WithEndpoint(...).Execute())....
IAllowRESTExecution WindowPageOffsetX()
Gets the window page offset X client.WindowPageOffsetX().Execute<int>();.
IAllowRESTExecution IsDisplayed(string elementId)
Checks if an element is displayed by ID client.IsDisplayed("element123").Execute<bool>();.
IAllowRESTExecution SetDownloadFilename(string downloadPath)
Sets the download filename for the current session client.SetDownloadFilename("/path/to/file")....
IAllowRESTScrollElement WithHPixelsFromResult(string name)
Sets the horizontal pixels from a result by name.
IAllowRESTParametersOrExecution WithEndpoint(string endpoint)
Sets the endpoint as a string.
IAllowRESTExecution QueryPersistentSelector(string css)
Queries a single element using a CSS selector client.QuerySelector(".class").Execute();.
IAllowRESTDragAndDrop WithDeltaY(int deltaY)
Sets the vertical drop offset for a drag-and-drop operation.
IAllowRESTAttribute WithIndex(int index)
Specifies the option index for select menu operations client.SetAttribute("element123")....
static bool TryConvertVkCodeToDomKey(byte vkCode, out string key, out string code)
Converts a Windows VK code to DOM KeyboardEvent key and code values.