Setting up 3DCart API with a C# App - c#

I have been trying to create an application to go through our database at a set interval and update/add any new items to 3DCarts database. Their code example uses soap in an xml file to send 1 request per call. So I need to to be able to generate the xml I need with the items information on the fly before sending it. I have done hardly anything with XML files like this and cannot figure out how to create the chunk of code I need and send it. One method that has been suggested is create a file but still executing has been a problem and would be very inefficient for a large number of items. Here is what I have so far
sqlStatement = "SELECT * FROM products WHERE name = '" + Convert.ToString(reader.GetValue(0)) + "'";
ServiceReferenceCart.cartAPIAdvancedSoapClient bcsClient = new ServiceReferenceCart.cartAPIAdvancedSoapClient();
ServiceReferenceCart.runQueryResponse bcsResponse = new ServiceReferenceCart.runQueryResponse();
bcsClient.runQuery(storeUrl, userKey, sqlStatement, callBackURL);
string result = Convert.ToString(bcsResponse);
listBox1.Items.Add(result);
EDIT: Changed from sample code block to current code block as I got a service reference setup finally. They provide no details though for using the functions in the reference. With this bcsResponse is just a blank, when I try adding .Body I have the same result but when I add .runQuery to the .Body I get a "Object reference not set to an instance of an object." error. As I have said I have not messed with service references before.
I hope I have explained well enough I just really have not worked with this kind of stuff before and it has become extremely frustrating.
Thank you in advance for any assistance.

I actually ended up figuring this out after playing around with it. Here is what I did to get the reference to work. This may have been easy for anyone who have used the references before but I have not and have decided to post this in case anyone else has this problem. The SQL can be SELECT, ADD, UPDATE and DELETE statements this was to see if the sku was listed before updating/adding.
//Will be using these multiple times so a variable makes more sense
// DO NOT include http:// in the url, also id is not shown in their
//database layout pdf they will give but it is the sku/product number
string sqlStatement = "SELECT id FROM products WHERE id = '" + Convert.ToString(reader.GetValue(0)) + "')))";
string userKey = "YourKeyHere";
string storeUrl = "YourStoresURLHere";
// Setting up instances from the 3DCart API
cartAPIAdvancedSoapClient bcsClient = new cartAPIAdvancedSoapClient();
runQueryRequest bcsRequest = new runQueryRequest();
runQueryResponse bcsResponse = new runQueryResponse();
runQueryResponseBody bcsRespBod = new runQueryResponseBody();
runQueryRequestBody bcsReqBod = new runQueryRequestBody();
//assigning required variables to the requests body
bcsReqBod.storeUrl = storeUrl;
bcsReqBod.sqlStatement = sqlStatement;
bcsReqBod.userKey = userKey;
//assigning the body to the request
bcsRequest.Body = bcsReqBod;
//Setting the response body to be the result
bcsRespBod.runQueryResult = bcsClient.runQuery(bcsReqBod.storeUrl, bcsReqBod.userKey, bcsReqBod.sqlStatement, bcsReqBod.callBackURL );
bcsResponse.Body = bcsRespBod;
//adding the result to a string
string result = bcsResponse.Body.runQueryResult.Value;
//displaying the string, this for me was more of a test
listBox1.Items.Add(result);
You will also need to activate the Advanced API on your shop as you may notice there is no actual option as the pdf's say, you need to go to their store and purchase(its free) and wait for them to activate it. This took about 2 hrs for us.

Related

Moving files with Google Drive API v3

Im trying to move a file from one folder to another using the Google Drive API v3. I found documentation how to this here. I used the .NET sample code from the documentation page and created a method that looks like this:
public ActionResult MoveFile(string fileToMove, string destination)
{
DriveService service = new DriveService(new BaseClientService.Initializer
{
HttpClientInitializer = <USER CREDENTIAL>,
ApplicationName = "APPNAME"
});
var searchFiles = service.Files.List();
searchFiles.Corpus = FilesResource.ListRequest.CorpusEnum.User;
searchFiles.Q = "name = '" + fileToMove + "'";
searchFiles.Fields = "files(*)";
string fileToMoveId = searchFiles.Execute().Files[0].Id;
searchFiles.Q = "name = '" + destination + "'";
string destinationId = searchFiles.Execute().Files[0].Id;
//Code used from documentation
// Retrieve the existing parents to remove
var getRequest = service.Files.Get(fileToMoveId);
getRequest.Fields = "parents";
var file = getRequest.Execute();
var previousParents = String.Join(",", file.Parents);
// Move the file to the new folder
var updateRequest = service.Files.Update(file, fileToMoveId);
updateRequest.Fields = "id, parents";
updateRequest.AddParents = destinationId;
updateRequest.RemoveParents = previousParents;
file = updateRequest.Execute();
return RedirectToAction("Files", new {folderId = destinationId});
}
When I execute this code I get the following error:
The parents field is not directly writable in update requests. Use the
addParents and removeParents parameters instead.
The error doesn't really makes sense to me because this code sample came from the documentation page itself. I can't figure out what other paramters they mean. What addParents and removeParents parameters do they mean? Are updateRequest.AddParents and updateRequest.RemoveParents not the right parameters?
Ok here is the problem.
var updateRequest = service.Files.Update(file, fileToMoveId);
The method is requiring that you send a body of a file to be updated. This normally makes sense as any changes you want to make you can add to the body.
Now the problem you are having is that you got your file from a file.get. Which is totally normal. This is how you should be doing it. THe problem is there are some fields in that file that you cant update. So by sending the full file the API is rejecting your update. If you check Files: update under Request body you will see which fiends are updateable.
Issue:
Now this is either a problem with the client library or the API I am going to have to track down a few people at Google to see which is the case.
Fix:
I did some testing and sending an empty file object as the body works just fine. The file is moved.
var updateRequest = service.Files.Update(new Google.Apis.Drive.v3.Data.File(), fileToMove.Id);
updateRequest.AddParents = directoryToMove.Id;
updateRequest.RemoveParents = fileToMove.Parents[0];
var movedFile = updateRequest.Execute();
This method works well when working in your own drive, but not in a team drive where a file (folder) can only have 1 parent strictly. I do not have the solution in a team drive

NetSuite SuiteTalk TransactionSearchAdvanced: recordList equals null

I have a small C# console application who's sole purpose is to receive records from a "Saved Search" in NetSuite(via SuiteTalk). I've been able to successfully connect and receive records from NetSuite for my Saved Search(the search runs fine through the web interface too), however when I attempt to access the results of the "Saved Search" through my application, I am unable to view them because the search object that is returned does not contain any data in the "recordList" property:
//Connect
var dataCenterAwareNetSuiteService = new DataCenterAwareNetSuiteService("XXXXXX");
dataCenterAwareNetSuiteService.Timeout = 1000 * 60 * 60 * 2;
//Adds Credentials etc...
dataCenterAwareNetSuiteService.tokenPassport = createTokenPassport();
//Setup Preferences
var prefs = new Preferences();
prefs.warningAsErrorSpecified = true;
prefs.warningAsError = false;
dataCenterAwareNetSuiteService.preferences = prefs;
var searchPrefs = new SearchPreferences();
dataCenterAwareNetSuiteService.searchPreferences = searchPrefs;
dataCenterAwareNetSuiteService.searchPreferences.pageSize = 5;
dataCenterAwareNetSuiteService.searchPreferences.pageSizeSpecified = true;
dataCenterAwareNetSuiteService.searchPreferences.bodyFieldsOnly = false;
dataCenterAwareNetSuiteService.searchPreferences.returnSearchColumns = false;
//Search
var tranSearchAdv = new TransactionSearchAdvanced();
var tranSearchRow = new TransactionSearchRow();
var tranSearchRowBasic = new TransactionSearchRowBasic();
tranSearchAdv.savedSearchId = "XXXX";
tranSearchRowBasic.internalId =
new SearchColumnSelectField[] { new SearchColumnSelectField() };
tranSearchRowBasic.tranId =
new SearchColumnStringField[] { new SearchColumnStringField() };
tranSearchRowBasic.dateCreated =
new SearchColumnDateField[] { new SearchColumnDateField() };
tranSearchRowBasic.total =
new SearchColumnDoubleField[] { new SearchColumnDoubleField() };
tranSearchRowBasic.entity =
new SearchColumnSelectField[] { new SearchColumnSelectField() };
tranSearchRow.basic = tranSearchRowBasic;
tranSearchAdv.columns = tranSearchRow;
//No errors,
//this works correctly and returns the "Saved Search" with the correct "totalRecords"
//but results.recordList == null while results.totalRecords = 10000000+
var results = dataCenterAwareNetSuiteService.search(tranSearchAdv);
I appears to me that the "recordList" object is the principal way data is retrieved from the results of a search(Related Java Example, Another Here). This is also the way the example API does it.
I have run this on multiple "Saved Search's" with the same results. I don't understand how you can have more than one record in "totalRecords" and yet the "recordList" remains null? Is there some configuration option that has to be set to allow me to access this property. Or maybe it's a security thing, the API user I have setup should have full access, is there anything else that need to be granted access?
NetSuite SuiteTalk is not well documented, and most of the examples online are not in C#, and not dealing with the issues that I'm experiencing. These factors make it very difficult to determine why the previously mentioned behavior is occurring, or even, to discover any alternative methods for retrieving the resulting data from the source "Saved Search".
Does anyone have any insight into this behavior? Is this the correct method of retrieving results from SuiteTalk? Is there any configuration from the API or Web Side that needs to be changed?
Update 1
I've also tried using the alternative way of getting result data by accessing the "searchRowList" object from the "SearchResult" object(suggested by #AdolfoGarza) However it returns mostly empty fields(null) similar to the "recordList" property I do not see a way to retrieve "Saved Search" data from this method.
Try getting the results with results.searchRowList.searchRow , thats how it works in php.
I was able to resolve the issue by removing this line in the code:
tranSearchRow.basic = tranSearchRowBasic;
Then like #AdolfoGarza reccomended, retrieving the results from "basic" field in "results.searchRowList"
For some reason the template API that I was using was setting up a "TransactionSearchAdvanced" referencing a blank "TransactionSearchBasic" record, not sure why but this was causing the results from "searchRowList" to be null. After removing it I now get non-null values in the proper fields.
As for "recordList", it's still null, not sure why, but as I have my data I don't think I'll continue to dig into this.

TFS WorkItem Validation error

So here's the case: I am trying to create a Task WorkItem. I have to get the data from a dataGridView, which I think I managed to. But the fun part comes when I try to save, or validate the WorkItem. No matter what value State has, the validator always comes with InvalidListValue on execution of wiTask.Validate();. I have tried even hard-coding the State's value, but nothing happens. There is one thing that bothers me a bit - in the Validation array the error comes within the Status property, where the actual control has only State available. Here are some code and a snippet of the error bit:
WorkItem wiTask = new WorkItem(workItemType)
{
Title = form1.dg_taskView.Rows[rows].Cells["titleDataGridViewTextBoxColumn"].Value.ToString() + " " + form1.tb_details.Text,
Description = form1.dg_taskView.Rows[rows].Cells["descriptionDataGridViewTextBoxColumn"].Value.ToString(),
AreaId = int.Parse(form1.dg_taskView.Rows[rows].Cells["areaIDDataGridViewTextBoxColumn"].Value.ToString()),
AreaPath = form1.dg_taskView.Rows[rows].Cells["areaPathDataGridViewTextBoxColumn"].Value.ToString(),
IterationId = int.Parse(form1.dg_taskView.Rows[rows].Cells["iterationIDDataGridViewTextBoxColumn"].Value.ToString()),
IterationPath = form1.dg_taskView.Rows[rows].Cells["iterationPathDataGridViewTextBoxColumn"].Value.ToString(),
State = form1.dg_taskView.Rows[rows].Cells["stateDataGridViewTextBoxColumn"].Value.ToString()
};
ArrayList result = wiTask.Validate();
wiTask.Save();
var hierarchicalLink = _workItemStore.WorkItemLinkTypes["System.LinkTypes.Hierarchy"];
userStory.WorkItemLinks.Add(new WorkItemLink(hierarchicalLink.ForwardEnd, wiTask.Id));
userStory.Save();
As it turns - the validation returns to the array the column that has issues. After further examination the issue is resolved by specifying to Assigned to name, which is part of the project contributors' list.

Lists.AddAttachment throws object reference error

I'm trying to add an attachment to a list item I just created on a sharepoint server using CAML. The code below is simplified (for example the path is normally a variable and the 2nd field (the item id, here 16847) is normally the id I get back from my insert statement in CAML).
This is my code:
String desktopPath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
String savePath = desktopPath + #"\" + "tutorials.txt";
byte[] data = GetData(savePath);
lists.AddAttachment("Tasks", "16847", "tutorials.txt", data);
I'm getting this error:
Unable to update the security according your changes. The following exception occurred during ItemUpdating: Object reference not set to an instance of an object.
See Event Viewer for more information.0x81020089
getdata is a method which converts the file on my desktop to a byte[]. data is not empty and it looks okay.
Other than that Tasks is the List needed and 16847 is the task id in which to add the attachment.
Most of the info I can find is about a different error: the index out of range exception (like on the msdn page: http://msdn.microsoft.com/en-us/library/lists.lists.addattachment(v=office.12).aspx).
I've also tried a http put (unauthorized access, obviously) and the copy service (I can post this code if we can't find the solution to the simpler way above).
Can anyone tell me what is wrong?
EDIT 1:
private dcp.Lists lists = new dcp.Lists();
lists.Credentials = System.Net.CredentialCache.DefaultCredentials;
lists.Url = Values.SERVERADDRESS + "/_vti_bin/lists.asmx";
This initializes the connection to our web service. It works perfectly fine for updates, inserts...
GetData code:
private byte[] GetData(String savePath)
{
byte[] contents;
using (FileStream fStream = File.OpenRead(savePath))
{
contents = new byte[fStream.Length];
sFileName = fStream.Name;
fStream.Read(contents, 0, Convert.ToInt32(fStream.Length));
}
return contents;
}
EDIT 2:
Note that the following does work (I get a correct list of existing attachments from the task):
XmlNode ndAttach = lists.GetAttachmentCollection("Tasks", "16847");
MessageBox.Show(ndAttach.OuterXml);
And the following doesn't (same error as for the AddAttachment method):
lists.DeleteAttachment("Tasks", "16847", ndAttach.ChildNodes[0].InnerText);
Whereas I'm quite certain this should work since it does exactly the same as the example code on msdn: http://msdn.microsoft.com/en-us/library/lists.lists.deleteattachment(v=office.12).aspx
The code above is fine, the error occured on our server. When adding an attachment, no contenttype is given in the xml. This was programmed to throw an error because we've always used this method to update and add new items. So we removed the contenttype out of the underlying code and it works like a charm.

Navigating to DNN Module

I'm forming a newsletter with links to various html modules within my DNN website. I have access to each of their ModuleID's and I'm wanting to use that to get the url. The current approach (made by a third party developer) worked, but only to a degree. The url's are incorrectly formed when the Modules are located deeper in the website.
For example module located at www.website.com/website/articles.aspx is works fine, but a module located www.website.com/website/articles/subarticles.aspx won't. I know this is because the url is incorrectly formed.
Here's the current code:
DotNetNuke.Entities.Modules.ModuleController objModCtrlg = new DotNetNuke.Entities.Modules.ModuleController();
DotNetNuke.Entities.Modules.ModuleInfo dgfdgdg = objModCtrlg.GetModule(ContentMID);
TabController objtabctrll = new TabController();
TabInfo objtabinfoo = objtabctrll.GetTab(tabidfrcontent);
string tabnamefremail= objtabinfoo.TabName;
moduletitlefrEmail = dgfdgdg.ModuleTitle;
string readmorelinkpath = basePath + "/" + tabnamefremail + ".aspx";
ContentMID is the current module ID I'm looking at. I've tried to use Globals.NavigateURL, but that always crashes with Object reference not set to an instance of an object. error. Same thing when I use objtabinfoo.FullUrl so I'm currently at a loss as to how I get the specific modules URL.
EDIT: Here's some more code as to how the tabId is retrieved.
IDictionary<int, TabInfo> dicTabInfo12 = new Dictionary<int, TabInfo>();
ContentMID = Convert.ToInt32(dsNewsList.Tables[0].Rows[i]["ModuleID"]);
dicTabInfo12 = objTabctrl.GetTabsByModuleID(ContentMID);
if (dicTabInfo12.Count > 0)
{
string tester = ""; //Debug
foreach (KeyValuePair<int, TabInfo> item1 in dicTabInfo12)
{
tabidfrcontent = item1.Key;
}
}
You really should be using NavigateUrl to build the links ance if you have the tabid, you are golden.
string readMoreLinkPath = NavigateUrl(tabidfrcontent);
Nice and simple
Okay, colleague suggested this and it works great within a scheduler.
string linkPath = basePath + "/Default.aspx?TabID=" + tabID;
Will Navigate you to the correct tab ID. So this would be the best solution if you're forced to work within a scheduler where you can't use NavigateUrl without some major workarounds.

Categories