diff --git a/src/Profiles/BagItProfile.php b/src/Profiles/BagItProfile.php index a6991b1..e1e900b 100644 --- a/src/Profiles/BagItProfile.php +++ b/src/Profiles/BagItProfile.php @@ -337,16 +337,11 @@ public function getBagInfoTags(): array /** * @param array $bagInfoTags Parsed profile Bag-Info sections * @return BagItProfile The profile object. - * @throws ProfileException If invalid options are specified for a tag. */ private function setBagInfoTags(array $bagInfoTags): BagItProfile { - $expectedKeys = ['required' => 0, 'values' => 0, 'repeatable' => 0, 'description' => 0]; $this->profileBagInfoTags = []; foreach ($bagInfoTags as $tagName => $tagOpts) { - if (count(array_diff_key($tagOpts, $expectedKeys)) > 0) { - throw new ProfileException("Invalid tag options for $tagName"); - } if (self::matchStrings('BagIt-Profile-Identifier', $tagName)) { $this->profileWarnings[] = "The tag BagIt-Profile-Identifier is always required, but SHOULD NOT be " . "listed under Bag-Info in the Profile."; diff --git a/src/Profiles/ProfileTags.php b/src/Profiles/ProfileTags.php index 2d0adba..1c66c68 100644 --- a/src/Profiles/ProfileTags.php +++ b/src/Profiles/ProfileTags.php @@ -13,6 +13,11 @@ */ class ProfileTags { + /** + * Array with keys matching optional keys from specification, all other keys are system specific. + */ + private const SPEC_TAGS = ['required' => 0, 'values' => 0, 'repeatable' => 0, 'description' => 0]; + /** * @var string */ @@ -38,6 +43,11 @@ class ProfileTags */ private string $description = ""; + /** + * @var array + */ + private array $otherOptions = []; + /** * ProfileTags constructor. * @param string $tag @@ -95,14 +105,43 @@ public function getDescription(): string return $this->description; } + /** + * Return any tags defined in the BagItProfile but not in the specification. + * @return array Array of tagName => tagValue + */ + public function getOtherTagOptions(): array + { + return $this->otherOptions; + } + + /** + * Set the other tag options. + * @param array $tagOptions Array of optionName => optionValue + */ + protected function setOtherTagOptions(array $tagOptions): void + { + $this->otherOptions = $tagOptions; + } + + /** + * Create a ProfileTags object from a JSON array. + * @param string $tag Tag name + * @param array $tagOpts Tag options + * @return ProfileTags The created object. + */ public static function fromJson(string $tag, array $tagOpts): ProfileTags { - return new ProfileTags( + $profileTag = new ProfileTags( $tag, $tagOpts['required'] ?? false, $tagOpts['values'] ?? [], $tagOpts['repeatable'] ?? true, $tagOpts['description'] ?? "" ); + $otherTags = array_diff_key($tagOpts, self::SPEC_TAGS); + if (count($otherTags) > 0) { + $profileTag->setOtherTagOptions($otherTags); + } + return $profileTag; } } diff --git a/tests/Profiles/BagProfileTest.php b/tests/Profiles/BagProfileTest.php index 639fca5..0ccf2fc 100644 --- a/tests/Profiles/BagProfileTest.php +++ b/tests/Profiles/BagProfileTest.php @@ -37,7 +37,7 @@ public function testValidateBag1(): void * @group Profiles * @covers ::setBagInfoTags */ - public function testInvalidBagInfoTags(): void + public function testOptionalBagInfoTags(): void { $profileJson = <<< JSON { @@ -65,9 +65,21 @@ public function testInvalidBagInfoTags(): void ] } JSON; - $this->expectException(ProfileException::class); - $this->expectExceptionMessage("Invalid tag options for Source-Organization"); - BagItProfile::fromJson($profileJson); + $profile = BagItProfile::fromJson($profileJson); + $this->assertTrue($profile->isValid()); + $this->assertArrayEquals(["source-organization"], array_keys($profile->getBagInfoTags())); + $tag = $profile->getBagInfoTags()["source-organization"]; + $this->assertEquals("Source-Organization", $tag->getTag()); + $this->assertTrue($tag->isRequired()); + $this->assertArrayEquals(["Simon Fraser University", "York University"], $tag->getValues()); + $this->assertEquals("", $tag->getDescription()); + $this->assertTrue($tag->isRepeatable()); + $this->assertArrayEquals( + [ + "help" => "This is the organization that originally created the bag." + ], + $tag->getOtherTagOptions() + ); } /** @@ -486,4 +498,84 @@ public function testAddSameProfileTwice(): void $bag->removeBagProfile("http://www.library.yale.edu/mssa/bagitprofiles/disk_images.json"); $this->assertCount(0, $bag->getBagProfiles()); } + + /** + * @group Profiles + * @covers \whikloj\BagItTools\Bag::addBagProfileByJson + * @covers \whikloj\BagItTools\Bag::addBagProfileInternal + * @covers \whikloj\BagItTools\Bag::removeBagProfile + */ + public function testAddDifferentProfiles(): void + { + $profile1 = file_get_contents(self::$profiles . "/bagProfileFoo.json"); + $profile2 = file_get_contents(self::$profiles . "/bagProfileBar.json"); + $profile3 = file_get_contents(self::$profiles . "/btrProfile.json"); + $bag = Bag::create($this->tmpdir); + $this->assertCount(0, $bag->getBagProfiles()); + $bag->addBagProfileByJson($profile1); + $this->assertCount(1, $bag->getBagProfiles()); + $bag->addBagProfileByJson($profile2); + $this->assertCount(2, $bag->getBagProfiles()); + $bag->addBagProfileByJson($profile3); + $this->assertCount(3, $bag->getBagProfiles()); + // Add profile a second time has no effect. + $bag->addBagProfileByJson($profile2); + $this->assertCount(3, $bag->getBagProfiles()); + $this->assertArrayEquals( + [ + "http://canadiana.org/standards/bagit/tdr_ingest.json", + "https://github.com/dpscollaborative/btr_bagit_profile/releases/download/1.0/btr-bagit-profile.json", + "http://www.library.yale.edu/mssa/bagitprofiles/disk_images.json", + ], + array_keys($bag->getBagProfiles()) + ); + // Remove profiles. + $bag->removeBagProfile("http://canadiana.org/standards/bagit/tdr_ingest.json"); + $this->assertCount(2, $bag->getBagProfiles()); + $bag->removeBagProfile( + "https://github.com/dpscollaborative/btr_bagit_profile/releases/download/1.0/btr-bagit-profile.json" + ); + $this->assertCount(1, $bag->getBagProfiles()); + // Remove profile that doesn't exist in bag has no effect + $bag->removeBagProfile("http://canadiana.org/standards/bagit/tdr_ingest.json"); + $this->assertCount(1, $bag->getBagProfiles()); + $bag->removeBagProfile("http://www.library.yale.edu/mssa/bagitprofiles/disk_images.json"); + $this->assertCount(0, $bag->getBagProfiles()); + } + + /** + * @group Profiles + * @covers \whikloj\BagItTools\Bag::clearAllProfiles + */ + public function testClearAllProfiles(): void + { + $profile1 = file_get_contents(self::$profiles . "/bagProfileFoo.json"); + $profile2 = file_get_contents(self::$profiles . "/bagProfileBar.json"); + $profile3 = file_get_contents(self::$profiles . "/btrProfile.json"); + $bag = Bag::create($this->tmpdir); + $this->assertCount(0, $bag->getBagProfiles()); + $bag->addBagProfileByJson($profile1); + $bag->addBagProfileByJson($profile2); + $bag->addBagProfileByJson($profile3); + $this->assertCount(3, $bag->getBagProfiles()); + $bag->clearAllProfiles(); + $this->assertCount(0, $bag->getBagProfiles()); + } + + /** + * @group Profiles + * @covers \whikloj\BagItTools\Bag::getBagProfiles + */ + public function testGetBagProfile(): void + { + $profile = file_get_contents(self::$profiles . "/bagProfileBar.json"); + $bag = Bag::create($this->tmpdir); + $bag->addBagProfileByJson($profile); + $testProfiles = $bag->getBagProfiles(); + $this->assertCount(1, $testProfiles); + $key = key($testProfiles); + $val = current($testProfiles); + $this->assertEquals("http://canadiana.org/standards/bagit/tdr_ingest.json", $key); + $this->assertInstanceOf(BagItProfile::class, $val); + } }