Sugar CRM 6.5.14 - Calling REST API with C# - c#

Amazingly low on doco when it comes to rest api. Can anyone give me a working example of Sugar CRM REST calls using C#?
I was trying out SugarSharp but it's just listing the services and not coming up with Data(null)

Appreciate this is an old question but for anyone else that comes across it, took me a while to get it all working with creating and updating relationships being the toughest to fathom.
Here is part of a wrapper class I wrote around the v4_1 rest api, hope it helps:-
public void Login()
{
object loginData = new
{
user_auth = new
{
user_name = Username,
password = CalculateMD5Hash(Password)
}
};
string jsonData = CreateFormattedPostRequest("login", loginData);
var request = GetRestRequest(jsonData, "POST");
var loginResponse = GetRestResponseByType<LoginResponse>(request);
if (string.IsNullOrEmpty(loginResponse.id))
{
throw new SugarException(string.Concat("Authorisation Failed for user: {0}, did not retrieve access token", Username));
}
SessionID = loginResponse.id;
}
Format the request:-
private string CreateFormattedPostRequest(string method, object data)
{
StringBuilder buffer = new StringBuilder();
using (StringWriter writer = new StringWriter(buffer))
{
serializer.Serialize(data, buffer);
}
string result = "method=" + method;
result += "&input_type=JSON&response_type=JSON&rest_data=" + buffer.ToString();
return result;
}
And finally get the response:-
private object GetRestResponseAsObject(HttpWebRequest request)
{
using (var response = (HttpWebResponse)request.GetResponse())
{
using (Stream input = response.GetResponseStream())
{
StreamReader reader = new StreamReader(input);
string buffer = reader.ReadToEnd();
var responseObj = serializer.DeserializeObject(buffer);
return responseObj;
}
}
}
And here is an example call to the set_entry method:-
/// <summary>
/// Creates or Updates a single bean, for update ensure that the name_value_list contains the ID of the record
/// name_value_lists - Dictionary where the keys of the are the SugarBean attributes (columns), the values of the array are the values the attributes should have.
/// </summary>
/// <param name="module">Module to update i.e Account</param>
/// <param name="record">key value pair object of record, include ID for update</param>
/// <returns>Returns the updated or created Bean ID</returns>
public string CreateUpdateBean(string module, object record)
{
var parameters = new Dictionary<string, object>();
parameters.Add("session", SessionID);
parameters.Add("module_name", module);
parameters.Add("name_value_list", record);
parameters.Add("track_view", false);
string jsonData = CreateFormattedPostRequest("set_entry", parameters);
var request = GetRestRequest(jsonData, "POST");
var result = GetRestResponseByType<object>(request);
return result.ToString();
}

For calling rest api SugarCRM/SuiteCRM with c#, you can use SugarRestSharp
Example for create Account:
var client = new SugarRestClient(TestAccount.Url, TestAccount.Username, TestAccount.Password);
Account insertAccount = AccountsModule.GetTestAccount();
// -------------------Create Account-------------------
SugarRestResponse response = AccountsModule.CreateAccount(client, insertAccount);
Assert.NotNull(response);
Assert.Equal(response.StatusCode, HttpStatusCode.OK);
string insertId = (response.Data == null) ? string.Empty : response.Data.ToString();
Assert.NotNull(insertId);
Assert.NotEmpty(insertId);
// -------------------End Create Account-------------------

Related

Read All Value From Cache

i am using .netcore with Microsoft.Extensions.Caching.Distributed , i have a scenario to get all the keys and also i need to flush all the values.
I have searched many articles no one gives the exact idea to get all values or Flush values. IDistributedCache don't have flush the redis cache.
Can anyone help on this.
For my project ,the follow function return all matched keys:
//using StackExchange.Redis;
/// <summary>
/// 搜索所有与<see cref="keyStr"/>相匹配的缓存的key
/// </summary>
/// <param name="keyStr">搜索词,*表示任意字符</param>
/// <param name="dbIndex">redis中查找db</param>
/// <param name="trimRedisInstanceName">是否在查询前及返回前去除<see cref="Extension.RedisInstanceName"/>前缀</param>
/// <returns>redis中的key列表,<see cref="trimRedisInstanceName"/>参数设置是否包括<see cref="Extension.RedisInstanceName"/></returns>
protected async Task<List<string>> FilterByKey(string keyStr, int dbIndex = 0, bool trimRedisInstanceName = true)
{
//创建连接
var conn = await ConnectionMultiplexer.ConnectAsync(_configuration.GetSection("Cache")["Redis"]);
//获取db
var db = conn.GetDatabase(dbIndex);
var listResult = new List<string>();
//遍历集群内服务器
foreach (var endPoint in conn.GetEndPoints())
{
//获取指定服务器
var server = conn.GetServer(endPoint);
//在指定服务器上使用 keys 或者 scan 命令来遍历key
foreach (var key in server.Keys(dbIndex, Extension.RedisInstanceName + keyStr))
{
if (trimRedisInstanceName)
{
listResult.Add(key.ToString().Replace(Extension.RedisInstanceName, ""));
}
else
{
listResult.Add(key);
}
//获取key对于的值
//var val = db.StringGet(key);
Console.WriteLine($"key: {key}, value:");
}
}
return listResult;
}
In the method,Extension.RedisInstanceName used in
services.AddStackExchangeRedisCache(options =>
{
options.Configuration = config.GetSection("Cache")["Redis"];
options.InstanceName = RedisInstanceName;
});
it used by this:
//刷新缓存
var allCacheCompany = await FilterByKey("xxxx.*");
foreach (var companyKey in allCacheCompany)
{
await _cache.RemoveAsync(companyKey);
}

.NET Core 2.0 Is the below code thread safe

Here's my code and I have doubt on thread safe implementation. My questions are below
The return value from GetHtmlPageAsync is object. Is it thread safe? I will use this object and add into the collection and finally upload into database.
The main method logic is below (implementation in-progress). I have set of domains, I have list of 10000 domains in the collection, the idea is, I will put it in the queue and call the GetHtmlPageAsync to get the HTML of the page. Based on the HTML, I will get the necessary hyperlinks. Once I get the hyper links, I will check certain word is available in the link. If the word is available in the link, I will call the same method GetHTMLPageAsync to get the HTML of that page. So the same thread may call the GetHtmlPageAsync to process another link. I am trying to reuse the same method for multiple calls in thread safe way. Please help.
#edit1 . I have added the main method. Instead of Queue. I have used ForEach
public static async Task<int> ProcessDomainAsync(List<string> domains)
{
Parallel.ForEach(domains, async (currentDomain) =>
{
var domainBody = await GetHtmlPageAsync(currentDomain);
var language = string.Empty;
var country = string.Empty;
var createdOn = DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Local);
var updatedOn = DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Local);
var machine = Environment.MachineName;
var message = "[" + domainBody.ErrorCode + "] - " + domainBody.ErrorMessage;
var active = false;
var stage = "End";
var url = currentDomain;
if (domainBody.ErrorCode == 0)
{
var html = domainBody.Body;
language = Common.GetLanguageIdentification(html);
country = Common.GetCountryIdentification(currentDomain);
message = string.Empty;
active = true;
stage = "Stage1";
var hyperLinks = Common.GetAllAHrefTags(html);
//Process Hyper Links
}
_domainList.Add(new Domain
{
Url = url,
Language = language,
Country = country,
MachineName = machine,
Message = message,
Active = active,
Stage = stage,
CreatedOn = createdOn,
UpdatedOn = updatedOn
});
domainCount++;
});
return domainCount;
}
public class DomainBody
{
public string Body;
public string ErrorMessage;
public int ErrorCode;
}
public static class DomainProcessing {
static async Task<DomainBody> GetHtmlPageAsync(string url)
{
#region Initialize Proxy
var sessionId = new Random().Next().ToString();
var proxy = new WebProxy(Constant.ProxyUrl, Constant.ProxyPort);
var login = Constant.ProxyUserName + "-session-" + sessionId;
proxy.Credentials = new NetworkCredential(login,Constant.ProxyPassword);
#endregion
#region Initialize Variables
var user_agent = Common.GenerateRandomUserAgent();
var body = string.Empty;
var errorCode = 0;
var errorMessage = string.Empty;
#endregion
try
{
#region Format URL with Http Protocol
var domainSB = new StringBuilder();
domainSB.Append("http://");
domainSB.Append(url);
#endregion
#region Process Domain
var request = (HttpWebRequest) WebRequest.Create(new Uri(url));
request.Proxy = proxy;
request.UserAgent = user_agent;
request.Timeout = Constant.TimeOut;
using (var response = await request.GetResponseAsync().ConfigureAwait(true))
using (var content = new MemoryStream())
using (var responseStream = response.GetResponseStream())
{
await responseStream.CopyToAsync(content);
var bodyArray = content.ToArray();
body = Encoding.UTF8.GetString(bodyArray, 0, bodyArray.Length);
}
errorCode = 0;
errorMessage = string.Empty;
#endregion
}
catch (HttpRequestException ex)
{
body = string.Empty;
errorCode = ex.InnerException.HResult;
errorMessage = ex.InnerException.Message;
}
catch (Exception ex)
{
body = string.Empty;
errorCode = ex.HResult;
errorMessage = ex.Message;
}
var domainBody = new DomainBody
{
Body = body,
ErrorCode = errorCode,
ErrorMessage = errorMessage
};
return domainBody;
}
}enter code here
Generally speaking, local variables should be thread safe (simply because they have no idea there even is another thread and other threads have no way to access them).
Anything that can be accessed by multiple threads should be looked at. _domainList for example. Make sure the Add method is thread-safe because you are calling it potentially in parallel.

Authenticate with OneDrive SDK in a c# console project

I'm trying to create a program that will download all my OneNote files from OneDrive. But when I try to authenticate using msaAuthenticationProvider a white window appears and then nothing happens. I think the window is supposed to be the Microsoft login, but nothing appears in it.
Here's my code:
string[] scopes = new string[] {
"onedrive.readonly",
"wl.signin"
};
var msaAuthenticationProvider = new MsaAuthenticationProvider(
clientId,
returnURL,
scopes);
await msaAuthenticationProvider.AuthenticateUserAsync();
var client = new OneDriveClient(URL, msaAuthenticationProvider);
It gets to the AuthenticateUserAsync method, then the window apperas, and after that nothing happens.
I'm also not sure what the returnURL is supposed to be because all examples where either for an app version or just said return URL without giving any examples.
sorry for the delay. Have you tried this method:
msaAuthenticationProvider.RestoreMostRecentFromCacheOrAuthenticateUserAsync();
Edit : If the last known connection token is usable, this method can be used to authenticate the user without prompt it. So, this restore the last authentication cache if it can or prompt the user to give his login and password. This can replace the already used AuthenticateUserAsync method. I had the same issue and this method solved it.
Edit 2 : The OneDrive SDK documentation is very poor, I found this myself fiercely as I found that you can get the connection token (to save it for example) and inject it when you need like that in an async task :
if (_OneDriveCacheBlob == null)
{
bool needtosaveblob = true;
_OneDriveCacheBlob = null;
CredentialCache cc = new CredentialCache();
_OneDriveCacheBlob = GetUser(CurrentUserName).OneDriveAuthProviderBlob;
if (_OneDriveCacheBlob != null)
{
cc.InitializeCacheFromBlob(_OneDriveCacheBlob);
needtosaveblob = false;
}
MsaAuthenticationProvider msaAuthProvider = new MsaAuthenticationProvider(OneDriveClass.clientId, OneDriveClass.returnUrl, scopes, cc);
int timeout = 15;
_ = Task.Run(() => WaitForODConnection(msaAuthProvider));
while (!WaitForODConnectionExecuted)
{
if (timeout <= 0)
break;
await Task.Delay(TimeSpan.FromSeconds(1));
timeout -= 1;
}
WaitForODConnectionExecuted = false;
if (timeout <= 0)
{
// Request for reconnection to OneDrive because of invalid Blob
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.High, () =>
{
//This method requests a new login by a simple msaAuthProvider.AuthenticateUserAsync() call from a new instance of MsaAuthenticationProvider and a new instance of CredentialCache.
//ChangeOneDriveAccount();
});
}
else
{
_OneDriveClient = new OneDriveClient(OneDriveClass.basUrl, msaAuthProvider);
}
string accessToken = msaAuthProvider.CurrentAccountSession.AccessToken;
JObject json = await GetUserInfos(msaAuthProvider.CurrentAccountSession.AccessToken);
if (json != null)
{
// If you need
oneDriveUserName = json["name"].ToString();
oneDriveEmail = json["emails"]["account"].ToString();
}
else
{
//Unable to get OneDrive user informations;
}
if (needtosaveblob)
{
_OneDriveCacheBlob = cc.GetCacheBlob();
//You can save _OneDriveCacheBlob to reuse it later;
}
}
To get the user infos :
/// <summary>
/// Return User informations as a JObject. To get username and email, if return isn't null :
/// username = json["name"].ToString();
/// email = json["emails"]["account"].ToString();
/// </summary>
/// <param name="accessToken">accesstoken of Onedrive account</param>
/// <returns>JObject value</returns>
public static async Task<JObject> GetUserInfos(string accessToken)
{
JObject json = null;
Uri uri = new Uri($"https://apis.live.net/v5.0/me?access_token={accessToken}");
System.Net.Http.HttpClient httpClient = new System.Net.Http.HttpClient();
System.Net.Http.HttpResponseMessage result = await httpClient.GetAsync(uri);
//user info returnd as JSON
string jsonUserInfo = await result.Content.ReadAsStringAsync();
if (jsonUserInfo != null)
{
json = JObject.Parse(jsonUserInfo);
//username = json["name"].ToString();
//email = json["emails"]["account"].ToString();
}
return json;
}
And because the OneDrive method never expires if the token is no longer usable :
bool WaitForODConnectionExecuted = false;
private async Task WaitForODConnection(MsaAuthenticationProvider msaAuthProvider)
{
await msaAuthProvider.RestoreMostRecentFromCacheOrAuthenticateUserAsync();
WaitForODConnectionExecuted = true;
}
It was not funny and I think my code is not clean so do not use it as it is without working a little on it.

How to return JSON to browser from model class using SignalR and URL call to Web API?

Here's what's going on. I have an ASP.NET MVC 4 Web API web application. I can call API resources via URL. One of these functions get performance monitoring data for a specified amount of time and returns it in JSON once it has completed. However, what I want to do is return
It is IMPORTANT to note that I am working with a the browser and API resources in the model, not with a View. Please don't casually tell me to use Javascript in a View, because there is no view, or tell me to look at the SignalR wiki because the information for ".NET" sections is meant for desktop applications, not web apps. For example, you can't "Console.WriteLine()" to a browser.
To reiterate, I am using ASP.NET MVC 4 Web API to develop an API, and am calling the API via URL in the browser and it is returning JSON. I am attempting to use SignalR to have the app send JSON to the browser, but it is not doing anything at all. Rather, the application simply returns the completed JSON from the controller action with all of the performance data values once the process has completed. In other words, SignalR is not working.
So what I'm trying to do is while the API resource is gathering all the information, SignalR sends JSON to the browser every second so that the client can see what's going on in real time.
What I need to find out is why SignalR isn't sending it, and how I can send information to be displayed in the browser without Javascript, since I'm working from a model class, not from a view.
As you can see, I subscribe to the event using On, and then use Invoke to call the server-side hub method SendToClient.
Please let me know if I'm trying to do is impossible. I have never heard of a "real-time", dynamic API call via URL.
Here is my hub class. It is located in ~/signalr/hubs and is in a file called LiveHub.cs. The method Send is what I am trying to invoke in the method seen in the next code block.
namespace PerfMon2.signalr.hubs
{
public class LiveHub : Hub
{
public void SendToClient(List<DataValueInfo> json)
{
Clients.showValue(json);
}
}
}
Here is the method from LogDBRepository.cs that includes the SignalR calls.
public List<LogInfo> LogTimedPerfData(string macName, string categoryName, string counterName,
string instanceName, string logName, string live, long? seconds)
{
iModsDBRepository modsDB = new iModsDBRepository();
List<MachineInfo> theMac = modsDB.GetMachineByName(macName);
if (theMac.Count == 0)
return new List<LogInfo>();
else if (instanceName == null)
{
if (!PerformanceCounterCategory.Exists(categoryName, macName) ||
!PerformanceCounterCategory.CounterExists(counterName, categoryName, macName) )
{
return new List<LogInfo>();
}
}
else if (instanceName != null)
{
if (!PerformanceCounterCategory.Exists(categoryName, macName) ||
!PerformanceCounterCategory.CounterExists(counterName, categoryName, macName) ||
!PerformanceCounterCategory.InstanceExists(instanceName, categoryName, macName))
{
return new List<LogInfo>();
}
}
else if (logName == null)
{
return new List<LogInfo>();
}
// Check if entered log name is a duplicate for the authenticated user
List<LogInfo> checkDuplicateLog = this.GetSingleLog(logName);
if (checkDuplicateLog.Count > 0)
{
return new List<LogInfo>();
}
PerformanceCounterCategory category = new PerformanceCounterCategory(categoryName, theMac[0].MachineName);
if (category.CategoryName == null || category.MachineName == null)
{
return new List<LogInfo>();
}
List<LogInfo> logIt = new List<LogInfo>();
if (category.CategoryType != PerformanceCounterCategoryType.SingleInstance)
{
List<InstanceInfo> instances = modsDB.GetInstancesFromCatMacName(theMac[0].MachineName, category.CategoryName);
foreach (InstanceInfo inst in instances)
{
if (!category.InstanceExists(inst.InstanceName))
{
continue;
}
else if (inst.InstanceName.Equals(instanceName, StringComparison.OrdinalIgnoreCase))
{
PerformanceCounter perfCounter = new PerformanceCounter(categoryName, counterName,
inst.InstanceName, theMac[0].MachineName);
//CounterSample data = perfCounter.NextSample();
//double value = CounterSample.Calculate(data, perfCounter.NextSample());
string data = "";
List<UserInfo> currUser = this.GetUserByName(WindowsIdentity.GetCurrent().Name);
string timeStarted = DateTime.Now.ToString("MM/dd/yyyy - h:mm:ss tt");
//string[] dataValues = new string[(int)seconds];
List<string> dataValues = new List<string>();
var hubConnection = new HubConnection("http://localhost/PerfMon2/");
hubConnection.Credentials = CredentialCache.DefaultNetworkCredentials;
var perfMon = hubConnection.CreateProxy("LiveHub");
// perfMon.On("sendValue", message => Console.WriteLine(message));
perfMon.On("showValue", json => Console.WriteLine(json));
hubConnection.Start().Wait();
List<DataValueInfo> lol = new List<DataValueInfo>();
for (int i = 0; i < seconds; i++)
{
data = "Value " + i + ": " + perfCounter.NextValue().ToString();
//dataValues[i] = data;
dataValues.Add(data);
lol.Add(new DataValueInfo
{
Value = perfCounter.NextValue().ToString()
});
// perfMon.Invoke<List<DataValueInfo>>("Send", lol);
perfMon.Invoke("SendToClient", lol);
Thread.Sleep(1000);
}
string timeFinished = DateTime.Now.ToString("MM/dd/yyyy - h:mm:ss tt");
Log log = new Log
{
LogName = logName,
CounterName = perfCounter.CounterName,
InstanceName = perfCounter.InstanceName,
CategoryName = perfCounter.CategoryName,
MachineName = perfCounter.MachineName,
TimeStarted = timeStarted,
TimeFinished = timeFinished,
PerformanceData = string.Join(",", dataValues),
UserID = currUser[0].UserID
};
this.CreateLog(log);
logIt.Add(new LogInfo
{
LogName = logName,
CounterName = perfCounter.CounterName,
InstanceName = perfCounter.InstanceName,
CategoryName = perfCounter.CategoryName,
MachineName = perfCounter.MachineName,
TimeStarted = timeStarted,
TimeFinished = timeFinished,
PerformanceData = dataValues.ToList<string>()
});
break;
}
}
}
else
{
PerformanceCounter perfCounter = new PerformanceCounter(categoryName, counterName,
"", theMac[0].MachineName);
string data = "";
List<UserInfo> currUser = this.GetUserByName(WindowsIdentity.GetCurrent().Name);
string timeStarted = DateTime.Now.ToString("MM/dd/yyyy - h:mm:ss tt");
//string[] dataValues = new string[(int)seconds];
List<string> dataValues = new List<string>();
var hubConnection = new HubConnection("http://localhost/PerfMon2/");
hubConnection.Credentials = CredentialCache.DefaultNetworkCredentials;
var perfMon = hubConnection.CreateProxy("LiveHub");
// perfMon.On("sendValue", message => Console.WriteLine(message));
perfMon.On("showValue", json => Console.WriteLine(json));
hubConnection.Start().Wait();
List<DataValueInfo> lol = new List<DataValueInfo>();
for (int i = 0; i < seconds; i++)
{
data = "Value " + i + ": " + perfCounter.NextValue().ToString();
//dataValues[i] = data;
dataValues.Add(data);
lol.Add(new DataValueInfo
{
Value = perfCounter.NextValue().ToString()
});
// perfMon.Invoke<List<DataValueInfo>>("Send", lol);
perfMon.Invoke("SendToClient", lol);
Thread.Sleep(1000);
}
string timeFinished = DateTime.Now.ToString("MM/dd/yyyy - h:mm:ss tt");
Log log = new Log
{
LogName = logName,
CounterName = perfCounter.CounterName,
InstanceName = perfCounter.InstanceName,
CategoryName = perfCounter.CategoryName,
MachineName = perfCounter.MachineName,
TimeStarted = timeStarted,
TimeFinished = timeFinished,
PerformanceData = string.Join(",", dataValues),
UserID = currUser[0].UserID
};
this.CreateLog(log);
logIt.Add(new LogInfo
{
LogName = logName,
CounterName = perfCounter.CounterName,
InstanceName = perfCounter.InstanceName,
CategoryName = perfCounter.CategoryName,
MachineName = perfCounter.MachineName,
TimeStarted = timeStarted,
TimeFinished = timeFinished,
PerformanceData = dataValues.ToList<string>()
});
}
return logIt;
}
Here is the controller for the method in LogController.cs :
[AcceptVerbs("GET", "POST")]
public List<LogInfo> Log_Perf_Data(string machine_name, string category_name, string counter_name, string instance_name,
string log_name, long? seconds, string live, string enforceQuery)
{
LogController.CheckUser();
// POST api/log/post_data?machine_name=&category_name=&counter_name=&instance_name=&log_name=&seconds=
if (machine_name != null && category_name != null && counter_name != null && log_name != null && seconds.HasValue && enforceQuery == null)
{
List<LogInfo> dataVal = logDB.LogTimedPerfData(machine_name, category_name, counter_name, instance_name,
log_name, live, seconds);
logDB.SaveChanges();
return dataVal;
}
return new List<LogInfo>();
}
Maybe you can implement it in push technique. Here is how I do it:
Class with message
public class Message
{
/// <summary>
/// The name who will receive this message.
/// </summary>
public string RecipientName { get; set; }
/// <summary>
/// The message content.
/// </summary>
public string MessageContent { get; set; }
}
Class that will represent client:
public class Client
{
private ManualResetEvent messageEvent = new ManualResetEvent(false);
private Queue<Message> messageQueue = new Queue<Message>();
/// <summary>
/// This method is called by a sender to send a message to this client.
/// </summary>
/// <param name="message">the new message</param>
public void EnqueueMessage(Message message)
{
lock (messageQueue)
{
messageQueue.Enqueue(message);
// Set a new message event.
messageEvent.Set();
}
}
/// <summary>
/// This method is called by the client to receive messages from the message queue.
/// If no message, it will wait until a new message is inserted.
/// </summary>
/// <returns>the unread message</returns>
public Message DequeueMessage()
{
// Wait until a new message.
messageEvent.WaitOne();
lock (messageQueue)
{
if (messageQueue.Count == 1)
{
messageEvent.Reset();
}
return messageQueue.Dequeue();
}
}
}
Class to send messages to clients:
public class ClientAdapter
{
/// <summary>
/// The recipient list.
/// </summary>
private Dictionary<string, Client> recipients = new Dictionary<string,Client>();
/// <summary>
/// Send a message to a particular recipient.
/// </summary>
public void SendMessage(Message message)
{
if (recipients.ContainsKey(message.RecipientName))
{
Client client = recipients[message.RecipientName];
client.EnqueueMessage(message);
}
}
/// <summary>
/// Called by a individual recipient to wait and receive a message.
/// </summary>
/// <returns>The message content</returns>
public string GetMessage(string userName)
{
string messageContent = string.Empty;
if (recipients.ContainsKey(userName))
{
Client client = recipients[userName];
messageContent = client.DequeueMessage().MessageContent;
}
return messageContent;
}
/// <summary>
/// Join a user to the recipient list.
/// </summary>
public void Join(string userName)
{
recipients[userName] = new Client();
}
/// <summary>
/// Singleton pattern.
/// This pattern will ensure there is only one instance of this class in the system.
/// </summary>
public static ClientAdapter Instance = new ClientAdapter();
private ClientAdapter() { }
}
Sending messages:
Message message = new Message
{
RecipientName = tbRecipientName.Text.Trim(),
MessageContent = tbMessageContent.Text.Trim()
};
if (!string.IsNullOrWhiteSpace(message.RecipientName) && !string.IsNullOrEmpty(message.MessageContent))
{
// Call the client adapter to send the message to the particular recipient instantly.
ClientAdapter.Instance.SendMessage(message);
}
Receive messages (this is JavaScript functions written in test page. They render content of the message on ASPX page. Here you should implement your logic):
// This method will persist a http request and wait for messages.
function waitEvent() {
CSASPNETReverseAJAX.Dispatcher.WaitMessage("<%= Session["userName"] %>",
function (result) {
displayMessage(result);
// Keep looping.
setTimeout(waitEvent, 0);
}, function () {
// Keep looping.
setTimeout(waitEvent, 0);
});
}
// Append a message content to the result panel.
function displayMessage(message) {
var panel = document.getElementById("<%= lbMessages.ClientID %>");
panel.innerHTML += currentTime() + ": " + message + "<br />";
}
// Return a current time string.
function currentTime() {
var currentDate = new Date();
return currentDate.getHours() + ":" + currentDate.getMinutes() + ":" + currentDate.getSeconds();
}

Synchronous VirusTotal code to Rx enabled asynchronous (and maybe some JSON parsing)

last time I posted a question on here everyone provided some great guidance on getting my problem solved. Move forward in time and here is another. I'm attempting to redo a small helper tool I have that checks URL's and Files against VirusTotal to get some basic information. The code below works quite well but locks up the UI. I was told that I should look into Rx and am enjoying reading up on it but cannot seem to get my head wrapped around it. So now here is where the question comes in, what is the best way to design the following code to make it utilize Rx so that it is asynchronous and leaves my UI alone while it does it's thing. VirusTotal also utilizes multilevel JSON for responses so if anyone has a nice way of integrating that into this that would even be better.
class Virustotal
{
private string APIKey = "REMOVED";
private string FileReportURL = "https://www.virustotal.com/vtapi/v2/file/report";
private string URLReportURL = "http://www.virustotal.com/vtapi/v2/url/report";
private string URLSubmitURL = "https://www.virustotal.com/vtapi/v2/url/scan";
WebRequest theRequest;
HttpWebResponse theResponse;
ArrayList theQueryData;
public string GetFileReport(string checksum) // Gets latest report of file from VT using a hash (MD5 / SHA1 / SHA256)
{
this.WebPostRequest(this.FileReportURL);
this.Add("resource", checksum);
return this.GetResponse();
}
public string GetURLReport(string url) // Gets latest report of URL from VT
{
this.WebPostRequest(this.URLReportURL);
this.Add("resource", url);
this.Add("scan", "1"); //Automatically submits to VT if no result found
return this.GetResponse();
}
public string SubmitURL(string url) // Submits URL to VT for insertion to scanning queue
{
this.WebPostRequest(this.URLSubmitURL);
this.Add("url", url);
return this.GetResponse();
}
public string SubmitFile() // Submits File to VT for insertion to scanning queue
{
// File Upload code needed
return this.GetResponse();
}
private void WebPostRequest(string url)
{
theRequest = WebRequest.Create(url);
theRequest.Method = "POST";
theQueryData = new ArrayList();
this.Add("apikey", APIKey);
}
private void Add(string key, string value)
{
theQueryData.Add(String.Format("{0}={1}", key, Uri.EscapeDataString(value)));
}
private string GetResponse()
{
// Set the encoding type
theRequest.ContentType="application/x-www-form-urlencoded";
// Build a string containing all the parameters
string Parameters = String.Join("&",(String[]) theQueryData.ToArray(typeof(string)));
theRequest.ContentLength = Parameters.Length;
// We write the parameters into the request
StreamWriter sw = new StreamWriter(theRequest.GetRequestStream());
sw.Write(Parameters);
sw.Close();
// Execute the query
theResponse = (HttpWebResponse)theRequest.GetResponse();
StreamReader sr = new StreamReader(theResponse.GetResponseStream());
return sr.ReadToEnd();
}
}
Your code is poorly written which makes it more difficult to make it asynchronous - primarily the three class-level variables. When coding in Rx you want to think "functional programming" and not "OOP" - so no class-level variables.
So, what I've done is this - I've recoded the GetResponse method to encapsulate all of the state into a single call - and I've made it return IObservable<string> rather than just string.
The public functions can now be written like this:
public IObservable<string> GetFileReport(string checksum)
{
return this.GetResponse(this.FileReportURL,
new Dictionary<string, string>() { { "resource", checksum }, });
}
public IObservable<string> GetURLReport(string url)
{
return this.GetResponse(this.URLReportURL,
new Dictionary<string, string>()
{ { "resource", url }, { "scan", "1" }, });
}
public IObservable<string> SubmitURL(string url)
{
return this.GetResponse(this.URLSubmitURL,
new Dictionary<string, string>() { { "url", url }, });
}
public IObservable<string> SubmitFile()
{
return this.GetResponse("UNKNOWNURL", new Dictionary<string, string>());
}
And GetResponse looks like this:
private IObservable<string> GetResponse(
string url,
Dictionary<string, string> theQueryData)
{
return Observable.Start(() =>
{
var theRequest = WebRequest.Create(url);
theRequest.Method = "POST";
theRequest.ContentType="application/x-www-form-urlencoded";
theQueryData.Add("apikey", APIKey);
string Parameters = String.Join("&",
theQueryData.Select(x =>
String.Format("{0}={1}", x.Key, x.Value)));
theRequest.ContentLength = Parameters.Length;
using (var sw = new StreamWriter(theRequest.GetRequestStream()))
{
sw.Write(Parameters);
sw.Close();
}
using (var theResponse = (HttpWebResponse)theRequest.GetResponse())
{
using (var sr = new StreamReader(theResponse.GetResponseStream()))
{
return sr.ReadToEnd();
}
}
});
}
I haven't actually tested this - I don't have the APIKEY for starters - but it should work OK. Let me know how you go.

Categories

Resources