Skip to content

Commit

Permalink
Add methods to add profile to bag and some tests
Browse files Browse the repository at this point in the history
  • Loading branch information
whikloj committed Apr 24, 2024
1 parent 859c829 commit 25243f8
Show file tree
Hide file tree
Showing 5 changed files with 328 additions and 29 deletions.
114 changes: 101 additions & 13 deletions src/Bag.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
use Normalizer;
use whikloj\BagItTools\Exceptions\BagItException;
use whikloj\BagItTools\Exceptions\FilesystemException;
use whikloj\BagItTools\Exceptions\ProfileException;
use whikloj\BagItTools\Profiles\BagItProfile;
use whikloj\BagItTools\Profiles\ProfileFactory;
use ZipArchive;

/**
Expand Down Expand Up @@ -134,14 +137,14 @@ class Bag
/**
* All the extensions in one array.
*
* @var array
* @var array<string>
*/
private array $packageExtensions;

/**
* Array of current bag version with keys 'major' and 'minor'.
*
* @var array
* @var array<string, int>
*/
private array $currentVersion = self::DEFAULT_BAGIT_VERSION;

Expand All @@ -155,21 +158,21 @@ class Bag
/**
* Array of payload manifests.
*
* @var array
* @var array<string, PayloadManifest>
*/
private array $payloadManifests;

/**
* Array of tag manifests.
*
* @var array
* @var array<string, TagManifest>
*/
private array $tagManifests;

/**
* List of relative file paths for all files.
*
* @var array
* @var array<string>
*/
private array $payloadFiles;

Expand Down Expand Up @@ -201,21 +204,21 @@ class Bag
* supported by the BagIt specification. Stored to avoid extraneous calls
* to hash_algos().
*
* @var array
* @var array<string>
*/
private array $validHashAlgorithms;

/**
* Errors when validating a bag.
*
* @var array
* @var array<int, array<string, string>>
*/
private array $bagErrors;

/**
* Warnings when validating a bag.
*
* @var array
* @var array<int, array<string, string>>
*/
private array $bagWarnings;

Expand All @@ -229,15 +232,15 @@ class Bag
/**
* Bag Info data.
*
* @var array
* @var array<int, array<string, string>>
*/
private array $bagInfoData = [];

/**
* Unique array of all Bag info tags/values. Tags are stored once in lower case with an array of all instances
* of values. This index does not save order.
*
* @var array
* @var array<string, array<string>>
*/
private array $bagInfoTagIndex = [];

Expand All @@ -254,6 +257,12 @@ class Bag
*/
private ?string $serialization = null;

/**
* Array of BagIt profiles.
* @var array<int, BagItProfile>
*/
private array $profiles = [];

/**
* Bag constructor.
*
Expand Down Expand Up @@ -361,6 +370,16 @@ public function isValid(): bool
$this->mergeErrors($manifest->getErrors());
$this->mergeWarnings($manifest->getWarnings());
}
foreach ($this->profiles as $profile) {
try {
$profile->validateBag($this);
} catch (ProfileException $e) {
$this->addBagError(
$profile->getProfileIdentifier(),
$e->getMessage()
);
}
}
return (count($this->bagErrors) == 0);
}

Expand Down Expand Up @@ -1276,10 +1295,74 @@ public function getSerializationMimeType(): ?string
return $this->serialization;
}

/**
* @return array The profiles that have been added to the bag.
*/
public function getBagProfiles(): array
{
return $this->profiles;
}

/**
* Add a profile to the bag.
* @param string $url The URL to the profile.
* @throws Exceptions\ProfileException If the profile cannot be loaded or parsed.
*/
public function addBagProfileByURL(string $url): void
{
$this->addBagProfileInternal(ProfileFactory::generateProfileFromUri($url));
}

/**
* Add a profile to the bag.
* @param string $json The JSON representation of the profile.
* @throws Exceptions\ProfileException If the profile cannot be parsed.
*/
public function addBagProfileByJson(string $json): void
{
$this->addBagProfileInternal(BagItProfile::fromJson($json));
}

/**
* Remove a profile from the bag.
* @param string $profileId The identifier of the profile to remove.
*/
public function removeBagProfile(string $profileId): void
{
if (array_key_exists($profileId, $this->profiles)) {
unset($this->profiles[$profileId]);
$this->changed = true;
}
}

/**
* Clear all profiles from the bag.
*/
public function clearAllProfiles(): void
{
if (count($this->profiles) > 0) {
$this->profiles = [];
$this->changed = true;
}
}

/*
* XXX: Private functions
*/

/**
* Add a profile to the bag if it doesn't already exist.
* @param BagItProfile $profile The profile to add.
*/
private function addBagProfileInternal(BagItProfile $profile): void
{
if (!array_key_exists($profile->getProfileIdentifier(), $this->profiles)) {
$this->setExtended(true);
$this->profiles[$profile->getProfileIdentifier()] = $profile;
$this->changed = true;
}
}

/**
* Common checks for interactions with custom tag files.
* @param string $tagFilePath The relative path to the tag file.
Expand Down Expand Up @@ -1963,8 +2046,8 @@ function (&$item) {
);
} else {
$this->currentVersion = [
'major' => $match[1],
'minor' => $match[2],
'major' => (int)$match[1],
'minor' => (int)$match[2],
];
}
if (
Expand Down Expand Up @@ -2188,7 +2271,12 @@ private static function untarBag(string $filename): string
private static function extensionTarCompression(string $filename): ?string
{
$filename = strtolower(basename($filename));
return (str_ends_with($filename, '.bz2') ? 'bz2' : (str_ends_with($filename, 'gz') ? 'gz' : null));
if (str_ends_with($filename, '.bz2')) {
return 'bz2';
} elseif (str_ends_with($filename, '.gz') || str_ends_with($filename, '.tgz')) {
return 'gz';
}
return null;
}

/**
Expand Down
12 changes: 4 additions & 8 deletions src/Profiles/BagItProfile.php
Original file line number Diff line number Diff line change
Expand Up @@ -923,8 +923,7 @@ public function validateBag(Bag $bag): bool
}
if ($this->getManifestsRequired() !== []) {
$manifests = array_keys($bag->getPayloadManifests());
$diff = array_diff($manifests, $this->getManifestsRequired()) +
array_diff($this->getManifestsRequired(), $manifests);
$diff = array_diff($this->getManifestsRequired(), $manifests);
if ($diff !== []) {
$errors[] = "Profile requires payload manifest(s) which are missing from the bag (" .
implode(", ", $diff) . ")";
Expand All @@ -940,8 +939,7 @@ public function validateBag(Bag $bag): bool
}
if ($this->getTagManifestsRequired() !== []) {
$manifests = array_keys($bag->getTagManifests());
$diff = array_diff($manifests, $this->getTagManifestsRequired()) +
array_diff($this->getTagManifestsRequired(), $manifests);
$diff = array_diff($this->getTagManifestsRequired(), $manifests);
if ($diff !== []) {
$errors[] = "Profile requires tag manifest(s) which are missing from the bag (" .
implode(", ", $diff) . ")";
Expand All @@ -959,8 +957,7 @@ public function validateBag(Bag $bag): bool
// Grab the first tag manifest, they should all be the same
$manifests = $bag->getTagManifests()[0];
$tag_files = array_keys($manifests->getHashes());
$diff = array_diff($this->getTagFilesRequired(), $tag_files) +
array_diff($tag_files, $this->getTagFilesRequired());
$diff = array_diff($this->getTagFilesRequired(), $tag_files);
if ($diff !== []) {
$errors[] = "Profile requires tag files(s) which are missing from the bag (" .
implode(", ", $diff) . ")";
Expand All @@ -980,8 +977,7 @@ public function validateBag(Bag $bag): bool
// Grab the first tag manifest, they should all be the same
$manifests = $bag->getPayloadManifests()[0];
$payload_files = array_keys($manifests->getHashes());
$diff = array_diff($this->getPayloadFilesRequired(), $payload_files) +
array_diff($payload_files, $this->getPayloadFilesRequired());
$diff = array_diff($this->getPayloadFilesRequired(), $payload_files);
if ($diff !== []) {
$errors[] = "Profile requires payload file(s) which are missing from the bag (" .
implode(", ", $diff) . ")";
Expand Down
35 changes: 27 additions & 8 deletions tests/BagItWebserverFramework.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,25 @@
use donatj\MockWebServer\MockWebServer;
use donatj\MockWebServer\Response;

/**
* Class to setup a mock webserver for testing remote file downloads.
* @package whikloj\BagItTools\Test
* @since 5.0.0
*
* To use this abstract class, extend it and then implement the setupBeforeClass methods, define the webserver_files
* variable and then call the parent::setUpBeforeClass() method.
*/
abstract class BagItWebserverFramework extends BagItTestFramework
{
/**
* Array of remote files defined in mock webserver.
* Outside key is a unique identifier, keys for the inside array are:
* filename (string) - path to file with response contents
* headers (array) - headers to return in response
* status_code (int) - status code
* content (string) - string to return, used instead of filename
* path (path) - the path of the URL, used if filename not defined. Otherwise is basename(filename)
* @var array<string, array<string, mixed>>
*/
protected static array $webserver_files = [];

Expand All @@ -22,34 +37,38 @@ abstract class BagItWebserverFramework extends BagItTestFramework
/**
* Array of file contents for use with comparing against requests against the same index in self::$remote_urls
*
* @var string|array|false
* @var array<int, mixed>
*/
protected static string|array|false $response_content = [];
protected static array $response_content = [];

/**
* Array of mock urls to get responses from. Match response bodies against matching key in self::$response_content
*
* @var string|array
* @var array<int, string>
*/
protected static string|array $remote_urls = [];
protected static array $remote_urls = [];

/**
* {@inheritdoc}
* NOTE: You should override this in your class, define self::$webserver_files, then call parent::setUpBeforeClass
*/
public static function setUpBeforeClass(): void
{
self::$webserver = new MockWebServer();
self::$webserver->start();
$counter = 0;
foreach (self::$webserver_files as $file) {
self::$response_content[$counter] = file_get_contents($file['filename']);
self::$response_content[$counter] = $file['content'] ?? file_get_contents($file['filename']);
// Add custom headers if defined.
$response_headers = [ 'Cache-Control' => 'no-cache', 'Content-Length' => stat($file['filename'])['size']] +
($file['headers'] ?? []);
$response_headers = [
'Cache-Control' => 'no-cache',
'Content-Length' => isset($file['content']) ? strlen($file['content']) :
stat($file['filename'])['size']
] + ($file['headers'] ?? []);
// Use custom status code if defined.
$status_code = $file['status_code'] ?? 200;
self::$remote_urls[$counter] = self::$webserver->setResponseOfPath(
"/example/" . basename($file['filename']),
"/" . ($file['path'] ?? "example/" . basename($file['filename'])),
new Response(
self::$response_content[$counter],
$response_headers,
Expand Down
Loading

0 comments on commit 25243f8

Please sign in to comment.