feat: expand cockpit integrations and support workflows

Add Phoenix remote desktop, action connector, documentation engine, and gamification support. Refactor support-case processing and search/action UI, and update installer assets and dependencies.
This commit is contained in:
Meik
2026-07-13 11:16:53 +02:00
parent bbedbf71c8
commit 0b52999b1d
303 changed files with 11235 additions and 3580 deletions

View File

@@ -0,0 +1,9 @@
namespace F4SD.ActionConnector.Enumerations
{
public enum ActionResultStatus
{
Success,
Failure,
Skipped
}
}

View File

@@ -0,0 +1,10 @@
namespace F4SD.ActionConnector.Enumerations
{
public enum ActionType
{
Unknown,
QuickAction,
HttpCall,
Webhook,
}
}

View File

@@ -0,0 +1,10 @@
namespace F4SD.ActionConnector.Enumerations
{
public enum TriggerEvent
{
Unknown,
ApplicationStartup,
CaseClosed,
CaseCreated,
}
}

View File

@@ -0,0 +1,21 @@
using System;
using F4SD.ActionConnector.Models;
using F4SD.ActionConnector.Payloads;
namespace F4SD.ActionConnector.Events
{
/// <summary>
/// Raised after a sync action finishes. Carries the result for Cockpit integration.
/// </summary>
public sealed class ActionCompletedEventArgs : EventArgs
{
public ActionResult Result { get; }
public ActionContext Context { get; }
public ActionCompletedEventArgs(ActionResult result, ActionContext context)
{
Result = result;
Context = context;
}
}
}

View File

@@ -0,0 +1,40 @@
using System;
using F4SD.ActionConnector.Payloads;
namespace F4SD.ActionConnector.Events
{
/// <summary>
/// Static event bus. F4SD calls the methods after a business event occurs.
/// The connector engine subscribes to the corresponding events and dispatches configured actions.
/// </summary>
public static class ActionConnectorEvents
{
/// <summary>Raised when a support case is closed.</summary>
public static event EventHandler<TriggerEventArgs<CaseClosedPayload>> CaseClosed;
/// <summary>Raised when a new support case is created.</summary>
public static event EventHandler<TriggerEventArgs<CaseCreatedPayload>> CaseCreated;
/// <summary>Raised once during application startup, after configuration is loaded.</summary>
public static event EventHandler<TriggerEventArgs<ApplicationStartupPayload>> ApplicationStartup;
/// <summary>
/// Raised after every sync action completes. The Cockpit Client subscribes here
/// to display results without coupling to specific trigger types.
/// </summary>
public static event EventHandler<ActionCompletedEventArgs> ActionCompleted;
public static void RaiseCaseClosed(object sender, CaseClosedPayload payload)
=> CaseClosed?.Invoke(sender, new TriggerEventArgs<CaseClosedPayload>(payload));
public static void RaiseCaseCreated(object sender, CaseCreatedPayload payload)
=> CaseCreated?.Invoke(sender, new TriggerEventArgs<CaseCreatedPayload>(payload));
public static void RaiseApplicationStartup(object sender, ApplicationStartupPayload payload)
=> ApplicationStartup?.Invoke(sender, new TriggerEventArgs<ApplicationStartupPayload>(payload));
public static void RaiseActionCompleted(object sender, ActionCompletedEventArgs args)
=> ActionCompleted?.Invoke(sender, args);
}
}

View File

@@ -0,0 +1,15 @@
using System;
using F4SD.ActionConnector.Payloads;
namespace F4SD.ActionConnector.Events
{
public sealed class TriggerEventArgs<TPayload> : EventArgs where TPayload : PayloadBase
{
public TPayload Payload { get; }
public TriggerEventArgs(TPayload payload)
{
Payload = payload;
}
}
}

View File

@@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace>F4SD.ActionConnector</RootNamespace>
<AssemblyName>F4SD-ActionConnector</AssemblyName>
<Nullable>disable</Nullable>
<LangVersion>7.3</LangVersion>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\F4SD-Logging\F4SD-Logging.csproj" />
<ProjectReference Include="..\FasdCockpitBase\F4SD-Cockpit-Client-Base.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,12 @@
using F4SD.ActionConnector.Enumerations;
using F4SD.ActionConnector.Payloads;
namespace F4SD.ActionConnector.Models
{
public sealed class ActionContext
{
public TriggerEvent TriggerEvent { get; set; }
public bool AwaitResult { get; set; }
public PayloadBase Payload { get; set; }
}
}

View File

@@ -0,0 +1,38 @@
using C4IT.XML;
using F4SD.ActionConnector.Enumerations;
using System;
using System.Xml;
using static C4IT.Logging.cLogManager;
namespace F4SD.ActionConnector.Models
{
public abstract class ActionDefinitionBase
{
public string Id { get; set; }
public abstract ActionType Type { get; }
public bool AwaitResult { get; set; }
public bool IsEnabled { get; set; }
public string Description { get; set; }
public bool IsValid { get; private protected set; }
protected ActionDefinitionBase(XmlElement xNode, cXmlParser parser)
{
try
{
Id = cXmlParser.GetStringFromXmlAttribute(xNode, "Id");
AwaitResult = cXmlParser.GetBoolFromXmlAttribute(xNode, "AwaitResult");
IsEnabled = cXmlParser.GetBoolFromXmlAttribute(xNode, "IsEnabled");
XmlNode descriptionNode = xNode.SelectSingleNode("Description");
if (descriptionNode != null)
Description = cXmlParser.GetInnerTextFromXmlElement(descriptionNode);
IsValid = true;
}
catch (Exception ex)
{
LogException(ex);
}
}
}
}

View File

@@ -0,0 +1,41 @@
using System.Collections.Generic;
using F4SD.ActionConnector.Enumerations;
namespace F4SD.ActionConnector.Models
{
public sealed class ActionResult
{
public string ActionId { get; set; }
public ActionResultStatus Status { get; set; }
/// <summary>
/// HTTP status code for HttpCall actions; null for other types.
/// </summary>
public int? HttpStatusCode { get; set; }
/// <summary>
/// Raw response body (HttpCall) or serialized output (QuickAction).
/// </summary>
public string RawResponse { get; set; }
/// <summary>
/// Named fields extracted from the response via jsonPath mappings.
/// Populated when DisplayInCockpit is true.
/// </summary>
public IDictionary<string, string> MappedFields { get; set; } = new Dictionary<string, string>();
/// <summary>
/// Human-readable error message; set when Status is Failure.
/// </summary>
public string ErrorMessage { get; set; }
public static ActionResult Ok(string actionId, string rawResponse = null)
=> new ActionResult { ActionId = actionId, Status = ActionResultStatus.Success, RawResponse = rawResponse };
public static ActionResult Fail(string actionId, string errorMessage)
=> new ActionResult { ActionId = actionId, Status = ActionResultStatus.Failure, ErrorMessage = errorMessage };
public static ActionResult Skip(string actionId)
=> new ActionResult { ActionId = actionId, Status = ActionResultStatus.Skipped };
}
}

View File

@@ -0,0 +1,74 @@
using C4IT.XML;
using F4SD.ActionConnector.Enumerations;
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
using System.Xml;
using static C4IT.Logging.cLogManager;
namespace F4SD.ActionConnector.Models
{
internal class ExternalCommunicationConfiguration
{
private const string FileNameConfig = "F4SD-ExternalCommunication-Configuration.xml";
private const string FileNameConfigSchema = "F4SD-ExternalCommunication-Configuration.xsd";
private const string ConfigRootElement = "F4SD-ExternalCommunication-Configuration";
public IDictionary<TriggerEvent, Trigger> Triggers { get; } = new Dictionary<TriggerEvent, Trigger>();
private const string TriggersNodeName = "Triggers";
private const string TriggerNodeName = "Trigger";
public void Initiate(XmlElement rootElement, cXmlParser parser)
{
var CM = MethodBase.GetCurrentMethod();
LogMethodBegin(CM);
try
{
var triggersNode = rootElement.SelectSingleNode(TriggersNodeName);
parser.EnterElement(TriggersNodeName);
var triggerNodes = triggersNode.SelectNodes(TriggerNodeName);
if (triggerNodes is null || triggerNodes.Count == 0)
return;
parser.EnterElement(TriggerNodeName);
foreach (XmlElement triggerNodeElement in triggerNodes)
{
try
{
Trigger trigger = new Trigger(triggerNodeElement, parser);
if (trigger is null || !Triggers.ContainsKey(trigger.Event))
continue;
Triggers.Add(trigger.Event, trigger);
}
catch (Exception ex)
{
LogException(ex);
}
finally
{
parser.SelectElementNext();
}
}
parser.LeaveElement(TriggerNodeName);
}
catch (Exception ex)
{
LogException(ex);
}
finally
{
parser.LeaveElement(TriggersNodeName);
LogMethodEnd(CM);
}
}
}
}

View File

@@ -0,0 +1,30 @@
using C4IT.XML;
using F4SD.ActionConnector.Enumerations;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Xml;
namespace F4SD.ActionConnector.Models
{
public sealed class HttpCallDefinition : ActionDefinitionBase
{
public override ActionType Type => ActionType.HttpCall;
public string Url { get; set; }
public HttpMethod Method { get; set; } = HttpMethod.Post;
public TimeSpan Timeout { get; set; } = TimeSpan.FromSeconds(30);
public IDictionary<string, string> Headers { get; set; } = new Dictionary<string, string>();
public string Body { get; set; }
/// <summary>
/// jsonPath expressions keyed by field name, used to extract values from the response.
/// </summary>
public IDictionary<string, string> MappedFieldJsonPaths { get; set; } = new Dictionary<string, string>();
internal HttpCallDefinition(XmlElement xNode, cXmlParser parser) : base(xNode, parser)
{
}
}
}

View File

@@ -0,0 +1,105 @@
using C4IT.XML;
using F4SD.ActionConnector.Enumerations;
using System;
using System.Collections.Generic;
using System.Xml;
using static C4IT.Logging.cLogManager;
namespace F4SD.ActionConnector.Models
{
public sealed class QuickActionDefinition : ActionDefinitionBase
{
private const string ParametersNodeName = "Parameters";
private const string ParameterNodeName = "Parameter";
public override ActionType Type => ActionType.QuickAction;
/// <summary>
/// Reference to the Quick Action to invoke, e.g. "Protocol case in JIRA".
/// </summary>
public string QuickActionRef { get; set; }
public IDictionary<string, string> Parameters { get; private set; } = new Dictionary<string, string>();
internal QuickActionDefinition(XmlElement xNode, cXmlParser parser) : base(xNode, parser)
{
try
{
if (!IsValid) return; else IsValid = false;
XmlNode quickActionRefNode = xNode.SelectSingleNode("QuickActionRef");
if (quickActionRefNode is null)
return;
QuickActionRef = cXmlParser.GetInnerTextFromXmlElement(quickActionRefNode);
if (QuickActionRef is null)
return;
XmlNode parametersNode = xNode.SelectSingleNode(ParametersNodeName);
if (parametersNode != null && parametersNode is XmlElement paramtersNodeElement)
ParseParametersElement(paramtersNodeElement, parser);
IsValid = true;
}
catch (Exception ex)
{
LogException(ex);
}
}
private void ParseParametersElement(XmlElement parametersNodeElement, cXmlParser parser)
{
try
{
parser.EnterElement(ParametersNodeName);
XmlNodeList parameterNodes = parametersNodeElement.SelectNodes(ParameterNodeName);
if (parameterNodes?.Count > 0)
parser.EnterElement(ParameterNodeName);
foreach (XmlElement parameterNode in parameterNodes)
{
ParseParameterElement(parameterNode, parser);
}
if (parameterNodes?.Count > 0)
parser.LeaveElement(ParameterNodeName);
}
catch (Exception ex)
{
LogException(ex);
}
finally
{
parser.LeaveElement(ParametersNodeName);
}
}
private void ParseParameterElement(XmlElement parameterNode, cXmlParser parser)
{
try
{
string parameterName = cXmlParser.GetStringFromXmlAttribute(parameterNode, "name");
string parameterVariable = cXmlParser.GetInnerTextFromXmlElement(parameterNode);
if (string.IsNullOrEmpty(parameterName) || string.IsNullOrEmpty(parameterVariable))
return;
if (Parameters.ContainsKey(parameterName))
LogEntry($"A paramter with name '{parameterName}' does allready exist.", C4IT.Logging.LogLevels.Warning);
else
Parameters.Add(parameterName, parameterVariable);
}
catch (Exception ex)
{
LogException(ex);
}
finally
{
parser.SelectElementNext();
}
}
}
}

View File

@@ -0,0 +1,101 @@
using C4IT.XML;
using F4SD.ActionConnector.Enumerations;
using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using static C4IT.Logging.cLogManager;
namespace F4SD.ActionConnector.Models
{
internal class Trigger
{
public TriggerEvent Event { get; set; }
public IDictionary<string, ActionDefinitionBase> Actions { get; } = new Dictionary<string, ActionDefinitionBase>();
public bool IsValid { get; private set; }
private const string ActionsNodeName = "Actions";
private const string ActionNodeName = "Action";
internal Trigger(XmlElement xNode, cXmlParser parser)
{
try
{
Event = cXmlParser.GetEnumFromAttribute(xNode, "action", TriggerEvent.Unknown);
XmlNode actionsNode = xNode.SelectSingleNode(ActionsNodeName);
if (actionsNode is null || !(actionsNode is XmlElement actionsNodeElement))
return;
ParseActionsNode(actionsNodeElement, parser);
IsValid = true; ;
}
catch (Exception ex)
{
LogException(ex);
}
}
private void ParseActionsNode(XmlElement actionsNode, cXmlParser parser)
{
try
{
parser.EnterElement(ActionsNodeName);
var actionNodes = actionsNode.SelectNodes(ActionNodeName);
if (actionNodes is null || actionNodes.Count == 0)
return;
parser.EnterElement(ActionNodeName);
foreach (XmlElement actionNode in actionNodes)
{
try
{
ActionType type = cXmlParser.GetEnumFromAttribute(actionNode, "type", ActionType.Unknown);
ActionDefinitionBase actionDefinition = null;
switch (type)
{
case ActionType.QuickAction:
actionDefinition = new QuickActionDefinition(actionNode, parser);
break;
case ActionType.HttpCall:
break;
case ActionType.Unknown:
case ActionType.Webhook:
default:
break;
}
if (actionDefinition?.Id != null && !Actions.ContainsKey(actionDefinition.Id))
Actions.Add(actionDefinition.Id, actionDefinition);
}
catch (Exception ex)
{
LogException(ex);
}
finally
{
parser.SelectElementNext();
}
}
parser.LeaveElement(ActionNodeName);
}
catch (Exception ex)
{
LogException(ex);
}
finally
{
parser.LeaveElement(ActionsNodeName);
}
}
}
}

View File

@@ -0,0 +1,9 @@
using F4SD.ActionConnector.Enumerations;
namespace F4SD.ActionConnector.Payloads
{
public sealed class ApplicationStartupPayload : PayloadBase
{
public override TriggerEvent Event => TriggerEvent.ApplicationStartup;
}
}

View File

@@ -0,0 +1,17 @@
using System;
using F4SD.ActionConnector.Enumerations;
namespace F4SD.ActionConnector.Payloads
{
public sealed class CaseClosedPayload : PayloadBase
{
public override TriggerEvent Event => TriggerEvent.CaseClosed;
public string Id { get; set; }
public string Subject { get; set; }
public string Priority { get; set; }
public Guid UserId { get; set; }
public string ClosedByUserName { get; set; }
public DateTime ClosedAt { get; set; }
}
}

View File

@@ -0,0 +1,14 @@
using System;
using F4SD.ActionConnector.Enumerations;
namespace F4SD.ActionConnector.Payloads
{
public sealed class CaseCreatedPayload : PayloadBase
{
public override TriggerEvent Event => TriggerEvent.CaseCreated;
public string Id { get; set; }
public string UserId { get; set; }
public DateTime CreatedAt { get; set; }
}
}

View File

@@ -0,0 +1,9 @@
using F4SD.ActionConnector.Enumerations;
namespace F4SD.ActionConnector.Payloads
{
public abstract class PayloadBase
{
public abstract TriggerEvent Event { get; }
}
}

View File

@@ -59,6 +59,7 @@ namespace F4SD_AdaptableIcon.Enums
misc_tool,
misc_user,
misc_user_disabled,
misc_disabledOverlay,
//StatusIcons
status_bad,

View File

@@ -519,6 +519,13 @@ namespace FasdDesktopUi.Basics.UserControls.AdaptableIcon
null
}
},
{ enumInternIcons.misc_disabledOverlay,
new PathGeometry[]
{
PathGeometry.CreateFromGeometry(Geometry.Parse("F1 M7.8133 5.364l-.844-.62L1.1307.4627c-.5307-.4014-1.1307.4013-.5987.8026L6.1253 5.364l.844.62 5.8387 4.2813c.536.396 1.1307-.412.5933-.808 Z")),
null
}
},
#endregion

View File

@@ -0,0 +1,41 @@
using F4SD.DocuEngine.DocuEngineDataProvider;
using F4SD.DocuEngine.DocuEngineParser;
using F4SD.DocuEngine.DocuEngineParser.ContentBlock;
using F4SD.DocuEngine.DocuEngineParser.Exceptions;
using System;
using System.Collections.Generic;
using System.Text;
namespace F4SD.DocuEngine
{
public class DocuEngine
{
public string DocuTemplate { get; private set; }
public IDocuEngineDataContainer Container { get; private set; }
private ParsingContext parsingContext;
public DocuEngine(string docuTemplate, IDocuEngineDataContainer container)
{
DocuTemplate = docuTemplate;
Container = container;
}
public string ParseTemplate()
{
var index = 0;
parsingContext = new ParsingContext();
ContentBlock contentBlock = new ContentBlock(DocuTemplate, Container);
try
{
parsingContext.Append(contentBlock.ParseContentBlock(ref index));
}
catch (DocuEngineParser.Exceptions.DocuEngineParserException parserException)
{
parsingContext.Append(parserException.CurrentText);
parsingContext.Append(parserException.PrintedErrorMessage);
return parsingContext.GetFinalText();
}
return parsingContext.GetFinalText();
}
}
}

View File

@@ -0,0 +1,93 @@
using C4IT.F4SD.DisplayFormatting;
using System.Collections.Generic;
using System.Linq;
namespace F4SD.DocuEngine.DocuEngineDataProvider
{
public class DocuEngineDataProperty : IDocuEngineDataProperty
{
public object Value { get; set; }
public RawValueType? ValueType { get; set; }
}
public class DocuEngineDataContainer : Dictionary<string, IDocuEngineDataToken>, IDocuEngineDataContainer
{
public IDocuEngineDataToken GetByName(string tokenName)
{
var tokenTree = tokenName.Split('.');
if (this.TryGetValue(tokenTree[0], out IDocuEngineDataToken token))
{
if (tokenTree.Length > 1)
{
switch (token)
{
case IDocuEngineDataContainer container:
return container.GetByName(string.Join(".", tokenTree.Skip(1)));
case IDocuEngineDataProperty property:
return property;
case IDocuEngineDataEnumeration enumeration:
return new DocuEngineDataProperty() { Value = tokenTree[0] };
default:
//Syntax Error
break;
}
}
else
{
switch (token)
{
case IDocuEngineDataContainer container:
return container;
case IDocuEngineDataProperty property:
return property;
case IDocuEngineDataEnumeration enumeration:
return enumeration;
default:
//Syntax Error
break;
}
}
return null;
}
else
{
//Syntax Error
return null;
}
}
public IDocuEngineDataContainer GetContainerByName(string containerName)
{
var token = GetByName(containerName);
if (token is IDocuEngineDataContainer container)
{
return container;
}
return null;
}
public IDocuEngineDataProperty GetPropertyByName(string propertyName)
{
var token = GetByName(propertyName);
if (token is IDocuEngineDataProperty property)
{
return property;
}
return null;
}
public IDocuEngineDataEnumeration GetEnumerationByName(string enumName)
{
var token = GetByName(enumName);
if (token is IDocuEngineDataEnumeration enumeration)
{
return enumeration;
}
return null;
}
}
public class DocuEngineDataEnumeration : List<IDocuEngineDataToken>, IDocuEngineDataEnumeration
{
}
}

View File

@@ -0,0 +1,31 @@
using C4IT.F4SD.DisplayFormatting;
using System.Collections.Generic;
namespace F4SD.DocuEngine.DocuEngineDataProvider
{
public interface IDocuEngineDataToken
{
}
public interface IDocuEngineDataIllegal : IDocuEngineDataToken
{
}
public interface IDocuEngineDataProperty : IDocuEngineDataToken
{
object Value { get; set; }
RawValueType? ValueType { get; set; }
}
public interface IDocuEngineDataContainer : IDocuEngineDataToken
{
IDocuEngineDataToken GetByName(string name);
IDocuEngineDataContainer GetContainerByName(string containerName);
IDocuEngineDataEnumeration GetEnumerationByName(string enumName);
IDocuEngineDataProperty GetPropertyByName(string propertyName);
}
public interface IDocuEngineDataEnumeration : IEnumerable<IDocuEngineDataToken>, IDocuEngineDataToken
{
}
}

View File

@@ -0,0 +1,227 @@
using C4IT.F4SD.DisplayFormatting;
using F4SD.DocuEngine.DocuEngineDataProvider;
using F4SD.DocuEngine.DocuEngineParser.Exceptions;
using System;
using System.Linq;
using System.Text.RegularExpressions;
namespace F4SD.DocuEngine.DocuEngineParser.ContentBlock
{
internal partial class ContentBlock
{
public string DocuTemplate { get; private set; }
public IDocuEngineDataContainer Container { get; private set; }
private ParsingContext parsingContext;
public ContentBlock(string DocuTemplate, IDocuEngineDataContainer Container)
{
this.DocuTemplate = DocuTemplate;
this.Container = Container;
parsingContext = new ParsingContext();
}
public string ParseContentBlock(ref int index)
{
bool isContentBlockFinished = false;
try
{
while (!isContentBlockFinished)
{
ParseTextBlock(ref index, out var finished);
if (finished)
{
isContentBlockFinished = true;
}
else
{
ParseCommandBlock(ref index);
}
}
}
catch (DocuEngineParserException parserException)
{
parsingContext.Append(parserException.CurrentText);
parserException.AddToCurrentText(parsingContext.GetFinalText());
throw;
}
return parsingContext.GetFinalText();
}
private void ParseTextBlock(ref int index, out bool isContextBlockFinished)
{
bool foundTrigger = false;
string[] keywords = { "@@", "[[", "]]", "{{", "}}" };
string pattern = string.Join("|", keywords.Select(Regex.Escape));
Regex regex = new Regex(pattern);
isContextBlockFinished = false;
while (!foundTrigger)
{
Match match = regex.Match(DocuTemplate, index);
if (match.Success)
{
parsingContext.Append(DocuTemplate.Substring(index, match.Index - index));
index = match.Index;
switch (match.Value)
{
case "@@":
if (DocuTemplate.Substring(index, 3).Equals("@@@"))
{
parsingContext.Append("@@");
index += 3;
}
else
{
foundTrigger = true;
index += 2;
}
break;
case "[[":
if (DocuTemplate.Substring(index, 3).Equals("[[["))
{
parsingContext.Append("[[");
index += 3;
}
else
{
index += 2;
SkipWhiteSpace(ref index);
string variableName = ParseVariableName(ref index);
SkipWhiteSpace(ref index);
if (DocuTemplate.ElementAtOrDefault(index).Equals(';'))
{
index++;
SkipWhiteSpace(ref index);
string displayTypeString = ParseDisplayType(ref index);
RawValueType? displayType = null;
if (Enum.TryParse(displayTypeString, true, out RawValueType parseDisplayType))
{
displayType = parseDisplayType;
}
parsingContext.Append(SubstituteVariable(variableName, displayType));
SkipWhiteSpace(ref index);
if (DocuTemplate.Substring(index, 2).Equals("]]") && !(DocuTemplate.Substring(index, 3).Equals("]]]")))
{
index += 2;
continue;
}
else
{
throw new SyntaxErrorException("]]", DocuTemplate.Substring(index, 3));
}
}
else if (DocuTemplate.Substring(index, 2).Equals("]]"))
{
parsingContext.Append(SubstituteVariable(variableName, null));
index += 2;
continue;
}
else
{
throw new SyntaxErrorException("]]\" / \";", DocuTemplate.Substring(index, 3));
}
}
break;
case "]]":
if (DocuTemplate.Substring(index, 3).Equals("]]]"))
{
parsingContext.Append("]]");
index += 3;
}
else
{
throw new SyntaxErrorException("]]]", "]]");
}
break;
case "{{":
if (DocuTemplate.Substring(index, 3).Equals("{{{"))
{
parsingContext.Append("{{");
index += 3;
}
else
{
throw new SyntaxErrorException("{{{", "{{");
}
break;
case "}}":
if (DocuTemplate.Substring(index, 3).Equals("}}}"))
{
parsingContext.Append("}}");
index += 3;
}
else if (DocuTemplate.Substring(index, 3).Equals("}};"))
{
foundTrigger = true;
isContextBlockFinished = true;
index += 3;
}
else
{
foundTrigger = true;
isContextBlockFinished = true;
index += 2;
}
break;
}
}
else
{
int length = DocuTemplate.Length - index;
if (length < 0)
{
length = 0;
}
else if (length > 15)
{
length = 15;
}
throw new SyntaxErrorException("}};", DocuTemplate.Substring(index, length));
}
}
}
private void ParseCommandBlock(ref int index)
{
string[] keywords = { "(" };
string pattern = string.Join("|", keywords.Select(Regex.Escape));
Regex regex = new Regex(pattern);
Match match = regex.Match(DocuTemplate, index);
if (match.Success)
{
string commandName = "";
commandName = DocuTemplate.Substring(index, match.Index - index);
index = match.Index + 1;
switch (commandName)
{
case "if":
ParseIfCommand(ref index);
break;
case "switch":
ParseSwitchCommand(ref index);
break;
case "foreach":
ParseForeachCommand(ref index);
break;
case "include":
//TODO
break;
}
}
else
{
int length = DocuTemplate.Length - index;
if (length < 0)
{
length = 0;
}
else if (length > 15)
{
length = 15;
}
throw new SyntaxErrorException("if\" / \"switch\" / \"foreach", DocuTemplate.Substring(index, length));
}
}
}
}

View File

@@ -0,0 +1,52 @@
using F4SD.DocuEngine.DocuEngineDataProvider;
using F4SD.DocuEngine.DocuEngineParser.Exceptions;
using System.ComponentModel;
using System.Linq;
namespace F4SD.DocuEngine.DocuEngineParser.ContentBlock
{
internal partial class ContentBlock
{
private void ParseForeachCommand(ref int index)
{
string variableName = "";
string enumName = "";
int tempIndex = 0;
SkipWhiteSpace(ref index);
variableName += ParseVariableName(ref index);
SkipWhiteSpace(ref index);
if (!(DocuTemplate.ElementAtOrDefault(index).Equals(';')))
{
throw new SyntaxErrorException(";", DocuTemplate.ElementAtOrDefault(index).ToString());
}
index++;
SkipWhiteSpace(ref index);
enumName += ParseVariableName(ref index);
SkipWhiteSpace(ref index);
if (!(DocuTemplate.ElementAtOrDefault(index).Equals(')')))
{
throw new SyntaxErrorException(")", DocuTemplate.ElementAtOrDefault(index).ToString());
}
index++;
SkipWhiteSpace(ref index);
if (!DocuTemplate.Substring(index, 2).Equals("{{"))
{
throw new SyntaxErrorException("{{", DocuTemplate.Substring(index, 2));
}
index += 2;
IDocuEngineDataEnumeration enumeration = Container.GetEnumerationByName(enumName);
if (enumeration == null)
{
//Syntax Error
}
tempIndex = index;
foreach (var token in enumeration)
{
index = tempIndex;
ContentBlock foreachBlock = new ContentBlock(DocuTemplate, new DocuEngineDataContainer() { { variableName, token } });
parsingContext.Append(foreachBlock.ParseContentBlock(ref index));
}
}
}
}

View File

@@ -0,0 +1,291 @@
using C4IT.F4SD.DisplayFormatting;
using F4SD.DocuEngine.DocuEngineParser.Exceptions;
using System.ComponentModel;
using System.Linq;
using System.Text.RegularExpressions;
namespace F4SD.DocuEngine.DocuEngineParser.ContentBlock
{
internal partial class ContentBlock
{
private string ParseVariableType(ref int index)
{
bool foundTypeEnd = false;
string variableType = "";
while (!foundTypeEnd)
{
char c = DocuTemplate.ElementAt(index);
if (char.IsLetter(c))
{
variableType += c;
index++;
}
else
{
foundTypeEnd = true;
}
}
return variableType;
}
private string ParseVariableName(ref int index)
{
bool foundVariableEnd = false;
string variableName = "";
while (!foundVariableEnd)
{
char c = DocuTemplate.ElementAt(index);
if (char.IsLetterOrDigit(c) || c == '.')
{
variableName += c;
index++;
}
else
{
foundVariableEnd = true;
}
}
return variableName;
}
private void SkipWhiteSpace(ref int index)
{
bool foundNonWhiteSpace = false;
while (!foundNonWhiteSpace)
{
char c = DocuTemplate.ElementAt(index);
if (char.IsWhiteSpace(c))
{
index++;
}
else
{
foundNonWhiteSpace = true;
}
}
}
private string ParseDisplayType(ref int index)
{
bool foundDisplayTypeEnd = false;
string displayType = "";
while (!foundDisplayTypeEnd)
{
char c = DocuTemplate.ElementAt(index);
if (char.IsLetterOrDigit(c))
{
displayType += c;
index++;
}
else
{
foundDisplayTypeEnd = true;
}
}
return displayType;
}
private string SubstituteVariable(string variableName, RawValueType? displayType)
{
IRawValueFormatter rawValueFormatter = new RawValueFormatter();
var property = Container.GetPropertyByName(variableName);
if (property == null)
{
//Syntax Error
}
if (displayType != null)
{
return rawValueFormatter.GetDisplayValue(property.Value, (RawValueType)displayType, null);
}
else if (property.ValueType != null)
{
return rawValueFormatter.GetDisplayValue(property.Value, (RawValueType)property.ValueType, null);
}
else
{
return property.Value.ToString();
}
}
private char ParseComparator(ref int index)
{
char comparator = DocuTemplate.ElementAtOrDefault(index);
if (!(comparator == '<' || comparator == '>' || comparator == '=' || comparator == '!'))
{
throw new SyntaxErrorException("<\" / \">\" / \"=\" / \"!", comparator.ToString());
}
index++;
return comparator;
}
private string ParseValue(ref int index)
{
string value = "";
string pattern = @"("")(.*?)(""\s*\))";
Regex regex = new Regex(pattern);
Match match = regex.Match(DocuTemplate, index);
if (match.Success)
{
value = match.Groups[2].Value;
string wholeMatch = match.Value.ToString();
index += wholeMatch.Length;
}
else
{
throw new SyntaxErrorException("\"VariableName\")", "");
}
return value;
}
private bool CheckCondition(string variableType, string variableName, char comparator, string value)
{
string stringVariable = "";
var property = Container.GetPropertyByName(variableName);
if (property == null)
{
//Syntax Error
}
stringVariable = property.Value.ToString();
switch (variableType)
{
case "string":
if (!(comparator == '=' || comparator == '!'))
{
throw new SyntaxErrorException("=\" / \"!", comparator.ToString());
}
return CompareStrings(stringVariable, comparator, value);
case "int":
if (!(comparator == '<' || comparator == '>' || comparator == '=' || comparator == '!'))
{
throw new SyntaxErrorException("<\" / \">\" / \"=\" / \"!", comparator.ToString());
}
if (!(int.TryParse(value, out var intValue)))
{
throw new VariableErrorException(value, "int");
}
stringVariable = stringVariable.Replace("\"", "");
if (!(int.TryParse(stringVariable, out var intVariable)))
{
throw new VariableErrorException(stringVariable, "int");
}
return CompareIntegers(intVariable, comparator, intValue);
case "bool":
if (!(comparator == '=' || comparator == '!'))
{
throw new SyntaxErrorException("=\" / \"!", comparator.ToString());
}
if (!(bool.TryParse(value, out var boolValue)))
{
throw new VariableErrorException(value, "bool");
}
if (!(bool.TryParse(stringVariable, out var boolVariable)))
{
throw new VariableErrorException(stringVariable, "bool");
}
return CompareBooleans(boolVariable, comparator, boolValue);
default:
throw new SyntaxErrorException("string\" / \"int\" / \"bool", variableType);
}
}
private bool CompareStrings(string variable, char comparator, string value)
{
switch (comparator)
{
case '=':
if (variable == value)
{
return true;
}
else
{
return false;
}
case '!':
if (variable != value)
{
return true;
}
else
{
return false;
}
default:
return false;
}
}
private bool CompareIntegers(int variable, char comparator, int value)
{
switch (comparator)
{
case '=':
if (variable == value)
{
return true;
}
else
{
return false;
}
case '!':
if (variable != value)
{
return true;
}
else
{
return false;
}
case '>':
if (variable > value)
{
return true;
}
else
{
return false;
}
case '<':
if (variable < value)
{
return true;
}
else
{
return false;
}
default:
return false;
}
}
private bool CompareBooleans(bool variable, char comparator, bool value)
{
switch (comparator)
{
case '=':
if (variable == value)
{
return true;
}
else
{
return false;
}
case '!':
if (variable != value)
{
return true;
}
else
{
return false;
}
default:
return false;
}
}
}
}

View File

@@ -0,0 +1,175 @@
using F4SD.DocuEngine.DocuEngineParser.Exceptions;
using System.ComponentModel;
using System.Linq;
namespace F4SD.DocuEngine.DocuEngineParser.ContentBlock
{
internal partial class ContentBlock
{
private void ParseIfCommand(ref int index)
{
string tempString = "";
bool foundTrueCondition = false;
bool foundAllElseIf = false;
int tempIndex = 0;
tempString = ParseIfBlock(ref index, out var isIfConditionTrue);
if (isIfConditionTrue)
{
foundTrueCondition = true;
parsingContext.Append(tempString);
}
tempString = "";
while (!foundAllElseIf)
{
tempIndex = index;
SkipWhiteSpace(ref index);
if (DocuTemplate.Substring(index, 7).Equals("else-if"))
{
index += 7;
tempString += ParseElseIfBlock(ref index, out var isElseIfConditionTrue);
if (isElseIfConditionTrue && !foundTrueCondition)
{
foundTrueCondition = true;
parsingContext.Append(tempString);
}
tempString = "";
}
else
{
foundAllElseIf = true;
index = tempIndex;
}
}
tempIndex = index;
SkipWhiteSpace(ref index);
if (DocuTemplate.Substring(index, 4).Equals("else"))
{
index += 4;
tempString += ParseElseBlock(ref index);
if (!foundTrueCondition)
{
foundTrueCondition = true;
parsingContext.Append(tempString);
tempString = "";
}
}
else
{
index = tempIndex;
}
}
private string ParseIfBlock(ref int index, out bool isConditionTrue)
{
string returnString = "";
string variableType = "";
string variableName = "";
char comparator;
string value = "";
SkipWhiteSpace(ref index);
variableType += ParseVariableType(ref index);
SkipWhiteSpace(ref index);
if (!(DocuTemplate.ElementAtOrDefault(index).Equals(';')))
{
throw new SyntaxErrorException(";", DocuTemplate.ElementAtOrDefault(index).ToString());
}
index++;
SkipWhiteSpace(ref index);
variableName += ParseVariableName(ref index);
SkipWhiteSpace(ref index);
if (!(DocuTemplate.ElementAtOrDefault(index).Equals(';')))
{
throw new SyntaxErrorException(";", DocuTemplate.ElementAtOrDefault(index).ToString());
}
index++;
SkipWhiteSpace(ref index);
comparator = ParseComparator(ref index);
SkipWhiteSpace(ref index);
if (!(DocuTemplate.ElementAtOrDefault(index).Equals(';')))
{
throw new SyntaxErrorException(";", DocuTemplate.ElementAtOrDefault(index).ToString());
}
index++;
SkipWhiteSpace(ref index);
value += ParseValue(ref index);
SkipWhiteSpace(ref index);
isConditionTrue = CheckCondition(variableType, variableName, comparator, value);
SkipWhiteSpace(ref index);
if (!DocuTemplate.Substring(index, 2).Equals("{{"))
{
throw new SyntaxErrorException("{{", DocuTemplate.Substring(index, 2));
}
index += 2;
ContentBlock contentBlock = new ContentBlock(DocuTemplate, Container);
returnString += contentBlock.ParseContentBlock(ref index);
return returnString;
}
private string ParseElseIfBlock(ref int index, out bool isConditionTrue)
{
string returnString = "";
string variableType = "";
string variableName = "";
char comparator;
string value = "";
SkipWhiteSpace(ref index);
if (!(DocuTemplate.ElementAt(index).Equals('(')))
{
throw new SyntaxErrorException("(", DocuTemplate.ElementAtOrDefault(index).ToString());
}
index++;
SkipWhiteSpace(ref index);
variableType += ParseVariableType(ref index);
SkipWhiteSpace(ref index);
if (!(DocuTemplate.ElementAtOrDefault(index).Equals(';')))
{
throw new SyntaxErrorException(";", DocuTemplate.ElementAtOrDefault(index).ToString());
}
index++;
SkipWhiteSpace(ref index);
variableName += ParseVariableName(ref index);
SkipWhiteSpace(ref index);
if (!(DocuTemplate.ElementAtOrDefault(index).Equals(';')))
{
throw new SyntaxErrorException(";", DocuTemplate.ElementAtOrDefault(index).ToString());
}
index++;
SkipWhiteSpace(ref index);
comparator = ParseComparator(ref index);
SkipWhiteSpace(ref index);
if (!(DocuTemplate.ElementAtOrDefault(index).Equals(';')))
{
throw new SyntaxErrorException(";", DocuTemplate.ElementAtOrDefault(index).ToString());
}
index++;
SkipWhiteSpace(ref index);
value += ParseValue(ref index);
SkipWhiteSpace(ref index);
isConditionTrue = CheckCondition(variableType, variableName, comparator, value);
SkipWhiteSpace(ref index);
if (!DocuTemplate.Substring(index, 2).Equals("{{"))
{
throw new SyntaxErrorException("{{", DocuTemplate.Substring(index, 2));
}
index += 2;
ContentBlock contentBlock = new ContentBlock(DocuTemplate, Container);
returnString += contentBlock.ParseContentBlock(ref index);
return returnString;
}
private string ParseElseBlock(ref int index)
{
string returnString = "";
SkipWhiteSpace(ref index);
if (!DocuTemplate.Substring(index, 2).Equals("{{"))
{
throw new SyntaxErrorException("{{", DocuTemplate.Substring(index, 2));
}
index += 2;
ContentBlock contentBlock = new ContentBlock(DocuTemplate, Container);
returnString += contentBlock.ParseContentBlock(ref index);
return returnString;
}
}
}

View File

@@ -0,0 +1,127 @@
using F4SD.DocuEngine.DocuEngineParser.Exceptions;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
namespace F4SD.DocuEngine.DocuEngineParser.ContentBlock
{
internal partial class ContentBlock
{
private void ParseSwitchCommand(ref int index)
{
string tempString = "";
bool foundTrueCase = false;
bool foundAllCases = false;
int tempIndex = 0;
(string VariableType, string VariableName) switchVariable = ParseSwitchBlock(ref index);
while (!foundAllCases)
{
tempIndex = index;
SkipWhiteSpace(ref index);
if (DocuTemplate.Substring(index, 4).Equals("case"))
{
index += 4;
tempString += ParseCaseBlock(ref index, switchVariable, out var isCaseTrue);
if (isCaseTrue && !foundTrueCase)
{
foundTrueCase = true;
parsingContext.Append(tempString);
}
tempString = "";
}
else
{
foundAllCases = true;
index = tempIndex;
}
}
tempIndex = index;
SkipWhiteSpace(ref index);
if (DocuTemplate.Substring(index, 7).Equals("default"))
{
index += 7;
tempString += ParseDefaultBlock(ref index);
if (!foundTrueCase)
{
foundTrueCase = true;
parsingContext.Append(tempString);
tempString = "";
}
}
else
{
index = tempIndex;
}
}
private (string VariableType, string VariableName) ParseSwitchBlock(ref int index)
{
string variableName = "";
string variableType = "";
SkipWhiteSpace(ref index);
variableType = ParseVariableType(ref index);
SkipWhiteSpace(ref index);
if (!(DocuTemplate.ElementAtOrDefault(index).Equals(';')))
{
throw new SyntaxErrorException(";", DocuTemplate.ElementAtOrDefault(index).ToString());
}
index++;
SkipWhiteSpace(ref index);
variableName = ParseVariableName(ref index);
SkipWhiteSpace(ref index);
if (!(DocuTemplate.ElementAtOrDefault(index).Equals(')')))
{
throw new SyntaxErrorException(")", DocuTemplate.ElementAtOrDefault(index).ToString());
}
index++;
SkipWhiteSpace(ref index);
if (!DocuTemplate.Substring(index, 2).Equals("{{"))
{
throw new SyntaxErrorException("{{", DocuTemplate.Substring(index, 2));
}
index += 2;
return (variableType, variableName);
}
private string ParseCaseBlock(ref int index, (string VariableType, string VariableName) switchVariable, out bool isCaseTrue)
{
string returnString = "";
isCaseTrue = false;
string value = "";
SkipWhiteSpace(ref index);
if (!(DocuTemplate.ElementAtOrDefault(index).Equals('(')))
{
throw new SyntaxErrorException("(", DocuTemplate.ElementAtOrDefault(index).ToString());
}
index++;
SkipWhiteSpace(ref index);
value = ParseValue(ref index);
isCaseTrue = CheckCondition(switchVariable.VariableType, switchVariable.VariableName, '=', value);
SkipWhiteSpace(ref index);
if (!DocuTemplate.Substring(index, 2).Equals("{{"))
{
throw new SyntaxErrorException("{{", DocuTemplate.Substring(index, 2));
}
index += 2;
ContentBlock contentBlock = new ContentBlock(DocuTemplate, Container);
returnString += contentBlock.ParseContentBlock(ref index);
return returnString;
}
private string ParseDefaultBlock(ref int index)
{
string returnString = "";
SkipWhiteSpace(ref index);
if (!DocuTemplate.Substring(index, 2).Equals("{{"))
{
throw new SyntaxErrorException("{{", DocuTemplate.Substring(index, 2));
}
index += 2;
ContentBlock contentBlock = new ContentBlock(DocuTemplate, Container);
returnString += contentBlock.ParseContentBlock(ref index);
return returnString;
}
}
}

View File

@@ -0,0 +1,15 @@
using System;
namespace F4SD.DocuEngine.DocuEngineParser
{
internal abstract class DocuEngineParserException : Exception
{
public string CurrentText { get; protected set; }
public string PrintedErrorMessage { get; protected set; }
public DocuEngineParserException(string message) : base(message)
{
}
public abstract void AddToCurrentText(string text);
}
}

View File

@@ -0,0 +1,15 @@
using System;
namespace F4SD.DocuEngine.DocuEngineParser.Exceptions
{
internal abstract class DocuEngineParserException : Exception
{
public string CurrentText { get; protected set; }
public string PrintedErrorMessage { get; protected set; }
public DocuEngineParserException(string message) : base(message)
{
}
public abstract void AddToCurrentText(string text);
}
}

View File

@@ -0,0 +1,26 @@
namespace F4SD.DocuEngine.DocuEngineParser.Exceptions
{
internal class SyntaxErrorException : DocuEngineParserException
{
private const string errorMessageTemplate = "<<Syntax Error | Expected Characters: \"{0}\" | Actual Characters: \"{1}\">>";
private const string defaultMessage = "You're syntax is wrong. Get good.";
public SyntaxErrorException(string currentText) : base(defaultMessage)
{
CurrentText = currentText;
}
public SyntaxErrorException(string expected, string actual) : base(defaultMessage)
{
PrintedErrorMessage = string.Format(errorMessageTemplate, expected, actual);
}
public SyntaxErrorException(string currentText, string expected, string actual) : base(defaultMessage)
{
CurrentText = currentText;
PrintedErrorMessage = string.Format(errorMessageTemplate, expected, actual);
}
public override void AddToCurrentText(string text)
{
CurrentText = text + CurrentText;
}
}
}

View File

@@ -0,0 +1,25 @@
namespace F4SD.DocuEngine.DocuEngineParser.Exceptions
{
internal class VariableErrorException : DocuEngineParserException
{
private const string errorMessageTemplate = "<<Variable Error | \"{0}\" can't be converted to type \"{1}\">>";
private const string defaultMessage = "Your variable is the wrong type. Get good.";
public VariableErrorException(string currentText) : base(defaultMessage)
{
CurrentText = currentText;
}
public VariableErrorException(string expected, string actual) : base(defaultMessage)
{
PrintedErrorMessage = string.Format(errorMessageTemplate, expected, actual);
}
public VariableErrorException(string currentText, string expected, string actual) : base(defaultMessage)
{
CurrentText = currentText;
PrintedErrorMessage = string.Format(errorMessageTemplate, expected, actual);
}
public override void AddToCurrentText(string text)
{
CurrentText = text + CurrentText;
}
}
}

View File

@@ -0,0 +1,13 @@
using System.Text;
namespace F4SD.DocuEngine.DocuEngineParser
{
internal class ParsingContext
{
private readonly StringBuilder _stringBuilder = new StringBuilder();
public void Append(string text) => _stringBuilder.Append(text);
public string GetFinalText() => _stringBuilder.ToString();
}
}

View File

@@ -0,0 +1,26 @@
namespace F4SD.DocuEngine.DocuEngineParser
{
internal class SyntaxErrorException : DocuEngineParserException
{
private const string errorMessageTemplate = "<<Syntax Error | Expected Characters: \"{0}\" | Actual Characters: \"{1}\">>";
private const string defaultMessage = "You're syntax is wrong. Get good.";
public SyntaxErrorException(string currentText) : base(defaultMessage)
{
CurrentText = currentText;
}
public SyntaxErrorException(string expected, string actual) : base(defaultMessage)
{
PrintedErrorMessage = string.Format(errorMessageTemplate, expected, actual);
}
public SyntaxErrorException(string currentText, string expected, string actual) : base(defaultMessage)
{
CurrentText = currentText;
PrintedErrorMessage = string.Format(errorMessageTemplate, expected, actual);
}
public override void AddToCurrentText(string text)
{
CurrentText = text + CurrentText;
}
}
}

View File

@@ -0,0 +1,25 @@
namespace F4SD.DocuEngine.DocuEngineParser
{
internal class VariableErrorException : DocuEngineParserException
{
private const string errorMessageTemplate = "<<Variable Error | \"{0}\" can't be converted to type \"{1}\">>";
private const string defaultMessage = "Your variable is the wrong type. Get good.";
public VariableErrorException(string currentText) : base(defaultMessage)
{
CurrentText = currentText;
}
public VariableErrorException(string expected, string actual) : base(defaultMessage)
{
PrintedErrorMessage = string.Format(errorMessageTemplate, expected, actual);
}
public VariableErrorException(string currentText, string expected, string actual) : base(defaultMessage)
{
CurrentText = currentText;
PrintedErrorMessage = string.Format(errorMessageTemplate, expected, actual);
}
public override void AddToCurrentText(string text)
{
CurrentText = text + CurrentText;
}
}
}

View File

@@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace>F4SD.DocuEngine</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="C4IT.F4SD.DisplayFormatting" Version="1.0.0" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,13 @@
namespace F4SD.Gamification
{
public enum CockpitAction
{
CaseOpened,
CaseClosed,
QuickActionExecuted,
CopyTemplateClicked,
BuiltDirectConnection,
AddNotes,
StartRemoteConnection,
}
}

View File

@@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace>F4SD.Gamification</RootNamespace>
</PropertyGroup>
<PropertyGroup>
<LangVersion>9.0</LangVersion>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,10 @@
namespace F4SD.Gamification
{
public class LevelEventArgs
{
public int CurrentLevel { get; set; }
public string LevelTitle { get; set; }
public int CurrentXp { get; set; }
public int XpRequiredForLevelUp { get; set; }
}
}

View File

@@ -0,0 +1,25 @@
using System;
namespace F4SD.Gamification.Services
{
public static class GamificationService
{
private static readonly LevelService _levelService = new();
public static void Initialize()
{
PersistenceService.Initialize();
foreach (var action in PersistenceService.GetActions())
{
_levelService.InitializeAction(action.Action);
}
}
public static void TrackAction(CockpitAction action, params object[] args)
{
PersistenceService.Persist(action);
_levelService.TrackAction(action);
}
}
}

View File

@@ -0,0 +1,178 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Numerics;
using System.Security.Cryptography;
namespace F4SD.Gamification.Services
{
public class LevelService
{
private static readonly List<(int MinimumTotalExperiencePoints, int Level)> _experienceToLevelMap = new()
{
(0,1),
(1128,2),
(2400,3),
(3832,4),
(5440,5),
(7240,6),
(9248,7),
(11480,8),
(13952,9),
(16680,10),
(19680,11),
(22968,12),
(26560,13),
(30472,14),
(34720,15),
(39320,16),
(44288,17),
(49640,18),
(55392,19),
(61560,20),
(68160,21),
(75208,22),
(82720,23),
(90712,24),
(99200,25),
(108200,26),
(117728,27),
(127800,28),
(138432,29),
(149640,30),
(161440,31),
(173848,32),
(186880,33),
(200552,34),
(214880,35),
(229880,36),
(245568,37),
(261960,38),
(279072,39),
(296920,40),
(315520,41),
(334888,42),
(355040,43),
(375992,44),
(397760,45),
(420360,46),
(443808,47),
(468120,48),
(493312,49),
(519400,50),
};
private int _totalExperiencePoints = 0;
private int _currentExperiencePoints;
private int _level;
internal void InitializeAction(CockpitAction action)
{
AddExperiencePoints(GetExperiencePointsFor(action), true);
}
internal void TrackAction(CockpitAction type, params object[] args)
{
AddExperiencePoints(GetExperiencePointsFor(type));
}
private void AddExperiencePoints(int points, bool isInitialization = false)
{
_totalExperiencePoints += points;
UpdateLevel(isInitialization);
UpdateCurrentExperiencePoints();
if (isInitialization)
return;
ExperiencePointsChanged?.Invoke(null, new LevelEventArgs()
{
CurrentLevel = _level,
LevelTitle = GetTitle(_level),
CurrentXp = _currentExperiencePoints,
XpRequiredForLevelUp = GetXpRequiredForNextLevel()
});
}
private void UpdateCurrentExperiencePoints()
{
int xpForCurrentLevel = _experienceToLevelMap.FirstOrDefault(mapEntry => mapEntry.Level == _level).MinimumTotalExperiencePoints;
_currentExperiencePoints = _totalExperiencePoints - xpForCurrentLevel;
}
private void UpdateLevel(bool isInitialization)
{
int previousLevel = _level;
_level = _experienceToLevelMap.FirstOrDefault(mapEntry => _totalExperiencePoints < mapEntry.MinimumTotalExperiencePoints).Level - 1;
if (!isInitialization && previousLevel < _level)
LevelChanged?.Invoke(null, new LevelEventArgs() { CurrentLevel = _level, LevelTitle = GetTitle(_level) });
}
private int GetXpRequiredForNextLevel()
{
int totalXpForNextLevel = _experienceToLevelMap.FirstOrDefault(mapEntry => mapEntry.Level == _level + 1).MinimumTotalExperiencePoints;
int totalXpForCurrentLevel = _experienceToLevelMap.FirstOrDefault(mapEntry => mapEntry.Level == _level).MinimumTotalExperiencePoints;
return totalXpForNextLevel - totalXpForCurrentLevel;
}
private int GetExperiencePointsFor(CockpitAction action)
{
switch (action)
{
case CockpitAction.CaseOpened:
break;
case CockpitAction.CaseClosed:
return 10;
case CockpitAction.QuickActionExecuted:
return 20;
case CockpitAction.CopyTemplateClicked:
return 5;
case CockpitAction.BuiltDirectConnection:
break;
case CockpitAction.AddNotes:
break;
case CockpitAction.StartRemoteConnection:
return 50;
default:
break;
}
return 1;
}
private static string GetTitle(int level)
{
switch (level)
{
case <= 5:
return "Support Trainee";
case <= 10:
return "Response Assistant";
case <= 15:
return "Service Aider";
case <= 20:
return "Ticket Resolver";
case <= 25:
return "Customer Navigator";
case <= 30:
return "Incident Coordinator";
case <= 35:
return "IT Guy";
case <= 40:
return "Support Specialist";
case <= 45:
return "Service Mentor";
case <= 49:
return "Operations Lead";
case > 49:
return "Ticket Titan";
}
}
public static event EventHandler<LevelEventArgs> ExperiencePointsChanged;
public static event EventHandler<LevelEventArgs> LevelChanged;
}
}

View File

@@ -0,0 +1,70 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace F4SD.Gamification.Services
{
internal static class PersistenceService
{
private static readonly string _databaseName = "F4SD-gmfc.txt";
private static readonly string _directory = $@"{Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)}\Consulting4IT GmbH\C4IT First Aid Service Desk";
private static string GetConnectionString()
{
return Path.Combine(_directory, _databaseName);
}
internal static void Persist(CockpitAction action)
{
Initialize();
using var writer = File.AppendText(GetConnectionString());
string actionLine = $"{DateTime.UtcNow},{action}";
string base64 = Convert.ToBase64String(Encoding.UTF8.GetBytes(actionLine));
writer.WriteLine(base64);
}
internal static void Initialize()
{
Directory.CreateDirectory(_directory);
if (!File.Exists(GetConnectionString()))
{
FileStream createdFile = File.Create(GetConnectionString());
File.SetAttributes(GetConnectionString(), FileAttributes.Hidden);
createdFile.Close();
}
}
internal static IEnumerable<(DateTime Time, CockpitAction Action)> GetActions()
{
using StreamReader reader = new(GetConnectionString());
string line;
while ((line = reader.ReadLine()) != null)
{
string encodedLine = string.Empty;
try { encodedLine = Encoding.UTF8.GetString(Convert.FromBase64String(line)); }
catch { continue; }
if (string.IsNullOrWhiteSpace(encodedLine))
continue;
string[] splittedLine = encodedLine.Split(',');
if (splittedLine.Length < 2)
continue;
if (!DateTime.TryParse(splittedLine[0], out var date))
continue;
if (!(Enum.TryParse(splittedLine[1], out CockpitAction action)))
continue;
yield return new(date, action);
}
}
}
}

View File

@@ -67,7 +67,7 @@
<HintPath>.\Interop.CLMgr.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.13.0.4\lib\net45\Newtonsoft.Json.dll</HintPath>
<HintPath>..\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Newtonsoft.Json" version="13.0.4" targetFramework="net472" />
<package id="Newtonsoft.Json" version="13.0.3" targetFramework="net472" />
</packages>

View File

@@ -0,0 +1,44 @@
using C4IT.FASD.Base;
using C4IT.MultiLanguage;
using FasdDesktopUi.Basics;
using FasdDesktopUi.Basics.Models;
using FasdDesktopUi.Basics.Services.SupportCase.Controllers;
using FasdDesktopUi.Basics.UiActions;
namespace F4SD.Cockpit.Client.Test.Basics.Sevices.SupportCase.Controllers;
public class MenuDataFactoryTest
{
public MenuDataFactoryTest()
{
cMultiLanguageSupport.CurrentLanguage = "EN";
}
[Fact]
public async Task Create_From_QuickActionDefinition()
{
cF4sdQuickActionRemoteComputer menuDataDefinition = new()
{
Id = Guid.NewGuid(),
Names = new() { ["EN"] = "Foo EN", ["DE"] = "Foo DE" },
Icon = { IconType = enumIconType.intern, Name = nameof(enumInternIcon.misc_computer) },
Descriptions = new() { ["EN"] = "Bar EN", ["DE"] = "Bar DE" },
AlternativeDescriptions = new() { ["EN"] = "FooBar EN", ["DE"] = "FooBar DE" },
Section = "Section 1",
Sections = ["Section 2", "Section 3"],
IsHidden = false
};
cMenuDataBase expected = new()
{
MenuText = "Foo EN",
MenuIcon = new cMenuDataBase.MenuIconInfo(new(F4SD_AdaptableIcon.Enums.enumInternIcons.misc_computer), null, false, null),
MenuSections = ["Section 1", "Section 2", "Section 3"],
UiAction = new cUiRemoteQuickAction(menuDataDefinition) { DisplayType = FasdDesktopUi.Basics.Enums.enumActionDisplayType.enabled }
};
cMenuDataBase actual = MenuDataFactory.Create(menuDataDefinition, [], []);
Assert.Equivalent(expected, actual);
}
}

View File

@@ -0,0 +1,224 @@
using C4IT.FASD.Base;
using C4IT.MultiLanguage;
using FasdDesktopUi.Basics.Services.Models;
using FasdDesktopUi.Basics.Services.SupportCase;
using FasdDesktopUi.Basics.Services.SupportCase.Processors;
using FasdDesktopUi.Basics.UiActions;
using NSubstitute;
using System.Globalization;
namespace F4SD.Cockpit.Client.Test.Basics.Sevices.SupportCase;
public class SupportCaseProcessorTest
{
private readonly SupportCaseProcessor _supportCaseProcessor;
private readonly ISupportCase _mockSupportCase = Substitute.For<ISupportCase>();
private static readonly cMultiLanguageDictionary _stateTitle = new() { ["EN"] = "Bar", ["DE"] = "Bar (DE)" };
public SupportCaseProcessorTest()
{
_supportCaseProcessor = new SupportCaseProcessor();
_supportCaseProcessor.SetSupportCase(_mockSupportCase);
cMultiLanguageSupport.CurrentLanguage = "EN";
cF4SDCockpitXmlConfig.Instance = new() { HealthCardConfig = new() { SearchResultAge = 14 } };
}
[Theory]
[MemberData(nameof(GetCockpitValueDisplayDataTestData_StateInfo))]
[MemberData(nameof(GetCockpitValueDisplayDataTestData_StateLevel))]
[MemberData(nameof(GetCockpitValueDisplayDataTestData_StateTranslation))]
[MemberData(nameof(GetCockpitValueDisplayDataTestData_StateDateTime))]
[MemberData(nameof(GetCockpitValueDisplayDataTestData_StateVersion))]
[MemberData(nameof(GetCockpitValueDisplayDataTestData_StateDetails))]
public void Test_CockpitValueDisplayData(MockState mockState, CockpitValueDisplayData expected)
{
_mockSupportCase.GetSupportCaseHealthcardData(default, default, Arg.Any<bool>()).ReturnsForAnyArgs([mockState.RawData]);
CockpitValueDisplayData actual = _supportCaseProcessor.GetCockpitValueDisplayData(mockState.StateDefinition, default, default);
Assert.Equivalent(expected, actual);
}
public static IEnumerable<TheoryDataRow<MockState, CockpitValueDisplayData>> GetCockpitValueDisplayDataTestData_StateInfo()
{
yield return (new(new cHealthCardStateInfo() { Names = _stateTitle, IsNotTransparent = false }, "Foo"), new() { Title = "Bar", Values = ["Foo"], Levels = [enumHealthCardStateLevel.None] });
yield return (new(new cHealthCardStateInfo() { Names = _stateTitle, IsNotTransparent = true }, "Foo"), new() { Title = "Bar", Values = ["Foo"], Levels = [enumHealthCardStateLevel.Info] });
}
public static IEnumerable<TheoryDataRow<MockState, CockpitValueDisplayData>> GetCockpitValueDisplayDataTestData_StateLevel()
{
yield return (new(new cHealthCardStateLevel() { Names = _stateTitle, Warning = 6, Error = 7, IsDirectionUp = true, IsNotTransparent = false }, 5), new() { Title = "Bar", Values = ["5"], Levels = [enumHealthCardStateLevel.None] });
yield return (new(new cHealthCardStateLevel() { Names = _stateTitle, Warning = 6, Error = 7, IsDirectionUp = true, IsNotTransparent = true }, 5), new() { Title = "Bar", Values = ["5"], Levels = [enumHealthCardStateLevel.Ok] });
yield return (new(new cHealthCardStateLevel() { Names = _stateTitle, Warning = 6, Error = 7, IsDirectionUp = true, IsNotTransparent = true }, 6), new() { Title = "Bar", Values = ["6"], Levels = [enumHealthCardStateLevel.Warning] });
yield return (new(new cHealthCardStateLevel() { Names = _stateTitle, Warning = 6, Error = 7, IsDirectionUp = true, IsNotTransparent = true }, 7), new() { Title = "Bar", Values = ["7"], Levels = [enumHealthCardStateLevel.Error] });
yield return (new(new cHealthCardStateLevel() { Names = _stateTitle, Warning = 4, Error = 2, IsDirectionUp = false, IsNotTransparent = false }, 5), new() { Title = "Bar", Values = ["5"], Levels = [enumHealthCardStateLevel.None] });
yield return (new(new cHealthCardStateLevel() { Names = _stateTitle, Warning = 4, Error = 2, IsDirectionUp = false, IsNotTransparent = true }, 5), new() { Title = "Bar", Values = ["5"], Levels = [enumHealthCardStateLevel.Ok] });
yield return (new(new cHealthCardStateLevel() { Names = _stateTitle, Warning = 4, Error = 2, IsDirectionUp = false, IsNotTransparent = true }, 4), new() { Title = "Bar", Values = ["4"], Levels = [enumHealthCardStateLevel.Warning] });
yield return (new(new cHealthCardStateLevel() { Names = _stateTitle, Warning = 4, Error = 2, IsDirectionUp = false, IsNotTransparent = true }, 2), new() { Title = "Bar", Values = ["2"], Levels = [enumHealthCardStateLevel.Error] });
}
public static IEnumerable<TheoryDataRow<MockState, CockpitValueDisplayData>> GetCockpitValueDisplayDataTestData_StateTranslation()
{
cF4SDCockpitXmlConfig translationConfigNode = new() { HealthCardConfig = new cF4SDHealthCardConfig() };
translationConfigNode.Translations.Add("MyTranslation", new cHealthCardTranslator()
{
DefaultTranslation = new() { Translation = new() { ["EN"] = "Foo translated" }, StateLevel = enumHealthCardStateLevel.Error },
Translations = [
new() { Translation = new() { ["EN"] = "Foo Info translated"}, Values = ["Foo Info"], StateLevel = enumHealthCardStateLevel.Info},
new() { Translation = new() { ["EN"] = "Foo Ok translated"}, Values = ["Foo Ok"], StateLevel = enumHealthCardStateLevel.Ok},
new() { Translation = new() { ["EN"] = "Foo Warning translated"}, Values = ["Foo Warning"], StateLevel = enumHealthCardStateLevel.Warning},
new() { Translation = new() { ["EN"] = "Foo Error translated"}, Values = ["Foo Error"], StateLevel = enumHealthCardStateLevel.Error},
]
});
yield return (new(new cHealthCardStateTranslation(translationConfigNode) { Names = _stateTitle, Translation = "MyTranslation", IsNotTransparent = false }, "Foo"), new() { Title = "Bar", Values = ["Foo translated"], Levels = [enumHealthCardStateLevel.Error] });
yield return (new(new cHealthCardStateTranslation(translationConfigNode) { Names = _stateTitle, Translation = "MyTranslation", IsNotTransparent = false }, "Foo Ok"), new() { Title = "Bar", Values = ["Foo Ok translated"], Levels = [enumHealthCardStateLevel.None] });
yield return (new(new cHealthCardStateTranslation(translationConfigNode) { Names = _stateTitle, Translation = "MyTranslation", IsNotTransparent = true }, "Foo Ok"), new() { Title = "Bar", Values = ["Foo Ok translated"], Levels = [enumHealthCardStateLevel.Ok] });
yield return (new(new cHealthCardStateTranslation(translationConfigNode) { Names = _stateTitle, Translation = "MyTranslation", IsNotTransparent = false }, "Foo Info"), new() { Title = "Bar", Values = ["Foo Info translated"], Levels = [enumHealthCardStateLevel.None] });
yield return (new(new cHealthCardStateTranslation(translationConfigNode) { Names = _stateTitle, Translation = "MyTranslation", IsNotTransparent = true }, "Foo Info"), new() { Title = "Bar", Values = ["Foo Info translated"], Levels = [enumHealthCardStateLevel.Info] });
yield return (new(new cHealthCardStateTranslation(translationConfigNode) { Names = _stateTitle, Translation = "MyTranslation", IsNotTransparent = true }, "Foo Warning"), new() { Title = "Bar", Values = ["Foo Warning translated"], Levels = [enumHealthCardStateLevel.Warning] });
yield return (new(new cHealthCardStateTranslation(translationConfigNode) { Names = _stateTitle, Translation = "MyTranslation", IsNotTransparent = true }, "Foo Error"), new() { Title = "Bar", Values = ["Foo Error translated"], Levels = [enumHealthCardStateLevel.Error] });
yield return (new(new cHealthCardStateTranslation(translationConfigNode) { Names = _stateTitle, Translation = "NoTranslation", IsNotTransparent = false }, "Foo Ok"), new() { Title = "Bar", Values = [null], Levels = [enumHealthCardStateLevel.None] });
}
public static IEnumerable<TheoryDataRow<MockState, CockpitValueDisplayData>> GetCockpitValueDisplayDataTestData_StateDateTime()
{
yield return (new(new cHealthCardStateDateTime() { Names = _stateTitle, WarningHours = 6, ErrorHours = 7, IsDirectionUp = true, IsNotTransparent = false }, DateTime.UtcNow.ToString(CultureInfo.CurrentCulture)), new() { Title = "Bar", Values = [DateTime.UtcNow.ToString(CultureInfo.CurrentCulture)], Levels = [enumHealthCardStateLevel.None] });
yield return (new(new cHealthCardStateDateTime() { Names = _stateTitle, WarningHours = 6, ErrorHours = 7, IsDirectionUp = true, IsNotTransparent = true }, DateTime.UtcNow.ToString(CultureInfo.CurrentCulture)), new() { Title = "Bar", Values = [DateTime.UtcNow.ToString(CultureInfo.CurrentCulture)], Levels = [enumHealthCardStateLevel.Ok] });
yield return (new(new cHealthCardStateDateTime() { Names = _stateTitle, WarningHours = 6, ErrorHours = 7, IsDirectionUp = true, IsNotTransparent = true }, DateTime.UtcNow.AddHours(-6).ToString(CultureInfo.CurrentCulture)), new() { Title = "Bar", Values = [DateTime.UtcNow.AddHours(-6).ToString(CultureInfo.CurrentCulture)], Levels = [enumHealthCardStateLevel.Warning] });
yield return (new(new cHealthCardStateDateTime() { Names = _stateTitle, WarningHours = 6, ErrorHours = 7, IsDirectionUp = true, IsNotTransparent = true }, DateTime.UtcNow.AddHours(-7).ToString(CultureInfo.CurrentCulture)), new() { Title = "Bar", Values = [DateTime.UtcNow.AddHours(-7).ToString(CultureInfo.CurrentCulture)], Levels = [enumHealthCardStateLevel.Error] });
yield return (new(new cHealthCardStateDateTime() { Names = _stateTitle, WarningHours = 4, ErrorHours = 2, IsDirectionUp = false, IsNotTransparent = false }, DateTime.UtcNow.AddHours(-5).ToString(CultureInfo.CurrentCulture)), new() { Title = "Bar", Values = [DateTime.UtcNow.AddHours(-5).ToString(CultureInfo.CurrentCulture)], Levels = [enumHealthCardStateLevel.None] });
yield return (new(new cHealthCardStateDateTime() { Names = _stateTitle, WarningHours = 4, ErrorHours = 2, IsDirectionUp = false, IsNotTransparent = true }, DateTime.UtcNow.AddHours(-5).ToString(CultureInfo.CurrentCulture)), new() { Title = "Bar", Values = [DateTime.UtcNow.AddHours(-5).ToString(CultureInfo.CurrentCulture)], Levels = [enumHealthCardStateLevel.Ok] });
yield return (new(new cHealthCardStateDateTime() { Names = _stateTitle, WarningHours = 4, ErrorHours = 2, IsDirectionUp = false, IsNotTransparent = true }, DateTime.UtcNow.AddHours(-4).ToString(CultureInfo.CurrentCulture)), new() { Title = "Bar", Values = [DateTime.UtcNow.AddHours(-4).ToString(CultureInfo.CurrentCulture)], Levels = [enumHealthCardStateLevel.Warning] });
yield return (new(new cHealthCardStateDateTime() { Names = _stateTitle, WarningHours = 4, ErrorHours = 2, IsDirectionUp = false, IsNotTransparent = true }, DateTime.UtcNow.AddHours(-2).ToString(CultureInfo.CurrentCulture)), new() { Title = "Bar", Values = [DateTime.UtcNow.AddHours(-2).ToString(CultureInfo.CurrentCulture)], Levels = [enumHealthCardStateLevel.Error] });
}
public static IEnumerable<TheoryDataRow<MockState, CockpitValueDisplayData>> GetCockpitValueDisplayDataTestData_StateVersion()
{
yield return (new(new cHealthCardStateVersion() { Names = _stateTitle, Warning = Version.Parse("4.2.4.2"), Error = Version.Parse("6.7.6.7"), IsDirectionUp = true, IsNotTransparent = false }, "4.2.4.1"), new() { Title = "Bar", Values = ["4.2.4.1"], Levels = [enumHealthCardStateLevel.None] });
yield return (new(new cHealthCardStateVersion() { Names = _stateTitle, Warning = Version.Parse("4.2.4.2"), Error = Version.Parse("6.7.6.7"), IsDirectionUp = true, IsNotTransparent = true }, "4.2.4.1"), new() { Title = "Bar", Values = ["4.2.4.1"], Levels = [enumHealthCardStateLevel.Ok] });
yield return (new(new cHealthCardStateVersion() { Names = _stateTitle, Warning = Version.Parse("4.2.4.2"), Error = Version.Parse("6.7.6.7"), IsDirectionUp = true, IsNotTransparent = true }, "4.2.4.2"), new() { Title = "Bar", Values = ["4.2.4.2"], Levels = [enumHealthCardStateLevel.Warning] });
yield return (new(new cHealthCardStateVersion() { Names = _stateTitle, Warning = Version.Parse("4.2.4.2"), Error = Version.Parse("6.7.6.7"), IsDirectionUp = true, IsNotTransparent = true }, "6.7.6.7"), new() { Title = "Bar", Values = ["6.7.6.7"], Levels = [enumHealthCardStateLevel.Error] });
yield return (new(new cHealthCardStateVersion() { Names = _stateTitle, Warning = Version.Parse("6.7.6.7"), Error = Version.Parse("4.2.4.2"), IsDirectionUp = false, IsNotTransparent = false }, "6.7.6.8"), new() { Title = "Bar", Values = ["6.7.6.8"], Levels = [enumHealthCardStateLevel.None] });
yield return (new(new cHealthCardStateVersion() { Names = _stateTitle, Warning = Version.Parse("6.7.6.7"), Error = Version.Parse("4.2.4.2"), IsDirectionUp = false, IsNotTransparent = true }, "6.7.6.8"), new() { Title = "Bar", Values = ["6.7.6.8"], Levels = [enumHealthCardStateLevel.Ok] });
yield return (new(new cHealthCardStateVersion() { Names = _stateTitle, Warning = Version.Parse("6.7.6.7"), Error = Version.Parse("4.2.4.2"), IsDirectionUp = false, IsNotTransparent = true }, "6.7.6.7"), new() { Title = "Bar", Values = ["6.7.6.7"], Levels = [enumHealthCardStateLevel.Warning] });
yield return (new(new cHealthCardStateVersion() { Names = _stateTitle, Warning = Version.Parse("6.7.6.7"), Error = Version.Parse("4.2.4.2"), IsDirectionUp = false, IsNotTransparent = true }, "4.2.4.2"), new() { Title = "Bar", Values = ["4.2.4.2"], Levels = [enumHealthCardStateLevel.Error] });
yield return (new(new cHealthCardStateVersion() { Names = _stateTitle, Warning = Version.Parse("4.2.4.2"), Error = Version.Parse("6.7.6.7"), IsDirectionUp = true, IsNotTransparent = false }, Version.Parse("4.2.4.1")), new() { Title = "Bar", Values = ["4.2.4.1"], Levels = [enumHealthCardStateLevel.None] });
yield return (new(new cHealthCardStateVersion() { Names = _stateTitle, Warning = Version.Parse("4.2.4.2"), Error = Version.Parse("6.7.6.7"), IsDirectionUp = true, IsNotTransparent = true }, Version.Parse("4.2.4.1")), new() { Title = "Bar", Values = ["4.2.4.1"], Levels = [enumHealthCardStateLevel.Ok] });
yield return (new(new cHealthCardStateVersion() { Names = _stateTitle, Warning = Version.Parse("4.2.4.2"), Error = Version.Parse("6.7.6.7"), IsDirectionUp = true, IsNotTransparent = true }, Version.Parse("4.2.4.2")), new() { Title = "Bar", Values = ["4.2.4.2"], Levels = [enumHealthCardStateLevel.Warning] });
yield return (new(new cHealthCardStateVersion() { Names = _stateTitle, Warning = Version.Parse("4.2.4.2"), Error = Version.Parse("6.7.6.7"), IsDirectionUp = true, IsNotTransparent = true }, Version.Parse("6.7.6.7")), new() { Title = "Bar", Values = ["6.7.6.7"], Levels = [enumHealthCardStateLevel.Error] });
yield return (new(new cHealthCardStateVersion() { Names = _stateTitle, Warning = Version.Parse("6.7.6.7"), Error = Version.Parse("4.2.4.2"), IsDirectionUp = false, IsNotTransparent = false }, Version.Parse("6.7.6.8")), new() { Title = "Bar", Values = ["6.7.6.8"], Levels = [enumHealthCardStateLevel.None] });
yield return (new(new cHealthCardStateVersion() { Names = _stateTitle, Warning = Version.Parse("6.7.6.7"), Error = Version.Parse("4.2.4.2"), IsDirectionUp = false, IsNotTransparent = true }, Version.Parse("6.7.6.8")), new() { Title = "Bar", Values = ["6.7.6.8"], Levels = [enumHealthCardStateLevel.Ok] });
yield return (new(new cHealthCardStateVersion() { Names = _stateTitle, Warning = Version.Parse("6.7.6.7"), Error = Version.Parse("4.2.4.2"), IsDirectionUp = false, IsNotTransparent = true }, Version.Parse("6.7.6.7")), new() { Title = "Bar", Values = ["6.7.6.7"], Levels = [enumHealthCardStateLevel.Warning] });
yield return (new(new cHealthCardStateVersion() { Names = _stateTitle, Warning = Version.Parse("6.7.6.7"), Error = Version.Parse("4.2.4.2"), IsDirectionUp = false, IsNotTransparent = true }, Version.Parse("4.2.4.2")), new() { Title = "Bar", Values = ["4.2.4.2"], Levels = [enumHealthCardStateLevel.Error] });
}
public static IEnumerable<TheoryDataRow<MockState, CockpitValueDisplayData>> GetCockpitValueDisplayDataTestData_StateDetails()
{
cMultiLanguageSupport.CurrentLanguage = "EN";
cHealthCardStateInfo stateDefinitionWithDetails = new() { Names = _stateTitle };
stateDefinitionWithDetails.Details = new cHealthCardDetailsValued(stateDefinitionWithDetails) { Format = cHealthCardDetailsValued.ValuedFormat.csv, RowSeparator = ',', ColSeparator = null };
yield return (new(stateDefinitionWithDetails, "42:42:42:42:42:42,67:67:67:67:67:67,69:69:69:69:69:69,,C0:FE:C0:FE:C0:FE"), new() { Title = "Bar", Values = ["4"], Levels = [enumHealthCardStateLevel.None], UiActions = [new cShowDetailedDataAction(stateDefinitionWithDetails, 0, null) { DisplayType = FasdDesktopUi.Basics.Enums.enumActionDisplayType.enabled }] });
stateDefinitionWithDetails = new() { Names = _stateTitle };
stateDefinitionWithDetails.Details = new cHealthCardDetailsValued(stateDefinitionWithDetails) { Format = cHealthCardDetailsValued.ValuedFormat.json, RowSeparator = ',', ColSeparator = null };
stateDefinitionWithDetails.Details.Add(new cHealthCardDetailsColumn() { Names = _stateTitle, Column = "Status" });
stateDefinitionWithDetails.Details.Add(new cHealthCardDetailsColumn() { Names = new() { ["EN"] = "Hello" }, Column = "Name" });
yield return (new(stateDefinitionWithDetails, "[{\"Availability\":2,\"BatteryStatus\":2,\"Caption\":\"Interner Akku\",\"Description\":\"Interner Akku\",\"EstimatedRunTime\":71582788,\"Name\":\"DELL 803W64C7\",\"Status\":\"OK\"}]"), new() { Title = "Bar", Values = ["OK"], Levels = [enumHealthCardStateLevel.None], UiActions = [new cShowDetailedDataAction(stateDefinitionWithDetails, 0, null) { DisplayType = FasdDesktopUi.Basics.Enums.enumActionDisplayType.enabled }] });
}
[Theory]
[MemberData(nameof(GetCockpitValueDisplayDataTestData_StateRefLink))]
public void Test_CockpitValueDisplayData_RefLink(MockState mockState, MockState referencedMockState, CockpitValueDisplayData expected)
{
_mockSupportCase.GetSupportCaseHealthcardData(default, Arg.Is(referencedMockState.StateDefinition.DatabaseInfo), Arg.Any<bool>()).Returns([referencedMockState.RawData]);
if (referencedMockState.StateDefinition is cHealthCardStateAggregation aggregation)
{
foreach (var state in aggregation.States)
{
_mockSupportCase.GetSupportCaseHealthcardData(default, Arg.Is(state.DatabaseInfo), Arg.Any<bool>()).Returns([42]);
}
}
_mockSupportCase.GetSupportCaseHealthcardData(default, Arg.Is(mockState.StateDefinition.DatabaseInfo), Arg.Any<bool>()).Returns([mockState.RawData]);
CockpitValueDisplayData actual = _supportCaseProcessor.GetCockpitValueDisplayData(mockState.StateDefinition, default, default);
Assert.Equivalent(expected, actual);
}
public static IEnumerable<TheoryDataRow<MockState, MockState, CockpitValueDisplayData>> GetCockpitValueDisplayDataTestData_StateRefLink()
{
const int referenceValue = 42;
var levelNoneStateDefinition = new cHealthCardStateLevel() { Name = "MyReferenceNone", DatabaseInfo = new() { ValueTable = "Foo", ValueColumn = "BarNone" }, Warning = referenceValue + 1, Error = referenceValue + 2, IsDirectionUp = true, IsNotTransparent = false };
var levelInfoStateDefinition = new cHealthCardStateInfo() { Name = "MyReferenceInfo", DatabaseInfo = new() { ValueTable = "Foo", ValueColumn = "BarInfo" }, IsNotTransparent = true };
var levelOkStateDefinition = new cHealthCardStateLevel() { Name = "MyReferenceOk", DatabaseInfo = new() { ValueTable = "Foo", ValueColumn = "BarOk" }, Warning = referenceValue + 1, Error = referenceValue + 2, IsDirectionUp = true, IsNotTransparent = true };
var levelWarningStateDefinition = new cHealthCardStateLevel() { Name = "MyReferenceWarning", DatabaseInfo = new() { ValueTable = "Foo", ValueColumn = "BarWarning" }, Warning = referenceValue, Error = referenceValue + 1, IsDirectionUp = true, IsNotTransparent = true };
var levelErrorStateDefinition = new cHealthCardStateLevel() { Name = "MyReferenceError", DatabaseInfo = new() { ValueTable = "Foo", ValueColumn = "BarError" }, Warning = referenceValue, Error = referenceValue, IsDirectionUp = true, IsNotTransparent = true };
var levelWarningAggregationDefinition = new cHealthCardStateAggregation() { Name = "MyReferenceAggregation", States = [levelOkStateDefinition, levelWarningStateDefinition] };
var stateConfigNode = new cF4SDHealthCardConfig();
stateConfigNode.Prerequisites.ReferencableStates.Add(levelNoneStateDefinition.Name, levelNoneStateDefinition);
stateConfigNode.Prerequisites.ReferencableStates.Add(levelInfoStateDefinition.Name, levelInfoStateDefinition);
stateConfigNode.Prerequisites.ReferencableStates.Add(levelOkStateDefinition.Name, levelOkStateDefinition);
stateConfigNode.Prerequisites.ReferencableStates.Add(levelWarningStateDefinition.Name, levelWarningStateDefinition);
stateConfigNode.Prerequisites.ReferencableStates.Add(levelErrorStateDefinition.Name, levelErrorStateDefinition);
stateConfigNode.Prerequisites.ReferencableStates.Add(levelWarningAggregationDefinition.Name, levelWarningAggregationDefinition);
yield return (new(new cHealthCardStateRefLink(stateConfigNode) { Names = _stateTitle, DatabaseInfo = new() { ValueTable = "Bar", ValueColumn = "Foo" }, Reference = levelNoneStateDefinition.Name }, "Foo"), new(levelNoneStateDefinition, referenceValue), new() { Title = "Bar", Values = ["Foo"], Levels = [enumHealthCardStateLevel.None] });
yield return (new(new cHealthCardStateRefLink(stateConfigNode) { Names = _stateTitle, DatabaseInfo = new() { ValueTable = "Bar", ValueColumn = "Foo" }, Reference = levelInfoStateDefinition.Name }, "Foo"), new(levelInfoStateDefinition, referenceValue), new() { Title = "Bar", Values = ["Foo"], Levels = [enumHealthCardStateLevel.Info] });
yield return (new(new cHealthCardStateRefLink(stateConfigNode) { Names = _stateTitle, DatabaseInfo = new() { ValueTable = "Bar", ValueColumn = "Foo" }, Reference = levelOkStateDefinition.Name, IsNotTransparent = true }, "Foo"), new(levelOkStateDefinition, referenceValue), new() { Title = "Bar", Values = ["Foo"], Levels = [enumHealthCardStateLevel.Ok] });
yield return (new(new cHealthCardStateRefLink(stateConfigNode) { Names = _stateTitle, DatabaseInfo = new() { ValueTable = "Bar", ValueColumn = "Foo" }, Reference = levelWarningStateDefinition.Name }, "Foo"), new(levelWarningStateDefinition, referenceValue), new() { Title = "Bar", Values = ["Foo"], Levels = [enumHealthCardStateLevel.Warning] });
yield return (new(new cHealthCardStateRefLink(stateConfigNode) { Names = _stateTitle, DatabaseInfo = new() { ValueTable = "Bar", ValueColumn = "Foo" }, Reference = levelErrorStateDefinition.Name }, "Foo"), new(levelErrorStateDefinition, referenceValue), new() { Title = "Bar", Values = ["Foo"], Levels = [enumHealthCardStateLevel.Error] });
yield return (new(new cHealthCardStateRefLink(stateConfigNode) { Names = _stateTitle, DatabaseInfo = new() { ValueTable = "Bar", ValueColumn = "Foo" }, Reference = levelWarningAggregationDefinition.Name }, "Foo"), new(levelWarningAggregationDefinition, referenceValue), new() { Title = "Bar", Values = ["Foo"], Levels = [enumHealthCardStateLevel.Warning] });
}
[Theory]
[MemberData(nameof(GetCockpitValueDisplayDataTestData_StateAggregation))]
public void Test_CockpitValueDisplayData_Aggregation(MockState mockState, CockpitValueDisplayData expected, params MockState[] aggregatedStates)
{
foreach (var aggregatedState in aggregatedStates)
{
_mockSupportCase.GetSupportCaseHealthcardData(default, Arg.Is(aggregatedState.StateDefinition.DatabaseInfo), Arg.Any<bool>()).Returns([aggregatedState.RawData]);
if (mockState.StateDefinition is cHealthCardStateAggregation stateAggreagtion)
stateAggreagtion.States.Add(aggregatedState.StateDefinition);
}
_mockSupportCase.GetSupportCaseHealthcardData(default, Arg.Is(mockState.StateDefinition.DatabaseInfo), Arg.Any<bool>()).Returns([mockState.RawData]);
CockpitValueDisplayData actual = _supportCaseProcessor.GetCockpitValueDisplayData(mockState.StateDefinition, default, default);
Assert.Equivalent(expected, actual);
}
public static IEnumerable<TheoryDataRow<MockState, CockpitValueDisplayData, MockState[]>> GetCockpitValueDisplayDataTestData_StateAggregation()
{
MockState stateOk = new(new cHealthCardStateLevel() { DatabaseInfo = new() { ValueTable = "MyAggregated", ValueColumn = "Ok" }, Warning = 43, Error = 44, IsDirectionUp = true }, 42);
MockState stateWarning = new(new cHealthCardStateLevel() { DatabaseInfo = new() { ValueTable = "MyAggregated", ValueColumn = "Warning" }, Warning = 42, Error = 43, IsDirectionUp = true }, 42);
MockState stateError = new(new cHealthCardStateLevel() { DatabaseInfo = new() { ValueTable = "MyAggregated", ValueColumn = "Error" }, Warning = 41, Error = 42, IsDirectionUp = true }, 42);
yield return (new(new cHealthCardStateAggregation() { Names = _stateTitle, IsNotTransparent = false }, null), new() { Title = "Bar", Values = ["Ø"], Levels = [enumHealthCardStateLevel.None] }, [stateOk]);
yield return (new(new cHealthCardStateAggregation() { Names = _stateTitle, IsNotTransparent = true }, null), new() { Title = "Bar", Values = ["Ø"], Levels = [enumHealthCardStateLevel.Ok] }, [stateOk]);
yield return (new(new cHealthCardStateAggregation() { Names = _stateTitle, IsNotTransparent = true }, null), new() { Title = "Bar", Values = ["Ø"], Levels = [enumHealthCardStateLevel.Warning] }, [stateOk, stateWarning]);
yield return (new(new cHealthCardStateAggregation() { Names = _stateTitle, IsNotTransparent = true }, null), new() { Title = "Bar", Values = ["Ø"], Levels = [enumHealthCardStateLevel.Error] }, [stateOk, stateWarning, stateError]);
}
public record MockState
{
public cHealthCardStateBase StateDefinition { get; set; }
public object? RawData { get; set; }
public MockState(cHealthCardStateBase stateDefinition, object? rawData)
{
StateDefinition = stateDefinition;
RawData = rawData;
}
}
}

View File

@@ -126,7 +126,7 @@ public class SupportCaseTest
_supportCase.UpdateSupportCaseDataCache(relation, [dataTable]);
// Assert
var actual = _supportCase.GetSupportCaseHealthcardData(relation, new cValueAddress() { ValueTable = tableName, ValueColumn = columnName });
var actual = _supportCase.GetSupportCaseHealthcardData(relation, new cValueAddress() { ValueTable = tableName, ValueColumn = columnName }, default);
Assert.Equal(expected, actual);
}
@@ -145,7 +145,7 @@ public class SupportCaseTest
]
};
// Act
var actual = _supportCase.GetSupportCaseHealthcardData(relation, new cValueAddress() { ValueTable = "NonExistentTable", ValueColumn = "NonExistentColumn" });
var actual = _supportCase.GetSupportCaseHealthcardData(relation, new cValueAddress() { ValueTable = "NonExistentTable", ValueColumn = "NonExistentColumn" }, default);
// Assert
Assert.Null(actual);
}

View File

@@ -5,7 +5,8 @@
<Nullable>enable</Nullable>
<OutputType>Exe</OutputType>
<RootNamespace>F4SD.Cockpit.Client.Test</RootNamespace>
<TargetFramework>net10.0</TargetFramework>
<TargetFramework>net10.0-windows</TargetFramework>
<UseWPF>true</UseWPF>
<!--
To enable the Microsoft Testing Platform 'dotnet test' experience, add property:
<TestingPlatformDotnetTestSupport>true</TestingPlatformDotnetTestSupport>
@@ -27,8 +28,11 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
<PackageReference Include="MaterialIcons" Version="1.0.3" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.3.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="NSubstitute" Version="5.3.0" />
<PackageReference Include="System.Drawing.Common" Version="10.0.3" />
<PackageReference Include="xunit.v3" Version="3.2.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.5">
<PrivateAssets>all</PrivateAssets>

View File

@@ -56,31 +56,31 @@
<Reference Include="C4IT.F4SD.DisplayFormatting, Version=1.0.9509.21303, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\C4IT.F4SD.DisplayFormatting.1.0.0\lib\netstandard2.0\C4IT.F4SD.DisplayFormatting.dll</HintPath>
</Reference>
<Reference Include="C4IT.F4SD.SupportCaseProtocoll, Version=1.0.9516.21165, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\C4IT.F4SD.SupportCaseProtocoll.1.0.0\lib\netstandard2.0\C4IT.F4SD.SupportCaseProtocoll.dll</HintPath>
<Reference Include="C4IT.F4SD.SupportCaseProtocoll, Version=1.0.9558.28081, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\C4IT.F4SD.SupportCaseProtocoll.1.0.1\lib\netstandard2.0\C4IT.F4SD.SupportCaseProtocoll.dll</HintPath>
</Reference>
<Reference Include="MaterialIcons, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\MaterialIcons.1.0.3\lib\MaterialIcons.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Bcl.AsyncInterfaces, Version=10.0.0.2, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Bcl.AsyncInterfaces.10.0.2\lib\net462\Microsoft.Bcl.AsyncInterfaces.dll</HintPath>
<Reference Include="Microsoft.Bcl.AsyncInterfaces, Version=10.0.0.3, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Bcl.AsyncInterfaces.10.0.3\lib\net462\Microsoft.Bcl.AsyncInterfaces.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Extensions.DependencyInjection.Abstractions, Version=10.0.0.2, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Extensions.DependencyInjection.Abstractions.10.0.2\lib\net462\Microsoft.Extensions.DependencyInjection.Abstractions.dll</HintPath>
<Reference Include="Microsoft.Extensions.DependencyInjection.Abstractions, Version=10.0.0.3, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Extensions.DependencyInjection.Abstractions.10.0.3\lib\net462\Microsoft.Extensions.DependencyInjection.Abstractions.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Extensions.Logging.Abstractions, Version=10.0.0.2, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Extensions.Logging.Abstractions.10.0.2\lib\net462\Microsoft.Extensions.Logging.Abstractions.dll</HintPath>
<Reference Include="Microsoft.Extensions.Logging.Abstractions, Version=10.0.0.3, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Extensions.Logging.Abstractions.10.0.3\lib\net462\Microsoft.Extensions.Logging.Abstractions.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.13.0.4\lib\net45\Newtonsoft.Json.dll</HintPath>
<HintPath>..\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Buffers, Version=4.0.5.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Buffers.4.6.1\lib\net462\System.Buffers.dll</HintPath>
</Reference>
<Reference Include="System.Core" />
<Reference Include="System.Diagnostics.DiagnosticSource, Version=10.0.0.2, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Diagnostics.DiagnosticSource.10.0.2\lib\net462\System.Diagnostics.DiagnosticSource.dll</HintPath>
<Reference Include="System.Diagnostics.DiagnosticSource, Version=10.0.0.3, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Diagnostics.DiagnosticSource.10.0.3\lib\net462\System.Diagnostics.DiagnosticSource.dll</HintPath>
</Reference>
<Reference Include="System.Drawing" />
<Reference Include="System.Memory, Version=4.0.5.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
@@ -104,6 +104,9 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\..\C4IT FASD\_Common\C4IT.F4SD.Base.Ticket.cs">
<Link>Common\C4IT.F4SD.Base.Ticket.cs</Link>
</Compile>
<Compile Include="..\..\C4IT FASD\_Common\C4IT.F4SD.GlobalConfig.cs">
<Link>Common\C4IT.F4SD.GlobalConfig.cs</Link>
</Compile>
@@ -158,10 +161,15 @@
<Compile Include="..\Shared\SharedAssemblyInfo.cs">
<Link>Properties\SharedAssemblyInfo.cs</Link>
</Compile>
<Compile Include="Models\RemoteDesktopConnection\IRemoteDesktopClientInfo.cs" />
<Compile Include="Models\RemoteDesktopConnection\RemoteDesktopConnectionStatusResult.cs" />
<Compile Include="RemoteDesktopCommunicationBase.cs" />
<Compile Include="ExternalToolExecutor.cs" />
<Compile Include="F4sdCockpitCommunicationM42Base.cs" />
<Compile Include="FasdCockpitCommunicationBase.cs" />
<Compile Include="Models\F4sdAgentScript.cs" />
<Compile Include="Models\RemoteDesktopConnection\IRemoteDesktopConnectionDetails.cs" />
<Compile Include="Models\RemoteDesktopConnection\RemoteDesktopConnectionStatus.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>

View File

@@ -4,6 +4,7 @@ using System.Threading;
using System.Threading.Tasks;
using C4IT.FASD.Base;
using FasdCockpitBase;
using FasdCockpitBase.Models;
namespace C4IT.FASD.Cockpit.Communication
@@ -23,6 +24,8 @@ namespace C4IT.FASD.Cockpit.Communication
public cF4sdCockpitCommunicationM42Base M42 = new cF4sdCockpitCommunicationM42Base();
public RemoteDesktopCommunicationBase RemoteDesktopManager = new RemoteDesktopCommunicationBase();
public abstract bool IsDemo();
public abstract bool CheckConnectionInfo();
@@ -62,18 +65,19 @@ namespace C4IT.FASD.Cockpit.Communication
public abstract Task<cFasdApiSearchResultCollection> GetUserSearchResults(string Name, List<string> SIDs);
public abstract Task<cF4sdStagedSearchResultRelationTaskId> StartGatheringRelations(IEnumerable<cFasdApiSearchResultEntry> relatedTo, CancellationToken token);
public abstract Task<cF4sdStagedSearchResultRelations> GetStagedRelations(Guid id, CancellationToken token);
public abstract Task<List<cF4sdApiSearchResultRelation>> GetSearchResultRelations(enumF4sdSearchResultClass resultType, List<cFasdApiSearchResultEntry> searchResults);
#region Ticketübersicht
public abstract Task<List<cF4sdApiSearchResultRelation>> GetTicketOverviewRelations(string key, bool useRoleScope, int count);
public abstract Task<Dictionary<string, int>> GetTicketOverviewCounts(IEnumerable<string> keys, bool useRoleScope);
#endregion
public abstract Task<cF4SDHealthCardRawData> GetHealthCardData(cF4sdHealthCardRawDataRequest requestData);
public abstract Task<cF4sdStagedSearchResultRelations> GetStagedRelations(Guid id, CancellationToken token);
public abstract Task StopGatheringRelations(Guid id, CancellationToken token);
public abstract Task<List<cF4sdApiSearchResultRelation>> GetSearchResultRelations(enumF4sdSearchResultClass resultType, List<cFasdApiSearchResultEntry> searchResults);
#region Ticketübersicht
public abstract Task<List<cF4sdApiSearchResultRelation>> GetTicketOverviewRelations(string key, bool useRoleScope, int count);
public abstract Task<Dictionary<string, int>> GetTicketOverviewCounts(IEnumerable<string> keys, bool useRoleScope);
#endregion
public abstract Task<cF4SDHealthCardRawData> GetHealthCardData(cF4sdHealthCardRawDataRequest requestData);
public abstract Task<cF4SDHealthCardRawData> GetHealthCardData(Guid healthCardId);

View File

@@ -0,0 +1,6 @@
namespace FasdCockpitBase.Models.RemoteDesktopConnection
{
public interface IRemoteDesktopClientInfo
{
}
}

View File

@@ -0,0 +1,6 @@
namespace FasdCockpitBase.Models.RemoteDesktopConnection
{
public interface IRemoteDesktopConnectionDetails
{
}
}

View File

@@ -0,0 +1,13 @@
namespace FasdCockpitBase.Models.RemoteDesktopConnection
{
public enum RemoteDesktopConnectionStatus
{
Unknown,
Initiated,
Accepted,
Connected,
Canceled,
Finished,
Error
}
}

View File

@@ -0,0 +1,16 @@
using System.Collections.Generic;
namespace FasdCockpitBase.Models.RemoteDesktopConnection
{
public class RemoteDesktopConnectionStatusResult
{
public RemoteDesktopConnectionStatus Status { get; private set; }
public IList<string> Errors { get; set; } = new List<string>();
public RemoteDesktopConnectionStatusResult(RemoteDesktopConnectionStatus status)
{
Status = status;
}
}
}

View File

@@ -0,0 +1,29 @@
using C4IT.FASD.Base;
using FasdCockpitBase.Models.RemoteDesktopConnection;
using System;
using System.Threading;
using System.Threading.Tasks;
namespace FasdCockpitBase
{
public class RemoteDesktopCommunicationBase
{
public virtual Task<T> InitiateConnection<T>(IRemoteDesktopClientInfo clientInfo, bool isElevated, CancellationToken token) where T : IRemoteDesktopConnectionDetails
=> Task.FromResult(default(T));
public virtual Task<RemoteDesktopConnectionStatusResult> GetConnectionStatus(Guid connectionId, CancellationToken token)
=> Task.FromResult(new RemoteDesktopConnectionStatusResult(RemoteDesktopConnectionStatus.Unknown));
public virtual Task StopConnection(Guid connectionId, CancellationToken token)
=> Task.CompletedTask;
public virtual Task<HealthInformation> GetHealth(CancellationToken token)
=> Task.FromResult(new HealthInformation(HealthStatus.healthy));
public virtual Task<bool> IsRemoteDesktopCommunicationAvailable()
{
bool isRemoteViewerInstalled = true;
return Task.FromResult(isRemoteViewerInstalled);
}
}
}

View File

@@ -4,7 +4,7 @@
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Microsoft.Extensions.Logging.Abstractions" publicKeyToken="adb9793829ddae60" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-10.0.0.2" newVersion="10.0.0.2" />
<bindingRedirect oldVersion="0.0.0.0-10.0.0.3" newVersion="10.0.0.3" />
</dependentAssembly>
</assemblyBinding>
</runtime>

View File

@@ -1,14 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="C4IT.F4SD.DisplayFormatting" version="1.0.0" targetFramework="net472" />
<package id="C4IT.F4SD.SupportCaseProtocoll" version="1.0.0" targetFramework="net472" />
<package id="C4IT.F4SD.SupportCaseProtocoll" version="1.0.1" targetFramework="net472" />
<package id="MaterialIcons" version="1.0.3" targetFramework="net472" />
<package id="Microsoft.Bcl.AsyncInterfaces" version="10.0.2" targetFramework="net472" />
<package id="Microsoft.Extensions.DependencyInjection.Abstractions" version="10.0.2" targetFramework="net472" />
<package id="Microsoft.Extensions.Logging.Abstractions" version="10.0.2" targetFramework="net472" />
<package id="Newtonsoft.Json" version="13.0.4" targetFramework="net472" />
<package id="Microsoft.Bcl.AsyncInterfaces" version="10.0.3" targetFramework="net472" />
<package id="Microsoft.Extensions.DependencyInjection.Abstractions" version="10.0.3" targetFramework="net472" />
<package id="Microsoft.Extensions.Logging.Abstractions" version="10.0.3" targetFramework="net472" />
<package id="Newtonsoft.Json" version="13.0.3" targetFramework="net472" />
<package id="System.Buffers" version="4.6.1" targetFramework="net472" />
<package id="System.Diagnostics.DiagnosticSource" version="10.0.2" targetFramework="net472" />
<package id="System.Diagnostics.DiagnosticSource" version="10.0.3" targetFramework="net472" />
<package id="System.Memory" version="4.6.3" targetFramework="net472" />
<package id="System.Numerics.Vectors" version="4.6.1" targetFramework="net472" />
<package id="System.Runtime.CompilerServices.Unsafe" version="6.1.2" targetFramework="net472" />

View File

@@ -56,7 +56,7 @@
</PropertyGroup>
<ItemGroup>
<Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.13.0.4\lib\net45\Newtonsoft.Json.dll</HintPath>
<HintPath>..\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
@@ -95,12 +95,15 @@
<Compile Include="..\Shared\SharedAssemblyInfo.cs">
<Link>Properties\SharedAssemblyInfo.cs</Link>
</Compile>
<Compile Include="F4sdCockpitCommunicationM42Web.cs" />
<Compile Include="FasdCockpitCommunicationWeb.cs" />
<Compile Include="FasdCockpitMachineConfiguration.cs" />
<Compile Include="TicketOverview\TicketOverviewCountsResponse.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<Compile Include="RemoteDesktopCommunication\AgentRemoteDesktopCommunication.cs" />
<Compile Include="F4sdCockpitCommunicationM42Web.cs" />
<Compile Include="FasdCockpitCommunicationWeb.cs" />
<Compile Include="FasdCockpitMachineConfiguration.cs" />
<Compile Include="RemoteDesktopCommunication\AgentRemoteDesktopConnecitonDetails.cs" />
<Compile Include="RemoteDesktopCommunication\ApiResult.cs" />
<Compile Include="TicketOverview\TicketOverviewCountsResponse.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\F4SD-Logging\F4SD-Logging.csproj">
<Project>{7793f281-b226-4e20-b6f6-5d53d70f1dc1}</Project>

View File

@@ -17,15 +17,14 @@ using C4IT.Security;
using C4IT.FASD.Communication.Agent;
using FasdCockpitBase.Models;
using FasdCockpitCommunication;
using FasdCockpitCommunication.TicketOverview;
using FasdCockpitCommunication;
using FasdCockpitCommunication.TicketOverview;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using static C4IT.Logging.cLogManager;
using C4IT.Configuration;
using System.Runtime.InteropServices.WindowsRuntime;
using FasdCockpitCommunication.RemoteDesktopCommunication;
namespace C4IT.FASD.Cockpit.Communication
{
@@ -38,6 +37,7 @@ namespace C4IT.FASD.Cockpit.Communication
public cFasdCockpitCommunicationWeb()
{
M42 = new cF4sdCockpitCommunicationM42Web();
RemoteDesktopManager = new AgentRemoteDesktopCommunication();
}
public override bool IsDemo() => false;
@@ -73,7 +73,8 @@ namespace C4IT.FASD.Cockpit.Communication
try
{
var http = GetHttpHelper(false);
var result = await http.GetHttpJson($"api/CheckConnection", 15000, CancellationToken.None);
var _url = $"api/CheckConnection";
var result = await http.GetHttpJson(_url, 15000, CancellationToken.None);
if (!result.IsOk)
{
@@ -81,7 +82,7 @@ namespace C4IT.FASD.Cockpit.Communication
return output;
}
SaveApiResultValueJson("CheckConnection", result.Result);
if (Debug_apiValues) SaveApiResultValueJson("CheckConnection", result.Result, _url);
var connectionResult = JsonConvert.DeserializeObject<cFasdApiConnectionInfo>(result.Result);
output.ApiConnectionInfo = connectionResult;
var assembly = Assembly.GetExecutingAssembly();
@@ -139,7 +140,7 @@ namespace C4IT.FASD.Cockpit.Communication
public static cOneTimePW GetOneTimePw() => new cOneTimePW("OneTimePw", cSecurePassword.Instance);
public cHttpHelper GetHttpHelper(bool useToken)
public static cHttpHelper GetHttpHelper(bool useToken)
{
try
{
@@ -171,7 +172,7 @@ namespace C4IT.FASD.Cockpit.Communication
if (result.IsOk)
{
SaveApiResultValue("Logon-GetUserIdByAccount", result.Result, "json");
if (Debug_apiValues) SaveApiResultValue("Logon-GetUserIdByAccount", result.Result, "json");
output = JsonConvert.DeserializeObject<Guid>(result.Result);
}
}
@@ -200,7 +201,7 @@ namespace C4IT.FASD.Cockpit.Communication
if (result.IsOk)
{
if (Debug_apiValues) SaveApiResultValueJson("Logon-Logon", result.Result);
if (Debug_apiValues) SaveApiResultValueJson("Logon-Logon", result.Result, _url);
var _retVal = JsonConvert.DeserializeObject<cF4sdUserInfo>(result.Result);
return _retVal;
}
@@ -234,11 +235,12 @@ namespace C4IT.FASD.Cockpit.Communication
{
var _payLoad = JsonConvert.SerializeObject(Token);
var http = GetHttpHelper(true);
var apiAccessInfo = await http.PostJsonAsync($"api/Logon/RegisterExternalToken", _payLoad, 14000, System.Threading.CancellationToken.None);
var _url = $"api/Logon/RegisterExternalToken";
var apiAccessInfo = await http.PostJsonAsync(_url, _payLoad, 14000, System.Threading.CancellationToken.None);
if (apiAccessInfo.IsOk)
{
var _str = apiAccessInfo.Result;
if (Debug_apiValues) SaveApiResultValueJson("Logon-RegisterExternalToken", _str);
if (Debug_apiValues) SaveApiResultValueJson("Logon-RegisterExternalToken", _str, _url);
var RetVal = JsonConvert.DeserializeObject<cF4SdUserInfoChange>(_str);
return RetVal;
}
@@ -271,9 +273,11 @@ namespace C4IT.FASD.Cockpit.Communication
try
{
var http = GetHttpHelper(true);
var result = await http.GetHttpJson($"api/Logon/GetAdditionalUserInfo?AccountType={Type}", 5000, CancellationToken.None);
var _url = $"api/Logon/GetAdditionalUserInfo?AccountType={Type}";
var result = await http.GetHttpJson(_url, 5000, CancellationToken.None);
if (result.IsOk)
{
if (Debug_apiValues) SaveApiResultValueJson("Logon-GetAdditionalUserInfo", result.Result, _url);
var _retVal = JsonConvert.DeserializeObject<cF4SDAdditionalUserInfo>(result.Result);
return _retVal;
}
@@ -307,17 +311,17 @@ namespace C4IT.FASD.Cockpit.Communication
try
{
var http = GetHttpHelper(false);
var result = await http.GetHttpJson("api/F4SDAnalytics/GetF4SDAnalyticsState", 2000, CancellationToken.None);
var _url = "api/F4SDAnalytics/GetF4SDAnalyticsState";
var result = await http.GetHttpJson(_url, 2000, CancellationToken.None);
if (result.IsOk)
{
if (Debug_apiValues) SaveApiResultValueJson("F4SDAnalytics-GetF4SDAnalyticsState", result.Result);
if (Debug_apiValues) SaveApiResultValueJson("F4SDAnalytics-GetF4SDAnalyticsState", result.Result, _url);
output = JsonConvert.DeserializeObject<bool>(result.Result);
}
else
{
apiError = (int)result.Status;
await CheckConnectionStatus?.Invoke();
}
}
@@ -353,7 +357,6 @@ namespace C4IT.FASD.Cockpit.Communication
if (!result.IsOk)
{
apiError = (int)result.Status;
await CheckConnectionStatus?.Invoke();
}
return result.IsOk;
@@ -391,7 +394,6 @@ namespace C4IT.FASD.Cockpit.Communication
if (!result.IsOk)
{
apiError = (int)result.Status;
await CheckConnectionStatus?.Invoke();
}
}
catch (Exception E)
@@ -576,7 +578,7 @@ namespace C4IT.FASD.Cockpit.Communication
try
{
var http = GetHttpHelper(false);
var result = await http.GetHttpJson("api/CheckCollectorStatus", 15000, CancellationToken.None);
if (!result.IsOk)
@@ -617,7 +619,6 @@ namespace C4IT.FASD.Cockpit.Communication
if (!result.IsOk)
{
apiError = (int)result.Status;
await CheckConnectionStatus?.Invoke();
return null;
}
@@ -629,7 +630,6 @@ namespace C4IT.FASD.Cockpit.Communication
catch (Exception E)
{
apiError = E.HResult;
await CheckConnectionStatus?.Invoke();
LogException(E);
}
finally
@@ -875,6 +875,8 @@ namespace C4IT.FASD.Cockpit.Communication
public override async Task<cFasdApiSearchResultCollection> GetUserSearchResults(string Name, List<string> SIDs)
{
const string ApiName = "SearchUserByNameAndSids";
var CM = MethodBase.GetCurrentMethod();
LogMethodBegin(CM);
@@ -895,7 +897,7 @@ namespace C4IT.FASD.Cockpit.Communication
strSids = HttpUtility.UrlEncode(strSids);
var strUrl = $"api/SearchUserByNameAndSids?Name={_Name}&SIDs={strSids}";
var strUrl = $"api/{ApiName}?Name={_Name}&SIDs={strSids}";
var http = GetHttpHelper(true);
var result = await http.GetHttpJson(strUrl, 10000, System.Threading.CancellationToken.None);
@@ -907,7 +909,7 @@ namespace C4IT.FASD.Cockpit.Communication
return null;
}
if (Debug_apiValues) SaveApiResultValueJson("SearchUserByNameAndSids", result.Result, strUrl);
if (Debug_apiValues) SaveApiResultValueJson(ApiName, result.Result, strUrl);
var deserializedObject = JsonConvert.DeserializeObject<cFasdApiSearchResultCollection>(result.Result);
return deserializedObject;
@@ -919,7 +921,7 @@ namespace C4IT.FASD.Cockpit.Communication
}
finally
{
if (Debug_apiTiming) SaveApiTimingEntry("SearchUserByNameAndSids", timeStart, apiError);
if (Debug_apiTiming) SaveApiTimingEntry(ApiName, timeStart, apiError);
LogMethodEnd(CM);
}
@@ -959,6 +961,9 @@ namespace C4IT.FASD.Cockpit.Communication
public override async Task<cF4sdStagedSearchResultRelationTaskId> StartGatheringRelations(IEnumerable<cFasdApiSearchResultEntry> relatedTo, CancellationToken token)
{
const string ApiName = "StagedSearchRelations";
const string LogName = ApiName + "_Start";
var CM = MethodBase.GetCurrentMethod();
LogMethodBegin(CM);
@@ -967,7 +972,7 @@ namespace C4IT.FASD.Cockpit.Communication
try
{
string url = $"api/StagedSearchRelations?age={cF4SDCockpitXmlConfig.Instance.HealthCardConfig.SearchResultAge}";
string url = $"api/{ApiName}?age={cF4SDCockpitXmlConfig.Instance.HealthCardConfig.SearchResultAge}";
string json = JsonConvert.SerializeObject(relatedTo);
var http = GetHttpHelper(true);
var result = await http.PostJsonAsync(url, json, 5_000, token);
@@ -979,6 +984,7 @@ namespace C4IT.FASD.Cockpit.Communication
return null;
}
if (Debug_apiValues) SaveApiResultValueJson(LogName, result.Result, url);
return JsonConvert.DeserializeObject<cF4sdStagedSearchResultRelationTaskId>(result.Result);
}
catch (Exception ex)
@@ -989,7 +995,7 @@ namespace C4IT.FASD.Cockpit.Communication
}
finally
{
if (Debug_apiTiming) SaveApiTimingEntry("StagedSearchRelationsStart", timeStart, apiError);
if (Debug_apiTiming) SaveApiTimingEntry(LogName, timeStart, apiError);
LogMethodEnd(CM);
}
return null;
@@ -997,6 +1003,9 @@ namespace C4IT.FASD.Cockpit.Communication
public override async Task<cF4sdStagedSearchResultRelations> GetStagedRelations(Guid id, CancellationToken token)
{
const string ApiName = "StagedSearchRelations";
const string LogName = ApiName + "_Get";
var CM = MethodBase.GetCurrentMethod();
LogMethodBegin(CM);
@@ -1005,7 +1014,7 @@ namespace C4IT.FASD.Cockpit.Communication
try
{
string url = $"api/StagedSearchRelations/{id}";
string url = $"api/{ApiName}/{id}";
var http = GetHttpHelper(true);
var result = await http.GetHttpJson(url, 25_000, token, true);
@@ -1016,6 +1025,7 @@ namespace C4IT.FASD.Cockpit.Communication
return null;
}
if (Debug_apiValues) SaveApiResultValueJson(LogName, result.Result, url);
return JsonConvert.DeserializeObject<cF4sdStagedSearchResultRelations>(result.Result);
}
catch (Exception ex)
@@ -1026,14 +1036,17 @@ namespace C4IT.FASD.Cockpit.Communication
}
finally
{
if (Debug_apiTiming) SaveApiTimingEntry("StagedSearchRelationsStart", timeStart, apiError);
if (Debug_apiTiming) SaveApiTimingEntry(LogName, timeStart, apiError);
LogMethodEnd(CM);
}
return null;
}
private async Task<cF4sdStagedSearchResultRelations> GetRelationResults(Guid relationTaskId, CancellationToken token)
public override async Task StopGatheringRelations(Guid id, CancellationToken token)
{
const string ApiName = "StagedSearchRelations";
const string LogName = ApiName + "_Stop";
var CM = MethodBase.GetCurrentMethod();
LogMethodBegin(CM);
@@ -1042,18 +1055,21 @@ namespace C4IT.FASD.Cockpit.Communication
try
{
string url = $"api/StagedSearchRelations/{relationTaskId}";
string url = $"api/{ApiName}/{id}/stop";
var http = GetHttpHelper(true);
var result = await http.GetHttpJson(url, 5_000, token, true);
var result = await http.GetHttpJson(url, 5_000, token);
if (!result.IsOk)
{
apiError = (int)result.Status;
await CheckConnectionStatus?.Invoke();
return null;
if (!token.IsCancellationRequested)
await CheckConnectionStatus?.Invoke();
}
return JsonConvert.DeserializeObject<cF4sdStagedSearchResultRelations>(result.Result);
}
catch (TaskCanceledException E) when (token.IsCancellationRequested)
{
apiError = E.HResult;
LogEntry($"{LogName} was cancelled by token.", LogLevels.Debug);
}
catch (Exception ex)
{
@@ -1063,10 +1079,9 @@ namespace C4IT.FASD.Cockpit.Communication
}
finally
{
if (Debug_apiTiming) SaveApiTimingEntry("StagedSearchRelations", timeStart, apiError);
if (Debug_apiTiming) SaveApiTimingEntry(LogName, timeStart, apiError);
LogMethodEnd(CM);
}
return null;
}
public override async Task<List<cF4sdApiSearchResultRelation>> GetSearchResultRelations(enumF4sdSearchResultClass resultType, List<cFasdApiSearchResultEntry> searchResults)
@@ -1101,7 +1116,7 @@ namespace C4IT.FASD.Cockpit.Communication
TimeSpan getRelationsDelay = TimeSpan.FromMilliseconds(500);
for (int i = 0; i < maxRetryCount; i++)
{
var relations = await GetRelationResults(relationTaskId.Id, CancellationToken.None).ConfigureAwait(false);
var relations = await GetStagedRelations(relationTaskId.Id, CancellationToken.None).ConfigureAwait(false);
output.AddRange(relations?.Relations);
if (relations.IsComplete)
@@ -1127,131 +1142,131 @@ namespace C4IT.FASD.Cockpit.Communication
LogMethodEnd(CM);
}
return output;
}
#region Ticketübersicht
public override async Task<Dictionary<string, int>> GetTicketOverviewCounts(IEnumerable<string> keys, bool useRoleScope)
{
var CM = MethodBase.GetCurrentMethod();
LogMethodBegin(CM);
var apiError = 0;
var timeStart = DateTime.UtcNow;
try
{
var normalizedKeys = (keys ?? Enumerable.Empty<string>())
.Where(k => !string.IsNullOrWhiteSpace(k))
.Distinct(StringComparer.OrdinalIgnoreCase)
.ToList();
var http = GetHttpHelper(true);
var scope = useRoleScope ? "role" : "personal";
var urlBuilder = new StringBuilder($"api/TicketOverview/GetCounts?scope={scope}");
if (normalizedKeys.Count > 0)
{
var joinedKeys = HttpUtility.UrlEncode(string.Join(",", normalizedKeys));
urlBuilder.Append($"&keys={joinedKeys}");
}
var url = urlBuilder.ToString();
var result = await http.GetHttpJson(url, 15000, CancellationToken.None);
if (!result.IsOk)
{
apiError = (int)result.Status;
if (CheckConnectionStatus != null)
await CheckConnectionStatus.Invoke();
LogEntry($"Error on requesting ticket overview counts ({scope}). Status: {result.Status}", LogLevels.Warning);
return new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
}
if (Debug_apiValues) SaveApiResultValueJson("TicketOverview.GetCounts", result.Result, url);
var response = JsonConvert.DeserializeObject<TicketOverviewCountsResponse>(result.Result);
return response?.ToDictionary(normalizedKeys) ?? new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
}
catch (Exception E)
{
apiError = E.HResult;
if (CheckConnectionStatus != null)
await CheckConnectionStatus.Invoke();
LogException(E);
}
finally
{
if (Debug_apiTiming) SaveApiTimingEntry("TicketOverview.GetCounts", timeStart, apiError);
LogMethodEnd(CM);
}
return new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
}
public override async Task<List<cF4sdApiSearchResultRelation>> GetTicketOverviewRelations(string key, bool useRoleScope, int count)
{
var CM = MethodBase.GetCurrentMethod();
LogMethodBegin(CM);
var apiError = 0;
var timeStart = DateTime.UtcNow;
var output = new List<cF4sdApiSearchResultRelation>();
try
{
if (string.IsNullOrWhiteSpace(key))
return output;
var http = GetHttpHelper(true);
var scope = useRoleScope ? "role" : "personal";
var safeKey = HttpUtility.UrlEncode(key);
var url = $"api/TicketOverview/GetRelations?key={safeKey}&scope={scope}&count={Math.Max(0, count)}";
var result = await http.GetHttpJson(url, 20000, CancellationToken.None);
if (!result.IsOk)
{
apiError = (int)result.Status;
if (CheckConnectionStatus != null)
await CheckConnectionStatus.Invoke();
LogEntry($"Error on requesting ticket overview relations for '{key}' ({scope}). Status: {result.Status}", LogLevels.Warning);
return output;
}
if (Debug_apiValues) SaveApiResultValueJson("TicketOverview.GetRelations", result.Result, url);
var relations = JsonConvert.DeserializeObject<List<cF4sdApiSearchResultRelation>>(result.Result);
if (relations != null)
output = relations;
}
catch (Exception E)
{
apiError = E.HResult;
if (CheckConnectionStatus != null)
await CheckConnectionStatus.Invoke();
LogException(E);
}
finally
{
if (Debug_apiTiming) SaveApiTimingEntry("TicketOverview.GetRelations", timeStart, apiError);
LogMethodEnd(CM);
}
return output;
}
#endregion
public override async Task<cF4SDHealthCardRawData> GetHealthCardData(cF4sdHealthCardRawDataRequest requestData)
{
var CM = MethodBase.GetCurrentMethod();
LogMethodBegin(CM);
return output;
}
#region Ticketübersicht
public override async Task<Dictionary<string, int>> GetTicketOverviewCounts(IEnumerable<string> keys, bool useRoleScope)
{
var CM = MethodBase.GetCurrentMethod();
LogMethodBegin(CM);
var apiError = 0;
var timeStart = DateTime.UtcNow;
try
{
var normalizedKeys = (keys ?? Enumerable.Empty<string>())
.Where(k => !string.IsNullOrWhiteSpace(k))
.Distinct(StringComparer.OrdinalIgnoreCase)
.ToList();
var http = GetHttpHelper(true);
var scope = useRoleScope ? "role" : "personal";
var urlBuilder = new StringBuilder($"api/TicketOverview/GetCounts?scope={scope}");
if (normalizedKeys.Count > 0)
{
var joinedKeys = HttpUtility.UrlEncode(string.Join(",", normalizedKeys));
urlBuilder.Append($"&keys={joinedKeys}");
}
var url = urlBuilder.ToString();
var result = await http.GetHttpJson(url, 15000, CancellationToken.None);
if (!result.IsOk)
{
apiError = (int)result.Status;
if (CheckConnectionStatus != null)
await CheckConnectionStatus.Invoke();
LogEntry($"Error on requesting ticket overview counts ({scope}). Status: {result.Status}", LogLevels.Warning);
return new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
}
if (Debug_apiValues) SaveApiResultValueJson("TicketOverview.GetCounts", result.Result, url);
var response = JsonConvert.DeserializeObject<TicketOverviewCountsResponse>(result.Result);
return response?.ToDictionary(normalizedKeys) ?? new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
}
catch (Exception E)
{
apiError = E.HResult;
if (CheckConnectionStatus != null)
await CheckConnectionStatus.Invoke();
LogException(E);
}
finally
{
if (Debug_apiTiming) SaveApiTimingEntry("TicketOverview.GetCounts", timeStart, apiError);
LogMethodEnd(CM);
}
return new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
}
public override async Task<List<cF4sdApiSearchResultRelation>> GetTicketOverviewRelations(string key, bool useRoleScope, int count)
{
var CM = MethodBase.GetCurrentMethod();
LogMethodBegin(CM);
var apiError = 0;
var timeStart = DateTime.UtcNow;
var output = new List<cF4sdApiSearchResultRelation>();
try
{
if (string.IsNullOrWhiteSpace(key))
return output;
var http = GetHttpHelper(true);
var scope = useRoleScope ? "role" : "personal";
var safeKey = HttpUtility.UrlEncode(key);
var url = $"api/TicketOverview/GetRelations?key={safeKey}&scope={scope}&count={Math.Max(0, count)}";
var result = await http.GetHttpJson(url, 20000, CancellationToken.None);
if (!result.IsOk)
{
apiError = (int)result.Status;
if (CheckConnectionStatus != null)
await CheckConnectionStatus.Invoke();
LogEntry($"Error on requesting ticket overview relations for '{key}' ({scope}). Status: {result.Status}", LogLevels.Warning);
return output;
}
if (Debug_apiValues) SaveApiResultValueJson("TicketOverview.GetRelations", result.Result, url);
var relations = JsonConvert.DeserializeObject<List<cF4sdApiSearchResultRelation>>(result.Result);
if (relations != null)
output = relations;
}
catch (Exception E)
{
apiError = E.HResult;
if (CheckConnectionStatus != null)
await CheckConnectionStatus.Invoke();
LogException(E);
}
finally
{
if (Debug_apiTiming) SaveApiTimingEntry("TicketOverview.GetRelations", timeStart, apiError);
LogMethodEnd(CM);
}
return output;
}
#endregion
public override async Task<cF4SDHealthCardRawData> GetHealthCardData(cF4sdHealthCardRawDataRequest requestData)
{
var CM = MethodBase.GetCurrentMethod();
LogMethodBegin(CM);
var apiError = 0;
var timeStart = DateTime.UtcNow;
@@ -1546,7 +1561,8 @@ namespace C4IT.FASD.Cockpit.Communication
try
{
var http = GetHttpHelper(false);
var apiAccessInfo = await http.GetHttpJson($"api/GetAgentApiConfiguration", 10000, System.Threading.CancellationToken.None);
var _url = $"api/GetAgentApiConfiguration";
var apiAccessInfo = await http.GetHttpJson(_url, 10000, System.Threading.CancellationToken.None);
if (!apiAccessInfo.IsOk)
{
@@ -1554,7 +1570,7 @@ namespace C4IT.FASD.Cockpit.Communication
return false;
}
if (Debug_apiValues) SaveApiResultValueJson("GetAgentApiConfiguration", apiAccessInfo.Result);
if (Debug_apiValues) SaveApiResultValueJson("GetAgentApiConfiguration", apiAccessInfo.Result, _url);
var apiConfiguration = JsonConvert.DeserializeObject<cAgentApiConfiguration>(apiAccessInfo.Result);
if (apiConfiguration != null)
apiConfiguration.ClientSecret = cSecurePassword.Instance.Decode(apiConfiguration.ClientSecret);
@@ -1570,7 +1586,6 @@ namespace C4IT.FASD.Cockpit.Communication
catch (Exception E)
{
apiError = E.HResult;
await CheckConnectionStatus?.Invoke();
LogException(E);
}
finally
@@ -1593,7 +1608,8 @@ namespace C4IT.FASD.Cockpit.Communication
try
{
var http = GetHttpHelper(true);
var cockpitconfigResult = await http.GetHttpJson($"api/GetCockpitConfiguration", 10000, System.Threading.CancellationToken.None);
var _url = $"api/GetCockpitConfiguration";
var cockpitconfigResult = await http.GetHttpJson(_url, 10000, System.Threading.CancellationToken.None);
if (!cockpitconfigResult.IsOk)
{
@@ -1601,7 +1617,7 @@ namespace C4IT.FASD.Cockpit.Communication
return false;
}
if (Debug_apiValues) SaveApiResultValueJson("GetCockpitConfiguration", cockpitconfigResult.Result);
if (Debug_apiValues) SaveApiResultValueJson("GetCockpitConfiguration", cockpitconfigResult.Result, _url);
var Configuration = JsonConvert.DeserializeObject<cCockpitConfiguration>(cockpitconfigResult.Result);
if (Configuration?.agentApiConfiguration?.ClientSecret != null)
Configuration.agentApiConfiguration.ClientSecret = cSecurePassword.Instance.Decode(Configuration.agentApiConfiguration.ClientSecret);
@@ -1616,7 +1632,6 @@ namespace C4IT.FASD.Cockpit.Communication
catch (Exception E)
{
apiError = E.HResult;
await CheckConnectionStatus?.Invoke();
LogException(E);
}
finally
@@ -1639,7 +1654,8 @@ namespace C4IT.FASD.Cockpit.Communication
try
{
var http = GetHttpHelper(false);
var result = await http.GetHttpJson("api/QuickAction/GetQuickActionList", 15000, CancellationToken.None);
var _url = "api/QuickAction/GetQuickActionList";
var result = await http.GetHttpJson(_url, 15000, CancellationToken.None);
if (!result.IsOk)
{
@@ -1652,6 +1668,7 @@ namespace C4IT.FASD.Cockpit.Communication
}
if (result.Result is string jsonString && !string.IsNullOrWhiteSpace(jsonString))
{
if (Debug_apiValues) SaveApiResultValueJson("GetQuickActionsOfServer", result.Result, _url);
return JsonConvert.DeserializeObject<List<string>>(result.Result);
}
}
@@ -2041,10 +2058,17 @@ namespace C4IT.FASD.Cockpit.Communication
private void SaveApiResultValueJson(string ApiName, string json, string comment = null)
{
List<string> comments = null;
if (comment != null)
comments = new List<string>() { comment };
SaveApiResultValueJson(ApiName, json, comments);
try
{
List<string> comments = null;
if (comment != null)
comments = new List<string>() { comment };
SaveApiResultValueJson(ApiName, json, comments);
}
catch (Exception E)
{
LogException(E);
}
}
private void SaveApiResultValueJson(string ApiName, string json, List<string> comment)
@@ -2157,6 +2181,6 @@ namespace C4IT.FASD.Cockpit.Communication
return null;
}
}
}

View File

@@ -0,0 +1,116 @@
using C4IT.FASD.Base;
using C4IT.FASD.Cockpit.Communication;
using C4IT.FASD.Communication.Agent;
using C4IT.HTTP;
using FasdCockpitBase;
using FasdCockpitBase.Models.RemoteDesktopConnection;
using Newtonsoft.Json;
using System;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
namespace FasdCockpitCommunication.RemoteDesktopCommunication
{
internal class AgentRemoteDesktopCommunication : RemoteDesktopCommunicationBase
{
private readonly HttpClient _httpClient = new HttpClient();
public AgentRemoteDesktopCommunication()
{
_httpClient.BaseAddress = new Uri(cFasdCockpitMachineConfiguration.Instance.ServerUrl);
}
public override async Task<T> InitiateConnection<T>(IRemoteDesktopClientInfo clientInfo, bool isElevated, CancellationToken token)
{
cHttpHelper httpHelper = cFasdCockpitCommunicationWeb.GetHttpHelper(false);
string json = JsonConvert.SerializeObject(clientInfo);
string path = "api/RemoteDesktop/Initiate";
if (isElevated)
path += "?isElevated=true";
cHttpResult result = await httpHelper.PostJsonAsync(path, json, 30_000, token);
return JsonConvert.DeserializeObject<T>(result.Result);
}
public override async Task<RemoteDesktopConnectionStatusResult> GetConnectionStatus(Guid connectionId, CancellationToken token)
{
cHttpHelper http = cFasdCockpitCommunicationWeb.GetHttpHelper(false);
string path = $"api/RemoteDesktop/{connectionId}/Status";
cHttpResult result = await http.GetHttpJson(path, 15_000, token, true);
if (!result.IsOk)
return new RemoteDesktopConnectionStatusResult(RemoteDesktopConnectionStatus.Unknown);
AgentRemoteConnectionStatusDto connectionStatus = JsonConvert.DeserializeObject<AgentRemoteConnectionStatusDto>(result.Result);
return GetGeneralConnectionStatus(connectionStatus);
}
private RemoteDesktopConnectionStatusResult GetGeneralConnectionStatus(AgentRemoteConnectionStatusDto connectionStatus)
{
switch (connectionStatus.Status)
{
case AgentRemoteConnectionStatus.New:
return new RemoteDesktopConnectionStatusResult(RemoteDesktopConnectionStatus.Initiated);
case AgentRemoteConnectionStatus.Accepted:
return new RemoteDesktopConnectionStatusResult(RemoteDesktopConnectionStatus.Accepted);
case AgentRemoteConnectionStatus.DisconnectedByHelpdesk:
return new RemoteDesktopConnectionStatusResult(RemoteDesktopConnectionStatus.Finished);
case AgentRemoteConnectionStatus.DisconnectedByClient:
case AgentRemoteConnectionStatus.Failed:
return new RemoteDesktopConnectionStatusResult(RemoteDesktopConnectionStatus.Canceled);
case AgentRemoteConnectionStatus.Unknown:
default:
return new RemoteDesktopConnectionStatusResult(RemoteDesktopConnectionStatus.Unknown);
}
}
public override async Task StopConnection(Guid connectionId, CancellationToken token)
{
cHttpHelper http = cFasdCockpitCommunicationWeb.GetHttpHelper(false);
string path = $"api/RemoteDesktop/{connectionId}/Stop";
await http.DeleteAsync(path, 15_000, token, true);
}
public override async Task<HealthInformation> GetHealth(CancellationToken token)
{
cHttpHelper http = cFasdCockpitCommunicationWeb.GetHttpHelper(false);
string path = $"api/RemoteDesktop/health";
cHttpResult result = await http.GetHttpJson(path, 10_000, token, true);
if (!result.IsOk)
return new HealthInformation(HealthStatus.unhealthy);
return JsonConvert.DeserializeObject<HealthInformation>(result.Result);
}
public override async Task<bool> IsRemoteDesktopCommunicationAvailable()
{
cHttpHelper http = cFasdCockpitCommunicationWeb.GetHttpHelper(false);
string path = $"api/RemoteDesktop/IsActive";
cHttpResult result = await http.GetHttpJson(path, 5_000, CancellationToken.None, false);
if (!result.IsOk)
return false;
bool isRemoteViewerActive = JsonConvert.DeserializeObject<bool>(result.Result); ;
if (!isRemoteViewerActive)
return false;
return await base.IsRemoteDesktopCommunicationAvailable();
}
}
public class AgentRemoteClientInfo : IRemoteDesktopClientInfo
{
[JsonProperty("orgCode")]
public int OrganisationCode { get; set; }
[JsonProperty("deviceCode")]
public int DeviceCode { get; set; }
[JsonProperty("accountCode")]
public int AccountCode { get; set; }
}
}

View File

@@ -0,0 +1,18 @@
using FasdCockpitBase.Models.RemoteDesktopConnection;
using Newtonsoft.Json;
using System;
namespace FasdCockpitCommunication.RemoteDesktopCommunication
{
public class AgentRemoteDesktopConnecitonDetails : ApiResult, IRemoteDesktopConnectionDetails
{
[JsonProperty("connectionId")]
public Guid ConnectionId { get; set; }
[JsonProperty("phoenixServiceUrl")]
public Uri PhoenixServiceUrl { get; set; }
[JsonProperty("secret")]
public string Secret { get; set; }
}
}

View File

@@ -0,0 +1,20 @@
using Newtonsoft.Json;
using System.Collections.Generic;
namespace FasdCockpitCommunication.RemoteDesktopCommunication
{
public class ApiResult
{
[JsonProperty("errors")]
public IList<ApiError> Errors { get; set; }
}
public class ApiError
{
[JsonProperty("code")]
public string Code { get; set; }
[JsonProperty("message")]
public string Message { get; set; }
}
}

View File

@@ -4,7 +4,7 @@
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Microsoft.Extensions.Logging.Abstractions" publicKeyToken="adb9793829ddae60" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-10.0.0.2" newVersion="10.0.0.2" />
<bindingRedirect oldVersion="0.0.0.0-10.0.0.3" newVersion="10.0.0.3" />
</dependentAssembly>
</assemblyBinding>
</runtime>

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Newtonsoft.Json" version="13.0.4" targetFramework="net472" />
<package id="Newtonsoft.Json" version="13.0.3" targetFramework="net472" />
</packages>

View File

@@ -54,7 +54,7 @@
param(
$ComputerName = &quot;&quot;,
$AI_Prompt = &quot;how can this be fixed? Answer in german&quot;,
$AI_Key = &quot;AIzaSyCotStXy-4lkxCc5ii4NtQEG-jJM6Cnzkc&quot;
$AI_Key = &quot;AIzaSyBhXz8EU0jTyaZ4e_CLJB5mn7SMPjlJrSw&quot;
)
$ServerAddress = $env:COMPUTERNAME
$ServerPort = 7000
@@ -70,22 +70,22 @@
# get the execution directory
$dirExe = $PSScriptRoot
if (-not (Test-Path -Path &quot;$dirExe\Phoenix.Viewer\Phoenix.Viewer.exe&quot; -PathType Leaf))
if (-not (Test-Path -Path &quot;$env:ProgramFiles\EgoMind\Phoenix\Viewer\Phoenix.Viewer.exe&quot; -PathType Leaf))
{
$dirExe = (Get-Location).Path
$dirExe = &quot;$dirExe\PhoenixDemo&quot;
}
if (-not (Test-Path -Path &quot;$dirExe\Phoenix.Viewer\Phoenix.Viewer.exe&quot; -PathType Leaf))
if (-not (Test-Path -Path &quot;$env:ProgramFiles\EgoMind\Phoenix\Viewer\Phoenix.Viewer.exe&quot; -PathType Leaf))
{
exit(1)
}
# save server address to config file
$jsonConfig = Get-Content &quot;$dirExe\Phoenix.Viewer\appSettings.json&quot; | ConvertFrom-Json
$jsonConfig = Get-Content &quot;$env:ProgramFiles\EgoMind\Phoenix\Viewer\appSettings.json&quot; | ConvertFrom-Json
$jsonConfig.HostOptions.Host = &quot;http://${ServerAddress}:${ServerPort}&quot;
$jsonConfig.AIOptions.Prompt = $AI_Prompt
$jsonConfig.AIOptions.ApiKey = $AI_Key
$jsonConfig | ConvertTo-Json -depth 100 | Out-File &quot;$dirExe\Phoenix.Viewer\appSettings.json&quot; -Encoding utf8
$jsonConfig | ConvertTo-Json -depth 100 | Out-File &quot;$env:ProgramFiles\EgoMind\Phoenix\Viewer\appSettings.json&quot; -Encoding utf8
# define the argumet list
$args = @(&quot;--config appSettings.json&quot;)
@@ -95,7 +95,7 @@
}
# start the viewer
$procViewer = Start-Process -WorkingDirectory &quot;$dirExe\Phoenix.Viewer&quot; -WindowStyle Normal -FilePath &quot;$dirExe\Phoenix.Viewer\Phoenix.Viewer.exe&quot; -PassThru -ArgumentList $args
$procViewer = Start-Process -WorkingDirectory &quot;$env:ProgramFiles\EgoMind\Phoenix\Viewer&quot; -WindowStyle Normal -FilePath &quot;$env:ProgramFiles\EgoMind\Phoenix\Viewer\Phoenix.Viewer.exe&quot; -PassThru -ArgumentList $args
# get &amp; resize the viewer main window
add-type -typedefinition &quot;using System;`n using System.Runtime.InteropServices;`n public class Windows { [DllImport(`&quot;user32.dll`&quot;)] [return: MarshalAs(UnmanagedType.Bool)] public extern static bool MoveWindow(IntPtr handle, int x, int y, int width, int height, bool redraw); }&quot;
@@ -500,7 +500,7 @@
</QuickAction-Demo>
<QuickAction-Demo InformationClass="VirtuelSession" Name="Session Logoff" Section="Citrix" RunImmediate="false" CheckNamedParameter="VirtualSessionStatus" SimulatedClientConnect="1" SimulatedRuntime="1">
<QuickAction-Demo InformationClass="VirtualSession" Name="Session Logoff" Section="Citrix" RunImmediate="false" CheckNamedParameter="VirtualSessionStatus" SimulatedClientConnect="1" SimulatedRuntime="1">
<Name Lang="DE">Session abmelden</Name>
<Icon IconType="material" Name="ic_vpn_key" />
<CheckNamedParameterValues>
@@ -508,7 +508,7 @@
</CheckNamedParameterValues>
<DemoResult Result="finished">[{&quot;&quot;:&quot;Erfolgreich von Session abgemeldet&quot;}]</DemoResult>
</QuickAction-Demo>
<QuickAction-Demo InformationClass="VirtuelSession" Name="Session Hidden" Section="Citrix" RunImmediate="false" CheckNamedParameter="VirtualSessionStatus" SimulatedClientConnect="1" SimulatedRuntime="1">
<QuickAction-Demo InformationClass="VirtualSession" Name="Session Hidden" Section="Citrix" RunImmediate="false" CheckNamedParameter="VirtualSessionStatus" SimulatedClientConnect="1" SimulatedRuntime="1">
<Name Lang="DE">Session verstecken</Name>
<Icon IconType="material" Name="ic_vpn_key" />
<CheckNamedParameterValues>
@@ -516,7 +516,7 @@
</CheckNamedParameterValues>
<DemoResult Result="finished">[{&quot;&quot;:&quot;Session erfolgreich versteckt&quot;}]</DemoResult>
</QuickAction-Demo>
<QuickAction-Demo InformationClass="VirtuelSession" Name="Send message to Session" Section="Citrix" RunImmediate="false" CheckNamedParameter="VirtualSessionStatus" SimulatedClientConnect="1" SimulatedRuntime="1">
<QuickAction-Demo InformationClass="VirtualSession" Name="Send message to Session" Section="Citrix" RunImmediate="false" CheckNamedParameter="VirtualSessionStatus" SimulatedClientConnect="1" SimulatedRuntime="1">
<Name Lang="DE">Sende Nachricht an Session</Name>
<Icon IconType="material" Name="ic_vpn_key" />
<CheckNamedParameterValues>
@@ -526,7 +526,7 @@
</QuickAction-Demo>
<QuickAction-Demo InformationClass="VirtuelSession" Name="Session Hidden" Section="Citrix" RunImmediate="false" CheckNamedParameter="VirtualSessionStatus" SimulatedClientConnect="1" SimulatedRuntime="1">
<QuickAction-Demo InformationClass="VirtualSession" Name="Session Hidden" Section="Citrix" RunImmediate="false" CheckNamedParameter="VirtualSessionStatus" SimulatedClientConnect="1" SimulatedRuntime="1">
<Name Lang="DE">Session verstecken</Name>
<Icon IconType="material" Name="ic_vpn_key" />
<CheckNamedParameterValues>
@@ -534,7 +534,7 @@
</CheckNamedParameterValues>
<DemoResult Result="finished">[{&quot;&quot;:&quot;Session erfolgreich versteckt&quot;}]</DemoResult>
</QuickAction-Demo>
<QuickAction-Demo InformationClass="VirtuelSession" Name="Send message to Session" Section="Citrix" RunImmediate="false" CheckNamedParameter="VirtualSessionStatus" SimulatedClientConnect="1" SimulatedRuntime="1">
<QuickAction-Demo InformationClass="VirtualSession" Name="Send message to Session" Section="Citrix" RunImmediate="false" CheckNamedParameter="VirtualSessionStatus" SimulatedClientConnect="1" SimulatedRuntime="1">
<Name Lang="DE">Sende Nachricht an Session</Name>
<Icon IconType="material" Name="ic_vpn_key" />
<CheckNamedParameterValues>

View File

@@ -27,7 +27,7 @@
<xs:enumeration value="User" />
<xs:enumeration value="Computer" />
<xs:enumeration value="Ticket" />
<xs:enumeration value="VirtuelSession" />
<xs:enumeration value="VirtualSession" />
<xs:enumeration value="MobileDevice" />
</xs:restriction>
</xs:simpleType>
@@ -197,6 +197,16 @@
</xs:attribute>
</xs:complexType>
<xs:element name="QuickAction-Native" substitutionGroup="QuickAction">
<xs:complexType >
<xs:complexContent>
<xs:extension base="QuickAction">
<xs:attribute name="NativeName" type="xs:NCName" use="required" />
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:element>
<xs:complexType name="QuickAction-Local" abstract="true">
<xs:complexContent>
<xs:extension base="QuickAction">
@@ -249,7 +259,7 @@
<xs:extension base="QuickAction-Remote">
<xs:attribute name="Category" use="optional" />
<xs:attribute name="Action" use="optional" />
<xs:attribute name="ParamaterType" use="optional" />
<xs:attribute name="ParameterType" use="optional" />
</xs:extension>
</xs:complexContent>
</xs:complexType>

View File

@@ -54,7 +54,7 @@
</PropertyGroup>
<ItemGroup>
<Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.13.0.4\lib\net45\Newtonsoft.Json.dll</HintPath>
<HintPath>..\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
@@ -73,7 +73,7 @@
<Compile Include="F4SDCockpitCommunicationDemo.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="TicketModel.cs" />
<Compile Include="TicketOverviewDataStore.cs" />
<Compile Include="TicketOverviewDataStore.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\F4SD-Logging\F4SD-Logging.csproj">
@@ -115,12 +115,12 @@
<SubType>Designer</SubType>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Config\F4SD-MenuSection-Configuration.xml">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="Config\F4SD-QuickAction-Configuration.xml">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="Config\F4SD-MenuSection-Configuration.xml">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="Config\F4SD-QuickAction-Configuration.xml">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<None Include="app.config" />
<None Include="Config\F4SD-CopyTemplate-Configuration.xsd">
<SubType>Designer</SubType>
@@ -164,9 +164,9 @@
<None Include="MockupData\Virtuell, Vera.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Include="MockupTicketOverview\TicketOverviewGeneratedTickets.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Include="MockupTicketOverview\TicketOverviewGeneratedTickets.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Include="MockupPickup\M42Wpm-Pickup-ObjectStateReason.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
@@ -245,12 +245,12 @@
<None Include="MockupPickup\M42Wpm-Ticket-CloseCase-Services.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Include="MockupPickup\M42Wpm-Ticket-QuickCalls.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Include="MockupPickup\M42Wpm-Ticket-Categories.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Include="MockupPickup\M42Wpm-Ticket-QuickCalls.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Include="MockupPickup\M42Wpm-Ticket-Categories.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
@@ -269,4 +269,4 @@
<PropertyGroup>
<PreBuildEvent>copy "$(ProjectDir)..\..\C4IT FASD\_Common\XmlSchemas\*" "$(ProjectDir)Config"</PreBuildEvent>
</PropertyGroup>
</Project>
</Project>

View File

@@ -2,19 +2,19 @@
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.IO;
using System.Threading.Tasks;
using System.Threading;
using System.Diagnostics;
using Newtonsoft.Json;
using FasdCockpitBase.Models;
using FasdCockpitCommunicationDemo;
using C4IT.FASD.Base;
using static C4IT.Logging.cLogManager;
using FasdCockpitBase;
using C4IT.Logging;
using System.IO;
using System.Threading.Tasks;
using System.Threading;
using System.Diagnostics;
using Newtonsoft.Json;
using FasdCockpitBase.Models;
using FasdCockpitCommunicationDemo;
using C4IT.FASD.Base;
using static C4IT.Logging.cLogManager;
using FasdCockpitBase;
using C4IT.Logging;
namespace C4IT.FASD.Cockpit.Communication
@@ -23,35 +23,35 @@ namespace C4IT.FASD.Cockpit.Communication
{
private int ticketCounter = 475;
private readonly List<cF4SDHealthCardJsonRawData> MockupData = new List<cF4SDHealthCardJsonRawData>();
private readonly Dictionary<string, cF4SDHealthCardRawData.cHealthCardTable> MockupPickup = new Dictionary<string, cF4SDHealthCardRawData.cHealthCardTable>();
private readonly Dictionary<string, string> CategoryNameLookup = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
#region Ticketübersicht
private readonly Dictionary<string, Dictionary<string, List<DemoTicketRecord>>> TicketOverviewRelations =
new Dictionary<string, Dictionary<string, List<DemoTicketRecord>>>(StringComparer.OrdinalIgnoreCase);
private readonly HashSet<string> _loadedOverviewPlacements = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
private readonly object _demoTicketSync = new object();
#endregion
public cFasdCockpitCommunicationDemo()
{
if (LoadMockupData(out var loadedData))
MockupData = loadedData;
MockupPickup = LoadMockupPickup();
BuildCategoryLookup();
LoadGeneratedTickets();
}
private readonly List<cF4SDHealthCardJsonRawData> MockupData = new List<cF4SDHealthCardJsonRawData>();
private readonly Dictionary<string, cF4SDHealthCardRawData.cHealthCardTable> MockupPickup = new Dictionary<string, cF4SDHealthCardRawData.cHealthCardTable>();
private readonly Dictionary<string, string> CategoryNameLookup = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
#region Ticketübersicht
private readonly Dictionary<string, Dictionary<string, List<DemoTicketRecord>>> TicketOverviewRelations =
new Dictionary<string, Dictionary<string, List<DemoTicketRecord>>>(StringComparer.OrdinalIgnoreCase);
private readonly HashSet<string> _loadedOverviewPlacements = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
private readonly object _demoTicketSync = new object();
#endregion
public cFasdCockpitCommunicationDemo()
{
if (LoadMockupData(out var loadedData))
MockupData = loadedData;
MockupPickup = LoadMockupPickup();
BuildCategoryLookup();
LoadGeneratedTickets();
}
public override bool IsDemo() => true;
private Dictionary<Guid, cFasdApiSearchResultCollection> SearchCache = new Dictionary<Guid, cFasdApiSearchResultCollection>();
private Dictionary<string, cF4SDHealthCardRawData.cHealthCardTable> LoadMockupPickup()
private Dictionary<string, cF4SDHealthCardRawData.cHealthCardTable> LoadMockupPickup()
{
var CM = MethodBase.GetCurrentMethod();
LogMethodBegin(CM);
@@ -64,10 +64,10 @@ namespace C4IT.FASD.Cockpit.Communication
var sampleDataFiles = Directory.GetFiles(path);
foreach (var file in sampleDataFiles)
{
try
{
foreach (var file in sampleDataFiles)
{
try
{
string jsonText;
using (StreamReader streamReader = new StreamReader(file))
{
@@ -95,67 +95,67 @@ namespace C4IT.FASD.Cockpit.Communication
LogMethodEnd(CM);
}
return null;
}
private void BuildCategoryLookup()
{
CategoryNameLookup.Clear();
if (MockupPickup == null || !MockupPickup.TryGetValue("M42Wpm-Ticket-Categories", out var table))
return;
if (table?.Columns == null)
return;
if (!table.Columns.TryGetValue("id", out var idColumn))
return;
if (!table.Columns.TryGetValue("Name", out var nameColumn))
return;
for (int i = 0; i < idColumn.Values.Count; i++)
{
var id = idColumn.Values[i]?.ToString();
if (string.IsNullOrWhiteSpace(id))
continue;
var name = i < nameColumn.Values.Count ? nameColumn.Values[i]?.ToString() : null;
if (string.IsNullOrWhiteSpace(name))
continue;
CategoryNameLookup[id] = name;
}
}
private string ResolveCategoryDisplayName(string categoryIdOrName)
{
if (string.IsNullOrWhiteSpace(categoryIdOrName))
return categoryIdOrName;
if (!Guid.TryParse(categoryIdOrName, out _))
return categoryIdOrName;
if (CategoryNameLookup.TryGetValue(categoryIdOrName, out var name))
return name;
return categoryIdOrName;
}
#region Ticketübersicht
private void LoadGeneratedTickets()
{
try
{
TicketOverviewRelations.Clear();
_loadedOverviewPlacements.Clear();
var records = TicketOverviewDataStore.LoadTickets();
foreach (var record in records)
{
AppendDemoTicket(record);
}
return null;
}
private void BuildCategoryLookup()
{
CategoryNameLookup.Clear();
if (MockupPickup == null || !MockupPickup.TryGetValue("M42Wpm-Ticket-Categories", out var table))
return;
if (table?.Columns == null)
return;
if (!table.Columns.TryGetValue("id", out var idColumn))
return;
if (!table.Columns.TryGetValue("Name", out var nameColumn))
return;
for (int i = 0; i < idColumn.Values.Count; i++)
{
var id = idColumn.Values[i]?.ToString();
if (string.IsNullOrWhiteSpace(id))
continue;
var name = i < nameColumn.Values.Count ? nameColumn.Values[i]?.ToString() : null;
if (string.IsNullOrWhiteSpace(name))
continue;
CategoryNameLookup[id] = name;
}
}
private string ResolveCategoryDisplayName(string categoryIdOrName)
{
if (string.IsNullOrWhiteSpace(categoryIdOrName))
return categoryIdOrName;
if (!Guid.TryParse(categoryIdOrName, out _))
return categoryIdOrName;
if (CategoryNameLookup.TryGetValue(categoryIdOrName, out var name))
return name;
return categoryIdOrName;
}
#region Ticketübersicht
private void LoadGeneratedTickets()
{
try
{
TicketOverviewRelations.Clear();
_loadedOverviewPlacements.Clear();
var records = TicketOverviewDataStore.LoadTickets();
foreach (var record in records)
{
AppendDemoTicket(record);
}
}
catch (Exception E)
{
@@ -175,49 +175,49 @@ namespace C4IT.FASD.Cockpit.Communication
}
}
private void AppendDemoTicket(DemoTicketRecord record)
{
if (record == null)
return;
lock (_demoTicketSync)
{
if (record.TicketId == Guid.Empty || string.IsNullOrWhiteSpace(record.TileKey))
return;
var tileKey = record.TileKey.Trim();
var scopeKey = record.UseRoleScope ? "Role" : "Personal";
var placementKey = $"{record.TicketId:N}|{tileKey}|{scopeKey}";
if (!_loadedOverviewPlacements.Add(placementKey))
return;
if (!TicketOverviewRelations.TryGetValue(tileKey, out var scopeDictionary))
{
scopeDictionary = new Dictionary<string, List<DemoTicketRecord>>(StringComparer.OrdinalIgnoreCase);
TicketOverviewRelations[tileKey] = scopeDictionary;
}
if (!scopeDictionary.TryGetValue(scopeKey, out var definitions))
{
definitions = new List<DemoTicketRecord>();
scopeDictionary[scopeKey] = definitions;
}
definitions.Add(record);
var targetSample = MockupData.FirstOrDefault(data => data.SampleDataId == record.UserId);
if (targetSample == null)
return;
private void AppendDemoTicket(DemoTicketRecord record)
{
if (record == null)
return;
lock (_demoTicketSync)
{
if (record.TicketId == Guid.Empty || string.IsNullOrWhiteSpace(record.TileKey))
return;
var tileKey = record.TileKey.Trim();
var scopeKey = record.UseRoleScope ? "Role" : "Personal";
var placementKey = $"{record.TicketId:N}|{tileKey}|{scopeKey}";
if (!_loadedOverviewPlacements.Add(placementKey))
return;
if (!TicketOverviewRelations.TryGetValue(tileKey, out var scopeDictionary))
{
scopeDictionary = new Dictionary<string, List<DemoTicketRecord>>(StringComparer.OrdinalIgnoreCase);
TicketOverviewRelations[tileKey] = scopeDictionary;
}
if (!scopeDictionary.TryGetValue(scopeKey, out var definitions))
{
definitions = new List<DemoTicketRecord>();
scopeDictionary[scopeKey] = definitions;
}
definitions.Add(record);
var targetSample = MockupData.FirstOrDefault(data => data.SampleDataId == record.UserId);
if (targetSample == null)
return;
if (targetSample.Tickets.Any(ticket => ticket.Id == record.TicketId))
return;
var generatedTicket = ConvertToTicket(record);
targetSample.Tickets.Add(generatedTicket);
}
}
var generatedTicket = ConvertToTicket(record);
targetSample.Tickets.Add(generatedTicket);
}
}
private static cF4SDTicket ConvertToTicket(DemoTicketRecord record)
private static cF4SDTicketDemo ConvertToTicket(DemoTicketRecord record)
{
var status = enumTicketStatus.New;
if (!string.IsNullOrWhiteSpace(record.StatusId) && Enum.TryParse(record.StatusId, true, out enumTicketStatus parsedStatus))
@@ -226,26 +226,26 @@ namespace C4IT.FASD.Cockpit.Communication
var detail = record.Detail ?? new DemoTicketDetail();
var createdAt = record.CreatedAt == default ? DateTime.UtcNow : record.CreatedAt;
var ticket = new cF4SDTicket
var ticket = new cF4SDTicketDemo
{
Id = record.TicketId,
Name = record.DisplayName,
Summary = record.Summary,
Status = status,
AffectedUser = detail.AffectedUser ?? record.UserDisplayName,
Asset = detail.Asset,
Category = detail.Category,
ActivityType = record.ActivityType,
Description = detail.Description,
DescriptionHtml = detail.DescriptionHtml,
Solution = detail.Solution,
SolutionHtml = detail.SolutionHtml,
AffectedUser = detail.AffectedUser ?? record.UserDisplayName,
Asset = detail.Asset,
Category = detail.Category,
ActivityType = record.ActivityType,
Description = detail.Description,
DescriptionHtml = detail.DescriptionHtml,
Solution = detail.Solution,
SolutionHtml = detail.SolutionHtml,
CreationDate = createdAt.ToLocalTime(),
CreationDaysSinceNow = Math.Max(0, (int)(DateTime.UtcNow - createdAt).TotalDays),
Priority = detail.Priority ?? 0,
CreationSource = cF4SDTicket.enumTicketCreationSource.F4SD,
CreationSource = cF4SDTicketDemo.enumTicketCreationSource.F4SD,
DirectLinks = new Dictionary<string, string>(),
JournalItems = new List<cF4SDTicket.cTicketJournalItem>()
JournalItems = new List<cF4SDTicketDemo.cTicketJournalItemDemo>()
};
if (detail.Journal != null)
@@ -253,7 +253,7 @@ namespace C4IT.FASD.Cockpit.Communication
foreach (var entry in detail.Journal)
{
var journalCreation = entry?.CreationDate ?? createdAt;
ticket.JournalItems.Add(new cF4SDTicket.cTicketJournalItem
ticket.JournalItems.Add(new cF4SDTicketDemo.cTicketJournalItemDemo
{
Header = entry.Header,
Description = entry.Description,
@@ -268,41 +268,41 @@ namespace C4IT.FASD.Cockpit.Communication
return ticket;
}
public override Task<Dictionary<string, int>> GetTicketOverviewCounts(IEnumerable<string> keys, bool useRoleScope)
{
var scopeKey = useRoleScope ? "Role" : "Personal";
var comparer = StringComparer.OrdinalIgnoreCase;
var result = new Dictionary<string, int>(comparer);
var requestedKeys = keys == null
? TicketOverviewRelations.Keys.ToList()
: keys.Where(k => !string.IsNullOrWhiteSpace(k)).Distinct(comparer).ToList();
if (requestedKeys.Count == 0)
requestedKeys.AddRange(TicketOverviewRelations.Keys);
foreach (var key in requestedKeys)
{
if (string.IsNullOrWhiteSpace(key))
continue;
if (TicketOverviewRelations.TryGetValue(key, out var scopeDictionary) &&
scopeDictionary != null &&
scopeDictionary.TryGetValue(scopeKey, out var definitions) &&
definitions != null)
{
result[key] = definitions.Count;
}
else
{
result[key] = 0;
}
}
return Task.FromResult(result);
}
public override async Task<List<cF4sdApiSearchResultRelation>> GetTicketOverviewRelations(string key, bool useRoleScope, int count)
public override Task<Dictionary<string, int>> GetTicketOverviewCounts(IEnumerable<string> keys, bool useRoleScope)
{
var scopeKey = useRoleScope ? "Role" : "Personal";
var comparer = StringComparer.OrdinalIgnoreCase;
var result = new Dictionary<string, int>(comparer);
var requestedKeys = keys == null
? TicketOverviewRelations.Keys.ToList()
: keys.Where(k => !string.IsNullOrWhiteSpace(k)).Distinct(comparer).ToList();
if (requestedKeys.Count == 0)
requestedKeys.AddRange(TicketOverviewRelations.Keys);
foreach (var key in requestedKeys)
{
if (string.IsNullOrWhiteSpace(key))
continue;
if (TicketOverviewRelations.TryGetValue(key, out var scopeDictionary) &&
scopeDictionary != null &&
scopeDictionary.TryGetValue(scopeKey, out var definitions) &&
definitions != null)
{
result[key] = definitions.Count;
}
else
{
result[key] = 0;
}
}
return Task.FromResult(result);
}
public override async Task<List<cF4sdApiSearchResultRelation>> GetTicketOverviewRelations(string key, bool useRoleScope, int count)
{
var CM = MethodBase.GetCurrentMethod();
LogMethodBegin(CM);
@@ -324,39 +324,39 @@ namespace C4IT.FASD.Cockpit.Communication
await SimulateTicketOverviewLatencyAsync(count);
return new List<cF4sdApiSearchResultRelation>();
}
int requestedCount = count <= 0 ? definitions.Count : Math.Min(count, definitions.Count);
await SimulateTicketOverviewLatencyAsync(requestedCount);
var relations = new List<cF4sdApiSearchResultRelation>(requestedCount);
foreach (var definition in definitions.Take(requestedCount))
{
var detailTicket = FindTicketForOverviewRelation(definition);
var summary = definition.Summary ?? string.Empty;
if (detailTicket != null && !string.IsNullOrWhiteSpace(detailTicket.Summary))
summary = detailTicket.Summary;
var activityType = string.IsNullOrWhiteSpace(definition.ActivityType)
? null
: definition.ActivityType.Trim();
var relation = new cF4sdApiSearchResultRelation
{
Type = enumF4sdSearchResultClass.Ticket,
DisplayName = definition.DisplayName ?? string.Empty,
Name = definition.DisplayName ?? string.Empty,
int requestedCount = count <= 0 ? definitions.Count : Math.Min(count, definitions.Count);
await SimulateTicketOverviewLatencyAsync(requestedCount);
var relations = new List<cF4sdApiSearchResultRelation>(requestedCount);
foreach (var definition in definitions.Take(requestedCount))
{
var detailTicket = FindTicketForOverviewRelation(definition);
var summary = definition.Summary ?? string.Empty;
if (detailTicket != null && !string.IsNullOrWhiteSpace(detailTicket.Summary))
summary = detailTicket.Summary;
var activityType = string.IsNullOrWhiteSpace(definition.ActivityType)
? null
: definition.ActivityType.Trim();
var relation = new cF4sdApiSearchResultRelation
{
Type = enumF4sdSearchResultClass.Ticket,
DisplayName = definition.DisplayName ?? string.Empty,
Name = definition.DisplayName ?? string.Empty,
id = definition.TicketId,
Status = enumF4sdSearchResultStatus.Active,
Infos = new Dictionary<string, string>
{
["Summary"] = summary,
["StatusId"] = definition.StatusId ?? string.Empty,
["UserDisplayName"] = definition.UserDisplayName ?? string.Empty,
["UserAccount"] = definition.UserAccount ?? string.Empty,
["UserDomain"] = definition.UserDomain ?? string.Empty,
["ActivityType"] = activityType
},
Identities = new cF4sdIdentityList
{
new cF4sdIdentityEntry { Class = enumFasdInformationClass.Ticket, Id = definition.TicketId },
new cF4sdIdentityEntry { Class = enumFasdInformationClass.User, Id = definition.UserId }
Status = enumF4sdSearchResultStatus.Active,
Infos = new Dictionary<string, string>
{
["Summary"] = summary,
["StatusId"] = definition.StatusId ?? string.Empty,
["UserDisplayName"] = definition.UserDisplayName ?? string.Empty,
["UserAccount"] = definition.UserAccount ?? string.Empty,
["UserDomain"] = definition.UserDomain ?? string.Empty,
["ActivityType"] = activityType
},
Identities = new cF4sdIdentityList
{
new cF4sdIdentityEntry { Class = enumFasdInformationClass.Ticket, Id = definition.TicketId },
new cF4sdIdentityEntry { Class = enumFasdInformationClass.User, Id = definition.UserId }
}
};
relations.Add(relation);
@@ -370,40 +370,40 @@ namespace C4IT.FASD.Cockpit.Communication
}
finally
{
LogMethodEnd(CM);
}
}
private cF4SDTicket FindTicketForOverviewRelation(DemoTicketRecord definition)
{
if (definition == null || definition.TicketId == Guid.Empty)
return null;
if (definition.UserId == Guid.Empty)
return null;
var selectedData = MockupData.FirstOrDefault(data => data.SampleDataId == definition.UserId);
if (selectedData?.Tickets == null)
return null;
return selectedData.Tickets.FirstOrDefault(ticket => ticket.Id == definition.TicketId);
}
private static Task SimulateTicketOverviewLatencyAsync(int count)
{
int baseMs = 420;
int perItem = 100;
LogMethodEnd(CM);
}
}
private cF4SDTicketDemo FindTicketForOverviewRelation(DemoTicketRecord definition)
{
if (definition == null || definition.TicketId == Guid.Empty)
return null;
if (definition.UserId == Guid.Empty)
return null;
var selectedData = MockupData.FirstOrDefault(data => data.SampleDataId == definition.UserId);
if (selectedData?.Tickets == null)
return null;
return selectedData.Tickets.FirstOrDefault(ticket => ticket.Id == definition.TicketId);
}
private static Task SimulateTicketOverviewLatencyAsync(int count)
{
int baseMs = 420;
int perItem = 100;
int capped = Math.Max(0, Math.Min(count, 5));
int delay = Math.Max(240, Math.Min(baseMs + capped * perItem, 2000));
return Task.Delay(delay);
}
#endregion
private bool LoadMockupData(out List<cF4SDHealthCardJsonRawData> sampleData)
{
var CM = MethodBase.GetCurrentMethod();
LogMethodBegin(CM);
int delay = Math.Max(240, Math.Min(baseMs + capped * perItem, 2000));
return Task.Delay(delay);
}
#endregion
private bool LoadMockupData(out List<cF4SDHealthCardJsonRawData> sampleData)
{
var CM = MethodBase.GetCurrentMethod();
LogMethodBegin(CM);
sampleData = new List<cF4SDHealthCardJsonRawData>();
@@ -663,33 +663,33 @@ namespace C4IT.FASD.Cockpit.Communication
return output;
}
public override async Task<cFasdApiSearchResultCollection> GetUserSearchResults(string Name, List<string> SIDs)
{
var output = new cFasdApiSearchResultCollection();
try
{
foreach (var data in MockupData)
{
if (!string.Equals(data.SampleDataName, Name, StringComparison.InvariantCultureIgnoreCase))
continue;
//todo: add a field in demo data
var searchResultClass = enumF4sdSearchResultClass.User;
if (!output.ContainsKey(data.SampleDataName))
{
output[data.SampleDataName] = new List<cFasdApiSearchResultEntry>()
{
new cFasdApiSearchResultEntry()
{
id = data.SampleDataId,
Name = data.SampleDataName,
DisplayName = data.SampleDataName,
Type = searchResultClass
}
};
}
}
public override async Task<cFasdApiSearchResultCollection> GetUserSearchResults(string Name, List<string> SIDs)
{
var output = new cFasdApiSearchResultCollection();
try
{
foreach (var data in MockupData)
{
if (!string.Equals(data.SampleDataName, Name, StringComparison.InvariantCultureIgnoreCase))
continue;
//todo: add a field in demo data
var searchResultClass = enumF4sdSearchResultClass.User;
if (!output.ContainsKey(data.SampleDataName))
{
output[data.SampleDataName] = new List<cFasdApiSearchResultEntry>()
{
new cFasdApiSearchResultEntry()
{
id = data.SampleDataId,
Name = data.SampleDataName,
DisplayName = data.SampleDataName,
Type = searchResultClass
}
};
}
}
await Task.CompletedTask;
}
catch (Exception E)
@@ -719,8 +719,8 @@ namespace C4IT.FASD.Cockpit.Communication
var output = new List<cF4sdApiSearchResultRelation>();
switch (resultIds.First().ToString())
{
switch (resultIds.First().ToString())
{
case constGuidOlliOffline: // Olli Offline
output.Add(new cF4sdApiSearchResultRelation() { id = Guid.NewGuid(), Name = "C4-NB005", DisplayName = "C4-NB005", Infos = new Dictionary<string, string>() { ["UserAccountType"] = "AD", ["UserAccount"] = "C4-NB005", ["UserDomain"] = "C4IT" }, LastUsed = DateTime.UtcNow.AddDays(-10), Type = enumF4sdSearchResultClass.Computer, UsingLevel = 1, Identities = new cF4sdIdentityList() { new cF4sdIdentityEntry() { Class = enumFasdInformationClass.Computer, Id = Guid.Parse(constGuidOlliOffline) }, new cF4sdIdentityEntry() { Class = enumFasdInformationClass.Computer, Id = Guid.NewGuid() } } });
@@ -878,10 +878,10 @@ namespace C4IT.FASD.Cockpit.Communication
var demoTickets = await GetDemoTicketData(new cF4sdHealthCardRawDataRequest() { Identities = new cF4sdIdentityList() { new cF4sdIdentityEntry() { Class = enumFasdInformationClass.User, Id = Guid.Parse(constGuidTimoTicket) }, new cF4sdIdentityEntry() { Class = enumFasdInformationClass.Computer, Id = Guid.NewGuid() } } });
foreach (var demoTicket in demoTickets)
{
output.Add(new cF4sdApiSearchResultRelation() { id = demoTicket.Id, Name = demoTicket.Name, DisplayName = demoTicket.Name, Infos = new Dictionary<string, string>() { ["Summary"] = demoTicket.Summary, ["Status"] = demoTicket.Status.ToString(), ["StatusId"] = ((int)demoTicket.Status).ToString(), ["Asset"] = demoTicket.Asset, ["ActivityType"] = demoTicket.ActivityType }, Type = enumF4sdSearchResultClass.Ticket, Identities = new cF4sdIdentityList() { new cF4sdIdentityEntry() { Class = enumFasdInformationClass.User, Id = Guid.Parse(constGuidTimoTicket) }, new cF4sdIdentityEntry() { Class = enumFasdInformationClass.Ticket, Id = demoTicket.Id } } });
}
foreach (var demoTicket in demoTickets)
{
output.Add(new cF4sdApiSearchResultRelation() { id = demoTicket.Id, Name = demoTicket.Name, DisplayName = demoTicket.Name, Infos = new Dictionary<string, string>() { ["Summary"] = demoTicket.Summary, ["Status"] = demoTicket.Status.ToString(), ["StatusId"] = ((int)demoTicket.Status).ToString(), ["Asset"] = demoTicket.Asset, ["ActivityType"] = demoTicket.ActivityType }, Type = enumF4sdSearchResultClass.Ticket, Identities = new cF4sdIdentityList() { new cF4sdIdentityEntry() { Class = enumFasdInformationClass.User, Id = Guid.Parse(constGuidTimoTicket) }, new cF4sdIdentityEntry() { Class = enumFasdInformationClass.Ticket, Id = demoTicket.Id } } });
}
break;
case constGuidTimoTicketComputer:
@@ -891,68 +891,68 @@ namespace C4IT.FASD.Cockpit.Communication
case constGuidComputer: // Computer
break;
default:
output.Add(new cF4sdApiSearchResultRelation() { id = Guid.NewGuid(), Name = "C4IT-007", DisplayName = "C4IT-007", LastUsed = DateTime.UtcNow, Type = enumF4sdSearchResultClass.Computer, UsingLevel = 1, Identities = new cF4sdIdentityList() { new cF4sdIdentityEntry() { Class = enumFasdInformationClass.User, Id = resultIds.First() }, new cF4sdIdentityEntry() { Class = enumFasdInformationClass.Computer, Id = Guid.NewGuid() } } });
break;
}
if (resultType == enumF4sdSearchResultClass.User)
{
foreach (var userId in resultIds)
{
AppendDemoTicketRelationsForUser(userId, output);
}
}
await Task.CompletedTask;
output = output.OrderByDescending(relation => relation.LastUsed).ToList();
return output;
}
private void AppendDemoTicketRelationsForUser(Guid userId, List<cF4sdApiSearchResultRelation> output)
{
if (userId == Guid.Empty || output == null)
return;
var selectedData = MockupData.FirstOrDefault(data => data?.SampleDataId == userId);
if (selectedData?.Tickets == null)
return;
foreach (var demoTicket in selectedData.Tickets)
{
if (demoTicket == null || demoTicket.Id == Guid.Empty)
continue;
if (output.Any(relation => relation.Type == enumF4sdSearchResultClass.Ticket && relation.id == demoTicket.Id))
continue;
output.Add(new cF4sdApiSearchResultRelation()
{
id = demoTicket.Id,
Name = demoTicket.Name,
DisplayName = demoTicket.Name,
LastUsed = demoTicket.CreationDate == default ? DateTime.UtcNow : demoTicket.CreationDate.ToUniversalTime(),
Type = enumF4sdSearchResultClass.Ticket,
UsingLevel = 1,
Infos = new Dictionary<string, string>()
{
["Summary"] = demoTicket.Summary ?? string.Empty,
["Status"] = demoTicket.Status.ToString(),
["StatusId"] = ((int)demoTicket.Status).ToString(),
["Asset"] = demoTicket.Asset ?? string.Empty,
["ActivityType"] = demoTicket.ActivityType
},
Identities = new cF4sdIdentityList()
{
new cF4sdIdentityEntry() { Class = enumFasdInformationClass.User, Id = userId },
new cF4sdIdentityEntry() { Class = enumFasdInformationClass.Ticket, Id = demoTicket.Id }
}
});
}
}
public override async Task<cF4SDHealthCardRawData> GetHealthCardData(cF4sdHealthCardRawDataRequest requestData)
{
output.Add(new cF4sdApiSearchResultRelation() { id = Guid.NewGuid(), Name = "C4IT-007", DisplayName = "C4IT-007", LastUsed = DateTime.UtcNow, Type = enumF4sdSearchResultClass.Computer, UsingLevel = 1, Identities = new cF4sdIdentityList() { new cF4sdIdentityEntry() { Class = enumFasdInformationClass.User, Id = resultIds.First() }, new cF4sdIdentityEntry() { Class = enumFasdInformationClass.Computer, Id = Guid.NewGuid() } } });
break;
}
if (resultType == enumF4sdSearchResultClass.User)
{
foreach (var userId in resultIds)
{
AppendDemoTicketRelationsForUser(userId, output);
}
}
await Task.CompletedTask;
output = output.OrderByDescending(relation => relation.LastUsed).ToList();
return output;
}
private void AppendDemoTicketRelationsForUser(Guid userId, List<cF4sdApiSearchResultRelation> output)
{
if (userId == Guid.Empty || output == null)
return;
var selectedData = MockupData.FirstOrDefault(data => data?.SampleDataId == userId);
if (selectedData?.Tickets == null)
return;
foreach (var demoTicket in selectedData.Tickets)
{
if (demoTicket == null || demoTicket.Id == Guid.Empty)
continue;
if (output.Any(relation => relation.Type == enumF4sdSearchResultClass.Ticket && relation.id == demoTicket.Id))
continue;
output.Add(new cF4sdApiSearchResultRelation()
{
id = demoTicket.Id,
Name = demoTicket.Name,
DisplayName = demoTicket.Name,
LastUsed = demoTicket.CreationDate == default ? DateTime.UtcNow : demoTicket.CreationDate.ToUniversalTime(),
Type = enumF4sdSearchResultClass.Ticket,
UsingLevel = 1,
Infos = new Dictionary<string, string>()
{
["Summary"] = demoTicket.Summary ?? string.Empty,
["Status"] = demoTicket.Status.ToString(),
["StatusId"] = ((int)demoTicket.Status).ToString(),
["Asset"] = demoTicket.Asset ?? string.Empty,
["ActivityType"] = demoTicket.ActivityType
},
Identities = new cF4sdIdentityList()
{
new cF4sdIdentityEntry() { Class = enumFasdInformationClass.User, Id = userId },
new cF4sdIdentityEntry() { Class = enumFasdInformationClass.Ticket, Id = demoTicket.Id }
}
});
}
}
public override async Task<cF4SDHealthCardRawData> GetHealthCardData(cF4sdHealthCardRawDataRequest requestData)
{
var CM = MethodBase.GetCurrentMethod();
LogMethodBegin(CM);
@@ -961,22 +961,22 @@ namespace C4IT.FASD.Cockpit.Communication
try
{
await Task.Delay(250);
var selectedData = MockupData.FirstOrDefault(data => requestData.Identities.Any(identity => identity.Id == data.SampleDataId));
if (selectedData is null)
return output;
await Task.Delay(250);
output = selectedData.GetHealthCardData();
var ticketRequest = requestData.Identities.FirstOrDefault(data => data.Class is enumFasdInformationClass.Ticket);
if (ticketRequest != null)
{
var selectedTicket = selectedData.Tickets.FirstOrDefault(ticket => ticket.Id == ticketRequest.Id);
if (selectedTicket != null)
{
string ticketStatusString = string.Empty;
var selectedData = MockupData.FirstOrDefault(data => requestData.Identities.Any(identity => identity.Id == data.SampleDataId));
if (selectedData is null)
return output;
output = selectedData.GetHealthCardData();
var ticketRequest = requestData.Identities.FirstOrDefault(data => data.Class is enumFasdInformationClass.Ticket);
if (ticketRequest != null)
{
var selectedTicket = selectedData.Tickets.FirstOrDefault(ticket => ticket.Id == ticketRequest.Id);
if (selectedTicket != null)
{
string ticketStatusString = string.Empty;
switch (selectedTicket.Status)
{
case enumTicketStatus.Unknown:
@@ -1009,15 +1009,15 @@ namespace C4IT.FASD.Cockpit.Communication
["AffectedUser"] = new cF4SDHealthCardRawData.cHealthCardTableColumn(outputTable) { Values = new List<object>() { selectedTicket.AffectedUser } },
["AssetName"] = new cF4SDHealthCardRawData.cHealthCardTableColumn(outputTable) { Values = new List<object>() { selectedTicket.Asset } },
["CreationDaysSinceNow"] = new cF4SDHealthCardRawData.cHealthCardTableColumn(outputTable) { Values = new List<object>() { selectedTicket.CreationDaysSinceNow } },
["CreationDate"] = new cF4SDHealthCardRawData.cHealthCardTableColumn(outputTable) { Values = new List<object>() { selectedTicket.CreationDate.ToUniversalTime() } },
["ClosingDaysSinceNow"] = new cF4SDHealthCardRawData.cHealthCardTableColumn(outputTable) { Values = new List<object>() { selectedTicket.ClosingDaysSinceNow } },
["ClosingDate"] = new cF4SDHealthCardRawData.cHealthCardTableColumn(outputTable) { Values = new List<object>() { selectedTicket.ClosingDate } },
["Category"] = new cF4SDHealthCardRawData.cHealthCardTableColumn(outputTable) { Values = new List<object>() { ResolveCategoryDisplayName(selectedTicket.Category) } },
["CategoryId"] = new cF4SDHealthCardRawData.cHealthCardTableColumn(outputTable) { Values = new List<object>() { selectedTicket.Category } },
["CreationSource"] = new cF4SDHealthCardRawData.cHealthCardTableColumn(outputTable) { Values = new List<object>() { selectedTicket.CreationSource.ToString() } },
["Description"] = new cF4SDHealthCardRawData.cHealthCardTableColumn(outputTable) { Values = new List<object>() { selectedTicket.Description?.ToString() } },
["DescriptionHtml"] = new cF4SDHealthCardRawData.cHealthCardTableColumn(outputTable) { Values = new List<object>() { selectedTicket.DescriptionHtml?.ToString() } },
["Summary"] = new cF4SDHealthCardRawData.cHealthCardTableColumn(outputTable) { Values = new List<object>() { selectedTicket.Summary.ToString() } },
["CreationDate"] = new cF4SDHealthCardRawData.cHealthCardTableColumn(outputTable) { Values = new List<object>() { selectedTicket.CreationDate.ToUniversalTime() } },
["ClosingDaysSinceNow"] = new cF4SDHealthCardRawData.cHealthCardTableColumn(outputTable) { Values = new List<object>() { selectedTicket.ClosingDaysSinceNow } },
["ClosingDate"] = new cF4SDHealthCardRawData.cHealthCardTableColumn(outputTable) { Values = new List<object>() { selectedTicket.ClosingDate } },
["Category"] = new cF4SDHealthCardRawData.cHealthCardTableColumn(outputTable) { Values = new List<object>() { ResolveCategoryDisplayName(selectedTicket.Category) } },
["CategoryId"] = new cF4SDHealthCardRawData.cHealthCardTableColumn(outputTable) { Values = new List<object>() { selectedTicket.Category } },
["CreationSource"] = new cF4SDHealthCardRawData.cHealthCardTableColumn(outputTable) { Values = new List<object>() { selectedTicket.CreationSource.ToString() } },
["Description"] = new cF4SDHealthCardRawData.cHealthCardTableColumn(outputTable) { Values = new List<object>() { selectedTicket.Description?.ToString() } },
["DescriptionHtml"] = new cF4SDHealthCardRawData.cHealthCardTableColumn(outputTable) { Values = new List<object>() { selectedTicket.DescriptionHtml?.ToString() } },
["Summary"] = new cF4SDHealthCardRawData.cHealthCardTableColumn(outputTable) { Values = new List<object>() { selectedTicket.Summary.ToString() } },
["Solution"] = new cF4SDHealthCardRawData.cHealthCardTableColumn(outputTable) { Values = new List<object>() { selectedTicket.Solution?.ToString() } },
["SolutionHtml"] = new cF4SDHealthCardRawData.cHealthCardTableColumn(outputTable) { Values = new List<object>() { selectedTicket.SolutionHtml?.ToString() } },
};
@@ -1470,17 +1470,17 @@ namespace C4IT.FASD.Cockpit.Communication
return null;
}
private cF4SDTicket GetTicketFromWriteParameter(cF4SDWriteParameters writeParams)
private cF4SDTicketDemo GetTicketFromWriteParameter(cF4SDWriteParameters writeParams)
{
cF4SDTicket output = null;
cF4SDTicketDemo output = null;
try
{
output = new cF4SDTicket()
output = new cF4SDTicketDemo()
{
Id = writeParams.id,
DirectLinks = new Dictionary<string, string>(),
JournalItems = new List<cF4SDTicket.cTicketJournalItem>()
JournalItems = new List<cF4SDTicketDemo.cTicketJournalItemDemo>()
};
if (writeParams.Values.TryGetValue("Summary", out var summary))
@@ -1498,15 +1498,15 @@ namespace C4IT.FASD.Cockpit.Communication
if (writeParams.Values.TryGetValue("Asset", out var asset))
output.Asset = asset.ToString();
if (writeParams.Values.TryGetValue("Category", out var category))
output.Category = category.ToString();
if (writeParams.Values.TryGetValue("ActivityType", out var activityType))
output.ActivityType = activityType?.ToString()?.Trim();
if (writeParams.Values.TryGetValue("CreationSource", out var creationSourceObj))
if (Enum.TryParse(creationSourceObj.ToString(), out cF4SDTicket.enumTicketCreationSource creationSource))
output.CreationSource = creationSource;
if (writeParams.Values.TryGetValue("Category", out var category))
output.Category = category.ToString();
if (writeParams.Values.TryGetValue("ActivityType", out var activityType))
output.ActivityType = activityType?.ToString()?.Trim();
if (writeParams.Values.TryGetValue("CreationSource", out var creationSourceObj))
if (Enum.TryParse(creationSourceObj.ToString(), out cF4SDTicketDemo.enumTicketCreationSource creationSource))
output.CreationSource = creationSource;
if (writeParams.Values.TryGetValue("Status", out var statusObj))
{
@@ -1588,13 +1588,13 @@ namespace C4IT.FASD.Cockpit.Communication
return false;
}
private cF4SDTicket.cTicketJournalItem GetJournalItemFromWriteParameter(cF4SDWriteParameters writeParams)
private cF4SDTicketDemo.cTicketJournalItemDemo GetJournalItemFromWriteParameter(cF4SDWriteParameters writeParams)
{
cF4SDTicket.cTicketJournalItem output = null;
cF4SDTicketDemo.cTicketJournalItemDemo output = null;
try
{
output = new cF4SDTicket.cTicketJournalItem();
output = new cF4SDTicketDemo.cTicketJournalItemDemo();
if (writeParams.Values.TryGetValue("Header", out var header))
output.Header = header.ToString();
@@ -1681,10 +1681,10 @@ namespace C4IT.FASD.Cockpit.Communication
public override Task<bool> Matrix42TicketFinalization(cApiM42Ticket ticketData) => Task.FromResult(true);
private async Task<List<cF4SDTicket>> GetDemoTicketData(cF4sdHealthCardRawDataRequest requestData)
private async Task<List<cF4SDTicketDemo>> GetDemoTicketData(cF4sdHealthCardRawDataRequest requestData)
{
await Task.CompletedTask;
var output = new List<cF4SDTicket>();
var output = new List<cF4SDTicketDemo>();
try
{
var selectedData = MockupData.FirstOrDefault(data => requestData.Identities.Any(identity => identity.Id == data.SampleDataId));
@@ -1702,16 +1702,16 @@ namespace C4IT.FASD.Cockpit.Communication
return output;
}
public async Task<List<cF4SDTicketSummary>> GetTicketSummaries(cF4sdHealthCardRawDataRequest requestData)
public async Task<List<cF4SDTicketSummaryDemo>> GetTicketSummaries(cF4sdHealthCardRawDataRequest requestData)
{
var output = new List<cF4SDTicketSummary>();
var output = new List<cF4SDTicketSummaryDemo>();
try
{
var demoTickets = await GetDemoTicketData(requestData);
foreach (var ticket in demoTickets)
{
output.Add(new cF4SDTicketSummary()
output.Add(new cF4SDTicketSummaryDemo()
{
Id = ticket.Id,
Name = ticket.Name,
@@ -1738,17 +1738,17 @@ namespace C4IT.FASD.Cockpit.Communication
return true;
}
public override async Task<bool> GetCockpitConfiguration()
{
cCockpitConfiguration.Instance = new cCockpitConfiguration();
cCockpitConfiguration.Instance.agentApiConfiguration = new cAgentApiConfiguration() { ApiUrl = "", ClientId = "", ClientSecret = "", LogonUrl = "", OrganizationCode = 0 };
cCockpitConfiguration.Instance.m42ServerConfiguration = new cM42ServerConfiguration() { Server = "https://srvwsm001.imagoverum.com" };
cCockpitConfiguration.Instance.GlobalConfig = null;
await Task.CompletedTask;
return true;
}
public override Task<bool> GetAgentOnlineStatus(int AgentDeviceId, int? AgentUserId = null) => Task.FromResult(true);
public override async Task<bool> GetCockpitConfiguration()
{
cCockpitConfiguration.Instance = new cCockpitConfiguration();
cCockpitConfiguration.Instance.agentApiConfiguration = new cAgentApiConfiguration() { ApiUrl = "", ClientId = "", ClientSecret = "", LogonUrl = "", OrganizationCode = 0 };
cCockpitConfiguration.Instance.m42ServerConfiguration = new cM42ServerConfiguration() { Server = "https://srvwsm001.imagoverum.com" };
cCockpitConfiguration.Instance.GlobalConfig = null;
await Task.CompletedTask;
return true;
}
public override Task<bool> GetAgentOnlineStatus(int AgentDeviceId, int? AgentUserId = null) => Task.FromResult(true);
public override Task<cF4sdAgentScript> GetQuickActionOfAgent(int ScriptId) => Task.FromResult(new cF4sdAgentScript() { Id = ScriptId, Name = "AgentScript", Type = enumAgentScriptType.user, UserPermissionRequired = false });
@@ -1793,6 +1793,11 @@ namespace C4IT.FASD.Cockpit.Communication
return new cF4sdStagedSearchResultRelations() { Relations = relations };
}
public override Task StopGatheringRelations(Guid id, CancellationToken token)
{
return Task.CompletedTask;
}
private async Task DoLocalQuickActionAsync(string ActionPrefix, string ActionNaming)
{
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }

View File

@@ -7,7 +7,7 @@ using C4IT.FASD.Base;
namespace FasdCockpitCommunicationDemo
{
public class cF4SDTicketSummary
public class cF4SDTicketSummaryDemo
{
public Guid Id { get; set; }
public string Name { get; set; }
@@ -15,7 +15,7 @@ namespace FasdCockpitCommunicationDemo
public enumTicketStatus Status { get; set; }
}
public class cF4SDTicket : cF4SDTicketSummary
public class cF4SDTicketDemo : cF4SDTicketSummaryDemo
{
public enum enumTicketCreationSource
{
@@ -26,7 +26,7 @@ namespace FasdCockpitCommunicationDemo
F4SD = 3
}
public class cTicketJournalItem
public class cTicketJournalItemDemo
{
public double CreationDaysSinceNow { get; set; }
public DateTime CreationDate { get; set; }
@@ -45,16 +45,16 @@ namespace FasdCockpitCommunicationDemo
public DateTime? ClosingDate { get; set; }
public enumTicketCreationSource CreationSource { get; set; }
public string Description { get; set; }
public string DescriptionHtml { get; set; }
public int Priority { get; set; }
public string Category { get; set; }
public string ActivityType { get; set; }
public string Solution { get; set; }
public string SolutionHtml { get; set; }
public Dictionary<string, string> DirectLinks { get; set; }
public string Description { get; set; }
public string DescriptionHtml { get; set; }
public int Priority { get; set; }
public string Category { get; set; }
public string ActivityType { get; set; }
public string Solution { get; set; }
public string SolutionHtml { get; set; }
public Dictionary<string, string> DirectLinks { get; set; }
public List<cTicketJournalItem> JournalItems { get; set; }
public List<cTicketJournalItemDemo> JournalItems { get; set; }
}
}

View File

@@ -4,7 +4,7 @@
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Microsoft.Extensions.Logging.Abstractions" publicKeyToken="adb9793829ddae60" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-10.0.0.2" newVersion="10.0.0.2" />
<bindingRedirect oldVersion="0.0.0.0-10.0.0.3" newVersion="10.0.0.3" />
</dependentAssembly>
</assemblyBinding>
</runtime>

View File

@@ -28,7 +28,7 @@ namespace C4IT.FASD.Base
public List<cF4SDHealthCardRawData.cHealthCardDetailsTable> DetailsTables { get; set; }
public List<cF4SDTicket> Tickets { get; set; } = new List<cF4SDTicket>();
public List<cF4SDTicketDemo> Tickets { get; set; } = new List<cF4SDTicketDemo>();
public cF4SDHealthCardRawData GetHealthCardData()
{

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Newtonsoft.Json" version="13.0.4" targetFramework="net472" />
<package id="Newtonsoft.Json" version="13.0.3" targetFramework="net472" />
</packages>

View File

@@ -29,7 +29,7 @@
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Extensions.Logging.Abstractions" publicKeyToken="adb9793829ddae60" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-10.0.0.2" newVersion="10.0.0.2" />
<bindingRedirect oldVersion="0.0.0.0-10.0.0.3" newVersion="10.0.0.3" />
</dependentAssembly>
</assemblyBinding>
</runtime>

View File

@@ -506,13 +506,14 @@ namespace FasdDesktopUi
closeUserSessionTask = cFasdCockpitCommunicationBase.Instance?.CloseUserSession(cFasdCockpitConfig.SessionId).ConfigureAwait(false);
}
await cFasdCockpitCommunicationBase.Instance?.TerminateAsync();
if (cFasdCockpitCommunicationBase.Instance != null)
await cFasdCockpitCommunicationBase.Instance?.TerminateAsync();
if (notifyIcon != null)
{
try
{
cConnectionStatusHelper.Instance.IsActive = false;
cConnectionStatusHelper.Instance.ApplicationIsExiting = true;
notifyIcon.Visible = false;
notifyIcon.Dispose();
cAppStartUp.Terminate();

View File

@@ -30,6 +30,8 @@ using C4IT.MultiLanguage;
using C4IT.F4SD.TAPI;
using static C4IT.Logging.cLogManager;
using F4SD.Gamification.Services;
using F4SD.Gamification;
namespace FasdDesktopUi
@@ -48,6 +50,9 @@ namespace FasdDesktopUi
LogMethodBegin(CM);
try
{
GamificationService.Initialize();
LevelService.LevelChanged += HandleLevelChanged;
#if isDemo
cFasdCockpitCommunicationBase.Instance = new cFasdCockpitCommunicationDemo();
#else
@@ -91,6 +96,7 @@ namespace FasdDesktopUi
}
finally
{
splashScreen?.Hide();
LogMethodEnd(CM);
}
@@ -98,6 +104,20 @@ namespace FasdDesktopUi
return false;
}
private static void HandleLevelChanged(object sender, LevelEventArgs e)
{
if (e.CurrentLevel <= 1 || !cFasdCockpitConfig.Instance.Global.UseGamification)
return;
Dispatcher.CurrentDispatcher.Invoke(async () =>
{
Pages.LevelUpPage.LevelUpPage levelUpWindow = new Pages.LevelUpPage.LevelUpPage();
levelUpWindow.NewLevel = e.CurrentLevel;
levelUpWindow.LevelTitle = e.LevelTitle;
levelUpWindow.Show();
});
}
public static bool ProcessCommandLine(string[] Args)
{
var CM = MethodBase.GetCurrentMethod();
@@ -669,11 +689,11 @@ namespace FasdDesktopUi
{
userInfo = cFasdCockpitCommunicationBase.CockpitUserInfo;
}
if (cFasdCockpitConfig.Instance.HasM42Configuration())
if (userInfo?.possibleLogons != null)
{
if (userInfo.possibleLogons.Contains(enumAdditionalAuthentication.M42WinLogon))
{
if (cFasdCockpitConfig.Instance.HasM42Configuration())
if (userInfo?.possibleLogons != null)
{
if (userInfo.possibleLogons.Contains(enumAdditionalAuthentication.M42WinLogon))
{
if (App.M42OptionMenuItem != null)
{
App.M42OptionMenuItem.Visible = true;

View File

@@ -0,0 +1,27 @@
using System;
using System.Globalization;
using System.Windows.Data;
using System.Windows.Markup;
namespace FasdDesktopUi.Basics.Converter
{
[ValueConversion(typeof(string), typeof(XmlLanguage))]
internal class LanguageCultureConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (!(value is string tag))
return Binding.DoNothing;
return XmlLanguage.GetLanguage(tag);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (!(value is XmlLanguage lang))
return Binding.DoNothing;
return lang.IetfLanguageTag;
}
}
}

View File

@@ -31,7 +31,7 @@ namespace FasdDesktopUi.Basics
var http = GetHttpHelper(false);
var searchResultInfoClass = cF4sdIdentityEntry.GetFromSearchResult(enumF4sdSearchResultClass.Computer);
var parameter = new cF4SDServerQuickActionParameters() { Action = ServerAction.Action, Category = ServerAction.Category, ParamaterType = ServerAction.ParameterType, Identities = dataProvider.Identities, AdjustableParameter = ParameterDictionary };
var parameter = new cF4SDServerQuickActionParameters() { Action = ServerAction.Action, Category = ServerAction.Category, ParameterType = ServerAction.ParameterType, Identities = dataProvider.Identities, AdjustableParameter = ParameterDictionary };
var payload = JsonConvert.SerializeObject(parameter);
var result = await http.PostJsonAsync("api/QuickAction/Run", payload, 15000, CancellationToken.None);

View File

@@ -0,0 +1,126 @@
using C4IT.FASD.Base;
using FasdDesktopUi.Basics.Enums;
using FasdDesktopUi.Basics.Models;
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using static C4IT.Logging.cLogManager;
namespace FasdDesktopUi.Basics.Helper
{
internal static class ActionDisplayTypeInspector
{
internal static enumActionDisplayType GetDisplayType(cFasdBaseConfigMenuItem menuDataDefinition, cNamedParameterList namedParameterEntries, IEnumerable<enumFasdInformationClass> availableInformationClasses)
{
if (menuDataDefinition.IsHidden)
return enumActionDisplayType.hidden;
else if (IsEnabled(menuDataDefinition, namedParameterEntries, availableInformationClasses))
return enumActionDisplayType.enabled;
else
return enumActionDisplayType.disabled;
}
private static bool IsEnabled(cFasdBaseConfigMenuItem menuDataDefinition, cNamedParameterList namedParameterEntries, IEnumerable<enumFasdInformationClass> availableInformationClasses)
{
if (!HasRequiredInformationClasses(menuDataDefinition, availableInformationClasses))
return false;
if (menuDataDefinition is cFasdQuickAction quickActionDefinition && !IsQuickActionEnabled(quickActionDefinition, namedParameterEntries))
return false;
else
return true;
}
private static bool IsQuickActionEnabled(cFasdQuickAction quickActionDefinition, cNamedParameterList namedParameterEntries)
{
if (!string.IsNullOrEmpty(quickActionDefinition.CheckFilePath) && !FileExists(quickActionDefinition.CheckFilePath))
return false;
if (!string.IsNullOrEmpty(quickActionDefinition.CheckRegistryEntry) && !RegistryEntryExists(quickActionDefinition.CheckRegistryEntry))
return false;
if (namedParameterEntries is null)
return true;
cNamedParameterEntryBase namedParameterValue = null;
if (!string.IsNullOrEmpty(quickActionDefinition.CheckNamedParameter) && !namedParameterEntries.TryGetValue(quickActionDefinition.CheckNamedParameter, out namedParameterValue))
return false;
if (namedParameterValue != null && quickActionDefinition.CheckNamedParameterValues != null && !quickActionDefinition.CheckNamedParameterValues.Contains(namedParameterValue.GetValue()))
return false;
return true;
}
private static bool HasRequiredInformationClasses(cFasdBaseConfigMenuItem definition, IEnumerable<enumFasdInformationClass> availableInformationClasses)
{
if (definition?.InformationClasses is null)
return true;
if (definition.InformationClasses.Count == 0)
return true;
if (definition.InformationClasses.Any(i => !availableInformationClasses.Contains(i)))
return false;
return true;
}
private static bool FileExists(string path)
{
try
{
if (string.IsNullOrWhiteSpace(path))
return true;
var specialFolders = Enum.GetValues(typeof(Environment.SpecialFolder)).Cast<Environment.SpecialFolder>();
foreach (var specialFolder in specialFolders)
{
string specialFolderName = $"%{specialFolder}%";
string specialFolderPath = Environment.GetFolderPath(specialFolder);
path = path.Replace(specialFolderName, specialFolderPath);
}
path = Environment.ExpandEnvironmentVariables(path);
return File.Exists(path);
}
catch (Exception E)
{
LogException(E);
return false;
}
}
private static bool RegistryEntryExists(string entry)
{
try
{
if (string.IsNullOrWhiteSpace(entry))
return true;
string rootPath = entry.Split('\\')[0];
entry = entry.Replace(rootPath, "").Remove(0, 1);
switch (rootPath)
{
case "HKEY_LOCAL_MACHINE":
case "HKLM":
return Registry.LocalMachine.OpenSubKey(entry) != null;
case "HKEY_CURRENT_USER":
case "HKCU":
return Registry.CurrentUser.OpenSubKey(entry) != null;
default:
return false;
}
}
catch (Exception E)
{
LogException(E);
return false;
}
}
}
}

View File

@@ -7,7 +7,6 @@ using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using C4IT.Configuration;
using C4IT.F4SD.DisplayFormatting;
@@ -27,7 +26,6 @@ using FasdDesktopUi.Pages.SlimPage.Models;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
using static C4IT.FASD.Base.cF4SDHealthCardRawData;
using static C4IT.Logging.cLogManager;
@@ -80,7 +78,7 @@ namespace FasdDesktopUi.Basics.Helper
{
this.dataProvider = dataProvider;
_menuDataProvider = new MenuItemDataProvider(dataProvider);
cUtility.RawValueFormatter.SetDefaultCulture(new CultureInfo(cFasdCockpitConfig.Instance.SelectedLanguage));
cUtility.RawValueFormatter.SetDefaultCulture(cFasdCockpitConfig.Instance.SelectedCulture);
cUtility.RawValueFormatter.SetDefaultTimeZone(TimeZoneInfo.Local);
HistoryData = new cHealthCardHistoryDataHelper(this);
@@ -149,7 +147,7 @@ namespace FasdDesktopUi.Basics.Helper
if (informationClasses is null)
return null;
foreach (var healthCard in cF4SDCockpitXmlConfig.Instance?.HealthCardConfig?.HealthCards.Values)
foreach (var healthCard in cF4SDCockpitXmlConfig.Instance?.HealthCardConfig?.HealthCards?.Values)
{
bool found = true;
@@ -250,7 +248,7 @@ namespace FasdDesktopUi.Basics.Helper
{
List<object> output = new List<object>();
if (healthCardColumn?.Values == null)
if (healthCardColumn?.Values == null || startingIndex < 0)
return output;
try
@@ -453,13 +451,9 @@ namespace FasdDesktopUi.Basics.Helper
var stateValue = GetStateValueAt(stateDefinition.DatabaseInfo, dayIndex, isStatic);
enumHighlightColor? tempColor = null;
if (stateValue != null)
{
tempColor = GetHighlightColor(stateValue, stateDefinition, dayIndex, isStatic);
if (tempColor == enumHighlightColor.none && stateValue != null)
tempColor = enumHighlightColor.green;
}
enumHighlightColor? tempColor = GetHighlightColor(stateValue, stateDefinition, dayIndex, isStatic);
if (tempColor == enumHighlightColor.none && stateValue != null)
tempColor = enumHighlightColor.green;
if (output == null)
output = tempColor;
@@ -492,7 +486,7 @@ namespace FasdDesktopUi.Basics.Helper
}
else
{
FormattingOptions options = new FormattingOptions() { ReferenceDate = DateTime.UtcNow.Date.AddDays(columnIndex), TimeZone = TimeZoneInfo.Local };
FormattingOptions options = new FormattingOptions() { ReferenceDate = DateTime.UtcNow.Date.AddDays(-columnIndex), TimeZone = TimeZoneInfo.Local };
var _c = cUtility.RawValueFormatter.GetDisplayValue(value, Requirements.valueState.DisplayType, options);
if (!string.IsNullOrWhiteSpace(_c))
cellContent.Content = _c;
@@ -689,11 +683,19 @@ namespace FasdDesktopUi.Basics.Helper
{
if (NamedParameters == null)
return false;
var _invert = false;
if (namedParameter.StartsWith("!"))
{
_invert = true;
namedParameter = namedParameter.Remove(0, 1);
}
if (!NamedParameters.TryGetValue(namedParameter, out var entry))
return false;
return _invert;
var entryValue = entry.GetValue();
cConfigRegistryHelper.ReadFromStringBoolean(entryValue, out var result);
if (_invert)
result = !result;
return result;
}
@@ -1113,11 +1115,14 @@ namespace FasdDesktopUi.Basics.Helper
}
}
const string defaultCopyTemplateParameterName = "Copy_default";
if (!dataProvider.NamedParameterEntries.ContainsKey(defaultCopyTemplateParameterName))
{
string defaultCopyTemplate = cF4SDCockpitXmlConfig.Instance?.CopyTemplateConfig?.CopyTemplates.DefaultTemplate.Name;
dataProvider.NamedParameterEntries.Add(defaultCopyTemplateParameterName, new cNamedParameterEntryCopyTemplate(dataProvider, defaultCopyTemplate));
dataProvider.NamedParameterEntries.Add(defaultCopyTemplateParameterName, new cNamedParameterEntryCopyTemplate(dataProvider, SelectedHealthCard.DefaultCopyTemplate != null ? SelectedHealthCard.DefaultCopyTemplate : defaultCopyTemplate));
}
foreach (var copyTemplate in cF4SDCockpitXmlConfig.Instance?.CopyTemplateConfig?.CopyTemplates.CopyTemplates)
@@ -1202,8 +1207,8 @@ namespace FasdDesktopUi.Basics.Helper
var historySectionValueColumns = new List<DetailsPageDataHistoryColumnModel>(valueColumnCount);
for (int i = 0; i < valueColumnCount; i++)
{
CultureInfo culture = new CultureInfo(cMultiLanguageSupport.CurrentLanguage);
var valueColumnHeader = i != 0 ? DateTime.Today.AddDays(-i).ToString(cMultiLanguageSupport.GetItem("Global.Date.Format.ShortDateWithDay", "ddd. dd.MM."), culture) : cMultiLanguageSupport.GetItem("Global.Date.Today");
CultureInfo culture = cFasdCockpitConfig.Instance.SelectedCulture;
var valueColumnHeader = i != 0 ? DateTime.Today.AddDays(-i).ToString($"ddd. {cUtility.GetShortDatePattern()}") : cMultiLanguageSupport.GetItem("Global.Date.Today");
var summaryStatusColor = parent.GetSummaryStatusColor(stateCategoryDefinition.States, false, i);
var valueColumn = new DetailsPageDataHistoryColumnModel() { ColumnValues = new List<cDataHistoryValueModel>(), Content = valueColumnHeader, HighlightColor = summaryStatusColor ?? enumHighlightColor.none };
historySectionValueColumns.Add(valueColumn);
@@ -2165,6 +2170,7 @@ namespace FasdDesktopUi.Basics.Helper
bool isDataIncomplete = true;
await LoadingRawDataCriticalSection.EnterAsync();
try
{
lock (HealthCardRawData)

View File

@@ -1,6 +1,7 @@
using C4IT.FASD.Base;
using FasdDesktopUi.Basics.Enums;
using FasdDesktopUi.Basics.Models;
using FasdDesktopUi.Basics.Services.SupportCase.Processors;
using FasdDesktopUi.Basics.UiActions;
using Microsoft.Win32;
using System;
@@ -15,7 +16,6 @@ namespace FasdDesktopUi.Basics.Helper
internal class MenuItemDataProvider
{
private readonly cSupportCaseDataProvider _dataProvider;
private const int defaultPinnedActionCount = 3; //search, notepad, copyTicketInformation
public MenuItemDataProvider(cSupportCaseDataProvider dataProvider)
@@ -58,7 +58,7 @@ namespace FasdDesktopUi.Basics.Helper
return menuItemData;
}
private cMenuDataBase GetMenuItem(cFasdBaseConfigMenuItem menuItemConfig)
internal cMenuDataBase GetMenuItem(cFasdBaseConfigMenuItem menuItemConfig)
{
try
{
@@ -81,7 +81,7 @@ namespace FasdDesktopUi.Basics.Helper
if (menuItem != null)
{
if (!cHealthCardDataHelper.IsUiVisible(menuItemConfig, _dataProvider.NamedParameterEntries))
if (_dataProvider != null && !cHealthCardDataHelper.IsUiVisible(menuItemConfig, _dataProvider.NamedParameterEntries))
menuItem.SetUiActionDisplayType(enumActionDisplayType.hidden);
}
return menuItem;
@@ -129,7 +129,7 @@ namespace FasdDesktopUi.Basics.Helper
}
private bool HasAllRequirements(cFasdQuickAction quickAction)
internal bool HasAllRequirements(cFasdQuickAction quickAction)
{
try
{
@@ -137,9 +137,14 @@ namespace FasdDesktopUi.Basics.Helper
return false;
// if CheckNamedParamter the value of NamedParameter is set and if required equals one of the necessary values
bool hasRequiredNamedParameter = quickAction.CheckNamedParameter is null
|| (_dataProvider.NamedParameterEntries.TryGetValue(quickAction.CheckNamedParameter, out var namedParameterEntry)
&& (quickAction.CheckNamedParameterValues is null || quickAction.CheckNamedParameterValues.Count == 0 || quickAction.CheckNamedParameterValues.Contains(namedParameterEntry.GetValue())));
bool hasRequiredNamedParameter = true;
if (_dataProvider != null)
{
hasRequiredNamedParameter = quickAction.CheckNamedParameter is null
|| (_dataProvider.NamedParameterEntries.TryGetValue(quickAction.CheckNamedParameter, out var namedParameterEntry)
&& (quickAction.CheckNamedParameterValues is null || quickAction.CheckNamedParameterValues.Count == 0 || quickAction.CheckNamedParameterValues.Contains(namedParameterEntry.GetValue())));
}
if (!hasRequiredNamedParameter)
return false;

View File

@@ -1,13 +1,17 @@
using System;
using System.Collections.Generic;
using C4IT.FASD.Base;
using C4IT.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using C4IT.FASD.Base;
using C4IT.Logging;
using FasdDesktopUi.Basics;
using static C4IT.Logging.cLogManager;
namespace FasdDesktopUi.Basics.Helper
{
internal static class TicketDeepLinkHelper
internal static class TicketExternalLinkHelper
{
internal static bool TryOpenTicketRelationExternally(cF4sdApiSearchResultRelation relation)
{
@@ -16,127 +20,51 @@ namespace FasdDesktopUi.Basics.Helper
if (relation == null || relation.Type != enumF4sdSearchResultClass.Ticket)
return false;
var ticketConfig = cFasdCockpitConfig.Instance?.Global?.TicketConfiguration;
if (ticketConfig == null)
// check how we should open this ticket (intern, extern or both)
var ticketType = GetTicketType(relation);
var processing = ShouldOpenExternally(ticketType);
// check if we have a valid user id in the id list => if not we could open this ticket only extern.
var hasUser = relation.Identities.Any(e => (e.Class == enumFasdInformationClass.User && e.Id != null && e.Id != Guid.Empty));
if (!hasUser)
processing = enumTicketProcessing.Extern;
if (processing == enumTicketProcessing.Intern)
return false;
var activityType = GetActivityType(relation);
var openExternally = ShouldOpenExternally(ticketConfig, activityType);
if (!openExternally)
return false;
var url = BuildTicketDeepLink(relation.id, activityType);
if (string.IsNullOrWhiteSpace(url))
return false;
if (relation?.Infos?.TryGetValue("TicketLink", out var ticketLink) == true && !string.IsNullOrWhiteSpace(ticketLink))
new cBrowsers().Start("default", ticketLink);
new cBrowsers().Start("default", url);
return true;
return processing == enumTicketProcessing.Extern;
}
catch (Exception ex)
{
LogException(ex);
}
return false;
}
private static string GetActivityType(cF4sdApiSearchResultRelation relation)
{
if (relation?.Infos != null && relation.Infos.TryGetValue("ActivityType", out var activityTypeValue))
return activityTypeValue;
return null;
}
private static bool ShouldOpenExternally(cF4sdTicketConfig ticketConfig, string activityType)
{
if (ticketConfig == null)
return false;
if (TryGetOverride(ticketConfig.OpenActivitiesExternallyOverrides, activityType, out var overrideValue))
return overrideValue;
return ticketConfig.OpenActivitiesExternally;
}
private static bool TryGetOverride(IEnumerable<string> overrides, string activityType, out bool value)
{
value = false;
if (string.IsNullOrWhiteSpace(activityType) || overrides == null)
return false;
foreach (var entry in overrides)
{
if (string.IsNullOrWhiteSpace(entry))
continue;
var parts = entry.Split(new[] { '=' }, 2, StringSplitOptions.RemoveEmptyEntries);
if (parts.Length != 2)
continue;
var typeName = parts[0].Trim();
if (!string.Equals(typeName, activityType, StringComparison.OrdinalIgnoreCase))
continue;
if (!TryParseBool(parts[1], out value))
return false;
return true;
}
return false;
}
private static bool TryParseBool(string value, out bool result)
{
result = false;
if (string.IsNullOrWhiteSpace(value))
return false;
switch (value.Trim().ToLowerInvariant())
{
case "true":
case "1":
case "yes":
result = true;
return true;
case "false":
case "0":
case "no":
result = false;
return true;
default:
return bool.TryParse(value, out result);
}
}
return false;
}
internal static string BuildTicketDeepLink(Guid ticketId, string activityType)
private static enumTicketType GetTicketType(cF4sdApiSearchResultRelation relation)
{
if (ticketId == Guid.Empty)
return null;
var server = cCockpitConfiguration.Instance?.m42ServerConfiguration?.Server;
if (string.IsNullOrWhiteSpace(server))
return null;
var baseUrl = server.TrimEnd('/');
if (!baseUrl.StartsWith("http://", StringComparison.OrdinalIgnoreCase) &&
!baseUrl.StartsWith("https://", StringComparison.OrdinalIgnoreCase))
if (relation?.Infos != null && relation.Infos.TryGetValue("TicketType", out var ticketTypeValue))
{
baseUrl = "https://" + baseUrl;
if (Enum.TryParse<enumTicketType>(ticketTypeValue, true, out var ticketType))
return ticketType;
}
if (!baseUrl.EndsWith("/wm", StringComparison.OrdinalIgnoreCase))
baseUrl += "/wm";
return enumTicketType.Ticket;
}
if (string.IsNullOrWhiteSpace(activityType))
return null;
private static enumTicketProcessing ShouldOpenExternally(enumTicketType ticketType)
{
var ticketConfig = cFasdCockpitConfig.Instance?.Global?.TicketConfiguration;
if (ticketConfig == null)
return enumTicketProcessing.Extern;
var viewOptionsJson = $"{{\"embedded\":false,\"objectId\":\"{ticketId}\",\"type\":\"{activityType}\",\"viewType\":\"preview\",\"archived\":0}}";
var viewOptionsEncoded = Uri.EscapeDataString(viewOptionsJson);
if (ticketConfig.TicketProcessing?.TryGetValue(ticketType, out var processing) == true)
return processing;
return $"{baseUrl}/app-ServiceDesk/?view-options={viewOptionsEncoded}";
return ticketConfig.DefaultProcessing;
}
}
}

View File

@@ -48,7 +48,9 @@ namespace FasdDesktopUi.Basics.Models
heartBeat
}
public bool IsActive = true;
public enumOnlineStatus ApiConnectionStatus { get; private set; } = enumOnlineStatus.notSpecified;
public bool ApplicationIsExiting { get; set; } = false;
public readonly Version MinServerVersion = new Version("0.0.0.0");
@@ -64,16 +66,15 @@ namespace FasdDesktopUi.Basics.Models
public static cConnectionStatusHelper Instance { get; set; }
public bool IsAuthorizationSupported { get; private set; } = false;
private bool IsAuthorizationSupported { get; set; } = false;
private System.Timers.Timer timer;
#region Lock Elements Connecion Status
private readonly object connectionStatusCheckLock = new object();
private enumCheckRunning IsConnectionStatusCheckRunning = enumCheckRunning.no;
private int OnlineCheckCounter = 0;
#endregion
public enumOnlineStatus ApiConnectionStatus = enumOnlineStatus.notSpecified;
public cConnectionStatusHelper()
{
ApiConnectionStatus = enumOnlineStatus.offline;
@@ -125,44 +126,47 @@ namespace FasdDesktopUi.Basics.Models
return (timerInterval, shortInterval);
}
private void HandleConnectionStatus(enumConnectionStatus status, ref int timerInterval, int timerInteralShort)
private enumOnlineStatus HandleConnectionStatus(enumConnectionStatus status, ref int timerInterval, int timerInteralShort)
{
var newStatus = ApiConnectionStatus;
switch (status)
{
case enumConnectionStatus.unknown:
case enumConnectionStatus.serverNotFound:
if (ApiConnectionStatus != enumOnlineStatus.offline)
ApiConnectionStatus = enumOnlineStatus.offline;
if (newStatus != enumOnlineStatus.offline)
newStatus = enumOnlineStatus.offline;
timerInterval = timerInteralShort;
LogEntry("RunConnectionStatusCheckAsync: Exit due to status 'serverNotFound'");
break;
case enumConnectionStatus.serverResponseError:
ApiConnectionStatus = enumOnlineStatus.connectionError;
newStatus = enumOnlineStatus.connectionError;
timerInterval = timerInteralShort;
LogEntry("RunConnectionStatusCheckAsync: Exit due to status 'serverResponseError'");
break;
case enumConnectionStatus.incompatibleServerVersion:
LogEntry("RunConnectionStatusCheckAsync: Exit due to status 'incompatibleServerVersion'");
ApiConnectionStatus = enumOnlineStatus.incompatibleServerVersion;
newStatus = enumOnlineStatus.incompatibleServerVersion;
break;
case enumConnectionStatus.serverStarting:
ApiConnectionStatus = enumOnlineStatus.serverStarting;
newStatus = enumOnlineStatus.serverStarting;
break;
case enumConnectionStatus.serverNotConfigured:
ApiConnectionStatus = enumOnlineStatus.serverNotConfigured;
newStatus = enumOnlineStatus.serverNotConfigured;
break;
case enumConnectionStatus.connected:
if (cCockpitConfiguration.Instance == null || cF4SDCockpitXmlConfig.Instance == null)
ApiConnectionStatus = enumOnlineStatus.illegalConfig;
newStatus = enumOnlineStatus.illegalConfig;
else if (ApiConnectionStatus != enumOnlineStatus.online)
ApiConnectionStatus = enumOnlineStatus.online;
newStatus = enumOnlineStatus.online;
break;
}
return newStatus;
}
public async Task RunConnectionStatusCheckAsync(SplashScreenView splashScreen)
{
if (!IsActive)
if (ApplicationIsExiting)
return;
var CM = MethodBase.GetCurrentMethod();
@@ -195,7 +199,7 @@ namespace FasdDesktopUi.Basics.Models
cCheckConnectionResult connectionResult = await cFasdCockpitCommunicationBase.Instance.CheckConnection(MinServerVersion);
IsAuthorizationSupported = connectionResult?.ApiConnectionInfo?.SupportAuthorisation ?? false;
HandleConnectionStatus(connectionResult.ConnectionStatus, ref timerInterval, shortTimerInterval);
ApiConnectionStatus = HandleConnectionStatus(connectionResult.ConnectionStatus, ref timerInterval, shortTimerInterval);
if (connectionResult.ConnectionStatus != enumConnectionStatus.connected)
return;
@@ -210,7 +214,12 @@ namespace FasdDesktopUi.Basics.Models
var configTasks = await Task.WhenAll(loadConfigFilesTask, getCockpitConfig);
if (configTasks.Any(t => t == false))
{
LogEntry("Connection status check wasn't successfull. Could not retrieve all configurations.", LogLevels.Warning);
await Task.Delay(TimeSpan.FromSeconds(5));
await RunConnectionStatusCheckAsync(splashScreen);
return;
}
if (cFasdCockpitConfig.Instance?.Global != null && cCockpitConfiguration.Instance?.GlobalConfig != null)
{
cFasdCockpitConfig.Instance.Global.Load(cCockpitConfiguration.Instance.GlobalConfig);
@@ -225,33 +234,34 @@ namespace FasdDesktopUi.Basics.Models
}
if (IsAuthorizationSupported)
{
if (userInfo is null || DateTime.UtcNow > userInfo.RenewUntil)
{
Dispatcher.CurrentDispatcher.Invoke(() => splashScreen?.SetStatusText(cMultiLanguageSupport.GetItem("StartUp.SplashScreen.AuthenticateUser")));
ApiConnectionStatus = enumOnlineStatus.unauthorized;
const string cockpitUserRole = "Cockpit.User";
Dispatcher.CurrentDispatcher.Invoke(() => splashScreen?.SetStatusText(cMultiLanguageSupport.GetItem("StartUp.SplashScreen.AuthenticateUser")));
ApiConnectionStatus = enumOnlineStatus.unauthorized;
const string cockpitUserRole = "Cockpit.User";
#if isNewFeature
const string cockpitTicketAgentRole = "Cockpit.TicketAgent";
#endif
if (userInfo is null || DateTime.UtcNow > userInfo.RenewUntil)
{
userInfo = await cFasdCockpitCommunicationBase.Instance.WinLogon();
lock (cFasdCockpitCommunicationBase.CockpitUserInfoLock)
{
cFasdCockpitCommunicationBase.CockpitUserInfo = userInfo;
}
if (userInfo?.Roles is null || !userInfo.Roles.Contains(cockpitUserRole))
{
Dispatcher.CurrentDispatcher.Invoke(() => splashScreen?.SetStatusText(cMultiLanguageSupport.GetItem("StartUp.SplashScreen.NoAuthorization")));
LogEntry($"Cockpit User ({userInfo?.Name} with Id {userInfo?.Id}, has not the required permissions.", LogLevels.Error);
}
else
{
await Task.Run(async () => await cFasdCockpitConfig.Instance.InstantiateAnalyticsAsync(cFasdCockpitConfig.SessionId));
ApiConnectionStatus = enumOnlineStatus.online;
}
if (userInfo?.Roles is null || !userInfo.Roles.Contains(cockpitUserRole))
{
Dispatcher.CurrentDispatcher.Invoke(() => splashScreen?.SetStatusText(cMultiLanguageSupport.GetItem("StartUp.SplashScreen.NoAuthorization")));
LogEntry($"Cockpit User ({userInfo?.Name} with Id {userInfo?.Id}, has not the required permissions.", LogLevels.Error);
}
else
{
await Task.Run(async () => await cFasdCockpitConfig.Instance.InstantiateAnalyticsAsync(cFasdCockpitConfig.SessionId));
ApiConnectionStatus = enumOnlineStatus.online;
#if isNewFeature
if (userInfo.Roles.Contains(cockpitTicketAgentRole))
cCockpitConfiguration.Instance.ticketSupport.EditTicket = true;
#endif
}
}
}
else
@@ -280,9 +290,9 @@ namespace FasdDesktopUi.Basics.Models
}
if (App.M42OptionMenuItem != null)
App.Current.MainWindow.Dispatcher.Invoke(() =>
Dispatcher.CurrentDispatcher.Invoke(() =>
{
App.M42OptionMenuItem.Enabled = userInfo != null;
App.M42OptionMenuItem.Enabled = userInfo != null;
});
// check, if the are logons needed
@@ -291,6 +301,7 @@ namespace FasdDesktopUi.Basics.Models
await cFasdCockpitConfig.Instance.CheckServerQuickActionAvailabilityAsync();
await cFasdCockpitCommunicationBase.Instance.InitializeAfterOnlineAsync();
cFasdCockpitConfig.Instance.OnUiSettingsChanged();
await Task.Run(async () => await cFasdCockpitConfig.Instance.InstantiateAnalyticsAsync(cFasdCockpitConfig.SessionId));
}
catch (Exception E)
{
@@ -312,7 +323,7 @@ namespace FasdDesktopUi.Basics.Models
LogException(E);
}
if (IsActive)
if (!ApplicationIsExiting)
{
if (ApiConnectionStatus == enumOnlineStatus.online)
NotifyerSupport.SetNotifyIcon("Default", null, NotifyerSupport.enumIconAlignment.BottomRight);
@@ -334,6 +345,7 @@ namespace FasdDesktopUi.Basics.Models
}
finally
{
OnlineCheckCounter++;
LogMethodEnd(CM);
}
}

View File

@@ -0,0 +1,175 @@
using C4IT.FASD.Base;
using F4SD_AdaptableIcon;
using F4SD_AdaptableIcon.Enums;
using FasdDesktopUi.Basics.Converter;
using FasdDesktopUi.Basics.UiActions;
using System;
using System.Collections.Generic;
namespace FasdDesktopUi.Basics.Models.DTOs
{
// __ __ ____ ____
// | |__| || || \
// | | | | | | | o )
// | | | | | | | _/
// | ` ' | | | | |
// \ / | | | |
// \_/\_/ |____||__|
// the following classes are work in Progress and shouldn't been used
[Obsolete]
internal readonly struct IconInfo
{
public IconData? Overlay { get; }
public double IconScale { get; }
public bool IsInactive { get; }
public string Description { get; }
public IconInfo(bool isInactive = default, string description = null, IconData? overlay = null, double iconScale = 1.0)
{
Overlay = overlay;
IsInactive = isInactive;
Description = description;
IconScale = iconScale;
}
}
[Obsolete]
internal abstract class MenuDataBaseDto
{
private readonly IconData _icon;
public string Title { get; set; }
public string SubTitle { get; set; }
public cUiActionBase Action { get; set; }
public IconInfo IconInformation { get; set; }
public int PositoinIndex { get; set; }
protected MenuDataBaseDto() { }
protected MenuDataBaseDto(cFasdBaseConfigMenuItem menuItemDefinition)
{
_icon = IconDataConverter.Convert(menuItemDefinition.Icon);
Title = menuItemDefinition.Names.GetValue();
}
internal virtual IconData GetIcon() => _icon;
}
[Obsolete]
internal sealed class ContainerMenuDataDto : MenuDataBaseDto
{
public IList<MenuDataBaseDto> SubMenuData { get; set; } = new List<MenuDataBaseDto>();
public ContainerMenuDataDto(cFasdMenuSection sectionDefintion) : base(sectionDefintion)
{
Action = new cSubMenuAction(true); // todo rework subMenuAction
}
}
[Obsolete]
internal sealed class LoadingMenuDataDto : MenuDataBaseDto
{
private readonly IconData _icon = new IconData(enumInternGif.loadingSpinner);
public LoadingMenuDataDto(string loadingText)
{
Title = loadingText;
}
internal override IconData GetIcon() => _icon;
}
[Obsolete]
internal sealed class SearchResultMenuDataDto : MenuDataBaseDto
{
private readonly cFasdApiSearchResultEntry _searchResultEntry;
public enumF4sdSearchResultClass Type { get => _searchResultEntry.Type; }
public SearchResultMenuDataDto(cFasdApiSearchResultEntry searchResultEntry)
{
_searchResultEntry = searchResultEntry;
//Action = new cUiProcessSearchRelationAction()
}
internal override IconData GetIcon()
{
switch (Type)
{
case enumF4sdSearchResultClass.Computer:
return new IconData(enumInternIcons.misc_computer);
case enumF4sdSearchResultClass.User:
return new IconData(enumInternIcons.misc_user);
case enumF4sdSearchResultClass.Phone:
return new IconData(MaterialIcons.MaterialIconType.ic_phone);
case enumF4sdSearchResultClass.Ticket:
break;
case enumF4sdSearchResultClass.VirtualSession:
break;
case enumF4sdSearchResultClass.MobileDevice:
break;
default:
break;
}
throw new NotImplementedException();
}
}
[Obsolete]
internal sealed class SearchRelationMenuDataDto : MenuDataBaseDto
{
private readonly cF4sdApiSearchResultRelation _searchRelation;
public enumF4sdSearchResultClass Type { get => _searchRelation.Type; }
public DateTime LastUsed { get; private set; }
public double UsingFactor { get; private set; }
public Dictionary<string, string> AdditionalInfos { get; private set; } // todo check
public bool IsUsedForCaseEnrichtment { get; set; } // todo check
public string TrailingText { get; set; }
public SearchRelationMenuDataDto(cF4sdApiSearchResultRelation searchRelation)
{
_searchRelation = searchRelation;
//Action = new cUiProcessSearchRelationAction()
}
internal override IconData GetIcon()
{
throw new NotImplementedException();
}
}
[Obsolete]
internal sealed class QuickActionMenuDataDto : MenuDataBaseDto
{
public QuickActionMenuDataDto(cFasdQuickAction quickActionDefinition) : base(quickActionDefinition)
{
Action = cUiActionBase.GetUiAction(quickActionDefinition);
}
}
[Obsolete]
internal sealed class QuickTipMenuDataDto : MenuDataBaseDto
{
public QuickTipMenuDataDto(cFasdQuickTip quickTipDefinition) : base(quickTipDefinition)
{
Title = quickTipDefinition.Names.GetValue();
Action = new cUiQuickTipAction(quickTipDefinition);
}
}
[Obsolete]
internal sealed class CopyTemplateMenuDataDto : MenuDataBaseDto
{
public CopyTemplateMenuDataDto(cCopyTemplate copyActionDefintion) : base(copyActionDefintion)
{
Action = new cUiCopyAction(copyActionDefintion);
}
}
}

View File

@@ -119,6 +119,9 @@ namespace FasdDesktopUi.Basics.Models
{
try
{
if (actionSteps is null)
return;
foreach (var step in actionSteps)
{
if (step.StepType.Equals(type) && step.QuickActionName.Equals(quickActionName))

View File

@@ -1,199 +1,292 @@
using System;
using System.Collections.Generic;
using System.Linq;
using C4IT.FASD.Base;
using F4SD_AdaptableIcon;
using F4SD_AdaptableIcon.Enums;
using FasdDesktopUi.Basics.Converter;
using FasdDesktopUi.Basics.UiActions;
using static C4IT.Logging.cLogManager;
namespace FasdDesktopUi.Basics.Models
{
//todo: check what properties can/should be set via constructor
public class cMenuDataBase
{
public string MenuText { get; set; }
public string SubMenuText { get; set; }
public string TrailingText { get; set; }
public IconData MenuIcon { get; set; }
public double MenuIconSize { get; set; } = 1.0;
public int IconPositionIndex { get; set; }
public object Data { get; set; }
public List<string> MenuSections { get; set; }
public cUiActionBase UiAction { get; set; }
public cMenuDataBase()
{
}
public cMenuDataBase(cFasdBaseConfigMenuItem menuItem)
{
MenuText = menuItem.Names.GetValue(Default: null);
MenuIcon = IconDataConverter.Convert(menuItem.Icon);
MenuSections = menuItem.Sections;
}
public cMenuDataBase(cFasdQuickAction quickAction, Enums.enumActionDisplayType display) : this(quickAction)
{
SetUiActionDisplayType(display);
}
public cMenuDataBase(cFasdQuickAction quickAction) : this((cFasdBaseConfigMenuItem)quickAction)
{
var tempUiAction = cUiActionBase.GetUiAction(quickAction);
tempUiAction.Name = quickAction.Name;
tempUiAction.Description = quickAction.Descriptions?.GetValue(Default: null);
tempUiAction.AlternativeDescription = quickAction.AlternativeDescriptions?.GetValue(Default: null);
UiAction = tempUiAction;
}
public cMenuDataBase(cCopyTemplate copyTemplate) : this((cFasdBaseConfigMenuItem)copyTemplate)
{
UiAction = new cUiCopyAction(copyTemplate) { Name = copyTemplate.Name, Description = copyTemplate.Descriptions.GetValue(Default: null), DisplayType = Enums.enumActionDisplayType.enabled };
}
public cMenuDataBase(cFasdMenuSection menuSection) : this((cFasdBaseConfigMenuItem)menuSection)
{
IconPositionIndex = -1;
}
public cMenuDataBase(cFasdQuickTip quickTip) : this((cFasdBaseConfigMenuItem)quickTip)
{
UiAction = new cUiQuickTipAction(quickTip) { DisplayType = Enums.enumActionDisplayType.enabled };
}
public void SetUiActionDisplayType(Enums.enumActionDisplayType display)
{
if (this.UiAction != null)
this.UiAction.DisplayType = display;
}
}
public class cMenuDataContainer : cMenuDataBase
{
public string ContainerName { get; private set; }
public List<cMenuDataBase> SubMenuData { get; set; }
public cMenuDataContainer()
{
UiAction = new cSubMenuAction(true) { Name = MenuText, DisplayType = Enums.enumActionDisplayType.enabled };
}
public cMenuDataContainer(cFasdMenuSection menuSection) : base(menuSection)
{
ContainerName = menuSection.TechName;
UiAction = new cSubMenuAction(true) { Name = MenuText, DisplayType = Enums.enumActionDisplayType.enabled };
}
}
public class cMenuDataSearchResult : cMenuDataBase
{
public cMenuDataSearchResult(string menuText, ISearchUiProvider SearchUiProvider, List<cFasdApiSearchResultEntry> searchResults) : base()
{
if (searchResults?.Count <= 0)
return;
MenuText = menuText;
UiAction = new cUiProcessSearchResultAction(menuText, SearchUiProvider, searchResults);
var firstSearchResult = searchResults.First();
MenuIcon = GetMenuIcon(firstSearchResult.Type, firstSearchResult.Infos);
}
static public IconData GetMenuIcon(enumF4sdSearchResultClass searchResultClass, Dictionary<string, string> infos)
{
switch (searchResultClass)
{
case enumF4sdSearchResultClass.Computer:
return new IconData(enumInternIcons.misc_computer);
case enumF4sdSearchResultClass.User:
return new IconData(enumInternIcons.misc_user);
case enumF4sdSearchResultClass.Phone:
return new IconData(MaterialIcons.MaterialIconType.ic_phone);
case enumF4sdSearchResultClass.Ticket:
return new IconData(enumInternIcons.misc_ticket);
case enumF4sdSearchResultClass.MobileDevice:
return new IconData(MaterialIcons.MaterialIconType.ic_smartphone);
case enumF4sdSearchResultClass.VirtualSession:
if (!infos.TryGetValue("Status", out string status))
return new IconData(MaterialIcons.MaterialIconType.ic_cloud_off);
else if (status == nameof(enumCitrixSessionStatus.Active))
return new IconData(MaterialIcons.MaterialIconType.ic_cloud_queue);
else
return new IconData(MaterialIcons.MaterialIconType.ic_cloud_off);
default:
return new IconData(MaterialIcons.MaterialIconType.ic_more_vert);
}
}
}
public sealed class cMenuDataSearchRelation : cMenuDataBase
{
public readonly DateTime LastUsed;
public readonly double UsingLevel = 0;
public readonly Dictionary<string, string> Infos = null;
public bool IsMatchingRelation = false;
public bool IsUsedForCaseEnrichment = false;
public readonly cF4sdApiSearchResultRelation searchResultRelation = null;
public cMenuDataSearchRelation(cF4sdApiSearchResultRelation searchResultRelation)
{
try
{
UiAction = null;
if (searchResultRelation is null)
return;
this.searchResultRelation = searchResultRelation;
MenuText = searchResultRelation.DisplayName;
Data = searchResultRelation;
LastUsed = searchResultRelation.LastUsed;
UsingLevel = searchResultRelation.UsingLevel;
Infos = searchResultRelation.Infos;
MenuIcon = cMenuDataSearchResult.GetMenuIcon(searchResultRelation.Type, searchResultRelation.Infos);
}
catch (Exception E)
{
LogException(E);
}
}
}
public sealed class cMenuDataLoading : cMenuDataBase
{
public cMenuDataLoading(string LoadingText)
{
MenuText = LoadingText;
}
}
public class cFilteredResults
{
public bool AutoContinue { get; set; } = false;
public cFasdApiSearchResultCollection Results { get; set; }
public cF4sdApiSearchResultRelation PreSelectedRelation { get; set; }
public cFilteredResults()
{
}
public cFilteredResults(cFasdApiSearchResultCollection _results)
{
Results = _results ?? new cFasdApiSearchResultCollection();
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using C4IT.FASD.Base;
using C4IT.MultiLanguage;
using F4SD_AdaptableIcon;
using F4SD_AdaptableIcon.Enums;
using FasdDesktopUi.Basics.Converter;
using FasdDesktopUi.Basics.UiActions;
using static C4IT.Logging.cLogManager;
namespace FasdDesktopUi.Basics.Models
{
//todo: check what properties can/should be set via constructor
public class cMenuDataBase
{
public string MenuText { get; set; }
public string SubMenuText { get; set; }
public string TrailingText { get; set; }
public MenuIconInfo MenuIcon { get; set; }
public double MenuIconSize { get; set; } = 1.0;
public int IconPositionIndex { get; set; }
public object Data { get; set; }
public List<string> MenuSections { get; set; }
public cUiActionBase UiAction { get; set; }
public class MenuIconInfo
{
public readonly IconData Icon;
public readonly IconData? Overlay;
public readonly bool IsInactive;
public readonly string Description;
public MenuIconInfo(IconData icon, string Description = null, bool isInactive = false, IconData? overlay = null)
{
Icon = icon;
IsInactive = isInactive;
this.Description = Description;
Overlay = overlay;
}
}
public cMenuDataBase()
{
}
public cMenuDataBase(cFasdBaseConfigMenuItem menuItem)
{
MenuText = menuItem.Names.GetValue(Default: null);
MenuIcon = new MenuIconInfo(IconDataConverter.Convert(menuItem.Icon));
MenuSections = menuItem.Sections;
if (!string.IsNullOrEmpty(menuItem.Section))
MenuSections.Add(menuItem.Section);
MenuSections = MenuSections.Distinct().ToList();
}
public cMenuDataBase(cFasdQuickAction quickAction, Enums.enumActionDisplayType display) : this(quickAction)
{
SetUiActionDisplayType(display);
}
public cMenuDataBase(cFasdQuickAction quickAction) : this((cFasdBaseConfigMenuItem)quickAction)
{
var tempUiAction = cUiActionBase.GetUiAction(quickAction);
tempUiAction.Name = quickAction.Name;
tempUiAction.Description = quickAction.Descriptions?.GetValue(Default: null);
tempUiAction.AlternativeDescription = quickAction.AlternativeDescriptions?.GetValue(Default: null);
UiAction = tempUiAction;
}
public cMenuDataBase(cCopyTemplate copyTemplate) : this((cFasdBaseConfigMenuItem)copyTemplate)
{
UiAction = new cUiCopyAction(copyTemplate) { Name = copyTemplate.Name, Description = copyTemplate.Descriptions.GetValue(Default: null), DisplayType = Enums.enumActionDisplayType.enabled };
}
public cMenuDataBase(cFasdMenuSection menuSection) : this((cFasdBaseConfigMenuItem)menuSection)
{
IconPositionIndex = -1;
}
public cMenuDataBase(cFasdQuickTip quickTip) : this((cFasdBaseConfigMenuItem)quickTip)
{
UiAction = new cUiQuickTipAction(quickTip) { DisplayType = Enums.enumActionDisplayType.enabled, Name = quickTip.Name };
}
public void SetUiActionDisplayType(Enums.enumActionDisplayType display)
{
if (this.UiAction != null)
this.UiAction.DisplayType = display;
}
}
public class cMenuDataContainer : cMenuDataBase
{
public string ContainerName { get; private set; }
public List<cMenuDataBase> SubMenuData { get; set; } = new List<cMenuDataBase>();
public cMenuDataContainer()
{
UiAction = new cSubMenuAction(true) { Name = MenuText, DisplayType = Enums.enumActionDisplayType.enabled };
}
public cMenuDataContainer(cFasdMenuSection menuSection) : base(menuSection)
{
ContainerName = menuSection.TechName;
UiAction = new cSubMenuAction(true) { Name = MenuText, DisplayType = Enums.enumActionDisplayType.enabled };
}
}
public class cMenuDataSearchResult : cMenuDataBase
{
public cMenuDataSearchResult(string menuText, ISearchUiProvider SearchUiProvider, List<cFasdApiSearchResultEntry> searchResults) : base()
{
if (searchResults?.Count <= 0)
return;
MenuText = menuText;
UiAction = new cUiProcessSearchResultAction(menuText, SearchUiProvider, searchResults);
var firstSearchResult = searchResults.First();
MenuIcon = GetMenuIcon(firstSearchResult.Type, firstSearchResult.Infos);
}
static private MenuIconInfo GetTicketIcon(Dictionary<string, string> infos)
{
bool isInactive = false;
if (infos.TryGetValue("StatusId", out var ticketStatusId))
{
if (Enum.TryParse(ticketStatusId, true, out enumTicketStatus ticketStatus))
{
switch (ticketStatus)
{
case enumTicketStatus.Closed:
isInactive = true;
break;
default:
break;
}
}
}
string description = null;
var overlay = isInactive ? (IconData?)new IconData(enumInternIcons.misc_disabledOverlay) : null;
if (isInactive)
{
}
if (infos.TryGetValue("TicketType", out var ticketType))
{
if (Enum.TryParse(ticketType, true, out enumTicketType parsedTicketType))
{
switch (parsedTicketType)
{
case enumTicketType.Incident:
if (isInactive)
description = cMultiLanguageSupport.GetItem("Searchbar.Item.Incident.Closed");
else
description = cMultiLanguageSupport.GetItem("Searchbar.Item.Incident.Active");
return new MenuIconInfo(new IconData(MaterialIcons.MaterialIconType.ic_bug_report), description, isInactive, overlay);
case enumTicketType.ServiceRequest:
if (isInactive)
description = cMultiLanguageSupport.GetItem("Searchbar.Item.ServiceRequest.Closed");
else
description = cMultiLanguageSupport.GetItem("Searchbar.Item.ServiceRequest.Active");
return new MenuIconInfo(new IconData(MaterialIcons.MaterialIconType.ic_room_service), description, isInactive, overlay);
case enumTicketType.UnclassifiedTicket:
if (isInactive)
description = cMultiLanguageSupport.GetItem("Searchbar.Item.Unclassified.Closed");
else
description = cMultiLanguageSupport.GetItem("Searchbar.Item.Unclassified.Active");
return new MenuIconInfo(new IconData(MaterialIcons.MaterialIconType.ic_confirmation_number), description, isInactive, overlay);
default:
break;
}
}
}
if (isInactive)
description = cMultiLanguageSupport.GetItem("Searchbar.Item.Ticket.Closed");
else
description = cMultiLanguageSupport.GetItem("Searchbar.Item.Ticket.Active");
return new MenuIconInfo(new IconData(MaterialIcons.MaterialIconType.ic_mail_outline), description, isInactive, overlay);
}
static private MenuIconInfo GetVirtualSessionIcon(Dictionary<string, string> infos)
{
if (infos.TryGetValue("Status", out string status))
{
if (status == nameof(enumCitrixSessionStatus.Active))
return new MenuIconInfo(new IconData(MaterialIcons.MaterialIconType.ic_cloud_queue));
}
return new MenuIconInfo(new IconData(MaterialIcons.MaterialIconType.ic_cloud_off));
}
static public MenuIconInfo GetMenuIcon(enumF4sdSearchResultClass searchResultClass, Dictionary<string, string> infos)
{
switch (searchResultClass)
{
case enumF4sdSearchResultClass.Computer:
return new MenuIconInfo(new IconData(enumInternIcons.misc_computer));
case enumF4sdSearchResultClass.User:
return new MenuIconInfo(new IconData(enumInternIcons.misc_user));
case enumF4sdSearchResultClass.Phone:
return new MenuIconInfo(new IconData(MaterialIcons.MaterialIconType.ic_phone));
case enumF4sdSearchResultClass.Ticket:
return GetTicketIcon(infos);
case enumF4sdSearchResultClass.MobileDevice:
return new MenuIconInfo(new IconData(MaterialIcons.MaterialIconType.ic_smartphone));
case enumF4sdSearchResultClass.VirtualSession:
return GetVirtualSessionIcon(infos);
default:
return new MenuIconInfo(new IconData(MaterialIcons.MaterialIconType.ic_more_vert));
}
}
}
public sealed class cMenuDataSearchRelation : cMenuDataBase
{
public readonly DateTime LastUsed;
public readonly double UsingLevel = 0;
public readonly Dictionary<string, string> Infos = null;
public bool IsMatchingRelation = false;
public bool IsUsedForCaseEnrichment = false;
public readonly cF4sdApiSearchResultRelation searchResultRelation = null;
public cMenuDataSearchRelation(cF4sdApiSearchResultRelation searchResultRelation)
{
try
{
UiAction = null;
if (searchResultRelation is null)
return;
this.searchResultRelation = searchResultRelation;
MenuText = searchResultRelation.DisplayName;
Data = searchResultRelation;
LastUsed = searchResultRelation.LastUsed;
UsingLevel = searchResultRelation.UsingLevel;
Infos = searchResultRelation.Infos;
MenuIcon = cMenuDataSearchResult.GetMenuIcon(searchResultRelation.Type, searchResultRelation.Infos);
}
catch (Exception E)
{
LogException(E);
}
}
}
public sealed class cMenuDataLoading : cMenuDataBase
{
public cMenuDataLoading(string LoadingText)
{
MenuText = LoadingText;
}
}
public class cFilteredResults
{
public bool AutoContinue { get; set; } = false;
public cFasdApiSearchResultCollection Results { get; set; }
public cF4sdApiSearchResultRelation PreSelectedRelation { get; set; }
public cFilteredResults()
{
}
public cFilteredResults(cFasdApiSearchResultCollection _results)
{
Results = _results ?? new cFasdApiSearchResultCollection();
}
}
}

View File

@@ -49,7 +49,7 @@ namespace FasdDesktopUi.Basics.Models
try
{
cUtility.RawValueFormatter.SetDefaultCulture(new System.Globalization.CultureInfo(cFasdCockpitConfig.Instance.SelectedLanguage));
cUtility.RawValueFormatter.SetDefaultCulture(cFasdCockpitConfig.Instance.SelectedCulture);
cUtility.RawValueFormatter.SetDefaultTimeZone(TimeZoneInfo.Local);
var outputTable = dataProvider.HealthCardDataHelper.HealthCardRawData.GetTableByName(valueAdress.ValueTable, true);

View File

@@ -1,6 +1,7 @@
using FasdDesktopUi.Basics.Enums;
using FasdDesktopUi.Basics.Services.Models;
using FasdDesktopUi.Basics.UiActions;
using System.Linq;
using static C4IT.FASD.Base.cF4SDHealthCardRawData;
namespace FasdDesktopUi.Basics.Models
@@ -17,5 +18,17 @@ namespace FasdDesktopUi.Basics.Models
public cUiActionBase UiActionTitle { get; set; } = null;
public cUiActionBase UiActionValue { get; set; } = null;
public cHealthCardDetailsTable ValuedDetails { get; set; } = null;
public cWidgetValueModel() { }
public cWidgetValueModel(CockpitValueDisplayData displayData, enumHighlightColor highlightColor, cUiActionBase titleUiAction)
{
Title = displayData?.Title;
Value = displayData?.Values?.FirstOrDefault() ?? (displayData.IsLoading ? "..." : null);
HighlightIn = highlightColor;
IsLoading = displayData?.IsLoading ?? true;
UiActionTitle = titleUiAction;
UiActionValue = displayData?.UiActions?.FirstOrDefault();
}
}
}

View File

@@ -286,18 +286,20 @@ namespace FasdDesktopUi.Basics
public bool isSeen { get; set; } = false;
protected DateTime LastRefresh = DateTime.MinValue;
public IRelationService RelationService { get; private set; }
public List<cFasdApiSearchResultEntry> SelectedSearchResult { get; private set; }
public List<cF4sdApiSearchResultRelation> Relations { get; protected set; }
public ISearchUiProvider SearchUiProvider { get; private set; }
public cSearchHistoryEntryBase(string DisplayText, List<cFasdApiSearchResultEntry> selectedSearchResult, List<cF4sdApiSearchResultRelation> relations, ISearchUiProvider SearchUiProvider)
public cSearchHistoryEntryBase(string DisplayText, List<cFasdApiSearchResultEntry> selectedSearchResult, List<cF4sdApiSearchResultRelation> relations, ISearchUiProvider SearchUiProvider, IRelationService relationService)
{
this.DisplayText = DisplayText;
this.isSeen = isSeen;
this.SelectedSearchResult = selectedSearchResult;
this.Relations = relations;
this.SearchUiProvider = SearchUiProvider;
RelationService = relationService;
LastRefresh = DateTime.UtcNow;
}
@@ -313,7 +315,7 @@ namespace FasdDesktopUi.Basics
{
public string HeaderText { get; private set; }
public cSearchHistorySearchResultEntry(string DisplayText, string HeaderText, List<cFasdApiSearchResultEntry> selectedSearchResult, List<cF4sdApiSearchResultRelation> relations, ISearchUiProvider SearchUiProvider) : base(DisplayText, selectedSearchResult, relations, SearchUiProvider)
public cSearchHistorySearchResultEntry(string DisplayText, string HeaderText, List<cFasdApiSearchResultEntry> selectedSearchResult, List<cF4sdApiSearchResultRelation> relations, ISearchUiProvider SearchUiProvider, IRelationService relationService) : base(DisplayText, selectedSearchResult, relations, SearchUiProvider, relationService)
{
this.HeaderText = HeaderText;
LastRefresh = DateTime.UtcNow;
@@ -337,7 +339,7 @@ namespace FasdDesktopUi.Basics
{
public cF4sdApiSearchResultRelation SelectedRelation { get; private set; }
public cSearchHistoryRelationEntry(string DisplayText, List<cFasdApiSearchResultEntry> selectedSearchResult, List<cF4sdApiSearchResultRelation> relations, cF4sdApiSearchResultRelation selectedRealtion, ISearchUiProvider SearchUiProvider) : base(DisplayText, selectedSearchResult, relations, SearchUiProvider)
public cSearchHistoryRelationEntry(string DisplayText, List<cFasdApiSearchResultEntry> selectedSearchResult, List<cF4sdApiSearchResultRelation> relations, cF4sdApiSearchResultRelation selectedRealtion, ISearchUiProvider SearchUiProvider, IRelationService relationService) : base(DisplayText, selectedSearchResult, relations, SearchUiProvider, relationService)
{
SelectedRelation = selectedRealtion;
}

View File

@@ -0,0 +1,15 @@
using C4IT.FASD.Base;
using FasdDesktopUi.Basics.UiActions;
using System.Collections.Generic;
namespace FasdDesktopUi.Basics.Services.Models
{
public class CockpitValueDisplayData
{
public string Title { get; set; }
public IList<string> Values { get; set; }
public IList<enumHealthCardStateLevel> Levels { get; set; }
public bool IsLoading { get; set; }
public IList<cUiActionBase> UiActions { get; set; }
}
}

View File

@@ -19,20 +19,31 @@ namespace FasdDesktopUi.Basics.Services.ProtocollService
public static QuickActionProtocollEntry GetQuickActionProtocollEntry(cFasdQuickAction quickActionDefinition, cQuickActionCopyData quickActionCopyData)
{
string ascii = GetAscii(quickActionDefinition, quickActionCopyData);
string html = GetHtml(quickActionDefinition, quickActionCopyData);
string currentLanguage = cMultiLanguageSupport.CurrentLanguage;
return new QuickActionProtocollEntry(ascii, html)
try
{
Id = quickActionDefinition.Id,
Name = quickActionDefinition.Name,
ExecutionTypeId = (int)quickActionDefinition.ExecutionType,
WasRunningOnAffectedDevice = quickActionCopyData.WasRunningOnAffectedDevice,
AffectedDeviceName = quickActionCopyData.AffectedDeviceName,
ResultCode = (int?)quickActionCopyData.QuickActionOutput?.ResultCode,
ErrorMessage = quickActionCopyData.QuickActionOutput?.ErrorDescription,
MeasureValues = GetQuickActionHtmlValueComparison(quickActionCopyData.MeasureValues)
};
cMultiLanguageSupport.CurrentLanguage = cF4SDCockpitXmlConfig.Instance.HealthCardConfig.ProtocollLanguage ?? currentLanguage;
string ascii = GetAscii(quickActionDefinition, quickActionCopyData);
string html = GetHtml(quickActionDefinition, quickActionCopyData);
return new QuickActionProtocollEntry(ascii, html)
{
Id = quickActionDefinition.Id,
Name = quickActionDefinition.Name,
ExecutionTypeId = (int)quickActionDefinition.ExecutionType,
WasRunningOnAffectedDevice = quickActionCopyData.WasRunningOnAffectedDevice,
AffectedDeviceName = quickActionCopyData.AffectedDeviceName,
ResultCode = (int?)quickActionCopyData.QuickActionOutput?.ResultCode,
ErrorMessage = quickActionCopyData.QuickActionOutput?.ErrorDescription,
MeasureValues = GetQuickActionHtmlValueComparison(quickActionCopyData.MeasureValues)
};
}
finally
{
cMultiLanguageSupport.CurrentLanguage = currentLanguage;
}
}
internal static cQuickActionCopyData GetCopyData(cFasdQuickAction quickActionDefinition, cSupportCaseDataProvider dataProvider, bool wasRunningOnAffectedDevice, cQuickActionOutput quickActionOutput, List<cQuickActionMeasureValue> measureValues)
@@ -93,7 +104,7 @@ namespace FasdDesktopUi.Basics.Services.ProtocollService
{
string ascii = string.Empty;
ascii += GetQuickActionAsciiDescription(quickActionDefinition,copyData.Name, copyData.AffectedDeviceName, copyData.WasRunningOnAffectedDevice, copyData.ExecutionTime, copyData.QuickActionOutput?.ResultCode);
ascii += GetQuickActionAsciiDescription(quickActionDefinition, copyData.Name, copyData.AffectedDeviceName, copyData.WasRunningOnAffectedDevice, copyData.ExecutionTime, copyData.QuickActionOutput?.ResultCode);
ascii += GetQuickActionAsciiError(copyData.QuickActionOutput?.ErrorDescription);
ascii += GetQuickActionAsciiOutput(quickActionDefinition, copyData.QuickActionOutput);
ascii += GetQuickActionAsciiValueComparisonString(copyData.MeasureValues);
@@ -101,7 +112,7 @@ namespace FasdDesktopUi.Basics.Services.ProtocollService
return ascii;
}
private static string GetQuickActionAsciiDescription(cFasdQuickAction quickActionDefinition,string quickActionName, string deviceName, bool wasRunningOnAffectedDevice, DateTime executionTime, enumQuickActionSuccess? quickActionStatus)
private static string GetQuickActionAsciiDescription(cFasdQuickAction quickActionDefinition, string quickActionName, string deviceName, bool wasRunningOnAffectedDevice, DateTime executionTime, enumQuickActionSuccess? quickActionStatus)
{
string asciiDescription = string.Empty;
try
@@ -125,7 +136,7 @@ namespace FasdDesktopUi.Basics.Services.ProtocollService
rawDescription = cMultiLanguageSupport.GetItem("QuickAction.RemoteSession.Copy.Description");
}
asciiDescription = string.Format(rawDescription, quickActionName, deviceName, executionTime.ToString("g", new CultureInfo(cFasdCockpitConfig.Instance.SelectedLanguage)), quickActionStatusString);
}
catch (Exception E)
@@ -259,7 +270,7 @@ namespace FasdDesktopUi.Basics.Services.ProtocollService
output += AsciiSeperator + cMultiLanguageSupport.GetItem("QuickAction.Copy.Measure");
cUtility.RawValueFormatter.SetDefaultCulture(new System.Globalization.CultureInfo(cFasdCockpitConfig.Instance.SelectedLanguage));
cUtility.RawValueFormatter.SetDefaultCulture(cFasdCockpitConfig.Instance.SelectedCulture);
cUtility.RawValueFormatter.SetDefaultTimeZone(TimeZoneInfo.Local);
foreach (var measureValue in measureValues)
@@ -299,7 +310,7 @@ namespace FasdDesktopUi.Basics.Services.ProtocollService
try
{
output += GetQuickActionHtmlDescription(quickActionDefinition,copyData.Name, copyData.AffectedDeviceName, copyData.WasRunningOnAffectedDevice, copyData.ExecutionTime, copyData.QuickActionOutput?.ResultCode);
output += GetQuickActionHtmlDescription(quickActionDefinition, copyData.Name, copyData.AffectedDeviceName, copyData.WasRunningOnAffectedDevice, copyData.ExecutionTime, copyData.QuickActionOutput?.ResultCode);
output += GetQuickActionHtmlError(copyData.QuickActionOutput?.ErrorDescription);
output += GetQuickActionHtmlOutput(quickActionDefinition, copyData.QuickActionOutput);
output += GetQuickActionHtmlValueComparison(copyData.MeasureValues);
@@ -331,7 +342,7 @@ namespace FasdDesktopUi.Basics.Services.ProtocollService
}
var rawDescription = wasRunningOnAffectedDevice ? cMultiLanguageSupport.GetItem("QuickAction.Remote.Copy.Description.Html") : cMultiLanguageSupport.GetItem("QuickAction.Local.Copy.Description.Html");
if(quickActionDefinition.Section == enumDataHistoryOrigin.Citrix.ToString())
if (quickActionDefinition.Section == enumDataHistoryOrigin.Citrix.ToString())
{
rawDescription = cMultiLanguageSupport.GetItem("QuickAction.RemoteSession.Copy.Description.Html");
@@ -479,7 +490,7 @@ namespace FasdDesktopUi.Basics.Services.ProtocollService
output += "<p>" + cMultiLanguageSupport.GetItem("QuickAction.Copy.Measure.Html") + "</p>";
cUtility.RawValueFormatter.SetDefaultCulture(new System.Globalization.CultureInfo(cFasdCockpitConfig.Instance.SelectedLanguage));
cUtility.RawValueFormatter.SetDefaultCulture(cFasdCockpitConfig.Instance.SelectedCulture);
cUtility.RawValueFormatter.SetDefaultTimeZone(TimeZoneInfo.Local);
foreach (var measureValue in measureValues)

View File

@@ -9,8 +9,10 @@ namespace FasdDesktopUi.Basics.Services.RelationService
public interface IRelationService
{
event EventHandler<StagedSearchResultRelationsEventArgs> RelationsFound;
event EventHandler RelationsReset;
IEnumerable<cF4sdApiSearchResultRelation> GetLoadedRelations();
void Reset();
IReadOnlyList<cF4sdApiSearchResultRelation> GetLoadedRelations();
Task<cF4sdStagedSearchResultRelationTaskId> LoadRelationsAsync(IEnumerable<cFasdApiSearchResultEntry> relatedTo, CancellationToken token = default);
IRelationService Clone();
}

View File

@@ -14,8 +14,17 @@ namespace FasdDesktopUi.Basics.Services.RelationService
{
internal class RelationService : IRelationService
{
private readonly object _relationsLock = new object();
private IEnumerable<cF4sdApiSearchResultRelation> _relations = new List<cF4sdApiSearchResultRelation>();
public void Reset()
{
lock (_relationsLock)
_relations = _relations.Where(r => r.Type == enumF4sdSearchResultClass.User).ToList();
RelationsReset?.Invoke(this, EventArgs.Empty);
}
/// <summary>
/// Asynchronously loads relations for the specified search results.
/// </summary>
@@ -27,24 +36,14 @@ namespace FasdDesktopUi.Basics.Services.RelationService
{
try
{
_relations = relatedTo?.Select(searchResult => new cF4sdApiSearchResultRelation(searchResult)).ToList() ?? new List<cF4sdApiSearchResultRelation>();
lock (_relationsLock)
_relations = relatedTo?.Select(searchResult => new cF4sdApiSearchResultRelation(searchResult)).ToList() ?? new List<cF4sdApiSearchResultRelation>();
cF4sdStagedSearchResultRelationTaskId gatherRelationTask = await cFasdCockpitCommunicationBase.Instance.StartGatheringRelations(relatedTo, token);
_ = Task.Run(async () =>
{
const int maxRetryCount = 10;
for (int i = 0; i < maxRetryCount; i++)
{
cF4sdStagedSearchResultRelations stagedRelations = await cFasdCockpitCommunicationBase.Instance.GetStagedRelations(gatherRelationTask.Id, token);
stagedRelations.MergeAsRelationInfosWith(relatedTo);
if (gatherRelationTask is null)
return null;
_relations = _relations.Union(stagedRelations.Relations);
RelationsFound?.Invoke(this, new StagedSearchResultRelationsEventArgs() { RelatedTo = relatedTo, StagedResultRelations = stagedRelations, RelationService = this });
if (stagedRelations?.IsComplete ?? false)
break;
}
});
_ = Task.Run(async () => await GatherRelationsAsync(gatherRelationTask.Id, relatedTo, token));
return gatherRelationTask;
}
@@ -56,15 +55,56 @@ namespace FasdDesktopUi.Basics.Services.RelationService
return null;
}
public IEnumerable<cF4sdApiSearchResultRelation> GetLoadedRelations() => _relations;
private async Task GatherRelationsAsync(Guid gatherTaskId, IEnumerable<cFasdApiSearchResultEntry> relatedTo, CancellationToken token = default)
{
try
{
const int maxRetryCount = 10;
for (int i = 0; i < maxRetryCount; i++)
{
if (token.IsCancellationRequested)
{
await cFasdCockpitCommunicationBase.Instance.StopGatheringRelations(gatherTaskId, CancellationToken.None);
return;
}
cF4sdStagedSearchResultRelations stagedRelations = await cFasdCockpitCommunicationBase.Instance.GetStagedRelations(gatherTaskId, token);
if (stagedRelations is null)
continue;
stagedRelations.MergeAsRelationInfosWith(relatedTo);
lock (_relationsLock)
_relations = _relations.Union(stagedRelations.Relations).ToList();
RelationsFound?.Invoke(this, new StagedSearchResultRelationsEventArgs() { RelatedTo = relatedTo, StagedResultRelations = stagedRelations, RelationService = this });
if (stagedRelations?.IsComplete ?? false)
break;
}
}
catch (Exception ex)
{
LogException(ex);
}
}
public IReadOnlyList<cF4sdApiSearchResultRelation> GetLoadedRelations()
{
lock (_relationsLock)
return _relations.ToList();
}
public IRelationService Clone()
{
RelationService copy = (RelationService)MemberwiseClone();
copy._relations = _relations.Select(r => r).ToList();
RelationService copy = new RelationService();
lock (_relationsLock)
copy._relations = _relations.ToList();
return copy;
}
public event EventHandler RelationsReset;
public event EventHandler<StagedSearchResultRelationsEventArgs> RelationsFound;
}
}

View File

@@ -0,0 +1,136 @@
using C4IT.FASD.Cockpit.Communication;
using F4SD.Gamification.Services;
using FasdCockpitBase.Models.RemoteDesktopConnection;
using FasdCockpitCommunication.RemoteDesktopCommunication;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using static C4IT.Logging.cLogManager;
namespace FasdDesktopUi.Basics.Services.RemoteDesktop
{
internal static class AgentRemoteDesktopService
{
private static readonly Dictionary<Guid, Process> _viewerProcesses = new Dictionary<Guid, Process>();
internal static async Task<RemoteDesktopConnectionStatusResult> StartRemoteDesktopConnectionAsync(AgentRemoteClientInfo clientInfo, bool isElevated, CancellationToken token = default)
{
const int maxRetryCount = 20;
TimeSpan baseDelay = TimeSpan.FromSeconds(0.5);
AgentRemoteDesktopConnecitonDetails connectionDetails = await cFasdCockpitCommunicationBase.Instance.RemoteDesktopManager.InitiateConnection<AgentRemoteDesktopConnecitonDetails>(clientInfo, isElevated, token);
RemoteConnetionStatusChanged?.Invoke(null, RemoteDesktopConnectionStatus.Initiated);
if (token.IsCancellationRequested)
{
RemoteConnetionStatusChanged?.Invoke(null, RemoteDesktopConnectionStatus.Canceled);
return new RemoteDesktopConnectionStatusResult(RemoteDesktopConnectionStatus.Canceled);
}
if (connectionDetails is null)
{
LogEntry("Could not initiate remote connection.", C4IT.Logging.LogLevels.Warning);
return new RemoteDesktopConnectionStatusResult(RemoteDesktopConnectionStatus.Unknown);
}
if (connectionDetails.Errors != null && connectionDetails.Errors.Count > 0)
{
RemoteConnetionStatusChanged?.Invoke(null, RemoteDesktopConnectionStatus.Error);
return new RemoteDesktopConnectionStatusResult(RemoteDesktopConnectionStatus.Error) { Errors = connectionDetails.Errors.Select(e => e.Message).ToArray() };
}
RemoteDesktopConnectionStatusResult connectionStatus = null;
for (int i = 0; i < maxRetryCount; i++)
{
try
{
connectionStatus = await cFasdCockpitCommunicationBase.Instance.RemoteDesktopManager.GetConnectionStatus(connectionDetails.ConnectionId, token);
LogEntry($"Update {i} RemoteDesktop connection status: {connectionStatus}", C4IT.Logging.LogLevels.Debug);
if (token.IsCancellationRequested || IsRemoteConnectionEstablished(connectionStatus.Status))
break;
await Task.Delay(TimeSpan.FromTicks(baseDelay.Ticks * i));
}
catch (Exception ex)
{
LogException(ex);
}
}
if (token.IsCancellationRequested)
{
RemoteConnetionStatusChanged?.Invoke(null, RemoteDesktopConnectionStatus.Canceled);
return new RemoteDesktopConnectionStatusResult(RemoteDesktopConnectionStatus.Canceled);
}
RemoteConnetionStatusChanged?.Invoke(null, connectionStatus.Status);
if (connectionStatus.Status != RemoteDesktopConnectionStatus.Accepted)
{
LogEntry($"Could not connect to remote desktop. Status: {connectionStatus}", C4IT.Logging.LogLevels.Warning);
return new RemoteDesktopConnectionStatusResult(connectionStatus.Status);
}
StartViewer(connectionDetails);
GamificationService.TrackAction(F4SD.Gamification.CockpitAction.StartRemoteConnection);
return new RemoteDesktopConnectionStatusResult(RemoteDesktopConnectionStatus.Connected);
}
private static bool IsRemoteConnectionEstablished(RemoteDesktopConnectionStatus status)
{
switch (status)
{
case RemoteDesktopConnectionStatus.Accepted:
case RemoteDesktopConnectionStatus.Connected:
case RemoteDesktopConnectionStatus.Finished:
case RemoteDesktopConnectionStatus.Canceled:
return true;
case RemoteDesktopConnectionStatus.Unknown:
case RemoteDesktopConnectionStatus.Initiated:
default:
return false;
}
}
private static void StartViewer(AgentRemoteDesktopConnecitonDetails connectionDetails)
{
try
{
ProcessStartInfo info = new ProcessStartInfo(GetViewerPath())
{
Arguments = $"-connectionId {connectionDetails.ConnectionId} -phoenixServiceUrl {connectionDetails.PhoenixServiceUrl} -secret {connectionDetails.Secret}"
};
Process process = Process.Start(info);
_viewerProcesses.Add(connectionDetails.ConnectionId, process);
RemoteConnetionStatusChanged?.Invoke(null, RemoteDesktopConnectionStatus.Connected);
}
catch (Exception ex)
{
LogException(ex);
}
}
internal static async Task StopRemoteDesktopConnection(Guid connectionId, CancellationToken token = default)
{
await cFasdCockpitCommunicationBase.Instance.RemoteDesktopManager.StopConnection(connectionId, token);
if (_viewerProcesses.TryGetValue(connectionId, out var process))
process.Close();
}
internal static string GetViewerPath()
{
return Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"Phoenix\Phoenix.Viewer.exe").ToString();
}
internal static EventHandler<RemoteDesktopConnectionStatus> RemoteConnetionStatusChanged;
}
}

View File

@@ -0,0 +1,48 @@
using C4IT.FASD.Base;
using FasdDesktopUi.Basics.Enums;
using FasdDesktopUi.Basics.Helper;
using FasdDesktopUi.Basics.Models;
using System.Collections.Generic;
namespace FasdDesktopUi.Basics.Services.SupportCase.Controllers
{
internal static class MenuDataFactory
{
internal static cMenuDataBase GetByName(string name, cNamedParameterList namedParameterEntries, IEnumerable<enumFasdInformationClass> availableInformationClasses)
{
if (!cF4SDCockpitXmlConfig.Instance.MenuItems.TryGetValue(name, out var menuDataDefinition))
return null;
return Create(menuDataDefinition, namedParameterEntries, availableInformationClasses);
}
internal static cMenuDataBase Create(cFasdBaseConfigMenuItem definition, cNamedParameterList namedParameterEntries, IEnumerable<enumFasdInformationClass> availableInformationClasses)
{
cMenuDataBase menuData;
switch (definition)
{
case cFasdQuickAction quickActionDefinition:
menuData = new cMenuDataBase(quickActionDefinition);
break;
case cCopyTemplate copyTemplate:
menuData = new cMenuDataBase(copyTemplate);
break;
case cFasdMenuSection sectionDefinition:
menuData = new cMenuDataContainer(sectionDefinition);
break;
case cFasdQuickTip quickTip:
menuData = new cMenuDataBase(quickTip);
break;
default:
menuData = new cMenuDataBase(definition);
break;
}
enumActionDisplayType displayType = ActionDisplayTypeInspector.GetDisplayType(definition, namedParameterEntries, availableInformationClasses);
menuData.SetUiActionDisplayType(displayType);
return menuData;
}
}
}

View File

@@ -1,7 +1,12 @@
using C4IT.FASD.Base;
using C4IT.FASD.Cockpit.Communication;
using FasdCockpitCommunication.RemoteDesktopCommunication;
using FasdDesktopUi.Basics.CustomEvents;
using FasdDesktopUi.Basics.Enums;
using FasdDesktopUi.Basics.Models;
using FasdDesktopUi.Basics.Services.Models;
using FasdDesktopUi.Basics.Services.SupportCase.Processors;
using FasdDesktopUi.Basics.UiActions;
using FasdDesktopUi.Pages.DetailsPage.Models;
using System;
using System.Collections.Generic;
@@ -12,18 +17,18 @@ using static C4IT.Logging.cLogManager;
namespace FasdDesktopUi.Basics.Services.SupportCase.Controllers
{
/// <summary>
/// Used to manage the <see cref="ISupportCase"/> for the UI via the <see cref="SupportCaseProcessor"/>
/// Used to manage the <see cref="ISupportCase"/> for the UI via the <see cref="ISupportCaseProcessor"/>
/// </summary>
public class SupportCaseController
{
private SupportCaseProcessor _supportCaseProcessor;
private ISupportCaseProcessor _supportCaseProcessor;
private cF4sdApiSearchResultRelation _focusedRelation;
private readonly Dictionary<enumFasdInformationClass, cF4sdApiSearchResultRelation> _selectedRelations = new Dictionary<enumFasdInformationClass, cF4sdApiSearchResultRelation>();
private cHealthCard _selectedHealthcard = null;
private bool _hasDirectionConnection = false;
public cSupportCaseDataProvider SupportCaseDataProviderArtifact { get => _supportCaseProcessor?.SupportCaseDataProviderArtifact; }
internal void SetSupportCaseProcessor(SupportCaseProcessor supportCaseProcessor, IEnumerable<cF4sdIdentityEntry> preselectedIdentities)
internal void SetSupportCaseProcessor(ISupportCaseProcessor supportCaseProcessor, IEnumerable<cF4sdIdentityEntry> preselectedIdentities)
{
IEnumerable<cF4sdApiSearchResultRelation> preselectedRelations = GetPreselectedRelations(supportCaseProcessor.GetCaseRelations(), preselectedIdentities);
@@ -88,6 +93,9 @@ namespace FasdDesktopUi.Basics.Services.SupportCase.Controllers
{
CaseDataChanged?.Invoke(this, e);
if (e.DataTables.Any(t => !t.Name.StartsWith("Computation_")))
_supportCaseProcessor.ProcessComputations(e.Relation, _supportCaseProcessor.GetHealthcardFor(e.Relation).Prerequisites.Computations.Values);
_ = Task.Run(async () =>
{
await UpdateStatusOfSelectedRelations();
@@ -220,7 +228,7 @@ namespace FasdDesktopUi.Basics.Services.SupportCase.Controllers
relationEntry.Value.Infos[StatusString] = statusValue;
}
IEnumerable<cHeadingDataModel> newHeadingData = SupportCaseHeadingController.GetHeadingData(_selectedRelations);
IEnumerable<cHeadingDataModel> newHeadingData = GetHeadingData();
HeadingDataChanged?.Invoke(this, new HeadingDataEventArgs(newHeadingData));
}
catch (Exception ex)
@@ -234,18 +242,209 @@ namespace FasdDesktopUi.Basics.Services.SupportCase.Controllers
SupportCaseDataProviderArtifact.HealthCardDataHelper.LoadingHelper.LastDataRequest = DateTime.Now;
await _supportCaseProcessor.UpdateLatestCaseDataFor(_focusedRelation);
}
public cCopyTemplate GetCopyTemplate()
{
string defaultCopyTemplate = _selectedHealthcard?.DefaultCopyTemplate
?? cF4SDCockpitXmlConfig.Instance?.CopyTemplateConfig?.CopyTemplates.DefaultTemplate.Name
?? string.Empty;
public List<List<cWidgetValueModel>> GetWidgetData()
=> _supportCaseProcessor.GetWidgetData(_focusedRelation);
Dictionary<string, cCopyTemplate> copyTemplates = cF4SDCockpitXmlConfig.Instance?.CopyTemplateConfig?.CopyTemplates?.CopyTemplates;
if (copyTemplates?.TryGetValue(defaultCopyTemplate, out cCopyTemplate _defaultCopyTemplate) ?? false)
return _defaultCopyTemplate;
else
return null;
}
public List<List<cWidgetValueModel>> GetWidgetsData()
{
try
{
List<List<cWidgetValueModel>> widgetsData = new List<List<cWidgetValueModel>>();
cHealthCard currentHealthCard = _supportCaseProcessor.GetHealthcardFor(_focusedRelation);
IEnumerable<cHealthCardStateCategory> widgetsDefinition = currentHealthCard.CategoriesStatic.StateCategories;
foreach (var widgetDefinition in widgetsDefinition)
{
List<cWidgetValueModel> widgetData = new List<cWidgetValueModel>();
foreach (var widgetValueDefinition in widgetDefinition.States)
{
CockpitValueDisplayData displayData = _supportCaseProcessor.GetCockpitValueDisplayData(widgetValueDefinition, _focusedRelation, true);
cUiActionBase uiAction = _supportCaseProcessor.GetUiAction(widgetValueDefinition);
var widgetValue = new cWidgetValueModel(displayData, GetHighlightColor(displayData?.Levels?.FirstOrDefault() ?? enumHealthCardStateLevel.None), uiAction);
if (widgetValue?.UiActionValue != null && ShouldHideUiActionValue(widgetValue.Value))
widgetValue.UiActionValue.DisplayType = enumActionDisplayType.hidden;
widgetData.Add(widgetValue);
}
widgetsData.Add(widgetData);
}
return widgetsData;
}
catch (Exception ex)
{
LogException(ex);
return null;
}
bool ShouldHideUiActionValue(string widgetValue)
=> string.IsNullOrWhiteSpace(widgetValue) || widgetValue == "0";
}
public cDetailsPageDataHistoryDataModel GetHistoryData()
=> _supportCaseProcessor.GetHistoryData(_focusedRelation);
=> ((SupportCaseProcessor)_supportCaseProcessor).GetHistoryData(_focusedRelation);
public List<cContainerCollectionData> GetContainerData()
=> _supportCaseProcessor.GetContainerData(_focusedRelation);
=> ((SupportCaseProcessor)_supportCaseProcessor).GetContainerData(_focusedRelation);
public List<cMenuDataBase> GetMenuBarData()
=> _supportCaseProcessor.GetMenuBarData(_focusedRelation);
public IEnumerable<cMenuDataBase> GetMenuData()
{
var menuBarDatas = cF4SDCockpitXmlConfig.Instance.MenuItems.ToDictionary(config => config.Key, config => (Config: config.Value, MenuValue: MenuDataFactory.Create(config.Value, _supportCaseProcessor.SupportCaseDataProviderArtifact.NamedParameterEntries, _selectedRelations.Values.Select(r => cF4sdIdentityEntry.GetFromSearchResult(r.Type)).ToList())));
try
{
List<string> menuBarDatasAddedToSection = new List<string>();
foreach (var menuBarData in menuBarDatas.Values)
{
bool isPinned = cF4SDCockpitXmlConfig.Instance.HealthCardConfig.QuickActionsPinned.Contains(menuBarData.Config.Name);
menuBarData.MenuValue.IconPositionIndex = isPinned ? cF4SDCockpitXmlConfig.Instance.HealthCardConfig.QuickActionsPinned.IndexOf(menuBarData.Config.Name) : -1;
// Same Logic in GetFilteredMenuData()
bool hasBeenAddedToSection = TryAddMenuDataToSection(ref menuBarDatas, menuBarData.Config.Section, menuBarData.MenuValue);
foreach (var section in menuBarData.Config.Sections)
{
// todo add test for adding to multiple sections
hasBeenAddedToSection = TryAddMenuDataToSection(ref menuBarDatas, section, menuBarData.MenuValue) || hasBeenAddedToSection; // order is relevent, because data has to be added to section
}
if (hasBeenAddedToSection)
menuBarDatasAddedToSection.Add(menuBarData.Config.Name);
}
foreach (var menuBarData in menuBarDatasAddedToSection)
{
if (cF4SDCockpitXmlConfig.Instance.HealthCardConfig.QuickActionsPinned.Contains(menuBarData))
continue;
menuBarDatas.Remove(menuBarData);
}
return menuBarDatas.Values.Select(data => data.MenuValue);
}
catch (Exception ex)
{
LogException(ex);
return new List<cMenuDataBase>();
}
}
public IEnumerable<cMenuDataBase> GetFilteredMenuData(MenuDataFilter filterQuery)
{
Dictionary<string, (cFasdBaseConfigMenuItem Config, cMenuDataBase MenuValue)> menuBarDatas = cF4SDCockpitXmlConfig.Instance.MenuItems.ToDictionary(config => config.Key, config => (Config: config.Value, MenuValue: MenuDataFactory.Create(config.Value, _supportCaseProcessor.SupportCaseDataProviderArtifact.NamedParameterEntries, _selectedRelations.Values.Select(r => cF4sdIdentityEntry.GetFromSearchResult(r.Type)).ToList())));
try
{
foreach (var menuBarData in menuBarDatas.Values)
{
// Same Logic in GetMenuData()
bool hasBeenAddedToSection = TryAddMenuDataToSection(ref menuBarDatas, menuBarData.Config.Section, menuBarData.MenuValue);
foreach (var section in menuBarData.Config.Sections)
{
// todo add test for adding to multiple sections
hasBeenAddedToSection = TryAddMenuDataToSection(ref menuBarDatas, section, menuBarData.MenuValue) || hasBeenAddedToSection; // order is relevent, because data has to be added to section
}
}
return menuBarDatas.Values.Select(data => data.MenuValue)
.Where(md => md.MenuText.ToLower().Contains(filterQuery?.SearchString?.ToLower()));
}
catch (Exception ex)
{
LogException(ex);
return new List<cMenuDataBase>();
}
}
private bool TryAddMenuDataToSection(ref Dictionary<string, (cFasdBaseConfigMenuItem Config, cMenuDataBase MenuValue)> menuBarDatas, string section, cMenuDataBase menuData)
{
if (string.IsNullOrEmpty(section))
return false;
if (!menuBarDatas.TryGetValue(section, out var sectionMenuBar))
return false;
if (!(sectionMenuBar.MenuValue is cMenuDataContainer containerData))
return false;
if (containerData.UiAction is cSubMenuAction subMenuAction)
{
if (!subMenuAction.SubMenuData.Any(item => item.MenuText == menuData.MenuText)) // todo SubMenuData maybe better as Dictionary<Guid, cMenuDataBase>
subMenuAction.SubMenuData.Add(menuData);
if (!containerData.SubMenuData.Any(item => item.MenuText == menuData.MenuText))
containerData.SubMenuData.Add(menuData);
}
return true;
}
internal IEnumerable<cHeadingDataModel> GetHeadingData()
=> SupportCaseHeadingController.GetHeadingData(_selectedRelations);
private static enumHighlightColor GetHighlightColor(enumHealthCardStateLevel level)
{
switch (level)
{
case enumHealthCardStateLevel.None:
return enumHighlightColor.none;
case enumHealthCardStateLevel.Ok:
return enumHighlightColor.green;
case enumHealthCardStateLevel.Warning:
return enumHighlightColor.orange;
case enumHealthCardStateLevel.Error:
return enumHighlightColor.red;
case enumHealthCardStateLevel.Info:
return enumHighlightColor.blue;
default:
return enumHighlightColor.none;
}
}
internal IEnumerable<cF4sdApiSearchResultRelation> GetRelationsOf(enumFasdInformationClass informationClass)
{
return _supportCaseProcessor.GetCaseRelations().FirstOrDefault(r => r.Key == informationClass);
}
internal AgentRemoteClientInfo GetAgentClientInfo()
{
if (_focusedRelation.Type != enumF4sdSearchResultClass.Computer)
return null;
int deviceId = 0;
int userId = 0;
bool hasAllRequiredNamedParameters =
_supportCaseProcessor.TryGetNamedParameterValue(_focusedRelation, SupportCaseProcessor.AgentOrganisationCodeNamedParameterName, out int organisationId)
&& _supportCaseProcessor.TryGetNamedParameterValue(_focusedRelation, SupportCaseProcessor.AgentUserIdNamedParameterName, out userId)
&& _supportCaseProcessor.TryGetNamedParameterValue(_focusedRelation, SupportCaseProcessor.AgentDeviceIdNamedParameterName, out deviceId);
if (!hasAllRequiredNamedParameters)
return null;
return new AgentRemoteClientInfo()
{
DeviceCode = deviceId,
AccountCode = userId,
OrganisationCode = organisationId
};
}
/// <summary>
/// Raised when the currently for a support case relevant and shown relations have been updated.
@@ -264,4 +463,15 @@ namespace FasdDesktopUi.Basics.Services.SupportCase.Controllers
public event EventHandler<HeadingDataEventArgs> HeadingDataChanged;
}
public class MenuDataFilter
{
public string SearchString { get; set; }
public MenuDataFilter(string searchString = null)
{
SearchString = searchString;
}
}
}

View File

@@ -68,6 +68,7 @@ namespace FasdDesktopUi.Basics.Services.SupportCase.Controllers
{
case enumF4sdSearchResultClass.Computer:
case enumF4sdSearchResultClass.User:
case enumF4sdSearchResultClass.Phone:
isOnline = string.Equals(statusValue, "Online", StringComparison.InvariantCultureIgnoreCase);
break;
case enumF4sdSearchResultClass.Ticket:

View File

@@ -14,7 +14,7 @@ namespace FasdDesktopUi.Basics.Services.SupportCase
void AddCaseRelations(ILookup<enumFasdInformationClass, cF4sdApiSearchResultRelation> relations);
ILookup<enumFasdInformationClass, cF4sdApiSearchResultRelation> GetCaseRelations();
Task LoadSupportCaseDataAsync(cF4sdApiSearchResultRelation relation, IEnumerable<string> tablesToLoad);
IEnumerable<object> GetSupportCaseHealthcardData(cF4sdApiSearchResultRelation relation, cValueAddress valueAddress);
IList<object> GetSupportCaseHealthcardData(cF4sdApiSearchResultRelation relation, cValueAddress valueAddress, bool getStatic);
void UpdateSupportCaseDataCache(cF4sdApiSearchResultRelation relation, IEnumerable<cF4SDHealthCardRawData.cHealthCardTable> tables);
void InvalidateCaseDataCacheFor(cF4sdApiSearchResultRelation relation);
void InvalidateLatestCaseDataCacheFor(cF4sdApiSearchResultRelation relation, out ICollection<cF4SDHealthCardRawData.cHealthCardTable> invalidatedTables);

View File

@@ -0,0 +1,29 @@
using C4IT.FASD.Base;
using FasdDesktopUi.Basics.CustomEvents;
using FasdDesktopUi.Basics.Services.Models;
using FasdDesktopUi.Basics.UiActions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace FasdDesktopUi.Basics.Services.SupportCase.Processors
{
internal interface ISupportCaseProcessor
{
cSupportCaseDataProvider SupportCaseDataProviderArtifact { get; }
event EventHandler<RelationEventArgs> AvailableCaseRelationsAdded;
event EventHandler<SupportCaseDataEventArgs> CaseDataChanged;
void SetSupportCase(ISupportCase supportCase);
CockpitValueDisplayData GetCockpitValueDisplayData(cHealthCardStateBase displayValueDefinition, cF4sdApiSearchResultRelation relation, bool getStatic);
Task LoadSupportCaseDataAsync(cF4sdApiSearchResultRelation relation, IEnumerable<string> tablesToLoad);
void ProcessComputations(cF4sdApiSearchResultRelation relation, IEnumerable<cHealthCardComputationBase> computations);
Task UpdateLatestCaseDataFor(cF4sdApiSearchResultRelation relation);
ILookup<enumFasdInformationClass, cF4sdApiSearchResultRelation> GetCaseRelations();
cHealthCard GetHealthcardFor(cF4sdApiSearchResultRelation relation);
cUiActionBase GetUiAction(cHealthCardStateBase stateDefinition);
bool TryGetNamedParameterValue<T>(cF4sdApiSearchResultRelation relation, string parameterName, out T value);
}
}

View File

@@ -0,0 +1,145 @@
using C4IT.F4SD.DisplayFormatting;
using C4IT.FASD.Base;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using static C4IT.Logging.cLogManager;
namespace FasdDesktopUi.Basics.Services.SupportCase.Processors
{
internal class SupportCaseDetailsValueProcessesor
{
/// <summary>
/// Transform a value to a <seealso cref="cF4SDHealthCardRawData.cHealthCardDetailsTable"/>
/// </summary>
/// <param name="rawValue">Raw value in form of CSV or JSON</param>
/// <param name="stateDetailsValued">Details definition the transformation is based on</param>
/// <returns></returns>
internal static cF4SDHealthCardRawData.cHealthCardDetailsTable GetDetailsTable(object rawValue, cHealthCardDetailsValued stateDetailsValued)
{
try
{
if (rawValue is null)
return null;
var stringValue = cUtility.RawValueFormatter.GetDisplayValue(rawValue, RawValueType.STRING);
if (stringValue is null)
return null;
List<object[]> tableValues = stateDetailsValued.Format == cHealthCardDetailsValued.ValuedFormat.json
? ParseJson(stringValue, stateDetailsValued)
: ParseCsv(stringValue, stateDetailsValued);
tableValues = tableValues ?? new List<object[]>();
var detailedValueTable = new cF4SDHealthCardRawData.cHealthCardDetailsTable()
{
Name = "Details-" + stateDetailsValued.ParentState.Name,
Columns = stateDetailsValued.Select(v => v.Names.GetValue()).ToList(),
Values = new Dictionary<int, List<object[]>>() { { 0, tableValues } }
};
return detailedValueTable;
}
catch (Exception ex)
{
LogException(ex);
}
return null;
}
private static List<object[]> ParseJson(string text, cHealthCardDetailsValued details)
{
try
{
var jObjects = JsonConvert.DeserializeObject<List<JObject>>(text);
if (jObjects == null || jObjects.Count == 0)
return new List<object[]>();
var values = new List<object[]>();
foreach (JObject jObject in jObjects)
{
var valueRow = new object[details.Count];
for (int i = 0; i < details.Count; i++)
valueRow[i] = null;
foreach (var jProp in jObject.Properties())
{
var name = jProp.Name;
var index = details.FindIndex(v => v.Column.Equals(name, StringComparison.InvariantCultureIgnoreCase));
if (index < 0)
continue;
var column = details[index];
if (jProp.Value is JValue jValue)
{
var value = jValue.Value;
valueRow[index] = cUtility.RawValueFormatter.GetDisplayValue(value, column.DisplayType);
}
}
values.Add(valueRow);
}
return values;
}
catch (Exception)
{
return new List<object[]>();
}
}
private static List<object[]> ParseCsv(string text, cHealthCardDetailsValued details)
{
var values = new List<object[]>();
var rows = text.Split(details.RowSeparator);
foreach (var row in rows)
{
if (string.IsNullOrWhiteSpace(row))
continue;
var entry = new List<object>();
if (details.ColSeparator == null)
{
entry.Add(row.Trim());
}
else
{
var columns = row.Split((char)details.ColSeparator);
foreach (var column in columns)
entry.Add(column?.Trim());
}
while (entry.Count < details.Count)
entry.Add(null);
values.Add(entry.ToArray());
}
return values;
}
/// <summary>
/// Get a display string based on StateDetails and its values
/// </summary>
internal static string GetDetailStringValue(cHealthCardDetailsBase details, cF4SDHealthCardRawData.cHealthCardDetailsTable detailTableValueTable)
{
if (detailTableValueTable?.Values is null || detailTableValueTable.Values.Count == 0)
return null;
var tableValues = detailTableValueTable.Values.First().Value;
if (detailTableValueTable.Columns.Count >= 1 && tableValues.Count == 1)
return cUtility.RawValueFormatter.GetDisplayValue(detailTableValueTable.Values.First().Value.First()?.First(), details.First().DisplayType);
else
return cUtility.RawValueFormatter.GetDisplayValue(detailTableValueTable.Values.First().Value.Count, RawValueType.STRING);
}
}
}

View File

@@ -0,0 +1,643 @@
using C4IT.FASD.Base;
using C4IT.FASD.Cockpit.Communication;
using C4IT.Logging;
using FasdDesktopUi.Basics.CustomEvents;
using FasdDesktopUi.Basics.Enums;
using FasdDesktopUi.Basics.Models;
using FasdDesktopUi.Basics.Services.Models;
using FasdDesktopUi.Basics.Services.SupportCase.Controllers;
using FasdDesktopUi.Basics.UiActions;
using FasdDesktopUi.Pages.DetailsPage.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using static C4IT.Logging.cLogManager;
namespace FasdDesktopUi.Basics.Services.SupportCase.Processors
{
/// <summary>
/// Used for processing raw data of a <see cref="ISupportCase"/> for the UI in a certain way.
/// </summary>
internal class SupportCaseProcessor : ISupportCaseProcessor
{
internal const string AgentDeviceIdNamedParameterName = "F4SD_Agent_DeviceId";
internal const string AgentUserIdNamedParameterName = "F4SD_Agent_UserId";
internal const string AgentOrganisationCodeNamedParameterName = "F4SD_Agent_OrganisationId";
private ISupportCase _supportCase;
public cSupportCaseDataProvider SupportCaseDataProviderArtifact { get => _supportCase?.SupportCaseDataProviderArtifact; }
private readonly Dictionary<cF4sdApiSearchResultRelation, cDetailsPageData> _detailsPageDataCache = new Dictionary<cF4sdApiSearchResultRelation, cDetailsPageData>();
private readonly Dictionary<cF4sdApiSearchResultRelation, Dictionary<string, object>> _namedParameterCache = new Dictionary<cF4sdApiSearchResultRelation, Dictionary<string, object>>();
private readonly Dictionary<cF4sdApiSearchResultRelation, Dictionary<string, IList<object>>> _computationCache = new Dictionary<cF4sdApiSearchResultRelation, Dictionary<string, IList<object>>>();
public void SetSupportCase(ISupportCase supportCase)
{
if (_supportCase != null)
{
_supportCase.CaseRelationsAdded -= HandleSupportCaseRelationsAdded;
_supportCase.SupportCaseDataCacheHasChanged -= HandleSupportCaseDataCacheHasChanged;
}
_supportCase = supportCase;
_supportCase.CaseRelationsAdded += HandleSupportCaseRelationsAdded;
_supportCase.SupportCaseDataCacheHasChanged += HandleSupportCaseDataCacheHasChanged;
}
private void HandleSupportCaseRelationsAdded(object sender, RelationEventArgs e)
=> AvailableCaseRelationsAdded?.Invoke(this, e);
private async void HandleSupportCaseDataCacheHasChanged(object sender, SupportCaseDataEventArgs e)
{
bool isArtifactShowingCorrectHealthCard
= SupportCaseDataProviderArtifact.HealthCardDataHelper.SelectedHealthCard == GetHealthcardFor(e.Relation);
if (!isArtifactShowingCorrectHealthCard)
{
// todo this can probably be removed, as soon as the last dependency of the SupportCaseDataProviderArtifact is gone.
// till then the detailspageData gets overriden with the detailspageData of the new relation.
// However, the removal shouldn't be much of a problem, due to the fact the Artifact also stores the raw data
_detailsPageDataCache.Remove(e.Relation);
return;
}
await EnsureDetailsPageDataCachedAsync(e.Relation).ConfigureAwait(false);
UpdateNamedParameters(e.Relation, e.DataTables);
CaseDataChanged?.Invoke(this, e);
}
private async Task EnsureDetailsPageDataCachedAsync(cF4sdApiSearchResultRelation relation)
{
if (!_detailsPageDataCache.TryGetValue(relation, out var cachedData))
{
_detailsPageDataCache[relation] = await _supportCase.SupportCaseDataProviderArtifact.HealthCardDataHelper.DetailPage.GetDataAsync().ConfigureAwait(false);
return;
}
cDetailsPageData detailData = _supportCase.SupportCaseDataProviderArtifact.HealthCardDataHelper.DetailPage.GetDataWithoutHeading();
cachedData.WidgetData = detailData.WidgetData;
cachedData.DataHistoryList = detailData.DataHistoryList;
cachedData.MenuBarData = detailData.MenuBarData;
cachedData.DataContainerCollectionList = detailData.DataContainerCollectionList;
}
private void UpdateNamedParameters(cF4sdApiSearchResultRelation relation, IEnumerable<cF4SDHealthCardRawData.cHealthCardTable> dataTables)
{
try
{
if (!_namedParameterCache.ContainsKey(relation))
_namedParameterCache.Add(relation, new Dictionary<string, object>());
var healthcard = GetHealthcardFor(relation);
foreach (var namedParameter in cHealthCardPrerequisites.GetNamedParameters(healthcard).Values)
{
var table = dataTables.FirstOrDefault(t => t.Name == namedParameter.DatabaseInfo.ValueTable);
if (table is null)
continue;
if (!table.Columns.TryGetValue(namedParameter.DatabaseInfo.ValueColumn, out var column))
continue;
string value = cUtility.RawValueFormatter.GetDisplayValue(column.Values.FirstOrDefault(), namedParameter.Display);
_namedParameterCache[relation][namedParameter.ParameterName] = value;
}
AddDefaultNamedParameters();
}
catch (Exception ex)
{
LogException(ex);
}
void AddDefaultNamedParameters()
{
var table = dataTables.FirstOrDefault(t => t.Name == "agnt-computer");
if (table != null)
{
if (table.Columns.TryGetValue("id", out var agentDeviceIdColumn))
_namedParameterCache[relation][AgentDeviceIdNamedParameterName] = agentDeviceIdColumn.Values.FirstOrDefault();
}
table = dataTables.FirstOrDefault(t => t.Name == "agnt-user");
if (table != null)
{
if (table.Columns.TryGetValue("id", out var agentUserColumn))
_namedParameterCache[relation][AgentUserIdNamedParameterName] = agentUserColumn.Values.FirstOrDefault();
}
_namedParameterCache[relation][AgentOrganisationCodeNamedParameterName] = cCockpitConfiguration.Instance.agentApiConfiguration.OrganizationCode;
}
}
public async Task LoadSupportCaseDataAsync(cF4sdApiSearchResultRelation relation, IEnumerable<string> tablesToLoad)
{
_ = Task.Run(async () => await _supportCase.LoadSupportCaseDataAsync(relation, tablesToLoad.Where(t => !t.Contains("-details-"))));
await EnsureDetailsPageDataCachedAsync(relation).ConfigureAwait(false);
_supportCase.SupportCaseDataProviderArtifact.HealthCardDataHelper.UpdateNamedParameterEntries();
CaseDataChanged?.Invoke(this, new SupportCaseDataEventArgs());
}
public void ProcessComputations(cF4sdApiSearchResultRelation relation, IEnumerable<cHealthCardComputationBase> computations)
{
if (!_computationCache.ContainsKey(relation))
_computationCache.Add(relation, new Dictionary<string, IList<object>>());
var cachedComputations = _computationCache[relation];
var computationTables = new List<cF4SDHealthCardRawData.cHealthCardTable>();
foreach (cHealthCardComputationBase computation in computations)
{
AddComputationTable(computation);
AddComputationTableStatic(computation);
}
CaseDataChanged?.Invoke(this, new SupportCaseDataEventArgs() { Relation = relation, DataTables = computationTables });
void AddComputationTable(cHealthCardComputationBase computation)
{
var computationTable = new cF4SDHealthCardRawData.cHealthCardTable() { Name = $"Computation_{computation.Name}", AlternateStaticTable = $"Computation_{computation.Name}_latest" };
var computationColumn = new cF4SDHealthCardRawData.cHealthCardTableColumn(computationTable);
for (int i = 0; i < cF4SDCockpitXmlConfig.Instance.HealthCardConfig.SearchResultAge; i++)
{
object[] valuesRequiredToCompute = computation.Values
.Where(v => v.ValueTable != null && v.ValueColumn != null)
.Select(v => _supportCase.GetSupportCaseHealthcardData(relation, v, false)?.ElementAtOrDefault(i))
.ToArray();
object computedValue = computation.Compute(valuesRequiredToCompute);
computationColumn.Values.Add(computedValue);
}
computationTable.Columns = new Dictionary<string, cF4SDHealthCardRawData.cHealthCardTableColumn>() { ["default"] = computationColumn };
computationTables.Add(computationTable);
cachedComputations[computation.Name] = computationColumn.Values;
}
void AddComputationTableStatic(cHealthCardComputationBase computation)
{
var computationTableStatic = new cF4SDHealthCardRawData.cHealthCardTable() { Name = $"Computation_{computation.Name}_latest" };
var computationColumnStatic = new cF4SDHealthCardRawData.cHealthCardTableColumn(computationTableStatic);
object[] staticValuesRequiredToCompute = computation.Values
.Where(v => v.ValueTable != null && v.ValueColumn != null)
.Select(v => _supportCase.GetSupportCaseHealthcardData(relation, v, true)?.ElementAtOrDefault(0))
.ToArray();
object computedStaticValue = computation.Compute(staticValuesRequiredToCompute);
computationColumnStatic.Values.Add(computedStaticValue);
computationTableStatic.Columns = new Dictionary<string, cF4SDHealthCardRawData.cHealthCardTableColumn>() { ["default"] = computationColumnStatic };
computationTables.Add(computationTableStatic);
cachedComputations[$"{computation.Name}_latest"] = computationColumnStatic.Values;
}
}
public async Task UpdateLatestCaseDataFor(cF4sdApiSearchResultRelation relation)
{
try
{
int? agentUserId = relation.Identities.FirstOrDefault(i => i.Class == enumFasdInformationClass.User)?.agentId;
int? agentDeviceId = relation.Identities.FirstOrDefault(i => i.Class == enumFasdInformationClass.Computer)?.agentId;
await ActualizeDataAsync(agentUserId, agentDeviceId);
_supportCase.InvalidateLatestCaseDataCacheFor(relation, out var invalidatedTables);
_detailsPageDataCache.Remove(relation);
await _supportCase.LoadSupportCaseDataAsync(relation, invalidatedTables.Where(t => !t.Name.StartsWith("Computation_")).Select(t => t.Name));
}
catch (Exception ex)
{
LogException(ex);
}
}
private async Task<enumActualizeStatus> ActualizeDataAsync(int? agentUserId, int? agentDeviceId)
{
var status = enumActualizeStatus.unknown;
if (!agentUserId.HasValue && !agentDeviceId.HasValue)
return status;
try
{
TimeSpan refreshDelay = TimeSpan.FromMilliseconds(500);
const int maxPollCount = 20;
if (!agentDeviceId.HasValue)
{
LogEntry("Coudldn't acutalize data. There was no valid AgentDeviceId found.", LogLevels.Error);
return status;
}
var taskId = await cFasdCockpitCommunicationBase.Instance.ActualizeAgentData(agentDeviceId.Value, agentUserId);
if (taskId == Guid.Empty)
return enumActualizeStatus.failed;
enumFasdInformationClass informationClass = agentUserId != null ? enumFasdInformationClass.User : enumFasdInformationClass.Computer;
int pollCount = 0;
do
{
status = await cFasdCockpitCommunicationBase.Instance.GetActualizeAgentDataStatus(taskId, informationClass);
if (status == enumActualizeStatus.unknown)
{
pollCount++;
if (pollCount >= maxPollCount)
return status;
await Task.Delay(refreshDelay);
}
} while (status == enumActualizeStatus.unknown);
}
catch (Exception E)
{
LogException(E);
}
return status;
}
public ILookup<enumFasdInformationClass, cF4sdApiSearchResultRelation> GetCaseRelations()
=> _supportCase.GetCaseRelations();
public cHealthCard GetHealthcardFor(cF4sdApiSearchResultRelation relation)
{
var availableHealthCards = cF4SDCockpitXmlConfig.Instance?.HealthCardConfig?.HealthCards?.Values;
if (availableHealthCards is null || availableHealthCards.Count == 0)
return null;
return availableHealthCards
.FirstOrDefault(hc =>
hc.InformationClasses.All(i => i == cF4sdIdentityEntry.GetFromSearchResult(relation.Type))
&& HasCockpitUserRequiredRoles(hc.RequiredRoles));
}
private static bool HasCockpitUserRequiredRoles(List<string> requiredRoles)
{
try
{
if (requiredRoles is null || requiredRoles.Count == 0)
return true;
List<string> roles = null;
lock (cFasdCockpitCommunicationBase.CockpitUserInfoLock)
{
roles = cFasdCockpitCommunicationBase.CockpitUserInfo?.Roles;
}
if (roles is null || roles.Count == 0)
return false;
foreach (var requiredRole in requiredRoles)
{
if (roles.Contains(requiredRole, StringComparer.InvariantCultureIgnoreCase))
return true;
}
}
catch (Exception E)
{
LogException(E);
}
return false;
}
public cDetailsPageDataHistoryDataModel GetHistoryData(cF4sdApiSearchResultRelation relation)
{
cDetailsPageDataHistoryDataModel historyData = null;
if (_detailsPageDataCache.TryGetValue(relation, out var detailsData))
historyData = detailsData?.DataHistoryList;
return historyData ?? new cDetailsPageDataHistoryDataModel();
}
public List<cContainerCollectionData> GetContainerData(cF4sdApiSearchResultRelation relation)
{
List<cContainerCollectionData> containerData = null;
if (_detailsPageDataCache.TryGetValue(relation, out var detailsData))
containerData = detailsData?.DataContainerCollectionList;
return containerData ?? new List<cContainerCollectionData>();
}
public CockpitValueDisplayData GetCockpitValueDisplayData(cHealthCardStateBase displayValueDefinition, cF4sdApiSearchResultRelation relation, bool getStatic)
{
IList<object> rawValues = GetRawValues(displayValueDefinition, relation, getStatic);
cHealthCardStateBase definitionForLevel = GetDefinitionForLevel(displayValueDefinition);
IList<enumHealthCardStateLevel> predefinedLevel = GetPredefinedLevels(displayValueDefinition, relation, getStatic);
cF4SDHealthCardRawData.cHealthCardDetailsTable valueDetailsTable = null;
if (displayValueDefinition?.Details is cHealthCardDetailsValued detailsValued)
valueDetailsTable = SupportCaseDetailsValueProcessesor.GetDetailsTable(rawValues?.FirstOrDefault(), detailsValued);
return new CockpitValueDisplayData()
{
IsLoading = false,
Title = displayValueDefinition?.Names.GetValue(),
Values = rawValues?.Select(raw => GetDisplayValue(raw, displayValueDefinition, valueDetailsTable)).ToList(),
Levels = predefinedLevel ?? rawValues?.Select(raw => GetLevel(raw, definitionForLevel, 0)).ToList(),
UiActions = GetValueUiActions(displayValueDefinition, valueDetailsTable)
};
}
private IList<object> GetRawValues(cHealthCardStateBase stateDefinition, cF4sdApiSearchResultRelation relation, bool getStatic)
{
const string computationPrefix = "Computation_";
if (stateDefinition?.DatabaseInfo?.ValueTable?.StartsWith(computationPrefix) ?? false)
if (_computationCache.TryGetValue(relation, out var cachedComputationValues))
if (cachedComputationValues.TryGetValue(stateDefinition.DatabaseInfo.ValueTable.Substring(computationPrefix.Length) + (getStatic ? "_latest" : string.Empty), out var computedValues))
return computedValues;
return _supportCase.GetSupportCaseHealthcardData(relation, stateDefinition.DatabaseInfo, getStatic);
}
private IList<cUiActionBase> GetValueUiActions(cHealthCardStateBase stateDefinition, cF4SDHealthCardRawData.cHealthCardDetailsTable detailedValueTable)
{
if (stateDefinition.Details is null)
return null;
List<cUiActionBase> uiActions = new List<cUiActionBase>(cF4SDCockpitXmlConfig.Instance.HealthCardConfig.SearchResultAge);
for (int i = 0; i < cF4SDCockpitXmlConfig.Instance.HealthCardConfig.SearchResultAge; i++)
{
uiActions.Add(new cShowDetailedDataAction(stateDefinition, i, detailedValueTable) { DisplayType = enumActionDisplayType.enabled });
}
return uiActions;
}
private IList<enumHealthCardStateLevel> GetHighestLevels(List<IList<enumHealthCardStateLevel>> allLevels, bool isNotTransparent)
{
var highestLevels = new List<enumHealthCardStateLevel>();
int maxLevelCount = allLevels?.Where(l => l != null).Select(l => l.Count).DefaultIfEmpty(0).Max() ?? 0;
for (int i = 0; i < maxLevelCount; i++)
{
enumHealthCardStateLevel highestLevel = enumHealthCardStateLevel.None;
foreach (var levels in allLevels)
{
if (levels == null || levels.Count <= i)
continue;
highestLevel = (enumHealthCardStateLevel)Math.Max((int)highestLevel, (int)levels[i]);
if (highestLevel == enumHealthCardStateLevel.Error)
break;
}
if (highestLevel <= enumHealthCardStateLevel.Ok)
highestLevel = isNotTransparent ? enumHealthCardStateLevel.Ok : enumHealthCardStateLevel.None;
highestLevels.Add(highestLevel);
}
return highestLevels;
}
private string GetDisplayValue(object rawValue, cHealthCardStateBase displayValueDefinition, cF4SDHealthCardRawData.cHealthCardDetailsTable detailedValueTable)
{
if (displayValueDefinition?.Details != null && detailedValueTable != null)
return SupportCaseDetailsValueProcessesor.GetDetailStringValue(displayValueDefinition.Details, detailedValueTable);
else if (displayValueDefinition is cHealthCardStateTranslation translationDefinition)
return GetTranslationValue(rawValue, translationDefinition);
else if (displayValueDefinition is cHealthCardStateAggregation)
return "Ø";
else
return cUtility.RawValueFormatter.GetDisplayValue(rawValue, displayValueDefinition.DisplayType);
}
private string GetTranslationValue(object rawValue, cHealthCardStateTranslation translationDefinition)
{
ITranslatorObject abstractTranslation = cF4SDHealthCardConfig.GetTranslationsWithName(translationDefinition, translationDefinition.Translation);
if (!(abstractTranslation is cHealthCardTranslator translation))
return null;
if (rawValue is null)
return translation.DefaultTranslation?.Translation?.GetValue();
string defaultValue = translation.DefaultTranslation?.Translation?.GetValue() ?? rawValue.ToString();
foreach (var translationEntry in translation.Translations)
{
if (translationEntry.Values.Any(v => string.Equals(rawValue.ToString(), v, StringComparison.InvariantCultureIgnoreCase)))
return translationEntry.Translation.GetValue();
}
return defaultValue;
}
private static enumHealthCardStateLevel GetLevel(object rawValue, cHealthCardStateBase stateDefinition, int referenceDays)
{
if (stateDefinition is null || rawValue is null)
return enumHealthCardStateLevel.None;
if (stateDefinition is cHealthCardStateAggregation)
throw new NotImplementedException();
try
{
if (stateDefinition is cHealthCardStateLevel levelDefinition)
{
var valueDouble = cF4SDHealthCardRawData.GetDouble(rawValue);
if (valueDouble != null)
{
if (levelDefinition.IsDirectionUp)
return valueDouble >= levelDefinition.Error ? enumHealthCardStateLevel.Error : valueDouble >= levelDefinition.Warning ? enumHealthCardStateLevel.Warning : stateDefinition.IsNotTransparent ? enumHealthCardStateLevel.Ok : enumHealthCardStateLevel.None;
else
return valueDouble <= levelDefinition.Error ? enumHealthCardStateLevel.Error : valueDouble <= levelDefinition.Warning ? enumHealthCardStateLevel.Warning : stateDefinition.IsNotTransparent ? enumHealthCardStateLevel.Ok : enumHealthCardStateLevel.None;
}
}
else if (stateDefinition is cHealthCardStateVersion stateVersion)
{
Version valueVersion = cF4SDHealthCardRawData.GetVersion(rawValue);
if (valueVersion is null)
return enumHealthCardStateLevel.None;
if (stateVersion.IsDirectionUp)
return valueVersion >= stateVersion.Error ? enumHealthCardStateLevel.Error : valueVersion >= stateVersion.Warning ? enumHealthCardStateLevel.Warning : stateDefinition.IsNotTransparent ? enumHealthCardStateLevel.Ok : enumHealthCardStateLevel.None;
else
return valueVersion <= stateVersion.Error ? enumHealthCardStateLevel.Error : valueVersion <= stateVersion.Warning ? enumHealthCardStateLevel.Warning : stateDefinition.IsNotTransparent ? enumHealthCardStateLevel.Ok : enumHealthCardStateLevel.None;
}
else if (stateDefinition is cHealthCardStateDateTime stateDateTime)
{
DateTime? valueDateTime = cF4SDHealthCardRawData.GetDateTime(rawValue);
if (valueDateTime is null)
return enumHealthCardStateLevel.None;
DateTime tempDateTime = valueDateTime.Value;
double differenceHours = Math.Floor((DateTime.UtcNow.AddDays(-referenceDays) - tempDateTime).TotalHours);
if (stateDateTime.IsDirectionUp)
return differenceHours >= stateDateTime.ErrorHours ? enumHealthCardStateLevel.Error : differenceHours >= stateDateTime.WarningHours ? enumHealthCardStateLevel.Warning : stateDefinition.IsNotTransparent ? enumHealthCardStateLevel.Ok : enumHealthCardStateLevel.None;
else
return differenceHours <= stateDateTime.ErrorHours ? enumHealthCardStateLevel.Error : differenceHours <= stateDateTime.WarningHours ? enumHealthCardStateLevel.Warning : stateDefinition.IsNotTransparent ? enumHealthCardStateLevel.Ok : enumHealthCardStateLevel.None;
}
else if (stateDefinition is cHealthCardStateInfo)
{
return stateDefinition.IsNotTransparent ? enumHealthCardStateLevel.Info : enumHealthCardStateLevel.None;
}
else if (stateDefinition is cHealthCardStateTranslation stateTranslation)
{
ITranslatorObject abstractTranslation = cF4SDHealthCardConfig.GetTranslationsWithName(stateTranslation, stateTranslation.Translation);
if (!(abstractTranslation is cHealthCardTranslator translation))
return enumHealthCardStateLevel.None;
enumHealthCardStateLevel translationStateLevel = translation.DefaultTranslation?.StateLevel ?? enumHealthCardStateLevel.Info;
foreach (var translationEntry in translation.Translations)
{
if (translationEntry.Values.Any(v => string.Equals(rawValue.ToString(), v, StringComparison.InvariantCultureIgnoreCase)))
translationStateLevel = translationEntry.StateLevel;
}
if (!stateTranslation.IsNotTransparent && (translationStateLevel == enumHealthCardStateLevel.Ok || translationStateLevel == enumHealthCardStateLevel.Info))
return enumHealthCardStateLevel.None;
else
return translationStateLevel;
}
else if (stateDefinition is cHealthCardStateRefLink)
{
throw new NotImplementedException();
}
return enumHealthCardStateLevel.None;
}
catch (Exception e)
{
LogException(e);
return enumHealthCardStateLevel.None;
}
}
public cUiActionBase GetUiAction(cHealthCardStateBase stateDefinition)
{
if (stateDefinition?.QuickActions is null || stateDefinition.QuickActions.Count == 0)
return null;
return stateDefinition.QuickActions.Count == 1
? GetSingleUiAction(stateDefinition)
: GetMultipleUiActions(stateDefinition);
}
private cHealthCardStateBase GetDefinitionForLevel(cHealthCardStateBase displayValueDefinition)
{
if (displayValueDefinition is cHealthCardStateRefLink stateRefLink)
return cF4SDHealthCardConfig.GetReferencableStateWithName(stateRefLink, stateRefLink.Reference);
return displayValueDefinition;
}
private IList<enumHealthCardStateLevel> GetPredefinedLevels(cHealthCardStateBase displayValueDefinition, cF4sdApiSearchResultRelation relation, bool getStatic)
{
if (displayValueDefinition is cHealthCardStateRefLink stateRefLink)
{
cHealthCardStateBase definition = cF4SDHealthCardConfig.GetReferencableStateWithName(stateRefLink, stateRefLink.Reference);
CockpitValueDisplayData displayData = GetCockpitValueDisplayData(definition, relation, getStatic);
return GetHighestLevels(new List<IList<enumHealthCardStateLevel>> { displayData.Levels }, stateRefLink.IsNotTransparent);
}
if (displayValueDefinition is cHealthCardStateAggregation stateAggregation)
{
List<IList<enumHealthCardStateLevel>> allLevels = stateAggregation.States.Select(s => GetCockpitValueDisplayData(s, relation, getStatic).Levels).ToList();
return GetHighestLevels(allLevels, stateAggregation.IsNotTransparent);
}
return null;
}
private cUiActionBase GetSingleUiAction(cHealthCardStateBase stateDefinition)
{
string quickActionName = stateDefinition.QuickActions?.FirstOrDefault();
if (string.IsNullOrWhiteSpace(quickActionName))
return null;
cUiActionBase uiAction = MenuDataFactory.GetByName(quickActionName, SupportCaseDataProviderArtifact.NamedParameterEntries, SupportCaseDataProviderArtifact.CaseRelations.Select(r => r.Key))?.UiAction;
if (uiAction is cUiQuickAction uiQuickAction)
uiQuickAction.QuickActionRecommendation = new cRecommendationDataModel { Category = quickActionName, Recommendation = stateDefinition.Descriptions.GetValue() };
return uiAction;
}
private cUiActionBase GetMultipleUiActions(cHealthCardStateBase stateDefinition)
{
List<cMenuDataBase> menuDatas = stateDefinition.QuickActions
.Select(quickActionName => MenuDataFactory.GetByName(quickActionName, SupportCaseDataProviderArtifact.NamedParameterEntries, SupportCaseDataProviderArtifact.CaseRelations.Select(r => r.Key)))
.Where(menuData => menuData?.UiAction != null)
.Select(menuData =>
{
if (menuData.UiAction is cUiQuickAction uiQuickAction)
uiQuickAction.QuickActionRecommendation = new cRecommendationDataModel
{
Category = stateDefinition.Names.GetValue(),
Recommendation = stateDefinition.Descriptions.GetValue()
};
return menuData;
})
.ToList();
if (menuDatas == null || menuDatas.Count == 0)
return null;
if (menuDatas.Count == 1)
return menuDatas[0].UiAction;
return new cSubMenuAction(false)
{
SubMenuData = menuDatas,
Name = stateDefinition.Names.GetValue(),
Description = stateDefinition.Descriptions.GetValue(),
DisplayType = enumActionDisplayType.enabled
};
}
public bool TryGetNamedParameterValue<T>(cF4sdApiSearchResultRelation relation, string parameterName, out T value)
{
value = default;
if (!_namedParameterCache.TryGetValue(relation, out var namedParameters))
return false;
if (!namedParameters.TryGetValue(parameterName, out var namedParameter))
return false;
try
{
value = (T)Convert.ChangeType(namedParameter, typeof(T));
}
catch
{
LogEntry($"Found named parameter, but can not be converted to type: {typeof(T)}. Value: {namedParameter} Actual type: {namedParameter.GetType()}", LogLevels.Info);
return false;
}
return true;
}
/// <summary>
/// Raised when newly available relations for a support case were added.
/// </summary>
public event EventHandler<RelationEventArgs> AvailableCaseRelationsAdded;
/// <summary>
/// Raised when the data set of a support case has changed.
/// </summary>
public event EventHandler<SupportCaseDataEventArgs> CaseDataChanged;
}
}

Some files were not shown because too many files have changed in this diff Show More