From: Jess Robinson Date: Thu, 17 Jan 2008 15:42:33 +0000 (+0000) Subject: Initial commit X-Git-Tag: v0.900201~97 X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=ec16e73a8dc1429581541a031a3bd95e0f9a3009;hp=52558dc45676830cfaf5a902b069caf45fa72541;p=dbsrgits%2FDBIx-Class-Journal.git Initial commit --- diff --git a/Changes b/Changes index b257781..6bada66 100644 --- a/Changes +++ b/Changes @@ -1,5 +1,5 @@ -0.01 2007-05-18: +0.01 2007-08-11: Initial version diff --git a/MANIFEST b/MANIFEST index d6e2233..0fa6f5f 100644 --- a/MANIFEST +++ b/MANIFEST @@ -4,7 +4,6 @@ lib/DBIx/Class/Schema/Journal.pm lib/DBIx/Class/Schema/Journal/DB.pm lib/DBIx/Class/Schema/Journal/DB/AuditHistory.pm lib/DBIx/Class/Schema/Journal/DB/AuditLog.pm -lib/DBIx/Class/Schema/Journal/DB/AuduitLog.pm lib/DBIx/Class/Schema/Journal/DB/Base.pm lib/DBIx/Class/Schema/Journal/DB/Change.pm lib/DBIx/Class/Schema/Journal/DB/ChangeSet.pm @@ -16,3 +15,4 @@ t/lib/DBICTest/Schema.pm t/lib/DBICTest/Schema/Artist.pm t/lib/DBICTest/Schema/CD.pm t/lib/DBICTest/Schema/Track.pm +META.yml Module meta-data (added by MakeMaker) diff --git a/lib/DBIx/Class/Journal.pm b/lib/DBIx/Class/Journal.pm index 19304d5..1ce2940 100644 --- a/lib/DBIx/Class/Journal.pm +++ b/lib/DBIx/Class/Journal.pm @@ -2,6 +2,11 @@ package DBIx::Class::Journal; use base qw/DBIx::Class/; +use strict; +use warnings; + +our $VERSION = '0.01'; + ## On create/insert, add new entry to AuditLog # sub new @@ -92,6 +97,197 @@ sub update $self->next::method($upd, @rest); } +=head1 NAME + +DBIx::Class::Journal - auditing for tables managed by DBIx::Class + +=head1 SYNOPSIS + + package My::Schema; + use base 'DBIx::Class::Schema'; + + __PACKAGE__->load_components(qw/+DBIx::Class::Schema::Journal/); + + __PACKAGE__->journal_connection(['dbi:SQLite:t/var/Audit.db']); + __PACKAGE__->journal_user(['My::Schema::User', {'foreign.userid' => 'self.user_id'}]); + + + ######## + + $schema->changeset_user($user->id); + my $new_artist = $schema->txn_do( sub { + return = $schema->resultset('Artist')->create({ name => 'Fred' }); + }); + + +=head1 DESCRIPTION + +The purpose of this L component module is to create an +audit-trail for all changes made to the data in your database (via a +DBIx::Class schema). It creates changesets and assigns each +create/update/delete operation an id. The creation and deletion date +of each row is stored, as well as the previous contents of any row +that gets changed. + +All queries which want auditing should be called using +L, which is used to create changesets for +each transaction. + +To track who did which changes, the user_id (an integer) of the +current user can be set, a session_id can also be set, both are +optional. + +To access the auditing schema to look at the auditdata or revert a +change, use C<< $schema->_journal_schema >>. + +=head2 TABLES + +The journal schema contains a number of tables. + +=over + +=item ChangeSet + +Each changeset row has an auto-incremented ID, optional user_id and +session_id, and a set_date which defaults to the current datetime. + +A ChangeSet has_many Changes. + +=item Change + +Each change/operation done in the transaction is recorded as a row in +the Change table. It contains an auto-incrementing ID, the +changeset_id and an order column for the ordering of each change in +the changeset. + +=item AuditLog + +For every table in the original database that is to be audited, an +AuditLog table is created. Each auditlog row has an id which will +contain the primary key of the table it is associated with. (NB: +currently only supports integer-based single column PKs). The +create_id and delete_id fields contain the IDs of the Changes that +created or deleted this row. + +=item AuditHistory + +For every table in the original database to be audited, an +AuditHistory table is created. Each row has a change_id field +containing the ID of the Change row. The other fields correspond to +all the fields from the original table. Each time a column value in +the original table is changed, the entire row contents before the +change are added as a new row in this table. + +=back + +=head2 METHODS + +=over + +=item journal_connection + +=item Arguments: \@connect_info + +=back + +Set the connection information for the database to save your audit +information to. Leaving this blank assumes you want to store the audit +data into your current database. + +=over + +=item journal_sources + +=item Arguments: \@source_names + +=back + +Set a list of source names you would like to audit, if unset, all +sources are used. + +NOTE: Currently only sources with a single-column PK are supported, so +use this method if you have sources with multi-column PKs. + +=over + +=item journal_storage_type + +=item Arguments: $storage_type + +=back + +Enter the special storage type of your journal schema if needed. See +L for more information on storage types. + +=over + +=item journal_user + +=item Arguments: \@relation_args + +=back + +The user_id column in the L will be linked to your user id +with a belongs_to relation, if this is set with the appropriate +arguments. + +=over + +=item changeset_user + +=item Arguments: $user_id + +=back + +Set the user_id for the following changeset(s). This must be an integer. + +=over + +=item changeset_session + +=item Arguments: $user_id + +=back + +Set the session_id for the following changeset(s). This must be an integer. + +=over + +=item txn_do + +=iitem Arguments: $code_ref + +=back + +Overloaded L, this must be used to start a +new changeset to cover a group of changes. Each subsequent change to +an audited table will use the changeset_id created in the most recent +txn_do call. + +=head1 SEE ALSO + +L - You'll need it to use this. + +=head1 NOTES + +Only single-column integer primary key'd tables are supported for auditing so far. + +Updates made via L are not yet supported. + +No API for viewing or restoring changes yet. + +Patches for the above welcome ;) + +=head1 AUTHOR + +Jess Robinson + +Matt S. Trout (ideas and prodding) + +=head1 LICENCE + +You may distribute this code under the same terms as Perl itself. +=cut 1; diff --git a/lib/DBIx/Class/Schema/Journal.pm b/lib/DBIx/Class/Schema/Journal.pm index daeb8c8..f4179c4 100644 --- a/lib/DBIx/Class/Schema/Journal.pm +++ b/lib/DBIx/Class/Schema/Journal.pm @@ -9,21 +9,24 @@ __PACKAGE__->mk_classdata('journal_storage_type'); __PACKAGE__->mk_classdata('journal_connection'); __PACKAGE__->mk_classdata('journal_sources'); ## [ source names ] __PACKAGE__->mk_classdata('journal_user'); ## [ class, field for user id ] -__PACKAGE__->mk_classdata('_journal_schema'); +__PACKAGE__->mk_classdata('_journal_schema'); ## schema object for journal our $VERSION = '0.01'; -sub throw_exception -{ -} +use strict; +use warnings; -sub exception_action -{ - my $self = shift; -# print STDERR Carp::longmess; +# sub throw_exception +# { +# } + +# sub exception_action +# { +# my $self = shift; +# # print STDERR Carp::longmess; - $self->next::method(@_); -} +# $self->next::method(@_); +# } # sub load_classes # { @@ -130,8 +133,24 @@ sub txn_do my $cs = $self->_journal_schema->resultset('ChangeSet'); $self->txn_begin; - my %changesetdata = ( $self->_journal_schema->current_user() ? ( 'user_id', $self->_journal_schema->current_user()) : () ), - ( $self->_journal_schema->current_session() ? ( session_id => $self->_journal_schema->current_session() ) : () ); + my %changesetdata; + if( defined $self->_journal_schema->current_user() ) + { + $changesetdata{user_id} = $self->_journal_schema->current_user(); + } + if( defined $self->_journal_schema->current_session() ) + { + $changesetdata{session_id} = $self->_journal_schema->current_session(); + } + +# ( +# $self->_journal_schema->current_user() +# ? ( user_id => $self->_journal_schema->current_user()) +# : (), +# $self->_journal_schema->current_session() +# ? ( session_id => $self->_journal_schema->current_session() ) +# : () +# ); if(!%changesetdata) { %changesetdata = ( ID => undef ); @@ -142,4 +161,23 @@ sub txn_do $self->next::method($code); } +sub changeset_user +{ + my ($self, $userid) = @_; + + return $self->_journal_schema->current_user() if(@_ == 1); + + $self->_journal_schema->current_user($userid); +} + +sub changeset_session +{ + my ($self, $sessionid) = @_; + + return $self->_journal_schema->current_session() if(@_ == 1); + + $self->_journal_schema->current_session($sessionid); +} + + 1; diff --git a/t/01test.t b/t/01test.t index c8d997e..7b5b4f1 100644 --- a/t/01test.t +++ b/t/01test.t @@ -10,7 +10,7 @@ BEGIN { eval "use DBD::SQLite"; plan $@ ? ( skip_all => 'needs DBD::SQLite for testing' ) - : ( tests => 9 ); + : ( tests => 12 ); } my $schema = DBICTest->init_schema(no_populate => 1); @@ -60,3 +60,29 @@ $schema->txn_do( sub { my $alentry = $search->find({ ID => $new_cd->get_column($new_cd->primary_columns) }); ok(defined($alentry->deleted), 'Deleted set in audit_log'); +$schema->changeset_user(1); +$schema->txn_do( sub { + $schema->resultset('CD')->create({ + title => 'Something 2', + artist => $artist, + year => 1999, + }); +} ); + +ok($search->count > 1, 'Created an second entry in the CD audit history'); + +my $cset = $schema->_journal_schema->resultset('ChangeSet')->find(5); +is($cset->user_id, 1, 'Set user id for 5th changeset'); + +$schema->changeset_session(1); +$schema->txn_do( sub { + $schema->resultset('CD')->create({ + title => 'Something 3', + artist => $artist, + year => 1999, + }); +} ); + +my $cset2 = $schema->_journal_schema->resultset('ChangeSet')->find(6); +is($cset2->session_id, 1, 'Set session id for 6th changeset'); +