NetSuite SuiteTalk TransactionSearchAdvanced: recordList equals null - c#

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.

Related

Google AdWords library doesn't create an access token

I'm trying to make a targetingIdeaService API call to Google AdWords. This is my code so far:
[HttpGet]
public IEnumerable<string> Get()
{
var user = new AdWordsUser();
using (TargetingIdeaService targetingIdeaService = (TargetingIdeaService)user.GetService(AdWordsService.v201802.TargetingIdeaService))
{
// Create selector.
TargetingIdeaSelector selector = new TargetingIdeaSelector();
selector.requestType = RequestType.IDEAS;
selector.ideaType = IdeaType.KEYWORD;
selector.requestedAttributeTypes = new AttributeType[] {
AttributeType.KEYWORD_TEXT,
AttributeType.SEARCH_VOLUME,
AttributeType.AVERAGE_CPC,
AttributeType.COMPETITION,
AttributeType.CATEGORY_PRODUCTS_AND_SERVICES
};
// Set selector paging (required for targeting idea service).
var paging = Paging.Default;
// Create related to query search parameter.
var relatedToQuerySearchParameter =
new RelatedToQuerySearchParameter
{ queries = new String[] { "bakery", "pastries", "birthday cake" } };
var searchParameters = new List<SearchParameter> { relatedToQuerySearchParameter };
var page = new TargetingIdeaPage();
page = targetingIdeaService.get(selector);
return new string[] { "value1", "value2" };
}
}
it looks ok, compiles at last, and so on. But then I went into debug mode. And I saw this:
So as you can see, the variable doesn't have the access token. The other data comes from app.config file.
I am quite certain the keys passed in are correct.
Then the code throws the famous invalid_grand error. In my case, I believe that's because the access token is not being generated. I'm new to AdWords and ASP.NET, so I probably missed something, but I have no idea what.
I used the
docs,
Code Structure instructions, and
code examples to put it all together.
I had my configuration wrong. I had to recreate all the credentials using incognito window. If you have any issues just open a thread here: https://groups.google.com/forum/#!forum/adwords-api

Error querying the Rally API - DynamicJsonObject does not contain a definition for 'Errors'

I'm trying to query the search API of Rally, here is my c# code:
var searchRequest = new Request()
{
ArtifactName = "search",
Limit = 25,
Project = "/project/" + CurrentProject,
ProjectScopeDown = true,
ProjectScopeUp = true,
PageSize = 25,
Fetch = new List<string>() { "true" }
};
searchRequest.AddParameter("keywords", "foo");
QueryResult queryTaskResult = api.Query(searchRequest);
this works as expected and returns result, however i want to pass a parameter of compact=true, which will return slightly different data (mainly a standard web link to the item).
var searchRequest = new Request()
{
ArtifactName = "search",
Limit = 25,
Project = "/project/" + CurrentProject,
ProjectScopeDown = true,
ProjectScopeUp = true,
PageSize = 25,
Fetch = new List<string>() { "true" }
};
searchRequest.AddParameter("keywords", "foo");
///this is the new item
searchRequest.AddParameter("compact", "true");
QueryResult queryTaskResult = api.Query(searchRequest);
However when i fire this request I get the following error
Rally.RestApi.Json.DynamicJsonObject' does not contain a definition for 'Errors'
However when I try to do this request in the browser it works fine.
Any help as to what I'm doing wrong would be greatly appreciated!
Why do you want to do this?
What I want to do is build up a link to the WEB view of the object, eg:
https://rally1.rallydev.com/#/{CurrentProject}d/detail/{ObjectType}/{ObjectId}
I already know the CurrentProject, I need to know the ObjectType and the ObjectId
I have found, when i pass compact=true, the _ref provides this, '/defect/1234567' but this throws an exception.
If I do not pass compact=true, the _ref returns the API reference 'https://rally1.rallydev.com/slm/webservice/v2.x/defect/1234567'
Unfortunately the compact functionality was added to WSAPI after the .NET toolkit was created and we've never updated it to support it.
I filed a github issue here: https://github.com/RallyTools/RallyRestToolkitFor.NET/issues/37
compact=true was mainly a performance optimization to reduce the size of responses in large result sets.
Other than performance is there a reason you'd like to use it?

Not All Site Collections show up in the results - Sharepoint 2013 Search through CSOM

I am working on a C# code that retrieves all site collection paths from a On-Premise Sharepoint 2013 server. I have the following Site Collections on the server:
/serverurl/
/serverurl/my
/serverurl/my/personal/site1
/serverurl/my/personal/site2
/serverurl/sites/TestSite
/serverurl/custompath/site3
when I run my code , I only get the following site collections:
/serverurl/
/serverurl/my
/serverurl/my/personal/site1
/serverurl/my/personal/site2
I was wondering why my search does not return all the site collections?
here is my code:
ClientContext context = new ClientContext(siteUrl);
var cred = new NetworkCredential(userName, password, domain);
context.Credentials = cred;
KeywordQuery query = new KeywordQuery(context);
query.QueryText = "contentclass:STS_Site";
SearchExecutor executor = new SearchExecutor(context);
query.TrimDuplicates = true;
var resultTable = executor.ExecuteQuery(query);
context.ExecuteQuery();
foreach (var row in resultTable.Value[0].ResultRows)
{
string siteName = row["siteName"] as string;
Console.WriteLine("Site Name: {0}", siteName);
}
Thanks!
I was having the same problem today. I found two solutions.
Regardless if your on-prem or on Office365 we can use Microsoft.Online.SharePoint.Client.Tenant dll. You can use this to get all the Site Collections. You do need your admins to run some power shell if your on-prem. Vesa was nice enough to write a blog about it here
Once you get that done, you can do something like the following (Note:I have not tested this method with a non Admin account) (solution taken from here) Sadly this one will not work for me as I want security trimming and this will code must be ran by a user with tenant read permissions which our users would not normal have.
var tenant = new Tenant(clientContext);
SPOSitePropertiesEnumerable spp = tenant.GetSiteProperties(0, true);
clientContext.Load(spp);
clientContext.ExecuteQuery();
foreach(SiteProperties sp in spp)
{
// you'll get your site collections here
}
I ended up doing this which gets back to using search, I still have a problem, we have well over 500 sites/webs so I'm working with our admins to see if we can increase the max rows search can return. However, the true secret here is TrimDuplicates being set to false, I don't know why SP thinks the results are dups, but it obviously does, so set it to false and you should see all your sits.
KeywordQuery query = new KeywordQuery(ctx);
query.QueryText = "contentclass:\"STS_Site\"";
query.RowLimit = 500;//max row limit is 500 for KeywordQuery
query.EnableStemming = true;
query.TrimDuplicates = false;
SearchExecutor searchExecutor = new SearchExecutor(ctx);
ClientResult<ResultTableCollection> results = searchExecutor.ExecuteQuery(query);
ctx.ExecuteQuery();
var data = results.Value.SelectMany(rs => rs.ResultRows.Select(r => r["Path"])).ToList();
Hope one of the two will work for you.

How does one connect to the RootDSE and/or retrieve highestCommittedUSN with System.DirectoryServices.Protocols?

Using System.DirectoryServices, one can get the highestCommittedUSN this way:
using(DirectoryEntry entry = new DirectoryEntry("LDAP://servername:636/RootDSE"))
{
var usn = entry.Properties["highestCommittedUSN"].Value;
}
However, I need to get this information from a remote ADLDS using System.DirectoryServices.Protocols, which does not leverage ADSI. Following is a simplified code sample of what I'm attempting to do:
using(LdapConnection connection = GetWin32LdapConnection())
{
var filter = "(&(highestCommittedUSN=*))";
var searchRequest = new SearchRequest("RootDSE", filter, SearchScope.Subtree, "highestCommittedUSN");
var response = connection.SendRequest(searchRequest) as SearchResponse;
var usn = response.Entries[0].Attributes["highestCommittedUSN"][0];
}
Unfortunately this kicks back a "DirectoryOperationException: The distinguished name contains invalid syntax." At first I thought there might be something wrong in GetWin32LdapConnection() but that code is called in numerous other places to connect to the directory and never errors out.
Any ideas?
Thanks for the idea, Zilog. Apparently to connect to the RootDSE, you have to specify null for the root container. I also switched the filter to objectClass=* and the search scope to "base." Now it works!
using(LdapConnection connection = GetWin32LdapConnection())
{
var filter = "(&(objectClass=*))";
var searchRequest = new SearchRequest(null, filter, SearchScope.Base, "highestCommittedUSN");
var response = connection.SendRequest(searchRequest) as SearchResponse;
var usn = response.Entries[0].Attributes["highestcommittedusn"][0];
}
I hope this saves someone else some time in the future.

NHIbernate & Rhino-Security does not save unless I call session.Flush()

I am having an issue using nHibernate and Rhino.Security. After struggling for hours to get the right config setup, I finally got the code to run without any errors. However, no entries are being saved to the database unless I call session.Flush().
Looking at various examples on the net; I should not have to call flush.
Here’s my config code:
var cfg = new Configuration()
.SetProperty(Environment.ConnectionDriver, typeof(SqlClientDriver).AssemblyQualifiedName)
.SetProperty(Environment.Dialect, typeof(MsSql2008Dialect).AssemblyQualifiedName)
.SetProperty(Environment.ConnectionString, "………")
.SetProperty(Environment.ProxyFactoryFactoryClass, typeof(ProxyFactoryFactory).AssemblyQualifiedName)
.SetProperty(Environment.ReleaseConnections, "on_close")
.SetProperty(Environment.UseSecondLevelCache, "true")
.SetProperty(Environment.UseQueryCache, "true")
.SetProperty(Environment.CacheProvider, typeof(HashtableCacheProvider).AssemblyQualifiedName)
.AddAssembly("GA.CAP.Website")
;
Security.Configure<AspNetUser>(cfg, SecurityTableStructure.Prefix);
var factory = cfg.BuildSessionFactory();
var session = factory.OpenSession();
var authorizationRepository = new AuthorizationRepository(session);
IoC.Container.RegisterInstance<IAuthorizationRepository>(authorizationRepository);
var permissionBuilderService = new PermissionsBuilderService(session, authorizationRepository);
IoC.Container.RegisterInstance<IPermissionsBuilderService>(permissionBuilderService);
var permissionService = new PermissionsService(authorizationRepository, session);
IoC.Container.RegisterInstance<IPermissionsService>(permissionService);
var authService = new AuthorizationService(permissionService, authorizationRepository);
IoC.Container.RegisterInstance<IAuthorizationService>(authService);
Test code:
authorizationRepository.CreateUsersGroup("GAAdmins");
var group = authorizationRepository.GetUsersGroupByName("GAAdmins");
The GetUsersGroupByName call returns null. If I add a session.Flush call in between the two calls, it works fine and returns the group.
Based on examples in various blogs, such as this one, I should not have to call flush. In addition, the test cases included with the Rhino.Security code do not do any flushing as shown here:
This is straight out of Rhino.Security's test case fixture:
// on first deploy
Operation operation = authorizationRepository.CreateOperation("/Account/View");
// when creating account
UsersGroup group = authorizationRepository.CreateUsersGroup("Belongs to " + account.Name);
// setting permission so only associated users can view
permissionsBuilderService
.Allow(operation)
.For(group)
.On(account)
.DefaultLevel()
.Save();
// when adding user to account
authorizationRepository.AssociateUserWith(user, group);
bool allowed = authorizationService.IsAllowed(user, account, "/Account/View");
Assert.True(allowed);
Is there some setting I am missing somewhere?
Thanks,
Rick
That is expected, RS uses the same session as your code, and calling Flush internally may result in unintended consequences.
Commit your transaction or call Flush

Categories