Skip to content

Commit

Permalink
Improved error handling (#6)
Browse files Browse the repository at this point in the history
- Do not fail the entire batch because one source failed.
- Improve behavior related to noRecordsMatch error.
- Improve tests.
  • Loading branch information
demiankatz authored Jun 7, 2021
1 parent ecf740c commit b0b5573
Show file tree
Hide file tree
Showing 6 changed files with 232 additions and 10 deletions.
7 changes: 4 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,16 @@

All notable changes to this project will be documented in this file, in reverse chronological order by release.

## Next Release - TBD
## 4.1.0 - 2021-06-07

### Added

- Nothing.
- The VuFindHarvest\Exception\OaiException class, for finer-grained error handling.

### Changed

- The minimum PHP version requirement has been raised to 7.3.
- The noRecordsMatch error is no longer treated as a fatal problem.

### Deprecated

Expand All @@ -22,7 +23,7 @@ All notable changes to this project will be documented in this file, in reverse

### Fixed

- Nothing.
- The failure to harvest from one OAI source no longer causes the entire batch to fail.

## 4.0.1 - 2020-03-23

Expand Down
93 changes: 93 additions & 0 deletions src/Exception/OaiException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
<?php
/**
* OAI-PMH exception class
*
* PHP version 7
*
* Copyright (c) Demian Katz 2021.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* @category VuFind
* @package Harvest_Tools
* @author Demian Katz <demian.katz@villanova.edu>
* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License
* @link https://vufind.org/wiki/indexing:oai-pmh Wiki
*/
namespace VuFindHarvest\Exception;

use Throwable;

/**
* OAI-PMH exception class
*
* @category VuFind
* @package Harvest_Tools
* @author Demian Katz <demian.katz@villanova.edu>
* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License
* @link https://vufind.org/wiki/indexing:oai-pmh Wiki
*/
class OaiException extends \RuntimeException
{
/**
* Error code
*
* @var string
*/
protected $oaiCode;

/**
* Error message
*
* @var string
*/
protected $oaiMessage;

/**
* Constructor
*
* @param string $oaiCode OAI-PMH error code
* @param string $oaiMessage OAI-PMH error message
* @param int $code Error code
* @param ?Throwable $previous Previous exception
*/
public function __construct(string $oaiCode, string $oaiMessage, int $code = 0,
?Throwable $previous = null
) {
$this->oaiCode = $oaiCode;
$this->oaiMessage = $oaiMessage;
$message = "OAI-PMH error -- code: $oaiCode, value: $oaiMessage";
parent::__construct($message, $code, $previous);
}

/**
* Get OAI-PMH error code
*
* @return string
*/
public function getOaiCode(): string
{
return $this->oaiCode;
}

/**
* Get OAI-PMH error message
*
* @return string
*/
public function getOaiMessage(): string
{
return $this->oaiMessage;
}
}
7 changes: 3 additions & 4 deletions src/OaiPmh/Harvester.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
namespace VuFindHarvest\OaiPmh;

use VuFindHarvest\ConsoleOutput\WriterAwareTrait;
use VuFindHarvest\Exception\OaiException;

/**
* OAI-PMH Harvest Tool
Expand Down Expand Up @@ -238,6 +239,7 @@ protected function loadGranularity()
* @return void
*
* @throws \Exception
* @throws OaiException
*/
protected function checkResponseForErrors($result)
{
Expand All @@ -255,10 +257,7 @@ protected function checkResponseForErrors($result)
"Token expired; removing last_state.txt. Please restart harvest."
);
}
throw new \Exception(
"OAI-PMH error -- code: {$attribs['code']}, " .
"value: {$result->error}"
);
throw new OaiException($attribs['code'], $result->error);
}
}

Expand Down
20 changes: 17 additions & 3 deletions src/OaiPmh/HarvesterCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
use Symfony\Component\Console\Output\OutputInterface;
use VuFindHarvest\ConsoleOutput\ConsoleWriter;
use VuFindHarvest\ConsoleOutput\WriterAwareTrait;
use VuFindHarvest\Exception\OaiException;

/**
* OAI-PMH Harvest Tool (Symfony Console Command)
Expand Down Expand Up @@ -332,7 +333,7 @@ protected function execute(InputInterface $input, OutputInterface $output)
}

// Loop through all the settings and perform harvests:
$processed = $skipped = 0;
$processed = $skipped = $errors = 0;
foreach ($allSettings as $target => $baseSettings) {
$settings = $this->updateSettingsWithConsoleOptions(
$input, $baseSettings
Expand All @@ -345,8 +346,14 @@ protected function execute(InputInterface $input, OutputInterface $output)
try {
$this->harvestSingleRepository($input, $output, $target, $settings);
} catch (\Exception $e) {
$this->writeLine($e->getMessage());
return 1;
if ($e instanceof OaiException
&& strtolower($e->getOaiCode()) == 'norecordsmatch'
) {
$this->writeLine("No new records found.");
} else {
$this->writeLine($e->getMessage());
$errors++;
}
}
$processed++;
}
Expand All @@ -359,6 +366,13 @@ protected function execute(InputInterface $input, OutputInterface $output)
);
return 1;
}
if ($errors > 0) {
$this->writeLine(
"Completed with {$errors} error(s) -- "
. "{$processed} source(s) processed."
);
return 1;
}
$this->writeLine(
"Completed without errors -- {$processed} source(s) processed."
);
Expand Down
60 changes: 60 additions & 0 deletions tests/unit-tests/src/VuFindTest/Exception/OaiExceptionTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<?php

/**
* OAI exception test
*
* PHP version 7
*
* Copyright (C) Villanova University 2021.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* @category VuFind
* @package Tests
* @author Demian Katz <demian.katz@villanova.edu>
* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License
* @link https://vufind.org/wiki/development
*/
namespace VuFindTest\Harvest\ConsoleOutput;

use VuFindHarvest\Exception\OaiException;

/**
* OAI exception test
*
* @category VuFind
* @package Tests
* @author Demian Katz <demian.katz@villanova.edu>
* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License
* @link https://vufind.org/wiki/development
*/
class OaiExceptionTest extends \PHPUnit\Framework\TestCase
{
/**
* Test exception
*
* @return void
*/
public function testException()
{
$exception = new OaiException('code', 'message');
$this->assertEquals('code', $exception->getOaiCode());
$this->assertEquals('message', $exception->getOaiMessage());
$this->assertEquals(
'OAI-PMH error -- code: code, value: message', $exception->getMessage()
);
$this->assertEquals(0, $exception->getCode());
$this->assertNull($exception->getPrevious());
}
}
55 changes: 55 additions & 0 deletions tests/unit-tests/src/VuFindTest/OaiPmh/HarvesterCommandTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@

use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Tester\CommandTester;
use VuFindHarvest\Exception\OaiException;
use VuFindHarvest\OaiPmh\HarvesterCommand;

/**
Expand Down Expand Up @@ -171,6 +172,60 @@ public function testRunFromIniFile()
$this->assertEquals(0, $commandTester->getStatusCode());
}

/**
* Test that an unexpected exception causes a bad return code.
*
* @return void
*/
public function testExceptionHandling()
{
$basePath = '/foo/bar';
$client = $this->getMockBuilder(\Laminas\Http\Client::class)->getMock();
$harvester = $this->getMockHarvester();
$harvester->expects($this->once())->method('launch')->will(
$this->throwException(new \Exception('kablooie'))
);
$factory = $this
->getMockBuilder(\VuFindHarvest\OaiPmh\HarvesterFactory::class)
->getMock();
$factory->expects($this->once())
->method('getHarvester')
->will($this->returnValue($harvester));
$ini = realpath(__DIR__ . '/../../../../fixtures/test.ini');
$commandTester = $this->getCommandTester(
['--ini' => $ini],
new HarvesterCommand($client, $basePath, $factory)
);
$this->assertEquals(1, $commandTester->getStatusCode());
}

/**
* Test that an OAI "noRecordsMatch" exception does not cause a bad return code.
*
* @return void
*/
public function testNoMatchExceptionHandling()
{
$basePath = '/foo/bar';
$client = $this->getMockBuilder(\Laminas\Http\Client::class)->getMock();
$harvester = $this->getMockHarvester();
$harvester->expects($this->once())->method('launch')->will(
$this->throwException(new OaiException('noRecordsMatch', 'empty!'))
);
$factory = $this
->getMockBuilder(\VuFindHarvest\OaiPmh\HarvesterFactory::class)
->getMock();
$factory->expects($this->once())
->method('getHarvester')
->will($this->returnValue($harvester));
$ini = realpath(__DIR__ . '/../../../../fixtures/test.ini');
$commandTester = $this->getCommandTester(
['--ini' => $ini],
new HarvesterCommand($client, $basePath, $factory)
);
$this->assertEquals(0, $commandTester->getStatusCode());
}

/**
* Test basic functionality of console runner w/ settings overridden
* by command-line options.
Expand Down

0 comments on commit b0b5573

Please sign in to comment.