View | Details | Raw Unified | Return to bug 14698
Collapse All | Expand All

(-)a/C4/Installer/PerlDependencies.pm (+10 lines)
Lines 217-222 our $PERL_DEPS = { Link Here
217
        'min_ver'  => '0.45',
217
        'min_ver'  => '0.45',
218
        # Also needed for our use of PDF::Reuse
218
        # Also needed for our use of PDF::Reuse
219
    },
219
    },
220
    'Data::Format::Pretty::Console' => {
221
        'usage'    => 'Core',
222
        'required' => '1',
223
        'min_ver'  => '0.34',
224
    },
225
    'Git' => {
226
        'usage'    => 'AtomicUpdater',
227
        'required' => '1',
228
        'min_ver'  => '0.41',
229
    },
220
    'DateTime' => {
230
    'DateTime' => {
221
        'usage'    => 'Core',
231
        'usage'    => 'Core',
222
        'required' => '1',
232
        'required' => '1',
(-)a/Koha/AtomicUpdate.pm (+136 lines)
Line 0 Link Here
1
package Koha::AtomicUpdate;
2
3
# Copyright Open Source Freedom Fighters
4
#
5
# This file is part of Koha.
6
#
7
# Koha is free software; you can redistribute it and/or modify it under the
8
# terms of the GNU General Public License as published by the Free Software
9
# Foundation; either version 3 of the License, or (at your option) any later
10
# version.
11
#
12
# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
13
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
14
# A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License along
17
# with Koha; if not, write to the Free Software Foundation, Inc.,
18
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19
20
use Modern::Perl;
21
use Carp;
22
use File::Basename;
23
24
use Koha::Database;
25
26
use base qw(Koha::Object);
27
28
use Koha::Exception::BadParameter;
29
30
sub type {
31
    return 'Atomicupdate';
32
}
33
34
=head @allowedIssueIdentifierPrefixes
35
Define the prefixes you want to attach to your atomicupdate filenames here.
36
This could be a syspref or in KOHA_CONF, but it is rather easy to just add more
37
generally used issue number prefixes here.
38
Nobody wants more sysprefs.
39
=cut
40
41
my @allowedIssueIdentifierPrefixes = (
42
    'Bug',
43
    '#',
44
);
45
46
=head new
47
48
    my $atomicUpdate = Koha::AtomicUpdate->new({filename => 'Bug54321-FixItPlease.pl'});
49
50
Creates a Koha::AtomicUpdate-object from the given parameters-HASH
51
@PARAM1 HASHRef of object parameters:
52
        'filename' => MANDATORY, The filename of the atomicupdate-script without the path-component.
53
        'issue_id' => OPTIONAL, the desired issue_id. It is better to let the module
54
                                find this from the filename, but is useful for testing purposes.
55
@RETURNS Koha::AtomicUpdate-object
56
@THROWS Koha::Exception::Parse from getIssueIdentifier()
57
@THROWS Koha::Exception::File from _validateFilename();
58
=cut
59
60
sub new {
61
    my ($class, $params) = @_;
62
    $class->_validateParams($params);
63
64
    my $self = {};
65
    bless($self, $class);
66
    $self->set($params);
67
    return $self;
68
}
69
70
sub _validateParams {
71
    my ($class, $params) = @_;
72
73
    my @mandatoryParams = ('filename');
74
    foreach my $mp (@mandatoryParams) {
75
        Koha::Exception::BadParameter->throw(
76
            error => "$class->_validateParams():> Param '$mp' must be given.")
77
                unless($params->{$mp});
78
    }
79
    $params->{filename} = $class->_validateFilename($params->{filename});
80
81
    $params->{issue_id} = $class->getIssueIdentifier($params->{issue_id} || $params->{filename});
82
}
83
84
=head _validateFilename
85
86
Makes sure the given file is a valid AtomicUpdate-script.
87
Currently simply checks for naming convention and file suffix.
88
89
NAMING CONVENTION:
90
    Filename must contain one of the unique issue identifier prefixes from this
91
    list @allowedIssueIdentifierPrefixes immediately followed by the numeric
92
    id of the issue, optionally separated by any of the following [ :-]
93
    Eg. Bug-45453, #102, #:53
94
95
@PARAM1 String, filename of validatable file, excluding path.
96
@RETURNS String, the koha.atomicupdates.filename if the given file is considered a well formed update script.
97
                 Removes the full path if present and returns only the filename component.
98
99
@THROWS Koha::Exception::File, if the given file doesn't have a proper naming convention
100
101
=cut
102
103
sub _validateFilename {
104
    my ($self, $fileName) = @_;
105
106
    Koha::Exception::File->throw(error => __PACKAGE__."->_validateFilename():> Filename '$fileName' has unknown suffix")
107
            unless $fileName =~ /\.(sql|perl|pl)$/;  #skip other files
108
    
109
    $fileName = File::Basename::basename($fileName);
110
111
    return $fileName;
112
}
113
114
=head getIssueIdentifier
115
116
Extracts the unique issue identifier from the atomicupdate DB upgrade script.
117
118
@PARAM1 String, filename of validatable file, excluding path, or Git commit title,
119
                or something else to parse.
120
@RETURNS String, The unique issue identifier
121
122
@THROWS Koha::Exception::Parse, if the unique identifier couldn't be parsed.
123
=cut
124
125
sub getIssueIdentifier {
126
    my ($self, $fileName) = @_;
127
128
    foreach my $prefix (@allowedIssueIdentifierPrefixes) {
129
        if ($fileName =~ m/$prefix[-: _]*?(\d+)/i) {
130
            return ucfirst("$prefix$1");
131
        }
132
    }
133
    Koha::Exception::Parse->throw(error => __PACKAGE__."->getIssueIdentifier($fileName):> couldn't parse the unique issue identifier from filename using allowed prefixes '@allowedIssueIdentifierPrefixes'");
134
}
135
136
1;
(-)a/Koha/AtomicUpdater.pm (+349 lines)
Line 0 Link Here
1
package Koha::AtomicUpdater;
2
3
# Copyright Open Source Freedom Fighters
4
#
5
# This file is part of Koha.
6
#
7
# Koha is free software; you can redistribute it and/or modify it under the
8
# terms of the GNU General Public License as published by the Free Software
9
# Foundation; either version 3 of the License, or (at your option) any later
10
# version.
11
#
12
# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
13
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
14
# A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License along
17
# with Koha; if not, write to the Free Software Foundation, Inc.,
18
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19
20
use Modern::Perl;
21
use Carp;
22
use Scalar::Util qw(blessed);
23
use Try::Tiny;
24
use Data::Format::Pretty::Console qw(format_pretty);
25
use Git;
26
27
use Koha::Database;
28
use Koha::Cache;
29
use Koha::AtomicUpdate;
30
31
use base qw(Koha::Objects);
32
33
use Koha::Exception::File;
34
use Koha::Exception::Parse;
35
use Koha::Exception::BadParameter;
36
37
sub type {
38
    return 'Atomicupdate';
39
}
40
41
sub object_class {
42
    return 'Koha::AtomicUpdate';
43
}
44
45
sub _get_castable_unique_columns {
46
    return ['atomicupdate_id'];
47
}
48
49
my $updateOrderFilename = '_updateorder';
50
51
sub new {
52
    my ($class, $params) = @_;
53
54
    my $cache = Koha::Cache->new();
55
    my $self = $cache->get_from_cache('Koha::AtomicUpdater') || {};
56
    bless($self, $class);
57
58
    $self->{verbose} = $params->{verbose} || $self->{verbose} || 0;
59
    $self->{scriptDir} = $params->{scriptDir} || $self->{scriptDir} || C4::Context->config('intranetdir') . '/installer/data/mysql/atomicupdate/';
60
    $self->{gitRepo} = $params->{gitRepo} || $self->{gitRepo} || $ENV{KOHA_PATH};
61
62
    return $self;
63
}
64
65
=head getAtomicUpdates
66
67
    my $atomicUpdates = $atomicUpdater->getAtomicUpdates();
68
69
Gets all the AtomicUpdate-objects in the DB. This result should be Koha::Cached.
70
@RETURNS HASHRef of Koha::AtomicUpdate-objects, keyed with the issue_id
71
=cut
72
73
sub getAtomicUpdates {
74
    my ($self) = @_;
75
76
    my @au = $self->search({});
77
    my %au; #HASHify the AtomicUpdate-objects for easy searching.
78
    foreach my $au (@au) {
79
        $au{$au->issue_id} = $au;
80
    }
81
    return \%au;
82
}
83
84
sub addAtomicUpdate {
85
    my ($self, $params) = @_;
86
    print "Adding atomicupdate '".$params->{issue_id}."'\n" if $self->{verbose} > 2;
87
88
    my $atomicupdate = Koha::AtomicUpdate->new($params);
89
    $atomicupdate->store();
90
    $atomicupdate = $self->find({issue_id => $atomicupdate->issue_id});
91
    return $atomicupdate;
92
}
93
94
sub removeAtomicUpdate {
95
    my ($self, $issueId) = @_;
96
    print "Deleting atomicupdate '$issueId'\n" if $self->{verbose} > 2;
97
98
    my $atomicupdate = $self->find({issue_id => $issueId});
99
    if ($atomicupdate) {
100
        $atomicupdate->delete;
101
        print "Deleted atomicupdate '$issueId'\n" if $self->{verbose} > 2;
102
    }
103
    else {
104
        Koha::Exception::BadParameter->throw(error => __PACKAGE__."->removeIssueFromLog():> No such Issue '$issueId' stored to the atomicupdates-table");
105
    }
106
}
107
108
sub listToConsole {
109
    my ($self) = @_;
110
    my @stringBuilder;
111
112
    my @atomicupdates = $self->search({});
113
    foreach my $au (@atomicupdates) {
114
        push @stringBuilder, $au->unblessed();
115
    }
116
    return Data::Format::Pretty::Console::format_pretty(\@stringBuilder);
117
}
118
119
sub listPendingToConsole {
120
    my ($self) = @_;
121
    my @stringBuilder;
122
123
    my $atomicUpdates = $self->getPendingAtomicUpdates();
124
    foreach my $key (sort keys %$atomicUpdates) {
125
        my $au = $atomicUpdates->{$key};
126
        push @stringBuilder, $au->unblessed();
127
    }
128
    return Data::Format::Pretty::Console::format_pretty(\@stringBuilder);
129
}
130
131
sub getPendingAtomicUpdates {
132
    my ($self) = @_;
133
134
    my %pendingAtomicUpdates;
135
    my $atomicupdateFiles = $self->_getValidAtomicUpdateScripts();
136
    my $atomicUpdatesDeployed = $self->getAtomicUpdates();
137
    foreach my $key (keys(%$atomicupdateFiles)) {
138
        my $au = $atomicupdateFiles->{$key};
139
        unless ($atomicUpdatesDeployed->{$au->issue_id}) {
140
            #This script hasn't been deployed.
141
            $pendingAtomicUpdates{$au->issue_id} = $au;
142
        }
143
    }
144
    return \%pendingAtomicUpdates;
145
}
146
147
=head applyAtomicUpdates
148
149
    my $atomicUpdater = Koha::AtomicUpdater->new();
150
    my $appliedAtomicupdates = $atomicUpdater->applyAtomicUpdates();
151
152
Checks the atomicupdates/-directory for any not-applied update scripts and
153
runs them in the order specified in the _updateorder-file in atomicupdate/-directory.
154
155
@RETURNS ARRAYRef of Koha::AtomicUpdate-objects deployed on this run
156
=cut
157
158
sub applyAtomicUpdates {
159
    my ($self) = @_;
160
161
    my %appliedUpdates;
162
163
    my $atomicUpdates = $self->getPendingAtomicUpdates();
164
    my $updateOrder = $self->getUpdateOrder();
165
    foreach my $issueId ( @$updateOrder ) {
166
        my $atomicUpdate = $atomicUpdates->{$issueId};
167
        next unless $atomicUpdate; #Not each ordered Git commit necessarily have a atomicupdate-script.
168
169
        my $filename = $atomicUpdate->filename;
170
        print "Applying file '$filename'\n" if $self->{verbose} > 2;
171
172
        if ( $filename =~ /\.sql$/ ) {
173
            my $installer = C4::Installer->new();
174
            my $rv = $installer->load_sql( $self->{scriptDir}.'/'.$filename ) ? 0 : 1;
175
        } elsif ( $filename =~ /\.(perl|pl)$/ ) {
176
            do $self->{scriptDir}.'/'.$filename;
177
        }
178
179
        $atomicUpdate->store();
180
        $appliedUpdates{$issueId} = $atomicUpdate;
181
        print "File '$filename' applied\n" if $self->{verbose} > 2;
182
    }
183
184
    #Check that we have actually applied all the updates.
185
    my $stillPendingAtomicUpdates = $self->getPendingAtomicUpdates();
186
    if (scalar(%$stillPendingAtomicUpdates)) {
187
        my @issueIds = sort keys %$stillPendingAtomicUpdates;
188
        print "Warning! After upgrade, the following atomicupdates are still pending '@issueIds'\n Try rebuilding the atomicupdate-scripts update order from the original Git repository.\n";
189
    }
190
191
    return \%appliedUpdates;
192
}
193
194
=head _getValidAtomicUpdateScripts
195
196
@RETURNS HASHRef of Koha::AtomicUpdate-objects, of all the files
197
                in the atomicupdates/-directory that can be considered valid.
198
                Validity is currently conforming to the naming convention.
199
                Keys are the issue_id of atomicupdate-scripts
200
                Eg. {'Bug8584' => Koha::AtomicUpdate,
201
                     ...
202
                    }
203
=cut
204
205
sub _getValidAtomicUpdateScripts {
206
    my ($self) = @_;
207
208
    my %atomicUpdates;
209
    opendir( my $dirh, $self->{scriptDir} );
210
    foreach my $file ( sort readdir $dirh ) {
211
        print "Looking at file $file\n" if $self->{verbose} > 2;
212
213
        my $atomicUpdate;
214
        try {
215
            $atomicUpdate = Koha::AtomicUpdate->new({filename => $file});
216
        } catch {
217
            if (blessed($_)) {
218
                if ($_->isa('Koha::Exception::File')) {
219
                    #We can ignore filename validation issues, since the directory has
220
                    #loads of other types of files as well. Like README . ..
221
                }
222
                else {
223
                    $_->rethrow();
224
                }
225
            }
226
            else {
227
                die $_; #Rethrow the unknown Exception
228
            }
229
        };
230
        next unless $atomicUpdate;
231
232
        $atomicUpdates{$atomicUpdate->issue_id} = $atomicUpdate;
233
    }
234
    return \%atomicUpdates;
235
}
236
237
=head getUpdateOrder
238
239
    $atomicUpdater->getUpdateOrder();
240
241
@RETURNS ARRAYRef of Strings, IssueIds ordered from the earliest to the newest.
242
=cut
243
244
sub getUpdateOrder {
245
    my ($self) = @_;
246
247
    my $updateOrderFilepath = $self->{scriptDir}."/$updateOrderFilename";
248
    open(my $FH, "<:encoding(UTF-8)", $updateOrderFilepath) or die "Koha::AtomicUpdater->_saveAsUpdateOrder():> Couldn't open the updateOrderFile for reading\n$!\n";
249
    my @updateOrder = map {chomp($_); $_;} <$FH>;
250
    close $FH;
251
    return \@updateOrder;
252
}
253
254
=head
255
256
    my $issueIdOrder = Koha::AtomicUpdater->buildUpdateOrderFromGit(10000);
257
258
Creates a update order file '_updateorder' for atomicupdates to know which updates come before which.
259
This is a simple way to make sure the atomicupdates are applied in the correct order.
260
The update order file is by default in your $KOHA_PATH/installer/data/mysql/atomicupdate/_updateorder
261
262
This requires a Git repository to be in the $ENV{KOHA_PATH} to be effective.
263
264
@PARAM1 Integer, How many Git commits to include to the update order file,
265
                 10000 is a good default.
266
@RETURNS ARRAYRef of Strings, The update order of atomicupdates from oldest to newest.
267
=cut
268
269
sub buildUpdateOrderFromGit {
270
    my ($self, $gitCommitsCount) = @_;
271
272
    my %orderedCommits; #Store the commits we have ordered here, so we don't reorder any followups.
273
    my @orderedCommits;
274
275
    my $i = 0; #Index of array where we push issue_ids
276
    my $commits = $self->_getGitCommits($gitCommitsCount);
277
    foreach my $commit (reverse @$commits) {
278
279
        my ($commitHash, $commitTitle) = $self->_parseGitOneliner($commit);
280
        unless ($commitHash && $commitTitle) {
281
            next();
282
        }
283
284
        my $issueId;
285
        try {
286
            $issueId = Koha::AtomicUpdate->getIssueIdentifier($commitTitle);
287
        } catch {
288
            if (blessed($_)) {
289
                if($_->isa('Koha::Exception::Parse')) {
290
                    #Silently ignore parsing errors
291
                    print "Koha::AtomicUpdater->buildUpdateOrderFromGit():> Couldn't parse issue_id from Git commit title '$commitTitle'.\n"
292
                                    if $self->{verbose} > 1;
293
                }
294
                else {
295
                    $_->rethrow();
296
                }
297
            }
298
            else {
299
                die $_;
300
            }
301
        };
302
        next unless $issueId;
303
    
304
        if ($orderedCommits{ $issueId }) {
305
            next();
306
        }
307
        else {
308
            $orderedCommits{ $issueId } = $issueId;
309
            $orderedCommits[$i] = $issueId;
310
            $i++;
311
        }
312
    }
313
314
    $self->_saveAsUpdateOrder(\@orderedCommits);
315
    return \@orderedCommits;
316
}
317
318
sub _getGitCommits {
319
    my ($self, $count) = @_;
320
    my $repo = Git->repository(Directory => $self->{gitRepo});
321
322
    #We can read and print 10000 git commits in less than three seconds :) good Git!
323
    my @commits = $repo->command('show', '--pretty=oneline', '--no-patch', '-'.$count);
324
    return \@commits;
325
}
326
327
sub _parseGitOneliner {
328
    my ($self, $gitLiner) = @_;
329
330
    my ($commitHash, $commitTitle) = ($1, $2) if $gitLiner =~ /^(\w{40}) (.+)$/;
331
    unless ($commitHash && $commitTitle) {
332
        print "Koha::AtomicUpdater->parseGitOneliner():> Couldn't parse Git commit '$gitLiner' to hash and title.\n"
333
                        if $self->{verbose} > 1;
334
        return();
335
    }
336
    return ($commitHash, $commitTitle);
337
}
338
339
sub _saveAsUpdateOrder {
340
    my ($self, $orderedUpdates) = @_;
341
342
    my $updateOrderFilepath = $self->{scriptDir}."/$updateOrderFilename";
343
    my $text = join("\n", @$orderedUpdates);
344
    open(my $FH, ">:encoding(UTF-8)", $updateOrderFilepath) or die "Koha::AtomicUpdater->_saveAsUpdateOrder():> Couldn't open the updateOrderFile for writing\n$!\n";
345
    print $FH $text;
346
    close $FH;
347
}
348
349
1;
(-)a/Koha/Schema/Result/Atomicupdate.pm (+93 lines)
Line 0 Link Here
1
use utf8;
2
package Koha::Schema::Result::Atomicupdate;
3
4
# Created by DBIx::Class::Schema::Loader
5
# DO NOT MODIFY THE FIRST PART OF THIS FILE
6
7
=head1 NAME
8
9
Koha::Schema::Result::Atomicupdate
10
11
=cut
12
13
use strict;
14
use warnings;
15
16
use base 'DBIx::Class::Core';
17
18
=head1 TABLE: C<atomicupdates>
19
20
=cut
21
22
__PACKAGE__->table("atomicupdates");
23
24
=head1 ACCESSORS
25
26
=head2 atomicupdate_id
27
28
  data_type: 'integer'
29
  extra: {unsigned => 1}
30
  is_auto_increment: 1
31
  is_nullable: 0
32
33
=head2 issue_id
34
35
  data_type: 'varchar'
36
  is_nullable: 0
37
  size: 20
38
39
=head2 filename
40
41
  data_type: 'varchar'
42
  is_nullable: 0
43
  size: 30
44
45
=head2 modification_time
46
47
  data_type: 'timestamp'
48
  datetime_undef_if_invalid: 1
49
  default_value: current_timestamp
50
  is_nullable: 0
51
52
=cut
53
54
__PACKAGE__->add_columns(
55
  "atomicupdate_id",
56
  {
57
    data_type => "integer",
58
    extra => { unsigned => 1 },
59
    is_auto_increment => 1,
60
    is_nullable => 0,
61
  },
62
  "issue_id",
63
  { data_type => "varchar", is_nullable => 0, size => 20 },
64
  "filename",
65
  { data_type => "varchar", is_nullable => 0, size => 30 },
66
  "modification_time",
67
  {
68
    data_type => "timestamp",
69
    datetime_undef_if_invalid => 1,
70
    default_value => \"current_timestamp",
71
    is_nullable => 0,
72
  },
73
);
74
75
=head1 PRIMARY KEY
76
77
=over 4
78
79
=item * L</atomicupdate_id>
80
81
=back
82
83
=cut
84
85
__PACKAGE__->set_primary_key("atomicupdate_id");
86
87
88
# Created by DBIx::Class::Schema::Loader v0.07039 @ 2015-08-20 16:04:49
89
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:vECF28CFdwiSozjA4WL7DA
90
91
92
# You can replace this text with custom code or comments, and it will be preserved on regeneration
93
1;
(-)a/installer/data/mysql/atomicupdate.pl (+148 lines)
Line 0 Link Here
1
#!/usr/bin/perl
2
#
3
# Copyright Vaara-kirjastot 2015
4
#
5
# This file is part of Koha.
6
#
7
# Koha is free software; you can redistribute it and/or modify it under the
8
# terms of the GNU General Public License as published by the Free Software
9
# Foundation; either version 3 of the License, or (at your option) any later
10
# version.
11
#
12
# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
13
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
14
# A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License along
17
# with Koha; if not, write to the Free Software Foundation, Inc.,
18
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19
#
20
21
use Modern::Perl;
22
use Getopt::Long;
23
24
use C4::Context;
25
26
use Koha::AtomicUpdater;
27
28
my $verbose = 0;
29
my $help = 0;
30
my $apply = 0;
31
my $remove = '';
32
my $insert = '';
33
my $list = 0;
34
my $pending = 0;
35
my $directory = '';
36
my $git = '';
37
38
GetOptions( 
39
    'v|verbose:i'       => \$verbose,
40
    'h|help'            => \$help,
41
    'a|apply'           => \$apply,
42
    'd|directory:s'     => \$directory,
43
    'r|remove:s'        => \$remove,
44
    'i|insert:s'        => \$insert,
45
    'l|list'            => \$list,
46
    'p|pending'         => \$pending,
47
    'g|git:s'           => \$git,
48
);
49
50
my $usage = << 'ENDUSAGE';
51
52
Runs all the not-yet-applied atomicupdate-scripts and sql in the
53
atomicupdates-directory, in the order specified by the _updateorder-file.
54
55
This script uses koha.atomicupdates-table to see if the update has already been
56
applied.
57
58
Also acts as a gateway to CRUD the koha.database_updates-table.
59
60
    -v --verbose        Integer, 1 is not so verbose, 3 is maximally verbose.
61
    -h --help           Flag, This nice help!
62
    -a --apply          Flag, Apply all the pending atomicupdates from the
63
                        atomicupdates-directory.
64
    -d --directory      Path, From which directory to look for atomicupdate-scripts.
65
                        Defaults to '$KOHA_PATH/installer/data/mysql/atomicupdate/'
66
    -r --remove         String, Remove the upgrade entry from koha.database_updates
67
                        eg. --remove "Bug71337"
68
    -i --insert         Path, Add an upgrade log entry for the given atomicupdate-file.
69
                        Useful to revert an accidental --remove -operation or for
70
                        testing.
71
                        eg. -i installer/data/mysql/atomicupdate/Bug5453-Example.pl
72
    -l --list           Flag, List all entries in the koha.database_updates-table.
73
                        This typically means all applied atomicupdates.
74
    -p --pending        Flag, List all pending atomicupdates from the
75
                        atomicupdates-directory.
76
    -g --git            Path, Build the update order from the Git repository given,
77
                        or default to the Git repository in $KOHA_PATH.
78
                        Eg. --git 1, to build with default values, or
79
                            --git /tmp/kohaclone/ to look for another repository
80
81
EXAMPLES:
82
83
    atomicupdate.pl -g 1 -a
84
85
Looks for the Git repository in $KOHA_PATH, parses the issue/commit identifiers
86
from the top 10000 commits and generates the _updateorder-file to tell in which
87
order the atomicupdates-scripts are executed.
88
Then applies all pending atomicupdate-scripts in the order (oldest to newest)
89
presented in the Git repository.
90
91
92
    atomicupdate --apply -d /home/koha/kohaclone/installer/data/mysql/atomicupdate/
93
94
Applies all pending atomicupdate-scripts from the given directory. If the file
95
'_updateorder' is not present, it must be first generated, for example with the
96
--git 1 argument.
97
98
UPDATEORDER:
99
100
When deploying more than one atomicupdate, it is imperative to know in which order
101
the updates are applied. Atomicupdates can easily depend on each other and fail in
102
very strange and hard-to-debug -ways if the prerequisite modifications are not
103
in effect.
104
The correct update order is defined in the atomicupdates/_updateorder-file. This is
105
a simple list of issue/commit identifiers, eg.
106
107
    Bug5454
108
    Bug12432
109
    Bug3218
110
    #45
111
112
This file is most easily generated directly from the original Git repository, since
113
the order in which the Commits have been introduced most definetely is the order
114
they should be applied.
115
When deploying the atomicupdates to production environments without the
116
Git repository, the _updateorder file must be copied along the atomicupdate-scripts.
117
118
P.S. Remember to put atomicupdate/_updateorder to your .gitignore
119
120
ENDUSAGE
121
 
122
if ( $help ) {
123
    print $usage;
124
    exit;
125
}
126
127
my $atomicupdater = Koha::AtomicUpdater->new({verbose => $verbose,
128
                                              scriptDir => $directory,
129
                                              gitRepo => (length($git) == 1) ? '' : $git});
130
131
if ($git) {
132
    $atomicupdater->buildUpdateOrderFromGit(10000);
133
}
134
if ($remove) {
135
    $atomicupdater->removeAtomicUpdate($remove);
136
}
137
if ($insert) {
138
    $atomicupdater->addAtomicUpdate({filename => $insert});
139
}
140
if ($list) {
141
    print $atomicupdater->listToConsole();
142
}
143
if ($pending) {
144
    print $atomicupdater->listPendingToConsole();
145
}
146
if ($apply) {
147
    $atomicupdater->applyAtomicUpdates();
148
}
(-)a/installer/data/mysql/atomicupdate/Bug14698-AtomicUpdater.pl (+36 lines)
Line 0 Link Here
1
#!/usr/bin/perl
2
3
# Copyright Open Source Freedom Fighters
4
#
5
# This file is part of Koha.
6
#
7
# Koha is free software; you can redistribute it and/or modify it under the
8
# terms of the GNU General Public License as published by the Free Software
9
# Foundation; either version 3 of the License, or (at your option) any later
10
# version.
11
#
12
# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
13
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
14
# A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License along
17
# with Koha; if not, write to the Free Software Foundation, Inc.,
18
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19
20
use C4::Context;
21
22
my $dbh = C4::Context->dbh();
23
24
$dbh->do("
25
CREATE TABLE `atomicupdates` (
26
  `atomicupdate_id` int(11) unsigned NOT NULL auto_increment,
27
  `issue_id` varchar(20) NOT NULL,
28
  `filename` varchar(30) NOT NULL,
29
  `modification_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
30
  PRIMARY KEY  (`atomicupdate_id`),
31
  UNIQUE KEY `origincode` (`issue_id`)
32
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
33
");
34
$dbh->do("INSERT INTO atomicupdates (issue_id, filename) VALUES ('Bug14698', 'Bug14698-AtomicUpdater.pl')");
35
36
print "Upgrade to Bug 14698 - AtomicUpdater - Keeps track of which updates have been applied to a database done\n";
(-)a/installer/data/mysql/kohastructure.sql (+14 lines)
Lines 1784-1789 CREATE TABLE `printers_profile` ( Link Here
1784
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
1784
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
1785
1785
1786
--
1786
--
1787
-- Table structure for table `atomicupdates`
1788
--
1789
1790
DROP TABLE IF EXISTS `atomicupdates`;
1791
CREATE TABLE `atomicupdates` (
1792
  `atomicupdate_id` int(11) unsigned NOT NULL auto_increment,
1793
  `issue_id` varchar(20) NOT NULL,
1794
  `filename` varchar(128) NOT NULL,
1795
  `modification_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
1796
  PRIMARY KEY  (`atomicupdate_id`),
1797
  UNIQUE KEY `atomic_issue_id` (`issue_id`)
1798
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
1799
1800
--
1787
-- Table structure for table `repeatable_holidays`
1801
-- Table structure for table `repeatable_holidays`
1788
--
1802
--
1789
1803
(-)a/t/db_dependent/Koha/AtomicUpdater.t (+274 lines)
Line 0 Link Here
1
#!/usr/bin/perl
2
3
# Copyright 2015 Open Source Freedom Fighters
4
#
5
# This file is part of Koha.
6
#
7
# Koha is free software; you can redistribute it and/or modify it
8
# under the terms of the GNU General Public License as published by
9
# the Free Software Foundation; either version 3 of the License, or
10
# (at your option) any later version.
11
#
12
# Koha is distributed in the hope that it will be useful, but
13
# WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
# GNU General Public License for more details.
16
#
17
# You should have received a copy of the GNU General Public License
18
# along with Koha; if not, see <http://www.gnu.org/licenses>.
19
20
use Modern::Perl;
21
use Test::More;
22
use Try::Tiny;
23
use Encode;
24
25
use t::lib::TestObjects::ObjectFactory;
26
use t::lib::TestObjects::AtomicUpdateFactory;
27
use t::lib::TestObjects::FileFactory;
28
use Koha::AtomicUpdater;
29
30
my $testContext = {};
31
my $atomicupdates = t::lib::TestObjects::AtomicUpdateFactory->createTestGroup([
32
                           {issue_id => 'Bug12',
33
                            filename => 'Bug12-WatchExMachinaYoullLikeIt.pl'},
34
                           {issue_id => 'Bug14',
35
                            filename => 'Bug14-ReturnOfZorro.perl'},
36
                           {issue_id => '#14',
37
                            filename => '#14-RobotronInDanger.sql'},
38
                           {issue_id => '#15',
39
                            filename => '#15-ILikedPrometheusButAlienWasBetter.pl'},
40
                           ], undef, $testContext);
41
42
#Make sure we get the correct update order, otherwise we get unpredictable results.
43
{ #Overload existing subroutines to provide a Mock implementation
44
    no warnings 'redefine';
45
    package Koha::AtomicUpdater;
46
    sub _getGitCommits { #instead of requiring a Git repository, we just mock the input.
47
        return [#Newest commit
48
                '2e8a39762b506738195f21c8ff67e4e7bfe6d7ab #:-55 : Fiftyfive',
49
                '2e8a39762b506738195f21c8ff67e4e7bfe6d7ab #54 - KohaCon in Finland next year',
50
                'b447b595acacb0c4823582acf9d8a08902118e59 #53 - Place to be.pl',
51
                '2e8a39762b506738195f21c8ff67e4e7bfe6d7ab bug 112 - Lapinlahden linnut',
52
                '5ac7101d4071fe11f7a5d1445bb97ed1a603a9b5 Bug:-911 - What are you going to do?',
53
                '1d54601b9cac0bd75ee97e071cf52ed49daef8bd #911 - Who are you going to call',
54
                '1d54601b9cac0bd75ee97e071cf52ed49daef8bd bug 30 - Feature Yes yes',
55
                '5ac7101d4071fe11f7a5d1445bb97ed1a603a9b5 #-29 - Bug squashable',
56
                '2e8a39762b506738195f21c8ff67e4e7bfe6d7ab Bug :- 28 - Feature Squash',
57
                'b447b595acacb0c4823582acf9d8a08902118e59 BUG 27 - Bug help',
58
                #Oldest commit
59
                ];
60
    }
61
}
62
63
subtest "Create update order from Git repository" => \&createUpdateOrderFromGit;
64
sub createUpdateOrderFromGit {
65
    eval {
66
        #Create the _updateorder-file to a temp directory and prepare it for autocleanup.
67
        my $files = t::lib::TestObjects::FileFactory->createTestGroup([
68
                        {   filepath => 'atomicupdate/',
69
                            filename => '_updateorder',
70
                            content  => '',},
71
                        ],
72
                        undef, undef, $testContext);
73
        #Instantiate the AtomicUpdater to operate on a temp directory.
74
        my $atomicUpdater = Koha::AtomicUpdater->new({
75
                                        scriptDir => $files->{'_updateorder'}->dirname(),
76
                            });
77
78
        #Start real testing.
79
        my $issueIds = $atomicUpdater->buildUpdateOrderFromGit(4);
80
81
        is($issueIds->[0],
82
           'Bug27',
83
           "First atomicupdate to deploy");
84
        is($issueIds->[1],
85
           'Bug28',
86
           "Second atomicupdate to deploy");
87
        is($issueIds->[2],
88
           '#29',
89
           "Third atomicupdate to deploy");
90
        is($issueIds->[3],
91
           'Bug30',
92
           "Last atomicupdate to deploy");
93
94
        #Testing file access
95
        $issueIds = $atomicUpdater->getUpdateOrder();
96
        is($issueIds->[0],
97
           'Bug27',
98
           "First atomicupdate to deploy, from _updateorder");
99
        is($issueIds->[1],
100
           'Bug28',
101
           "Second atomicupdate to deploy, from _updateorder");
102
        is($issueIds->[2],
103
           '#29',
104
           "Third atomicupdate to deploy, from _updateorder");
105
        is($issueIds->[3],
106
           'Bug30',
107
           "Last atomicupdate to deploy, from _updateorder");
108
    };
109
    if ($@) {
110
        ok(0, $@);
111
    }
112
}
113
114
115
116
subtest "List all deployed atomicupdates" => \&listAtomicUpdates;
117
sub listAtomicUpdates {
118
    eval {
119
    my $atomicUpdater = Koha::AtomicUpdater->new();
120
    my $text = $atomicUpdater->listToConsole();
121
    print $text;
122
123
    ok($text =~ m/Bug12-WatchExMachinaYoullLik/,
124
       "Bug12-WatchExMachinaYoullLikeIt");
125
    ok($text =~ m/Bug14-ReturnOfZorro.perl/,
126
       "Bug14-ReturnOfZorro");
127
    ok($text =~ m/#14-RobotronInDanger.sql/,
128
       "#14-RobotronInDanger");
129
    ok($text =~ m/#15-ILikedPrometheusButAli/,
130
       "#15-ILikedPrometheusButAlienWasBetter");
131
132
    };
133
    if ($@) {
134
        ok(0, $@);
135
    }
136
}
137
138
subtest "Delete an atomicupdate entry" => \&deleteAtomicupdate;
139
sub deleteAtomicupdate {
140
    eval {
141
    my $atomicUpdater = Koha::AtomicUpdater->new();
142
    my $atomicupdate = $atomicUpdater->cast($atomicupdates->{Bug12}->id);
143
    ok($atomicupdate,
144
       "AtomicUpdate '".$atomicupdates->{Bug12}->issue_id."' exists prior to deletion");
145
146
    $atomicUpdater->removeAtomicUpdate($atomicupdate->issue_id);
147
    $atomicupdate = $atomicUpdater->find($atomicupdates->{Bug12}->id);
148
    ok(not($atomicupdate),
149
       "AtomicUpdate '".$atomicupdates->{Bug12}->issue_id."' deleted");
150
151
    };
152
    if ($@) {
153
        ok(0, $@);
154
    }
155
}
156
157
subtest "Insert an atomicupdate entry" => \&insertAtomicupdate;
158
sub insertAtomicupdate {
159
    eval {
160
    my $atomicUpdater = Koha::AtomicUpdater->new();
161
    my $subtestContext = {};
162
    my $atomicupdates = t::lib::TestObjects::AtomicUpdateFactory->createTestGroup([
163
                           {issue_id => 'Bug15',
164
                            filename => 'Bug15-Inserted.pl'},
165
                           ], undef, $subtestContext, $testContext);
166
    my $atomicupdate = $atomicUpdater->find({issue_id => 'Bug15'});
167
    ok($atomicupdate,
168
       "Bug15-Inserted.pl");
169
170
    t::lib::TestObjects::ObjectFactory->tearDownTestContext($subtestContext);
171
172
    $atomicupdate = $atomicUpdater->find({issue_id => 'Bug15'});
173
    ok(not($atomicupdate),
174
       "Bug15-Inserted.pl deleted");
175
    };
176
    if ($@) {
177
        ok(0, $@);
178
    }
179
}
180
181
subtest "List pending atomicupdates" => \&listPendingAtomicupdates;
182
sub listPendingAtomicupdates {
183
    my ($atomicUpdater, $files, $text, $atomicupdates);
184
    my $subtestContext = {};
185
    eval {
186
    ##Test adding update scripts and deploy them, confirm that no pending scripts detected
187
    $files = t::lib::TestObjects::FileFactory->createTestGroup([
188
                        {   filepath => 'atomicupdate/',
189
                            filename => '#911-WhoYouGonnaCall.pl',
190
                            content  => '$ENV{ATOMICUPDATE_TESTS} = 1;',},
191
                        {   filepath => 'atomicupdate/',
192
                            filename => 'Bug911-WhatchaGonnaDo.pl',
193
                            content  => '$ENV{ATOMICUPDATE_TESTS}++;',},
194
                        {   filepath => 'atomicupdate/',
195
                            filename => 'Bug112-LapinlahdenLinnut.pl',
196
                            content  => '$ENV{ATOMICUPDATE_TESTS}++;',},
197
                        ],
198
                        undef, $subtestContext, $testContext);
199
    $atomicUpdater = Koha::AtomicUpdater->new({
200
                            scriptDir => $files->{'#911-WhoYouGonnaCall.pl'}->dirname()
201
                        });
202
203
    $text = $atomicUpdater->listPendingToConsole();
204
    print $text;
205
206
    ok($text =~ m/#911-WhoYouGonnaCall.pl/,
207
       "#911-WhoYouGonnaCall is pending");
208
    ok($text =~ m/Bug911-WhatchaGonnaDo.pl/,
209
       "Bug911-WhatchaGonnaDo is pending");
210
    ok($text =~ m/Bug112-LapinlahdenLinnut.pl/,
211
       'Bug112-LapinlahdenLinnut is pending');
212
213
    $atomicupdates = $atomicUpdater->applyAtomicUpdates();
214
    t::lib::TestObjects::AtomicUpdateFactory->addToContext($atomicupdates, undef, $subtestContext, $testContext); #Keep track of changes
215
216
    is($atomicupdates->{'#911'}->issue_id,
217
       '#911',
218
       "#911-WhoYouGonnaCall.pl deployed");
219
    is($atomicupdates->{'Bug112'}->issue_id,
220
       'Bug112',
221
       'Bug112-LapinlahdenLinnut.pl deployed');
222
    is($atomicupdates->{'Bug911'}->issue_id,
223
       'Bug911',
224
       "Bug911-WhatchaGonnaDo.pl deployed");
225
226
    ##Test adding scripts to the atomicupdates directory and how we deal with such change.
227
    $files = t::lib::TestObjects::FileFactory->createTestGroup([
228
                        {   filepath => 'atomicupdate/',
229
                            filename => '#53-PlaceToBe.pl',
230
                            content  => '$ENV{ATOMICUPDATE_TESTS}++;',},
231
                        {   filepath => 'atomicupdate/',
232
                            filename => '#54-KohaConInFinlandNextYear.pl',
233
                            content  => '$ENV{ATOMICUPDATE_TESTS}++;',},
234
                        {   filepath => 'atomicupdate/',
235
                            filename => '#55-Fiftyfive.pl',
236
                            content  => '$ENV{ATOMICUPDATE_TESTS}++;',},
237
                        ],
238
                        undef, $subtestContext, $testContext);
239
240
    $text = $atomicUpdater->listPendingToConsole();
241
    print $text;
242
243
    ok($text =~ m/#53-PlaceToBe.pl/,
244
       "#53-PlaceToBe.pl is pending");
245
    ok($text =~ m/#54-KohaConInFinlandNextYear.pl/,
246
       "#54-KohaConInFinlandNextYear.pl is pending");
247
    ok($text =~ m/#55-Fiftyfive.pl/u,
248
       '#55-Fiftyfive.pl');
249
250
    $atomicupdates = $atomicUpdater->applyAtomicUpdates();
251
    t::lib::TestObjects::AtomicUpdateFactory->addToContext($atomicupdates, undef, $subtestContext, $testContext); #Keep track of changes
252
253
    is($atomicupdates->{'#53'}->issue_id,
254
       '#53',
255
       "#53-PlaceToBe.pl deployed");
256
    is($atomicupdates->{'#54'}->issue_id,
257
       '#54',
258
       '#54-KohaConInFinlandNextYear.pl deployed');
259
    is($atomicupdates->{'#55'}->issue_id,
260
       '#55',
261
       "#55-Fiftyfive.pl deployed");
262
263
    is($ENV{ATOMICUPDATE_TESTS},
264
       6,
265
       "All configured AtomicUpdates deployed");
266
    };
267
    if ($@) {
268
        ok(0, $@);
269
    }
270
    t::lib::TestObjects::AtomicUpdateFactory->tearDownTestContext($subtestContext);
271
}
272
273
t::lib::TestObjects::ObjectFactory->tearDownTestContext($testContext);
274
done_testing;
(-)a/t/lib/TestObjects/AtomicUpdateFactory.pm (+105 lines)
Line 0 Link Here
1
package t::lib::TestObjects::AtomicUpdateFactory;
2
3
# Copyright Vaara-kirjastot 2015
4
#
5
# This file is part of Koha.
6
#
7
# Koha is free software; you can redistribute it and/or modify it under the
8
# terms of the GNU General Public License as published by the Free Software
9
# Foundation; either version 3 of the License, or (at your option) any later
10
# version.
11
#
12
# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
13
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
14
# A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License along
17
# with Koha; if not, write to the Free Software Foundation, Inc.,
18
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19
#
20
21
use Modern::Perl;
22
use Carp;
23
24
use Koha::AtomicUpdater;
25
use Koha::Database;
26
27
use Koha::Exception::UnknownProgramState;
28
29
use base qw(t::lib::TestObjects::ObjectFactory);
30
31
sub getDefaultHashKey {
32
    return 'issue_id';
33
}
34
sub getObjectType {
35
    return 'Koha::AtomicUpdate';
36
}
37
38
=head t::lib::TestObjects::createTestGroup
39
40
    my $atomicupdates = t::lib::TestObjects::AtomicUpdateFactory->createTestGroup([
41
                            {'issue_id' => 'Bug3432',
42
                             'filename' => 'Bug3432-RavingRabbitsMayhem',
43
                             'modification_time' => '2015-01-02 15:59:32',
44
                            },
45
                        ], undef, $testContext1, $testContext2, $testContext3);
46
47
Calls Koha::AtomicUpdater to add a Koha::AtomicUpdate object to DB.
48
49
The HASH is keyed with the 'koha.atomicupdates.issue_id', or the given $hashKey.
50
51
There is a duplication check to first look for atomicupdate-rows with the same 'issue_id'.
52
If a matching atomicupdate is found, then we use the existing Record instead of adding a new one.
53
54
@RETURNS HASHRef of Koha::AtomicUpdate-objects
55
56
See t::lib::TestObjects::ObjectFactory for more documentation
57
=cut
58
59
sub handleTestObject {
60
    my ($class, $object, $stashes) = @_;
61
62
    ##First see if the given Record already exists in the DB. For testing purposes we use the isbn as the UNIQUE identifier.
63
    my $atomicupdate = Koha::AtomicUpdater->find({issue_id => $object->{issue_id}});
64
    unless ($atomicupdate) {
65
        my $atomicupdater = Koha::AtomicUpdater->new();
66
        $atomicupdate = $atomicupdater->addAtomicUpdate($object);
67
    }
68
69
    Koha::Exception::UnknownProgramState->throw(error => "$class->handleTestObject():> Cannot create a new object\n$@\n")
70
                unless $atomicupdate;
71
72
    return $atomicupdate;
73
}
74
75
=head validateAndPopulateDefaultValues
76
@OVERLOAD
77
78
Validates given Object parameters and makes sure that critical fields are given
79
and populates defaults for missing values.
80
=cut
81
82
sub validateAndPopulateDefaultValues {
83
    my ($self, $object, $hashKey) = @_;
84
85
    $object->{issue_id} = 'BugRancidacid' unless $object->{issue_id};
86
    $object->{filename} = 'BugRancidacid-LaboratoryExperimentsGoneSour' unless $object->{filename};
87
88
    $self->SUPER::validateAndPopulateDefaultValues($object, $hashKey);
89
}
90
91
sub deleteTestGroup {
92
    my ($class, $objects) = @_;
93
94
    while( my ($key, $object) = each %$objects) {
95
        my $atomicupdate = Koha::AtomicUpdater->cast($object);
96
        eval {
97
            $atomicupdate->delete();
98
        };
99
        if ($@) {
100
            warn "$class->deleteTestGroup():> Error hapened: $@\n";
101
        }
102
    }
103
}
104
105
1;
(-)a/t/lib/TestObjects/ObjectFactory.pm (+5 lines)
Lines 166-171 sub tearDownTestContext { Link Here
166
        t::lib::TestObjects::BiblioFactory->deleteTestGroup($stash->{biblio});
166
        t::lib::TestObjects::BiblioFactory->deleteTestGroup($stash->{biblio});
167
        delete $stash->{biblio};
167
        delete $stash->{biblio};
168
    }
168
    }
169
    if ($stash->{atomicupdate}) {
170
        require t::lib::TestObjects::AtomicUpdateFactory;
171
        t::lib::TestObjects::AtomicUpdateFactory->deleteTestGroup($stash->{atomicupdate});
172
        delete $stash->{atomicupdate};
173
    }
169
    if ($stash->{borrower}) {
174
    if ($stash->{borrower}) {
170
        require t::lib::TestObjects::BorrowerFactory;
175
        require t::lib::TestObjects::BorrowerFactory;
171
        t::lib::TestObjects::BorrowerFactory->deleteTestGroup($stash->{borrower});
176
        t::lib::TestObjects::BorrowerFactory->deleteTestGroup($stash->{borrower});
(-)a/t/lib/TestObjects/objectFactories.t (-1 / +50 lines)
Lines 30-35 use t::lib::TestObjects::BorrowerFactory; Link Here
30
use Koha::Borrowers;
30
use Koha::Borrowers;
31
use t::lib::TestObjects::ItemFactory;
31
use t::lib::TestObjects::ItemFactory;
32
use Koha::Items;
32
use Koha::Items;
33
use t::lib::TestObjects::AtomicUpdateFactory;
34
use Koha::AtomicUpdater;
33
use t::lib::TestObjects::BiblioFactory;
35
use t::lib::TestObjects::BiblioFactory;
34
use Koha::Biblios;
36
use Koha::Biblios;
35
use t::lib::TestObjects::CheckoutFactory;
37
use t::lib::TestObjects::CheckoutFactory;
Lines 115-120 sub testSerialFactory { Link Here
115
    my ($subscriptions, $subscription, $frequency, $numberpattern, $biblio, $sameBiblio, $borrower, $bookseller, $items, $serials);
117
    my ($subscriptions, $subscription, $frequency, $numberpattern, $biblio, $sameBiblio, $borrower, $bookseller, $items, $serials);
116
    my $subtestContext = {};
118
    my $subtestContext = {};
117
    my $dontDeleteTestContext = {};
119
    my $dontDeleteTestContext = {};
120
    eval {
118
    ##Create and delete
121
    ##Create and delete
119
    $subscriptions = t::lib::TestObjects::Serial::SubscriptionFactory->createTestGroup([
122
    $subscriptions = t::lib::TestObjects::Serial::SubscriptionFactory->createTestGroup([
120
                                {internalnotes => 'TESTDEFAULTS',
123
                                {internalnotes => 'TESTDEFAULTS',
Lines 219-224 sub testSerialFactory { Link Here
219
    ok(defined($borrower), "Attached Borrower not deleted.");
222
    ok(defined($borrower), "Attached Borrower not deleted.");
220
    $bookseller = Koha::Acquisition::Booksellers->find( $bookseller->id );
223
    $bookseller = Koha::Acquisition::Booksellers->find( $bookseller->id );
221
    ok(defined($bookseller), "Attached Bookseller not deleted.");
224
    ok(defined($bookseller), "Attached Bookseller not deleted.");
225
    };
226
    if ($@) {
227
        ok(0, $@);
228
    }
222
    t::lib::TestObjects::ObjectFactory->tearDownTestContext($dontDeleteTestContext);
229
    t::lib::TestObjects::ObjectFactory->tearDownTestContext($dontDeleteTestContext);
223
};
230
};
224
231
Lines 229-234 subtest 't::lib::TestObjects::Acquisition' => \&testAcquisitionFactories; Link Here
229
sub testAcquisitionFactories {
236
sub testAcquisitionFactories {
230
    my ($booksellers, $bookseller, $contacts, $contact);
237
    my ($booksellers, $bookseller, $contacts, $contact);
231
    my $subtestContext = {};
238
    my $subtestContext = {};
239
    eval {
232
    ##Create and delete
240
    ##Create and delete
233
    $booksellers = t::lib::TestObjects::Acquisition::BooksellerFactory->createTestGroup([{}], undef, $subtestContext);
241
    $booksellers = t::lib::TestObjects::Acquisition::BooksellerFactory->createTestGroup([{}], undef, $subtestContext);
234
    $bookseller = Koha::Acquisition::Booksellers->find({name => 'Bookselling Vendor'});
242
    $bookseller = Koha::Acquisition::Booksellers->find({name => 'Bookselling Vendor'});
Lines 256-261 sub testAcquisitionFactories { Link Here
256
    ok(not(defined($contact)), "Contact 'Hippocrates' deleted.");
264
    ok(not(defined($contact)), "Contact 'Hippocrates' deleted.");
257
    $bookseller = Koha::Acquisition::Booksellers->find({name => 'Bookselling Vendor'});
265
    $bookseller = Koha::Acquisition::Booksellers->find({name => 'Bookselling Vendor'});
258
    ok(not(defined($bookseller)), "Bookseller 'Bookselling Vendor' deleted.");
266
    ok(not(defined($bookseller)), "Bookseller 'Bookselling Vendor' deleted.");
267
    };
268
    if ($@) {
269
        ok(0, $@);
270
    }
271
    t::lib::TestObjects::ObjectFactory->tearDownTestContext($subtestContext);
259
};
272
};
260
273
261
274
Lines 472-477 sub testLetterTemplateFactory { Link Here
472
485
473
486
474
487
488
########## AtomicUpdateFactory subtests ##########
489
subtest 't::lib::TestObjects::AtomicUpdateFactory' => \&testAtomicUpdateFactory;
490
sub testAtomicUpdateFactory {
491
    my ($atomicUpdater, $atomicupdate);
492
    my $subtestContext = {};
493
    ##Create and Delete using dependencies in the $testContext instantiated in previous subtests.
494
    my $atomicupdates = t::lib::TestObjects::AtomicUpdateFactory->createTestGroup([
495
                            {'issue_id' => 'Bug10',
496
                             'filename' => 'Bug10-RavingRabbitsMayhem.pl',
497
                             'modification_time' => '2015-01-02 15:59:32',},
498
                            {'issue_id' => 'Bug11',
499
                             'filename' => 'Bug11-RancidSausages.perl',
500
                             'modification_time' => '2015-01-02 15:59:33',},
501
                            ],
502
                            undef, $subtestContext);
503
    $atomicUpdater = Koha::AtomicUpdater->new();
504
    $atomicupdate = $atomicUpdater->find({issue_id => $atomicupdates->{Bug10}->issue_id});
505
    is($atomicupdate->issue_id,
506
       'Bug10',
507
       "Bug10-RavingRabbitsMayhem created");
508
    $atomicupdate = $atomicUpdater->find({issue_id => $atomicupdates->{Bug11}->issue_id});
509
    is($atomicupdate->issue_id,
510
       'Bug11',
511
       "Bug11-RancidSausages created");
512
513
    t::lib::TestObjects::ObjectFactory->tearDownTestContext($subtestContext);
514
515
    $atomicupdate = $atomicUpdater->find({issue_id => $atomicupdates->{Bug10}->issue_id});
516
    ok(not($atomicupdate),
517
       "Bug10-RavingRabbitsMayhem deleted");
518
    $atomicupdate = $atomicUpdater->find({issue_id => $atomicupdates->{Bug11}->issue_id});
519
    ok(not($atomicupdate),
520
       "Bug11-RancidSausages created");
521
};
522
523
524
475
########## SystemPreferenceFactory subtests ##########
525
########## SystemPreferenceFactory subtests ##########
476
subtest 't::lib::TestObjects::SystemPreferenceFactory' => \&testSystemPreferenceFactory;
526
subtest 't::lib::TestObjects::SystemPreferenceFactory' => \&testSystemPreferenceFactory;
477
sub testSystemPreferenceFactory {
527
sub testSystemPreferenceFactory {
478
- 

Return to bug 14698