Introduce a contextmanger function to handle exceptions and nested
transactions. Using the nested_transaction will start a
nested transaction with `db.session.begin_nested`, once the nested
transaction is complete the commit will happen.
`@transactional` has been updated to commit unless in a nested
transaction.
db update/insert.
Using a savepoint for the multiple transactions allows us to rollback if
there is an error when executing the second db transaction.
However, this does add a bit of complexity. Developers need to manage
the db session when calling multiple nested tranactions.
Unit tests have been added to test this functionality and some end to
end tests have been done to make sure all transactions are rollback if
there is an exception while executing the transaction.
we don't need it here - as exceptions are re-raised, they will be logged
additionally by error handlers further up. All this exception logger
tells us is that service names are already in use, which isn't something
we're really interested in.
The behaviour of stacking the version decorators does not work as
expected.
What you would expect to happen is that each decorator causes a history
row to be written for its respective model object.
What actually happens is that the first decorator adds history records
to the database session, but then causes the database session to commit.
This means that subsequent uses of this decorator find a clean session,
and therefore no changes to copy to their respective history tables.
This commit changes the intended use of the decorator so that it is only
used once per function, and accepts multiple definitions of what to
record history for. This way it can record everything that needs to go
into the history before doing anything that would risk flushing the
session.
This is fiendishly difficult error to discover on your own.
It’s caused when, during the creation of a row in the database, you run
a query on the same table, or a table that joins to the table you’re
inserting into. What I think is happening is that the database is forced
to flush the session before running the query in order to maintain
consistency.
This means that the session is clean by the time the history stuff comes
to do its work, so there’s nothing for it to copy into the history
table, and it silently fails to record history.
Hopefully raising an exception will:
- prevent this from failing silently
- save whoever comes across this issue in the future a whole load of
time
this means that any errors will cause the entire thing to roll back
unfortunately, to do this we have to circumvent our regular code, which calls commit a lot, and lazily loads a lot of things, which will flush, and cause the version decorators to fail. so we have to write a lot of stuff by hand and re-select the service (even though it's already been queried) just to populate the api_keys and templates relationship on it
* changed POST to PUT - we are modifiying an already present resource
* improved error handling on PUT
- return 400 if bad
- rollback the delete of the previous whitelist on error
* return 204 if PUT succeeds ( NO CONTENT )
history-meta's dynamic magic is insufficient for templates, where we
need to be able to refer to the specific history table to take
advantage of sqlalchemy's relationship management (2/3rds of an ORM).
So replace it with a custom made version table.
Had to change the version decorator slightly for this
revoking an api key the service it associated with was of course added
to db.session.dirty.
That resulted in an updated version of service being added to the
service history table that showed no visible difference from that
record immediately precending it as the change was to another table,
namely the api_key table. A new api key or revoked api key was correctly
added to api_key and api_key_history tables. However I think an
'unchanged' service history record may be a bit confusing as you'd need
to correlate with api_keys to work out what the change was.
I think it's best to just record the new/revoked api_key and not create
another version of the service.
This pr wraps the exisiting versioned decorator with one that take a
class which you are interested in versioning.
Using the new decorator you only get a new version and history record
for the class you pass to outer decorator.
If the exising behaviour is acceptable to the powers that be then by all
means ignore/close this pr.
adapted to recording inserts and updates.
This removes need to manually create history tables.
Our code still remains in control of when history records are
created.