XML-RPC .NET call with associative array - c#

I'm trying to perform some actions on a remote server using the XML-RPC .NET library and C#. I have no prior experience using this protocol but most examples seemed pretty straight forward. But the server I'm trying to communicate with seems to parse commands slightly different than most examples I've seen.
All calls are made using a 'perform_actions' function and it expects a list of action(s) along side with it as parameter. Fortunately there's a pretty decent documentation with some code samples included but these examples are done in Ruby/Perl with which I have no experience. I've tried translating these to C# with which I believe I'm on the right path but I'm consistently getting the error"Server returned a fault exception: [400] Invalid request: expected list of actions."
My current code
[XmlRpcUrl("https://DOMAIN/admin/rpc")]
public interface iFace : IXmlRpcProxy
{
[XmlRpcMethod("perform_actions")]
XmlRpcStruct[] perform_actions(XmlRpcStruct struc);
}
public void GetData()
{
XmlRpcStruct actions = new XmlRpcStruct();
actions.Add("name", "registrations.accounts.list");
iFace proxy = XmlRpcProxyGen.Create<iFace>();
proxy.Credentials = new NetworkCredential("USERNAME", "PASSWORD");
XmlRpcStruct[] response = proxy.perform_actions(actions);
}
And here is a Ruby example from the API documentation which I was trying to replicate which is functional
require 'xmlrpc/client'
url = 'https://user:passwd#qmanage.example.com/admin/rpc'
c = XMLRPC::Client.new_from_uri(url)
# Call the action to list the access groups.
ags = c.call('perform_actions', [{
'name' => 'network.accessgroups.list',
'args' => {}
}])
The server doesn't really seem to recognize the XmlRpcStruct I'm sending as the error seems to complain about not receiving a list of actions. (I receive the same error if I send no parameter). However if I change the XmlRpcStruct to a regular string array it will complain about expecting a struct instead so the data isn't ignored entirely.
Is anyone able to help me in the right direction with my problem or does anyone know why this error is returned?

Finally managed to figure out my dilemma. Seems I had to pass an array of XmlRpcStruct's rather than just singular XmlRpcStruct, the following solved my problem:
XmlRpcStruct[] actions = new XmlRpcStruct[1];
XmlRpcStruct action = new XmlRpcStruct();
action.Add("name", "registrations.accounts.list");
actions[0] = action;
I just passed actions as parameter to the perform_actions function.

Related

Getting started with soap web service client c#

My boss asked how long it would take to build a client to access a web service that will send and receive some basic data and embedded documents. Just starting playing with it to see what's involved. I have been doing web and desktop development for about 20 years but have literally never touched a web service so with that I'm at the extreme newb level.
So far I used the wsdl to create the ServiceReference1 and I can see the methods in intellisense but I don't have the first clue where to start with calling the methods, passing parameters and consuming the response. I feel stupid because I'm sure it's pretty simple but just flailing at the code and looking for on point examples has gotten me nowhere. Usually I can find something through google in minutes that is exactly on point but not having luck here. Would appreciate a push in the right direction.
So basic questions. Proper way to make the calls. How and where to land the returned data. How to add parameters.
Here is my first attempt. This gets a simple list and has no parameters. The result in fiddler returns data but there is a runtime type mismatch error which I think is caused by some stray characters leading the response which appear to be caused by chucking, what ever that is. The response starts with 1ffs every time then contains the remainder of the xml. Secondarily I need to get the list into a dataset or some other container but I was hoping to just be able to step into the code and see a result
ServiceReference1.FilingInfoClient webservice = new FilingInfoClient();
ServiceReference1.courtListRequest cr = new ServiceReference1.courtListRequest();
ServiceReference1.courtListResponse lr = new ServiceReference1.courtListResponse();
lr = webservice .getCourtList(cr);
This is essentially the same but takes a date param. When I run this fiddler shows the parameter is not being sent. No other errors but I'm sure only because it exploded immediately.
ServiceReference1.FilingInfoClient webservice = new FilingInfoClient();
ServiceReference1.messageListRequest mr = new ServiceReference1.messageListRequest();
ServiceReference1.MessageListResponse mlr = new ServiceReference1.MessageListResponse();
mr.latestMessagePullTimestamp = DateTime.Now.AddDays(-5);
mr.endTimestamp = DateTime.Now;
mlr.latestMessagePullTimestamp = DateTime.Now;
mlr = webservice.getMessageList(mr);
This is the info provided by the web service host
<x:Envelope xmlns:x="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:urn1="urn:green:partner:ws:schema:FilingInfo">
<x:Header/>
<x:Body>
<urn1:getcourtList>
<urn1:courtListRequest/>
</urn1:getcourtList>
</x:Body>
</x:Envelope>
<x:Envelope xmlns:x="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:urn1="urn:green:partner:ws:schema:FilingInfo">
<x:Header/>
<x:Body>
<urn1:getMessageList>
<urn1:messageListRequest>
<urn1:latestMessagePullTimestamp>?</urn1:latestMessagePullTimestamp>
</urn1:messageListRequest>
</urn1:getMessageList>
</x:Body>
</x:Envelope>
we've got request and response pairs for each operation in the webservice. think like request => input, response => output, operation => method.
the webservice is an API. things that consume APIs are clients. the WSDL describes the API's operations and their requests and responses. tools like visual studio know how to read WSDLs and build C# code to perform those (SOAP) operations under-the-hood. this is the client (here FilingInfoClient). visual studio'll also generate classes representing each request and response.
this allows for a familiar programming experience. you call a method, give it some input, and it returns some output.
using (var client = new FilingInfoClient())
{
var request = new courtListRequest
{
//TODO fill in relevant properties
};
var response = client.getCourtList(request);
}

Using Force.com for arbitrary REST API methods

NB: This is a duplicate of my question on https://github.com/developerforce/Force.com-Toolkit-for-NET/issues/357. I'll sync both threads after this is resolved.
I'm using the Force.com toolkit for .NET. SOQL queries are working fine for me, but I'm trying to also make a simple REST call similar to one I can make with the REST Explorer tool at https://workbench.developerforce.com/restExplorer.php.
My code is:
using (var queryClient = new ForceClient(authClient.InstanceUrl, authClient.AccessToken, authClient.ApiVersion))
{
// Works fine
string soql = GetMyQuery();
QueryResult<dynamic> result = await queryClient.QueryAsync<dynamic>(soql);
// Throws exception. Changing version # doesn't seem to have any effect.
dynamic restResult = await queryClient.ExecuteRestApiAsync<dynamic>("/services/data/v46.0/sobjects/Task/describe");
When I get to ExecuteRestApiAsync, I get the exception:
Salesforce.Common.ForceException
HResult=0x80131500
Message=Could not find a match for URL
Source=Salesforce.Common
StackTrace:
at Salesforce.Common.JsonHttpClient.<HttpGetAsync>d__4`1.MoveNext()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
[...]
I assume that my argument "/services/data/v46.0/sobjects/Task/describe", which works fine in the REST Explorer tool, needs to change. But I've guessed a bunch of different options (different versions, with or without leading URL string, etc.) and so far haven't gotten anything to work.
Can someone tell me how to use ForceClient to get the same results I get back from the Salesforce Workbench REST Explorer?
I ended up looking more closely at the ForceClient code. ExecuteRestApiAsync was not the correct call for this. For my specific example, there are 3 ways I found that work:
dynamic restResult = await queryClient.DescribeAsync<dynamic>("Task");
restResult = await queryClient.BasicInformationAsync<dynamic>("Task/describe/");
restResult = await queryClient.BasicInformationAsync<dynamic>("Task/describe");
(All 3 of the above lines return the same result)
This will work fine for sobjects. I don't think it's currently possible to use this library directly for things like reports which use "analytics" in their REST API definitions, but there are a couple workarounds that don't require recompiling the code. The cleanest and most performant of these that I've found is to pass your own HttpClient instances into the ForceClient ctor and use those directly instead of the methods on the ForceClient. An inspection of the ForceClient code will reveal that most of these methods are just thin string.Format() wrappers on HttpClient methods anyway.

ElasticSearch Doesn't Return Data Through Nest Call with Dynamic class

Am new to Elastic search and NEST, I am trying to get connected with my ES server through NEST. My ES Connection initialization looks like below.
ElasticClient client = null;
public void Connect()
{
var local = new Uri("http://192.168.40.95:9200/");
var settings = new ConnectionSettings(local).DisableDirectStreaming();
client = new ElasticClient(settings);
settings.DefaultIndex("gisgcc18q4");
ReadAllData();
}
public void ReadAllData()
{
var x= client.Search<dynamic>(s=> s.MatchAll());
}
The response is attached as image below,
I am Never getting any Hits, or data. Did i made any mistake in my connector, Also please suggest me good tutorials to convert JSOn ES query to NEST as well.
Looking at the Uri in the screenshot
POST /gisgcc18q4/object/_search?typed_keys=true
suggests that you're using a version older than 7, such as 5 or 6, where document types are used. In this case, the document type name "object" has been inferred from the dynamic type passed as the generic parameter argument, but I suspect that documents have not been indexed with a document type name of "object", but something else.
If the index "gisgcc18q4" only contains one type of document, you can use
var x = client.Search<dynamic>(s=> s.MatchAll().AllTypes());
Or you can pass the specific document type name to use
var x = client.Search<dynamic>(s=> s.MatchAll().Type("_doc"));
A good getting started tutorial for the client is the elasticsearch-net-example GitHub repository. It is a walkthrough in building out a ASP.NET Core web application to search Nuget packages.
Your connection looks fine, can you please validate the detailed summary under DebugInfrormation by clicking on it and get the row query and response.
After apply same query on Postman.
Please copy and paste below expression on quick watch window on the same line which is displayed in your screenshot.
((Elasticsearch.Net.ApiCallDetails)response.ApiCall).DebugInformation
You will get the detailed information, it will be helpful for you to investigate this issue.

Creating Facebook Event in C# Returns #100 Invalid Parameter

I'm trying to put together a small app that will allow me to create events in Facebook. I've already got my Facebook app set up and have successfully tested a post to my feed through the application using the code below.
wc.UploadString("https://graph.facebook.com/me/feed", "access_token=" + AccessToken + "&message=" + Message);
When I try to take things to the next step, I've just hit a brick wall.
The code that I've written is here:
JavaScriptSerializer ser = new JavaScriptSerializer();
wc.UploadString("https://graph.facebook.com/me/events?" + "access_token=" + AccessToken, ser.Serialize(rawevent));
rawevent is a small object I wrote that puts together the elements of an event so I can pass it around my application.
I'm using a similar method using ser.Deserialize to parse the user data coming back from Facebook, so I believe this should work the other way too.
Setting the above code aside for a moment, I also have tried simply putting plain text in there in various formats and with differing levels of parameters, and nothing seems to work.
Is there something wrong with the way I'm approaching this? I've read over everything I could get my hands on, and very few of the samples out there that I could find deal with creating events, and when they do, they're not in C#.
I would appreciate any help on this. If you even just have a clean copy of JSON code that I can look at and see where mine should be tweaked I would appreciate it.
I have included a copy of what the ser.Serialize(rawevent) call produces below:
{"name":"Dev party!","start_time":"1308360696.86778","end_time":"1310952696.86778","location":"my house!"}
EDIT:
thanks to bronsoja below, I used the code below to successfully post an event to Facebook!
System.Collections.Specialized.NameValueCollection nvctest = new System.Collections.Specialized.NameValueCollection();
nvctest.Add("name", "test");
nvctest.Add("start_time", "1272718027");
nvctest.Add("end_time", "1272718027");
nvctest.Add("location", "myhouse");
wc.UploadValues("https://graph.facebook.com/me/events?" + "access_token=" + AccessToken, nvctest);
All the posting examples in the graph api examples in FB docs show using curl -F, which indicates values be POSTed as normal form data. Just key value pair like you did in your first example.
The error is likely due to sending JSON. If you are using WebClient you may be able to simply create a NameValueCollection with your data and use WebClient.UploadValues to send the request.
I've recently found that Facebook returns (#100) Invalid parameter when we are trying to post data when there is already a record on file with the same name. So for example, if you are creating a FriendList via the API, and the name is "foo", submitting another identical request for the same name will immediately return that error.
In testing events you probably deleted the "Dev party!" event after each test, or maybe changing the time since you don't want two events to collide. But I'm wondering if you duplicated your wc.UploadValues(...) statement just as a test if you would see that error again. You're either deleting your 'test' event or maybe changing names and didn't notice that two events with the same name might return the error.
What's really bad here is that the error comes back as a OAuthException, which seems very wrong. This isn't a matter of authentication or authorization, it's purely a data issue.
Facebook Engineers, if I'm right about how this works, it's a bug to return this error under these conditions, and this forum has many examples of related confusion. Please return more appropriate errors.

Consuming a void service operation in a WCF data service

from a Wcf data service client (inherited from System.Data.Services.Client.DataServiceContext) I would like to invoke a service operation defined on a Wcf Data Service that returns void
[WebGet]
public void Operation()
{
// logic
}
The only reasonable method I found is the Execute, but how could I use it with a void operation?
Thank you
You can use just plain HttpWebRequest to do this. I think it will need to be POST service operation (as GET would assume some response, but since you declare it as void it would have no response). In which case Execute can't be used anyway (as it always issues a GET request).
Using plain HttpWebRequest just issue a simple POST to the service operation URL and just check the response status code (should be 204 No Content).
Currently WCF Data Services doesn't have native client support for service operations, so you need to write one for yourself.
I have found a workaround for this problem.
This website solved me quite a few problems before, so I thought it would be nice to share back.
The quick answer to your question is:
string empty = context.Execute<string>(new Uri("Operation", UriKind.Relative)).FirstOrDefault();
The "empty" string should be null or empty upon response. It "works around" the HttpWebRequest method mentioned on the post above.
Further more, it is also possible to get primitive types back using this technique.
Lets say I have this Method:
[WebGet]
public bool Authenticate(string Username, string Password)
{...do stuff here...}
When you try the normal execution it fails (Vitek Karas explains it well in his reponse above):
var query = context.CreateQuery<bool>("Authenticate").AddQueryOption("Username", "'itye'").AddQueryOption("Password","'123456'");
DataServiceCollection<bool> list = new DataServiceCollection<bool>();
list.Load(query);
But the following will do the trick:
var query = context.CreateQuery<bool>("Authenticate").AddQueryOption("Username", "'itye'").AddQueryOption("Password","'123456'");
bool authenticated = context.Execute<bool>(new Uri(query.RequestUri.ToString().Replace("Authenticate()", "Authenticate"))).FirstOrDefault();
Please note the Replace("Authenticate()", "Authenticate"), which omits () from the query string (otherwise it will cause error).
Hope it helps.
- Itye
Thanks Itye
I was looking for similar solution. Did using HttpWebRequest way first. But your two lines of code helped me doing the same task. Very Happy. Thanks Once Again..
var query = context.CreateQuery("Authenticate").AddQueryOption("Username", "'itye'").AddQueryOption("Password","'123456'");
bool authenticated = context.Execute(new Uri(query.RequestUri.ToString().Replace("Authenticate()", "Authenticate"))).FirstOrDefault();

Categories

Resources