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