I am unable to use AsanaNet from C#. When instantiating a new Asana instance, i am unable to pass errorCallback. The error I get is "The errorCallback does not exist in current context". Below is the my code.
class Program
{
private const string _apiKey = "API";
private static Asana _asana;
static void Main(string[] args)
{
Console.WriteLine("Step 1");
_asana = new Asana(_apiKey, AuthenticationType.Basic, errorCallback);
var user = new AsanaUser();
_asana.GetMe(o =>
{
user = o as AsanaUser;
});
Console.WriteLine("Step 2");
_asana.GetWorkspaces(o =>
{
foreach (AsanaWorkspace workspace in o)
{
Console.WriteLine("Workspace Name={0}", workspace.Name);
}
});
Console.WriteLine("Step 3");
_asana.GetWorkspaces(o =>
{
foreach (AsanaWorkspace workspace in o)
{
_asana.GetProjectsInWorkspace(workspace, projects =>
{
foreach (AsanaProject project in projects)
{
Console.WriteLine("Project Name=" + project.Name);
}
}
);
}
});
}
}
According to https://github.com/acron0/AsanaNet/blob/master/AsanaNet/Asana.cs, the constructor has this signature:
public Asana(string apiKeyOrBearerToken, AuthenticationType authType, Action<string, string, string> errorCallback)
So you can declare your error callback method like this:
static void errorCallback(string s1, string s2, string s3)
{
}
Also, if you don't want handling anything, you can just pass an empty lambda into constructor:
_asana = new Asana(_apiKey, AuthenticationType.Basic, (s1, s2, s3) => {});
Related
I inherited a developer's code and I wanted to convert it to c# since the developer made his library in C#. However the only example I have that shows me how to subscribe to this service is in vba which I dont get how its attaching to it.
namespace exampleExcelAddin.Common.Services
{
public class LogEntry
{
public Type Type;
public string Message;
public IDictionary<string, object> Meta = new Dictionary<string, object>();
}
public interface ILogger
{
void LogMessage(string message);
void LogMessage(Exception exception);
}
public static class Logger
{
readonly static Lazy<ISubject<LogEntry>> _outputStream = new Lazy<ISubject<LogEntry>>(() => {
return new ReplaySubject<LogEntry>();
});
public static ILogger Create(Type loggerType) => new TypedLogger(loggerType, _outputStream.Value);
public static IObservable<LogEntry> Output => _outputStream.Value;
class TypedLogger : ILogger
{
readonly ISubject<LogEntry> outputStream;
readonly Type loggerType;
internal TypedLogger(Type loggerType, ISubject<LogEntry> outputStream)
{
this.loggerType = loggerType;
this.outputStream = outputStream;
}
public void LogMessage(string message)
{
outputStream.OnNext(new LogEntry {
Type = loggerType,
Message = message
});
}
public void LogMessage(Exception exception)
{
var logEntry = new LogEntry {
Type = loggerType,
Message = $"Exception: {exception.Message}"
};
logEntry.Meta.Add("StackTrace", exception.StackTrace);
outputStream.OnNext(logEntry);
}
}
}
}
The working example in vb.net is like so...
Private Shared log As ILogger = Logger.Create(GetType(myRibbon))
Logger.Output.Subscribe(
Sub(entry)
If MySettings.Default.EnableLogging Then
Dim logBuilder As New StringBuilder()
logBuilder.
AppendLine("-------------------------------------------------").
AppendLine($"Type: {entry.Type}").
AppendLine($"Message: {entry.Message}")
For Each meta In entry.Meta
logBuilder.
AppendLine($"Meta-Key: {meta.Key}").
AppendLine($"Meta-Value: {meta.Value}")
Next
logBuilder.
AppendLine("-------------------------------------------------" & Environment.NewLine)
IO.File.AppendAllText(logPath, logBuilder.ToString())
End If
End Sub)
Had some help with converting it and keep getting issues with my lambda expression because it is not a delegate type which I understand but keep hitting a wall. Out my element with how to use this services.
Logger.Output.Subscribe(entry => {
if (Settings.Default.EnableLogging) {
var logBuilder = new StringBuilder();
logBuilder.AppendLine("-------------------------------------------------").AppendLine($"Type: {entry.Type}").AppendLine($"Message: {entry.Message}");
foreach (var meta in entry.Meta) { logBuilder.AppendLine($"Meta-Key: {meta.Key}").AppendLine($"Meta-Value: {meta.Value}"); }
_ = logBuilder.AppendLine("-------------------------------------------------" + Environment.NewLine); System.IO.File.AppendAllText(logPath, logBuilder.ToString());
}
});
Figured it out, just needed a package the library uses.
Install-Package System.Reactive -Version 5.0.0
I'm creating a program which will execute a command after user input.
Some commands I want to implement are: creating, reading a file, getting current working directory etc.
I created a dictionary which will store user input and corresponding command:
public static Dictionary<string, Action<string[]>> Commands { get; set; } = new Dictionary<string, Action<string[]>>()
{
{"pwd", PrintWorkingDirectory },
{"create", CreateFile },
{"print", ReadFile },
};
Unfortunately I have issues with triggering the method:
public void Run()
{
Console.WriteLine("Welcome, type in command.");
string input = null;
do
{
Console.Write("> ");
input = Console.ReadLine();
Execute(input);
} while (input != "exit");
}
public int Execute(string input)
{
if(Commands.Keys.Contains(input))
{
var action = Commands.Values.FirstOrDefault(); //doesn't work, gives '{command} not found'
}
Console.WriteLine($"{input} not found");
return 1;
}
Also I noticed that this solution would not work with method which is not void, but returns something, as for example CreateFile.
public static string CreateFile(string path)
{
Console.WriteLine("Create a file");
string userInput = Console.ReadLine();
try
{
string[] file = userInput.Split(new char[] { ' ' }).Skip(1).ToArray();
string newPath = Path.GetFullPath(Path.Combine(file));
using (FileStream stream = new FileStream(newPath, FileMode.Create, FileAccess.ReadWrite))
{
stream.Close();
}
using (StreamWriter sw = new StreamWriter(newPath))
{
Console.WriteLine("Please type the content.Press Enter to save.");
sw.WriteLine(Console.ReadLine());
sw.Close();
Console.WriteLine("File {0} has been created", newPath);
}
}
catch (Exception)
{
throw;
}
return path;
}
public static void ReadFile(string[] args)
{
Console.WriteLine("Reading file");
string userInput = Console.ReadLine();
string[] file = userInput.Split(new char[] { ' ' }).Skip(1).ToArray();
string newPath = Path.GetFullPath(Path.Combine(file));
string[] lines = File.ReadAllLines(newPath);
foreach (string line in lines)
Console.WriteLine(line);
}
public static void PrintWorkingDirectory(string[] args)
{
var currentDirectory = Directory.GetCurrentDirectory();
Console.WriteLine(currentDirectory);
}
Could somebody advise me how to deal with these issues?
Is it that this dictionary I created does not make much sense at all?
First problem: You're always fetching the first element of the dictionary and are not using the index operator to retrieve the correct value. Therefore change:
if(Commands.Keys.Contains(input))
{
var action = Commands.Values.FirstOrDefault(); //doesn't work, gives '{command} not found'
}
to:
public int Execute(string input)
{
if (Commands.Keys.Contains(input))
{
var action = Commands[input]; //doesn't work, gives '{command} not found'
action?.Invoke(new string[] { });
}
else
{
Console.WriteLine($"{input} not found");
}
return 1;
}
Regarding to your second question about dictionary usage. I think it is ok to use a dictionary to map different commands based on a given key. The alternative would be switch or if constructs, which can be prevented in Object Oriented Programming.
Regarding to your question about string CreateFile(string path). Since C# is strongly typed language your dictionary can only contain objects of type Action<string[]>, so you can't use methods with another signature than that. One solution is to add another dictionary in the form of Dictionary<string,Func<string[], string>. As a result you'll get more and more dictionaries depending on your method signatures. From here on you should think to build to encapsulate your commands in an e.g. CommandInterpreter class, that could offer an API like that:
void Request(string cmdName, string[] cmdParameters);
string GetLastResult();
int GetLastCode();
Update:
Below code shows a possible object oriented solution (I've left out interfaces to make the code more compact):
using System;
using System.Collections.Generic;
using System.Linq;
namespace ConsoleApp1
{
public class Command<T>
{
public string Name { get; }
public T TheCommand { get; }
public Command(string name, T theCommand)
{
Name = name;
TheCommand = theCommand;
}
}
public interface ICommandResult
{
void Ok(Action<ICommandResult> yes, Action<ICommandResult> no);
int Code { get; }
string Description { get; }
}
public abstract class CommandResult : ICommandResult
{
public int Code { get; }
public string Description { get; }
protected CommandResult(int code, string description)
{
Code = code;
Description = description;
}
public abstract void Ok(Action<ICommandResult> yes, Action<ICommandResult> no);
}
public class NullCommandResult : CommandResult
{
public NullCommandResult() : base(-1, "null")
{
}
public override void Ok(Action<ICommandResult> yes, Action<ICommandResult> no) => no?.Invoke(this);
}
public class SuccessCommandResult : CommandResult
{
public SuccessCommandResult(string description) : base(0, description)
{
}
public override void Ok(Action<ICommandResult> yes, Action<ICommandResult> no) => yes?.Invoke(this);
}
public class CommandInterpreter
{
private Dictionary<string, Func<IEnumerable<string>, ICommandResult>> Commands = new Dictionary<string, Func<IEnumerable<string>, ICommandResult>>();
public void RegisterCommand(Command<Func<IEnumerable<string>, ICommandResult>> cmd)
=> Commands.Add(cmd.Name, cmd.TheCommand);
public ICommandResult RunCommand(string name, IEnumerable<string> parameters)
=> Commands.Where(kvp => kvp.Key.Equals(name))
.Select(kvp => kvp.Value)
.DefaultIfEmpty(strArr => new NullCommandResult())
.Single()
.Invoke(parameters);
}
class Program
{
private CommandInterpreter _cmdInterpreter;
private Program()
{
_cmdInterpreter = new CommandInterpreter();
_cmdInterpreter.RegisterCommand(new Command<Func<IEnumerable<string>, ICommandResult>>("pwd", PrintWorkingDirectory));
_cmdInterpreter.RegisterCommand(new Command<Func<IEnumerable<string>, ICommandResult>>("create", CreateFile));
_cmdInterpreter.RegisterCommand(new Command<Func<IEnumerable<string>, ICommandResult>>("print", ReadFile));
}
private static CommandResult ReadFile(IEnumerable<string> arg) => new SuccessCommandResult("File read");
private static CommandResult CreateFile(IEnumerable<string> arg) => new SuccessCommandResult("File xyz created");
private static CommandResult PrintWorkingDirectory(IEnumerable<string> arg) => new SuccessCommandResult("Printed something");
static void Main() => new Program().Run();
private void Run()
{
Console.WriteLine("Welcome, type in command.");
string input;
do
{
Console.Write("> ");
input = Console.ReadLine();
var cmdResult = _cmdInterpreter.RunCommand(input, Enumerable.Empty<string>());
cmdResult.Ok(
r => Console.WriteLine($"Success: {cmdResult.Code}, {cmdResult.Description}"),
r => Console.WriteLine($"FAILED: {cmdResult.Code}, {cmdResult.Description}"));
} while (input != "exit");
}
}
}
Output:
Welcome, type in command.
> pwd
Success: 0, Printed something
> create
Success: 0, File xyz created
> abc
FAILED: -1, null
>
You can just copy the code and play around with it.
I write async file reader working with specific state option. I need get notice when all files have been read, but this observable object "reader" never completes. (couldn't get "Done" notification after "reader.Wait()" operator). Could you help me understand why? How I can complete it manually?
class AsyncReader
{
public enum States { Processing, Stopped, Paused};
private Subject<string[]> filesProvider = new Subject<string[]>();
private Subject<States> state = new Subject<States>();
public void Run()
{
state.OnNext(States.Processing);
}
public IObservable<KeyValuePair<string, string>> GetDataSource()
{
return filesProvider.Select(files => ReadFiles(files, state)).Switch();
}
public AsyncReader(string[] args)
{
var reader = GetDataSource();
Observable.Start(() =>
{
reader.Wait();
Console.WriteLine("Done");
});
reader.Subscribe(line =>
{
Console.WriteLine(line);
});
filesProvider.OnNext(args);
}
public static IObservable<KeyValuePair<string, string>> ReadFile(string filePath, IObservable<States> rState) =>
rState.Where(state => state == States.Processing)
.SelectMany(_ =>
Observable
.Using(
() => new StreamReader(filePath),
reader =>
Observable
.Defer(
() =>
Observable
.FromAsync(reader.ReadLineAsync))
.Repeat()
.TakeWhile(line => line != null)
.Select(line => new KeyValuePair<string, string>(filePath, line))));
public static IObservable<KeyValuePair<string, string>> ReadFiles(string[] files, IObservable<States> readState)
{
IObservable<KeyValuePair<string, string>> dataSource = Observable.Empty<KeyValuePair<string, string>>();
foreach (var file in files)
{
dataSource = dataSource.Concat(ReadFile(file, readState));
}
return dataSource;
}
}
Short example for use:
class Program
{
static void Main(string[] args)
{
AsyncReader reader = new AsyncReader(args);
reader.Run();
Console.ReadKey();
}
}
I am using Xamarin to develop an Apple Watch app. I am trying to send a message from my watch to the iPhone with my SendMessage function. When I do this, I get in the out error the message payload could not be delivered. I can only read part of the message ("payload could n...") because I am writing it on a label (since my debugger doesn't work in Xamarin I can't take a look at the message), but after doing some googling I'm assuming that's what it is written. Any idea why? Here is my code:
public sealed class WCSessionManager : NSObject, IWCSessionDelegate
{
// Setup is converted from https://www.natashatherobot.com/watchconnectivity-say-hello-to-wcsession/
// with some extra bits
private static readonly WCSessionManager sharedManager = new WCSessionManager();
private static WCSession session = WCSession.IsSupported ? WCSession.DefaultSession : null;
#if __IOS__
public static string Device = "Phone";
#else
public static string Device = "Watch";
#endif
public event ApplicationContextUpdatedHandler ApplicationContextUpdated;
public delegate void ApplicationContextUpdatedHandler(WCSession session, Dictionary<string, object> applicationContext);
public event MessageReceivedHandler MessageReceived;
public delegate void MessageReceivedHandler(Dictionary<string, object> message, Action<Dictionary<string, object>> replyHandler);
private WCSession validSession
{
get
{
#if __IOS__
// Even though session.Paired and session.WatchAppInstalled are underlined, it will still build as they are available on the iOS version of WatchConnectivity.WCSession
Console.WriteLine($"Paired status:{(session.Paired ? '✓' : '✗')}\n");
Console.WriteLine($"Watch App Installed status:{(session.WatchAppInstalled ? '✓' : '✗')}\n");
return (session.Paired && session.WatchAppInstalled) ? session : null;
//return session;
#else
return session;
#endif
}
}
private WCSession validReachableSession
{
get
{
return session.Reachable ? validSession : null;
}
}
private WCSessionManager() : base() { }
public static WCSessionManager SharedManager
{
get
{
return sharedManager;
}
}
public void StartSession()
{
if (session != null)
{
session.Delegate = this;
session.ActivateSession();
Console.WriteLine($"Started Watch Connectivity Session on {Device}");
}
}
[Export("sessionReachabilityDidChange:")]
public void SessionReachabilityDidChange(WCSession session)
{
Console.WriteLine($"Watch connectivity Reachable:{(session.Reachable ? '✓' : '✗')} from {Device}");
// handle session reachability change
if (session.Reachable)
{
// great! continue on with Interactive Messaging
}
else {
// 😥 prompt the user to unlock their iOS device
}
}
#region Application Context Methods
public void UpdateApplicationContext(Dictionary<string, object> applicationContext)
{
// Application context doesnt need the watch to be reachable, it will be received when opened
if (validSession != null)
{
try
{
var NSValues = applicationContext.Values.Select(x => new NSString(JsonConvert.SerializeObject(x))).ToArray();
var NSKeys = applicationContext.Keys.Select(x => new NSString(x)).ToArray();
var NSApplicationContext = NSDictionary<NSString, NSObject>.FromObjectsAndKeys(NSValues, NSKeys);
UpdateApplicationContextOnSession(NSApplicationContext);
}
catch (Exception ex)
{
Console.WriteLine($"Exception Updating Application Context: {ex.Message}");
}
}
}
public void GetApplicationContext()
{
UpdateApplicationContext(new Dictionary<string, object>() { { "GET", null } });
}
[Export("session:didReceiveApplicationContext:")]
public void DidReceiveApplicationContext(WCSession session, NSDictionary<NSString, NSObject> applicationContext)
{
Console.WriteLine($"Recieving Message on {Device}");
if (ApplicationContextUpdated != null)
{
var keys = applicationContext.Keys.Select(k => k.ToString()).ToArray();
IEnumerable<object> values;
try
{
values = applicationContext.Values.Select(v => JsonConvert.DeserializeObject(v.ToString(), typeof(DoorWatchDTO)));
}
catch (Exception)
{
values = applicationContext.Values.Select(v => JsonConvert.DeserializeObject(v.ToString()));
}
var dictionary = keys.Zip(values, (k, v) => new { Key = k, Value = v })
.ToDictionary(x => x.Key, x => x.Value);
ApplicationContextUpdated(session, dictionary);
}
}
[Export("session:didReceiveMessage::")]
public void DidReceiveMessage(WCSession session, NSDictionary<NSString, NSObject> message, WCSessionReplyHandler replyHandler)
{
if (MessageReceived != null)
{
var keys = message.Keys.Select(k => k.ToString()).ToArray();
IEnumerable<object> values;
values = message.Values.Select(v => JsonConvert.DeserializeObject(v.ToString()));
var dictionary = keys.Zip(values, (k, v) => new { Key = k, Value = v })
.ToDictionary(x => x.Key, x => x.Value);
MessageReceived(dictionary, (dict) =>
{
var NSValues = dict.Values.Select(x => new NSString(JsonConvert.SerializeObject(x))).ToArray();
var NSKeys = dict.Keys.Select(x => new NSString(x)).ToArray();
var NSDict = NSDictionary<NSString, NSObject>.FromObjectsAndKeys(NSValues, NSKeys);
replyHandler.Invoke(NSDict);
});
}
}
public void SendMessage(Dictionary<string, object> message, Action<Dictionary<string, object>> replyHandler, WKInterfaceLabel label)
{
if (validSession != null)
{
try
{
var NSValues = message.Values.Select(x => new NSString(JsonConvert.SerializeObject(x))).ToArray();
var NSKeys = message.Keys.Select(x => new NSString(x)).ToArray();
var NSMessage = NSDictionary<NSString, NSObject>.FromObjectsAndKeys(NSValues, NSKeys);
var reply = new WCSessionReplyHandler((replyMessage) =>
{
var values = replyMessage.Values.Select(x => JsonConvert.SerializeObject(x)).ToArray();
var keys = replyMessage.Keys.Select(x => x.ToString()).ToArray();
var dict = keys.Zip(values, (k, v) => new { Key = k, Value = v })
.ToDictionary(x => x.Key, x => (object)x.Value);
replyHandler.Invoke(dict);
});
validSession.SendMessage(NSMessage, reply, (error) =>
{
label.SetText(error.ToString()); // I can see the error in here: "payload could n..."
});
}
catch (Exception ex)
{
Console.WriteLine($"Exception sending message: {ex.Message}");
}
}
}
private void UpdateApplicationContextOnSession(NSDictionary<NSString, NSObject> NSApplicationContext)
{
NSError error;
var sendSuccessfully = validSession.UpdateApplicationContext(NSApplicationContext, out error);
if (sendSuccessfully)
{
Console.WriteLine($"Sent App Context from {Device} \nPayLoad: {NSApplicationContext.ToString()} \n");
#if __IOS__
Logging.Log("Success, payload: " + NSApplicationContext.ToString());
#endif
}
else
{
Console.WriteLine($"Error Updating Application Context: {error.LocalizedDescription}");
#if __IOS__
Logging.Log("error: " + error.LocalizedDescription);
#endif
}
}
#endregion
I solved it. Instead of implementing IWCSessionDelegate, I simply implement WCSessionDelegate instead and override the functions as needed.
We have today an event based application centered completely on desktop. We want to move to web and drive events using Web Sockets using Fleck. I was unable to figure out how to send message asynchronously from server to client in fleck. Below is the sample for what I am trying to achieve
public class Program
{
static readonly WebSocketServer _webSocketServer = new WebSocketServer("ws://127.0.0.1:8181");
static readonly List<IWebSocketConnection> SocketClients = new List<IWebSocketConnection>();
static void Main(string[] args)
{
_webSocketServer.Start(socket =>
{
socket.OnOpen = () =>
{
SocketClients.Add(socket);
OnOpening(socket.ConnectionInfo.Id);
};
socket.OnClose = () =>
{
OnClosing(socket.ConnectionInfo.Id);
SocketClients.Remove(socket);
};
socket.OnMessage = message => OnMessage(message, socket.ConnectionInfo.Id);
});
Console.ReadLine();
}
private static void OnMessage(string message, Guid id)
{
Console.WriteLine("<i> Recived message " + message + " from Client Id : " + id + " </i>");
}
private static void OnClosing(Guid id)
{
Console.WriteLine("<i> Client Left With Id : " + id + "</i>");
}
private static void OnOpening(Guid id)
{
//Do Something
}
// CALL THIS METHOD FROM MY APPLICATION AND SEND THE MESSAGE TO CLIENT BASED ON THE ID
private static void SendMessage(string message, Guid id)
{
// HOW TO DO SOMETHING LIKE BELOW SEND MESSAGE ASYNC
//_webSocketServer.Start(socket =>
//{
// var clientToSend = SocketClients.Find(client => client.ConnectionInfo.Id == id);
// socket.Send(message);
//});
}
}
You have a similar example in their GitHub: https://github.com/statianzo/Fleck/blob/master/src/Samples/ConsoleApp/Server.cs
I just added the SendToSocketById method>
class Server
{
static void Main()
{
FleckLog.Level = LogLevel.Debug;
var allSockets = new List<IWebSocketConnection>();
var server = new WebSocketServer("ws://0.0.0.0:8181");
server.Start(socket =>
{
socket.OnOpen = () =>
{
Console.WriteLine("Open!");
allSockets.Add(socket);
};
socket.OnClose = () =>
{
Console.WriteLine("Close!");
allSockets.Remove(socket);
};
socket.OnMessage = message =>
{
Console.WriteLine(message);
allSockets.ToList().ForEach(s => s.Send("Echo: " + message));
};
});
var input = Console.ReadLine();
while (input != "exit")
{
foreach (var socket in allSockets.ToList())
{
socket.Send(input);
}
input = Console.ReadLine();
}
}
static void SendToSocketById(String input, Guid id)
{
var socket = allSockets.Find(client => client.ConnectionInfo.Id == id);
socket.Send(input);
}
}