release 0.900001_05
[dbsrgits/DBIx-Class-Journal.git] / lib / DBIx / Class / Journal.pm
CommitLineData
f0f14c64 1package DBIx::Class::Journal;
2
3use base qw/DBIx::Class/;
f0f14c64 4
ec16e73a 5use strict;
6use warnings;
7
4bd0bd90 8our $VERSION = '0.900001_05';
22f043c8 9$VERSION = eval $VERSION; # no errors in dev versions
ec16e73a 10
f2188d3f 11## On create/insert, add new entry to AuditLog and new content to AuditHistory
74f04ccc 12
7adb876c 13sub _journal_schema {
14 my $self = shift;
15 $self->result_source->schema->_journal_schema;
6bfb7a1d 16}
17
548cc9f7 18sub insert {
6bfb7a1d 19 my ($self, @args) = @_;
548cc9f7 20 return if $self->in_storage;
6bfb7a1d 21
22 my $res = $self->next::method(@args);
548cc9f7 23 $self->journal_log_insert;
6bfb7a1d 24
c5fba518 25 return $res;
74f04ccc 26}
27
548cc9f7 28sub journal_log_insert {
6bfb7a1d 29 my ($self) = @_;
30
7adb876c 31 if ( $self->in_storage ) {
32 my $j = $self->_journal_schema;
33 my $change_id = $j->journal_create_change()->id;
34 $j->journal_update_or_create_log_entry( $self, create_id => $change_id );
35 $j->journal_record_in_history( $self, audit_change_id => $change_id );
36 }
6bfb7a1d 37}
38
74f04ccc 39## On delete, update delete_id of AuditLog
40
548cc9f7 41sub delete {
42 my $self = shift;
43 $self->next::method(@_);
44 $self->journal_log_delete(@_);
6bfb7a1d 45}
1e996809 46
548cc9f7 47sub journal_log_delete {
6bfb7a1d 48 my ($self) = @_;
49
7adb876c 50 unless ($self->in_storage) {
51 my $j = $self->_journal_schema;
52 $j->journal_update_or_create_log_entry( $self, delete_id => $j->journal_create_change->id );
53 }
1e996809 54}
55
f2188d3f 56## On update, copy row's new contents to AuditHistory
74f04ccc 57
548cc9f7 58sub update {
59 my $self = shift;
548cc9f7 60 $self->next::method(@_);
f2188d3f 61 $self->journal_log_update(@_);
6bfb7a1d 62}
63
548cc9f7 64sub journal_log_update {
65 my $self = shift;
1e996809 66
3ef10e31 67 if ($self->in_storage) {
7adb876c 68 my $j = $self->_journal_schema;
e0a0e192 69 my $change_id = $j->journal_create_change->id;
3ef10e31 70 $j->journal_record_in_history( $self, audit_change_id => $change_id );
1e996809 71 }
1e996809 72}
73
ec16e73a 74=head1 NAME
75
86e977a3 76DBIx::Class::Journal - Auditing for tables managed by DBIx::Class
ec16e73a 77
78=head1 SYNOPSIS
79
172e6afe 80Load the module into your L<DBIx::Class> Schema Class:
81
22f043c8 82 package My::Schema;
83 use base 'DBIx::Class::Schema';
ec16e73a 84
22f043c8 85 __PACKAGE__->load_components(qw/Schema::Journal/);
ec16e73a 86
ac1354cf 87And then call C<< $schema->bootstrap_journal >> (I<once only>) to create all
88the tables necessary for the journal, in your database.
86e977a3 89
b468e28b 90Optionally set where the journal is stored:
86e977a3 91
22f043c8 92 __PACKAGE__->journal_connection(['dbi:SQLite:t/var/Audit.db']);
ec16e73a 93
b468e28b 94Later on, in your application, wrap operations in transactions, and optionally
95associate a user with the changeset:
ec16e73a 96
22f043c8 97 $schema->changeset_user($user->id);
98 my $new_artist = $schema->txn_do( sub {
99 return $schema->resultset('Artist')->create({ name => 'Fred' });
100 });
ec16e73a 101
ec16e73a 102=head1 DESCRIPTION
103
104The purpose of this L<DBIx::Class> component module is to create an
105audit-trail for all changes made to the data in your database (via a
b468e28b 106DBIx::Class schema). It creates I<changesets> and assigns each
107create/update/delete operation an I<id>. The creation and deletion date of
108each row is stored, as well as the historical contents of any row that gets
109changed.
ec16e73a 110
172e6afe 111All queries which need auditing B<must> be called using
b468e28b 112L<DBIx::Class::Schema/txn_do>, which is used to create changesets for each
113transaction.
ec16e73a 114
b468e28b 115To track who did which changes, the C<user_id> (an integer) of the current
172e6afe 116user can be set, and a C<session_id> can also be set; both are optional. To
117access the auditing schema to look at the auditdata or revert a change, use
b468e28b 118C<< $schema->_journal_schema >>.
ec16e73a 119
b468e28b 120=head1 DEPLOYMENT
ec16e73a 121
b468e28b 122Currently the module expects to be deployed alongside a new database schema,
172e6afe 123and track all changes from first entry. To do that you need to create some
124tables in which to store the journal, and you can opitonally configure which
125data sources (tables) have their operations journalled by the module.
ec16e73a 126
b468e28b 127Connect to your schema and deploy the journal tables as below. The module
128automatically scans your schema and sets up storage for journal entries.
ec16e73a 129
172e6afe 130 # optional - defaults to all sources
b468e28b 131 My::Schema->journal_sources([qw/ table1 table2 /]);
4bd0bd90 132
b468e28b 133 $schema = My::Schema->connect(...);
134 $schema->journal_schema_deploy;
ec16e73a 135
b468e28b 136Note that if you are retrofitting journalling to an existing database, then as
137well as creating the journal you will need to populate it with a history so
138that when rows are deleted they can be mapped back to a (fake) creation.
ec16e73a 139
172e6afe 140If you ever update your original schema, remember that you must then also
141update the journal's schema to match, so that the AuditHistory has the
142corresponding new columns in which to save data.
143
b468e28b 144=head1 TABLES
145
146The journal schema contains a number of tables. These track row creation,
147update and deletion, and also are aware of multiple operations taking place
148within one transaction.
149
150=over 4
151
152=item ChangeSet
153
154Each changeset row has an auto-incremented C<ID>, optional C<user_id> and
155C<session_id>, and a C<set_date> which defaults to the current datetime. This
156is the authoritative log of one discrete change to your database, which may
157possible consist of a number of ChangeLog operations within a single
158transaction.
ec16e73a 159
41daf590 160=item ChangeLog
ec16e73a 161
b468e28b 162Each operation done within the transaction is recorded as a row in the
163ChangeLog table. It contains an auto-incrementing C<ID>, the C<changeset_id>
164and an C<order> column to establish the order in which changes took place.
ec16e73a 165
166=item AuditLog
167
b468e28b 168For every table in the original database that is to be audited, an AuditLog
169table is created. When a row appears in the original database a corresponding
170row is added here with a ChangeLog ID in the C<create_id> column, and when
171that original row is deleted the AuditLog is updated to add another ChangeLog
172ID this time into the C<delete_id> column. A third id column contains the
173primary key of the original row, so you can find it in the AuditHistory.
174
175Note that currently only integer-based single column primary keys are
176supported in your original database tables.
ec16e73a 177
178=item AuditHistory
179
b468e28b 180For every table in the original database to be audited, an AuditHistory table
181is created. This is where the actual field data from your original table rows
182are stored on creation and on each update.
183
184Each row in the AuditHistory has a C<change_id> field containing the ID of the
185ChangeLog row. The other fields correspond to all the fields from the original
186table (with any constraints removed). Each time a column value in the original
187table is changed, the entire row contents after the change are added as a new
188row in this table.
ec16e73a 189
190=back
191
b468e28b 192=head1 CLASS METHODS
193
194Call these in your Schema Class such as the C<My::Schema> package file, as in
195the SYNOPSIS, above.
ec16e73a 196
b468e28b 197=over 4
ec16e73a 198
c1c87879 199=item journal_connection \@connect_info
ec16e73a 200
b468e28b 201Set the connection information for the database to save your audit information
202to.
ec16e73a 203
c1c87879 204Leaving this blank assumes you want to store the audit data into your current
205database. The storage object will be shared by the regular schema and the
206journalling schema.
ec16e73a 207
27e45f70 208=item journal_components @components
209
210If you want to add components to your journal
86e977a3 211(L<DBIx::Class::Schema::Versioned> for example) pass them here.
27e45f70 212
c1c87879 213=item journal_sources \@source_names
ec16e73a 214
b468e28b 215Set a list of source names you would like to audit. If unset, all sources are
216used.
ec16e73a 217
b468e28b 218NOTE: Currently only sources with a single-column integer PK are supported, so
219use this method if you have sources which don't comply with that limitation.
ec16e73a 220
c1c87879 221=item journal_storage_type $type
ec16e73a 222
ec16e73a 223Enter the special storage type of your journal schema if needed. See
224L<DBIx::Class::Storage::DBI> for more information on storage types.
225
c1c87879 226=item journal_user \@rel
ec16e73a 227
b468e28b 228The user_id column in the L</ChangeSet> will be linked to your user id with a
229C<belongs_to> relation, if this is set with the appropriate arguments. For
230example:
231
232 __PACKAGE__->journal_user(['My::Schema::User', {'foreign.userid' => 'self.user_id'}]);
233
234=back
235
236=head1 OBJECT METHODS
237
238Once you have a connection to your database, call these methods to manage the
239journalling.
240
241=over 4
ec16e73a 242
462d8e70 243=item bootstrap_journal
244
245This calls C<journal_schema_deploy> followed by C<prepopulate_journal> to
246create your journal tables and if necessary populate them with a snapshot of
247your current original schema data.
248
ac1354cf 249Do not run this method more than once on your database, as redeploying the
250journal schema is not supported.
251
86e977a3 252=item journal_schema_deploy
253
b468e28b 254Will use L<DBIx::Class::Schema/deploy> to set up the tables for journalling in
255your schema. Use this method to set up your journal.
256
257Note that if you are retrofitting journalling to an existing database, then as
258well as creating the journal you will need to populate it with a history so
259that when rows are deleted they can be mapped back to a (fake) creation.
86e977a3 260
ac1354cf 261Do not run this method more than once on your database, as redeploying the
262journal schema is not supported.
ec16e73a 263
1d09727d 264=item prepopulate_journal
265
266Will load the current state of your original source tables into the audit
267history as fake inserts in a single initial changeset. The advantage to this
268is that later deletetions of the row will be consistent in the journal with an
269initial state.
270
271Note that this can be an intensive and time consuming task, depending on how
272much data you have in your original sources; all of it will be copied to the
273journal history. However this step is essential if you are retrofitting
274Journalling to a schema with existing data, otherwise when you delete a row
275the Journal will die because it cannot relate that to an initial row insert.
276
c1c87879 277=item changeset_user $user_id
ec16e73a 278
b468e28b 279Set the C<user_id> for the following changeset(s). This must be an integer.
ec16e73a 280
c1c87879 281=item changeset_session $session_id
ec16e73a 282
b468e28b 283Set the C<session_id> for the following changeset(s). This must be an integer.
ec16e73a 284
86e977a3 285=item deploy
286
b468e28b 287Overloaded L<DBIx::Class::Schema/deploy> which will deploy your original
86e977a3 288database schema and following that will deploy the journal schema.
289
c1c87879 290=item txn_do $code_ref, @args
ec16e73a 291
b468e28b 292Overloaded L<DBIx::Class::Schema/txn_do>, this must be used to start a new
293ChangeSet to cover a group of changes. Each subsequent change to an audited
294table will use the C<changeset_id> created in the most recent C<txn_do> call.
ec16e73a 295
c1c87879 296Currently nested C<txn_do> calls cause a single ChangeSet object to be created.
297
90dae731 298=back
299
ac1354cf 300=head2 Deprecated Methods
301
302=over 4
303
304=item journal_deploy_on_connect $bool
305
306If set to a true value will cause C<journal_schema_deploy> to be called on
307C<connect>. Not recommended (because re-deploy of a schema is not supported),
308but present for backwards compatibility.
309
310=back
311
312=head1 TROUBLESHOOTING
313
314For PostgreSQL databases you must enable quoting on SQL command generation by
315passing C<< { quote_char => q{`}, name_sep => q{.} } >> when connecting to the
316database.
317
ec16e73a 318=head1 SEE ALSO
319
b468e28b 320=over 4
321
322=item *
323
ec16e73a 324L<DBIx::Class> - You'll need it to use this.
325
b468e28b 326=back
ec16e73a 327
b468e28b 328=head1 LIMITATIONS
329
330=over 4
331
332=item *
333
334Only single-column integer primary key'd tables are supported for auditing.
335
336=item *
ec16e73a 337
338Updates made via L<DBIx::Class::ResultSet/update> are not yet supported.
339
b468e28b 340=item *
341
ec16e73a 342No API for viewing or restoring changes yet.
343
b468e28b 344=back
345
346Patches for the above are welcome ;-)
ec16e73a 347
348=head1 AUTHOR
349
350Jess Robinson <castaway@desert-island.me.uk>
351
352Matt S. Trout <mst@shadowcatsystems.co.uk> (ideas and prodding)
353
354=head1 LICENCE
355
356You may distribute this code under the same terms as Perl itself.
f0f14c64 357
ec16e73a 358=cut
f0f14c64 359
3601;