Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

invoice.save returns false but invoice.errors is empty #538

Open
virajvchavan opened this issue Jul 20, 2021 · 15 comments
Open

invoice.save returns false but invoice.errors is empty #538

virajvchavan opened this issue Jul 20, 2021 · 15 comments

Comments

@virajvchavan
Copy link

virajvchavan commented Jul 20, 2021

invoice.save returns false but invoice.errors is empty

invoice object is built with some params

invoice = xero.Invoice.build(...)
invoice.valid? # returns true
invoice.save # returns false
invoice.errors # returns empty array []
invoice.save! # raises Xeroizer::ApiException

How do I know what caused invoice.save to return false?

@virajvchavan
Copy link
Author

Update: found the specific error by adding a logger and seeing the actual HTTP response. The item_code I sent for the line_items was incorrect.
Such obvious errors should be handled/shown in the code itself instead of raising a generic Xeroizer::ApiException exception.

@toxaq
Copy link

toxaq commented Jul 26, 2021

I have the same problem. Upgrading to 3.00 has broken validation errors on all models.

@toxaq
Copy link

toxaq commented Jul 26, 2021

It seems in version 2.18 the save! (bang) method was introduced. Previously validation errors would throw an exception. There is no code, that I could find, that will populate the errors from the validation errors in the XML response.

If you use the bang method (save!) then you can catch the original validation exception and handle this yourself.

@rjaus
Copy link
Collaborator

rjaus commented Sep 23, 2021

I'm not seeing this behaviour.

3.0.0 :034 > invoice = client.Invoice.build
 => #<Xeroizer::Record::Invoice > 
3.0.0 :035 > invoice.valid?
 => false 
3.0.0 :036 > invoice.save
 => false 
3.0.0 :037 > invoice.errors
 => [[:date, "can't be blank"], [:type, "not one of ACCPAY, ACCREC"], [:contact, "must be valid"]] 
3.0.0 :038 > invoice.save!
Traceback (most recent call last):
        5: from /Users/riley.james/.rvm/rubies/ruby-3.0.0/bin/irb:23:in `<main>'
        4: from /Users/riley.james/.rvm/rubies/ruby-3.0.0/bin/irb:23:in `load'
        3: from /Users/riley.james/.rvm/rubies/ruby-3.0.0/lib/ruby/gems/3.0.0/gems/irb-1.3.0/exe/irb:11:in `<top (required)>'
        2: from (irb):38:in `<main>'
        1: from /Users/riley.james/Code/xeroizer/lib/xeroizer/record/base.rb:116:in `save!'
Xeroizer::RecordInvalid (Xeroizer::RecordInvalid)

Screen Shot 2021-09-23 at 12 05 31 PM

Did someone fix it?

Feel free to re-open if this issue persists, but I'm unable to reproduce based on the above details provided.

@rjaus rjaus closed this as completed Sep 23, 2021
@toxaq
Copy link

toxaq commented Sep 23, 2021

Those are all local validation errors. From memory, this was about errors returned from the API not been populated anywhere. So if the contact already exists for example, the response is just captured and disregarded.

@rjaus
Copy link
Collaborator

rjaus commented Sep 23, 2021

Right, thanks for details.

Yes, I've also noticed that OAuth related errors such as token invalid / expired, return false, but it's unclear unless you're catching those errors from save!

@rjaus rjaus reopened this Sep 23, 2021
@toxaq
Copy link

toxaq commented Sep 23, 2021

Yes, our original logic utilised the nature of save throwing errors so it was simple for us to switch to the bang method (after the 2.18 change) to get back to where we were. Ideally though the save method would do as OP expected.

@rjaus
Copy link
Collaborator

rjaus commented Sep 23, 2021

Ok, seems like a relatively easy fix. None of the error classes were included in the OAuth2 class.

I'll get a PR up shortly, @toxaq I'll ping you to review, be good to have some eyes on it as I'm a bit dubious on the test coverage.

edit: That's not it. Investigating further

@rjaus
Copy link
Collaborator

rjaus commented Sep 23, 2021

Looks like things changed when this commit came in:
3f7f173
4a181bc

But it's not super clear to me where to take it to improve.

@toxaq
Copy link

toxaq commented Sep 23, 2021

I would probably start around here:

rescue XeroizerError => e

The exceptions are still being raised, as per the pre-2.18 functionality, they're just captured and dumped.

@toxaq
Copy link

toxaq commented Sep 23, 2021

Could be as simple as

self.errors[:base] << e.message

There's nothing else available error wise.

This is where they are extracted from the response

@parsed_xml.xpath("//ValidationError").each do |err|

@joseairosa
Copy link

Having the same issue here, any updates?

@timdiggins
Copy link
Contributor

@rjaus I'm trying to work on a PR for this, but struggling to run individual tests. I'll make a separate issue for this (I don't normally use minitest, so could be a dumb error).

timdiggins added a commit to timdiggins/xeroizer that referenced this issue Oct 19, 2021
This does a very basic fix to waynerobinson#538 by parsing the thrown
ApiException.

This won't help if an ApiException isn't thrown (unexpected error).
It also feels rather ugly having to catch and re-raise, but any other
change would be a very significant refactor.
@timdiggins
Copy link
Contributor

I've made a pretty crappy little PR for this - it does tick the basic box of providing an errors object, but it's not super nice.

I feel like the errors object (it's an array of arrays) could be abstracted and encapsulated (this is a pre-existing issue) so you don't have to know its structure to add an error and extract the messages. Could even go as far as adapting an ActiveModel::Errors approach.

I'm not too happy with using the Exceptions as the mechanism for communicating the validation errors between the http layer and the record layer. However I don't have a clear vision how to change this.

@timdiggins
Copy link
Contributor

Yes, I've also noticed that OAuth related errors such as token invalid / expired, return false, but it's unclear unless you're catching those errors from save!

I think that these two errors should never be caught by save (they aren't validation errors, they are failures) and should be passed on to the library user more or less as is to handle. However this is probably a separate issue (though might be addressed by:

Perhaps the way to do this is to only allow .save to swallow the error (and return false) if there is a meaningful ValidationError to pass on, otherwise it should raise the (lower-level) error (including the response body). This is a bit of a weird contract for the .save method, but seems the most useful to me as a consumer of this library.

(see #551 (comment))

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants