quickbooks online sdk v3 how to troubleshot a badrequest/ValidationException - debugging

When debugging a program using the QuickBooks Online API V3 with the SDK. How do you troubleshoot a bad request coming from DataServices. I have gone through several layers of the Exception innerExceptions and I only see ValidationException.
But what tripped the validation exception???
Is there a log to see what caused the problem?

catch (Intuit.Ipp.Exception.IdsException ex)
{
//TODO: handle dupe or other....
var returnMessage = string.Empty;
var innerException = ((Intuit.Ipp.Exception.ValidationException)(ex.InnerException)).InnerExceptions.FirstOrDefault();
if (innerException != null)
{
returnMessage = innerException.Message;
}
}

I just found out this when looking deeply into the exception generated.
Generally speaking you will get an exception and you check if the inner exception object is not null or nothing, if it has an inner exception you look at it.
You will notice that every exception also has a property called "InnerExceptions" (plural) which is a list containing inner exceptions, this property will be null when you have a proper inner exception, but in the last one (in my case) when the inner exception property is null this list contains one inner exception with the details.
This property is available on Intuit.Ipp.Exception.IdsException and contain a list of IdsError
I'm using the IPP .Net SDK 3.0 API (version 2.0.2.0)

For .Net SDK please enable request/response logs. Th details for bad exception get saved in these files.
https://developer.intuit.com/docs/0025_quickbooksapi/0055_devkits/0150_ipp_.net_devkit_3.0/logging

Please mention which SDK(JAVA/.NET/PHP) you are using.
https://developer.intuit.com/docs/0025_quickbooksapi/0055_devkits
You can set the logger to debug mode to capture the raw request and response XMLs. From the response XML, you'll get the details of failure(if any).
Otherwise, you can call these endpoints directly from APIExplorer.
https://developer.intuit.com/apiexplorer?apiname=V3QBO
Thanks

Simply collect the Error Message coming from Quickbook API. Make following the first catch block statement:
catch (Intuit.Ipp.Exception.IdsException ex)
{
string errorMessage = (((Intuit.Ipp.Exception.ValidationException)(ex.InnerException)).InnerExceptions.FirstOrDefault() ?? new Exception()).Message;
// you can further log the error .
}

Related

Can a LINQ query with a where clause on the key to an OData service be done with filter query option instead of a canonical URL?

The problem
I'm trying to query data from an OData V4 service. This is done with a C# client generated by the OData Connected Service extension for Visual Studio. The query is done with a LINQ expression with a where clause. The where clause contains criteria for the key fields of the entity being queried.
The query results in a DataServiceQueryException ("An error occurred while processing this request") with an inner DataServiceClientException ("NotFound"), both from the Microsoft.OData.Client namespace. See below for the full stack trace.
Analysis
Using Fiddler I can see that the request being sent is using a canonical URL (also called a by-key request). If the criteria values do not match any existing data, the response has the code 404 Not Found. This code seems to cause the exception.
When the where clause is changed to also include non-key fields, the request is sent using a $filter query option. In this case, if the criteria values do not match any existing data, the response has the code 200 OK. This does not cause an exception and returns null as result of the LINQ query.
Another workaround is to not use LINQ and instead specify explicitely that a filter query option should be used.
Comparison with the OData reference service TripPin showed that the 404 response does not seem to be the correct response in this case. TripPin instead returns 204 No Content. While the OData specification has several indications that this seems the correct response in this case, I could not find an explicit statement to that effect. In any case, this point is moot since I don't have control over the OData service and can't change its behavior.
Repro details
Unfortunately, the OData service in question is not publicly available. It may be possible to mock such a service or find a public service that shows the same behavior. I have not looked into this since I found a solution (see my answer).
Nevertheless, here is the code that causes the exception:
static void GetData()
{
Uri odataUri = new Uri("https://the-odata-service", UriKind.Absolute);
// Resources is a class generated by the OData Connected Service extension
// and extends Microsoft.OData.Client.DataServiceContext
Resources context = new Resources(odataUri);
var entity = context.Entities.Where(x => x.Key == 1).SingleOrDefault();
}
Producing this request and response:
GET https://the-odata-service/entities(1) HTTP/1.1
HTTP/1.1 404 Not Found
The exception:
Unhandled exception. Microsoft.OData.Client.DataServiceQueryException: An error occurred while processing this request.
---> Microsoft.OData.Client.DataServiceClientException: NotFound
at Microsoft.OData.Client.QueryResult.ExecuteQuery()
at Microsoft.OData.Client.DataServiceRequest.Execute[TElement](DataServiceContext context, QueryComponents queryComponents)
--- End of inner exception stack trace ---
at Microsoft.OData.Client.DataServiceRequest.Execute[TElement](DataServiceContext context, QueryComponents queryComponents)
at Microsoft.OData.Client.DataServiceQuery`1.GetEnumerator()
at System.Linq.Enumerable.TryGetSingle[TSource](IEnumerable`1 source, Boolean& found)
at System.Linq.Enumerable.SingleOrDefault[TSource](IEnumerable`1 source)
at Microsoft.OData.Client.DataServiceQueryProvider.ReturnSingleton[TElement](Expression expression)
at System.Linq.Queryable.SingleOrDefault[TSource](IQueryable`1 source)
at <my test program in the GetData method in the line of the SingleOrDefault call>
If I change the LINQ to
var entity = context.Entities
.Where(x =>
x.Key == 1
&& x.AnotherNonKeyField == "2")
.SingleOrDefault();
I get
GET https://the-odata-service/Entities?$filter=Key%20eq%201%20and%20AnotherNonKeyField%20eq%20'2'&$top=2 HTTP/1.1
HTTP/1.1 200 OK
{
"#odata.context":"https://the-odata-service/$metadata#Entities","value":[
]
}
which does not result in an exception, but entity being null.
The question
To sum up, while there are workarounds, I would prefer if I could query the odata service with LINQ and without having to add dummy criteria (which would not always be possible). Is there a way to do that?
TLDR
The KeyComparisonGeneratesFilterQuery property of the DataServiceContext can be used to generate a $filter query option.
Some more background
I spent some time researching this issue in context of LINQ and the client that was generated. In hindsight, it is obvious that the Microsoft OData Client library would have been a better place to start, since it throws the exception. But who has time to read a stack trace when instead you can furiously google and debug for a few hours *sigh* ?
Eventually I found my way to issue #851 DataServiceQuery makes a "by key" request when Where clause compares just the ID, causing exception instead of empty result if the entity is not found. and pull request #1762 Enable Where clause to generate $filter query options for key predicates. Especially the later does a much better job of explaining the purpose and how to use the KeyComparisonGeneratesFilterQuery property than the documentation.
With that, the above code can be fixed like this:
static void GetData()
{
Uri odataUri = new Uri("https://the-odata-service", UriKind.Absolute);
// Resources is a class generated by the OData Connected Service extension
// and extends Microsoft.OData.Client.DataServiceContext
Resources context = new Resources(odataUri);
context.KeyComparisonGeneratesFilterQuery = true;
var entity = context.Entities.Where(x => x.Key == 1).SingleOrDefault();
}
Which produces
GET https://the-odata-service/Entities?$filter=Key%20eq%201&$top=2 HTTP/1.1
HTTP/1.1 200 OK
{
"#odata.context":"https://the-odata-service/$metadata#Entities","value":[
]
}

How to get generic message as json from MassTransit Fault event

I have a microservices based application and wish to create a service that captures all Fault events with their message payloads (as json) and stores them in a database for later analysis and potential resubmission. I have created a Fault consumer and can capture the Fault but am unable to generically extract the message payload as json.
public Task Consume(ConsumeContext<Fault> context)
{
if (context is PipeContext pipeContext)
{
var result = pipeContext.TryGetPayload(out ConsumeContext<Fault> payload2);
var serCont = context.SerializerContext;
}
Console.WriteLine($"A message faulted:{context.Message.FaultedMessageId} " +
$"{context.Message.Exceptions} " +
$"{context.ConversationId}"
);
return Task.CompletedTask;
}
I can see the full details I want in the context.SerializerContext._message but this is unaccessable.
context.SerializerContext._message
I saw you comment for a similar question:
If you did want to later get the actual fault message, you could use
consumeContext.TryGetMessage<Fault>(out var faultContext) and if it
was present you'd get it back.
I don't have "T" from every service and therefore want to handle all Faults a JSON.
Is there a way I can capture the full Fault with the message, ideally as json, without having access to every T across my system?
I am on MassTransit 8.
Thanks
If you have the message type (T), you can use TryGetMessage<Fault<T>> and it will return the message type deserialized.
If you don't, or if you want to deal with the JSON in a message directly, using V8 you can get the actual JsonElement from the deserializer and navigate the JSON yourself:
var jsonElement = context.TryGetMessage<JsonElement>()
Previous answer, but for Newtonsoft: https://stackoverflow.com/a/46779547/1882

How to configure Spring's Rabbit Template to throw exception on timeout

I've been looking on google for a bit by now and I can't find a solution to my problem.
The problem is the default behavior of RabbitTemplate's methods, namely convertSendAndReceive() and convertSendAndReceiveAsType().
When you invoke these methods and they are not processed and replied to (default direct reply-to with queue=amq.rabbitmq.reply-to) the RabbitTemplate simply returns null response instead of indicating that message was not replied to.
That is pretty important when you send almost empty body on queue and expect to receive like user's books or something similar, with null response you can't tell if user has no books or if message wasn't processed in time.
Example invocation
final List<String> messages = rabbitTemplate.convertSendAndReceiveAsType("getMessagesQueue", 0, new ParameterizedTypeReference<>() {});
I found a workaround for this - using AsyncRabbitTemplate as it's RabbitConverterFuture throws exception on method .get(timeout), but that's not my go-to. I don't want to have to use AsyncRabbitTemplate just to get notified on unprocessed message.
Example
final AsyncRabbitTemplate.RabbitConverterFuture<List<String>> messages = asyncRabbitTemplate.convertSendAndReceiveAsType("getMessagesQueue", 0, new ParameterizedTypeReference<>() {});
try {
messages.get(5000, TimeUnit.MILLISECONDS);
} catch (InterruptedException | ExecutionException | TimeoutException e) {
// message not processed
}
My problem is how to configure RabbitTemplate (configure template itself, not wrap template calls with aspects, decorator, proxy or similar) to actually throw some exception instead of returning null values.
There is currently no such feature; feel free to open a new feature request on GitHub. https://github.com/spring-projects/spring-amqp/issues

NotSupportedException when adding entity via odata on windows phone

I'm using the odata client generator (DataSvcUtil.exe) in a Windows Phone 7 application. Retrieving entities and collections is fine as is updating an existing entity. But when I try to add a new entity, I get a NotSupportedException. Here's my code.
private void Button_Click(object sender, RoutedEventArgs e)
{
Drinks d =new Drink();
d.BarCode = "1234567890";
d.Description = "Test Drink";
d.Quantity = -1;
context.AddToDrinks(d);
context.BeginSaveChanges(SaveChangesOptions.Batch, OnChangesSaved, context);
}
private void OnChangesSaved(IAsyncResult result)
{
Dispatcher.BeginInvoke(() =>
{
try
{
var something = result.AsyncState;
context = result.AsyncState as DrinkTrackerModelContainer;
// Complete the save changes operation and display the response.
ShowSaveResponse("Drink Logged!", context.EndSaveChanges(result));
}
catch (DataServiceRequestException ex)
{
ShowSaveResponse("Error Logging Drink", ex.Response);
}
catch (InvalidOperationException ex)
{
ShowSaveResponse(ex.Message, null);
}
}
);
}
As soon as EndSaveChanges is called, I get the
NotSupportedException.
EDIT: I used fiddler and saw that I was in fact getting a different exception from the service. That exception data was not being shown in the debugger. Once I corrected the actual exception, the insert worked fine.
I think you have first chance exceptions turned on which is causing an internal exception thrown by the client library to surface as an exception. Try turning off First Chance Exceptions in the "Exceptions" menu in VS and running the app.
As you mentioned in your edit, the NotSupportedException was a red herring. I think that when debugging a phone app you will hit the NotSupportedException even if you have cleared the setting to break on unhandled CLR exceptions.
If you continue(F5), you'll hit an actual DataServiceRequestException exception. If it doesn't have enough information to debug it, you can follow the steps in this post to get more detailed information in the exception: http://blogs.msdn.com/b/phaniraj/archive/2008/06/18/debugging-ado-net-data-services.aspx
I ran into the same problem yesterday, and after following the steps in the blog I was able to successfully debug the problem.

Windows Azure: Error 300 Ambiguous Redirect when creating a blob container

I followed a tutorial on creating a blob on windows azure. But when I do that, I get an exception error:
Error while creating containerThe server encountered an unknown failure: The remote server returned an error: (300) Ambiguous Redirect.
The code is:
private void SetContainersAndPermission()
{
try
{
// create a container
var CloudAccountStorage = CloudStorageAccount.FromConfigurationSetting("BlobConnectionString");
cloudBlobClient = CloudAccountStorage.CreateCloudBlobClient();
CloudBlobContainer blobContainer = cloudBlobClient.GetContainerReference("documents");
blobContainer.CreateIfNotExist();
// permissions
var containerPermissions = blobContainer.GetPermissions();
containerPermissions.PublicAccess = BlobContainerPublicAccessType.Container;
blobContainer.SetPermissions(containerPermissions);
}
catch(Exception ex)
{
throw new Exception("Error while creating container" + ex.Message);
}
}
Can anyone tell me How to solve this problem....
I would guess the connection string is somehow wrong? Can you share the connection string? (X out your shared key...)
You could also install Fiddler (debugging HTTP proxy) and see what the HTTP request looks like. That may make the issue more obvious.
I also faced the same issue. I am not sure if this is the workaround for it. I modified the container name value in ServiceConfiguration.csfg from "Photograph" to "photograph" and it worked.
I think you can not give upper case letters in queue, table or blob name. The name should have only lower case characters.

Resources