Compare commits

10 Commits

12 changed files with 552 additions and 238 deletions

View File

@@ -13,140 +13,140 @@ use ML\IRI\IRI;
*/ */
class COIDParser { class COIDParser {
const COID_INVALID = 0; const COID_INVALID = 0;
const COID_ROOT = 1; const COID_ROOT = 1;
const COID_UNVERSIONED = 2; const COID_UNVERSIONED = 2;
const COID_VERSIONED = 3; const COID_VERSIONED = 3;
const COID_VERSION_WILDCARD = 4; const COID_VERSION_WILDCARD = 4;
const REGEX_HOSTNAME = "/^([a-z0-9-]+\.)?[a-z0-9-]+\.[a-z]+$/"; const REGEX_HOSTNAME = "/^([a-z0-9-]+\.)*[a-z0-9-]+\.[a-z]+$/";
const REGEX_SEGMENT = "/^[A-Za-z-_0-9\.]+$/"; const REGEX_SEGMENT = "/^[A-Za-z-_0-9\.]+$/";
const REGEX_VERSION_WILDCARD = "/^((\^|~)(\d+\.)?\d|(\d+\.){1,2}\*)$/"; const REGEX_VERSION_WILDCARD = "/^((\^|~)(\d+\.)?\d|(\d+\.){1,2}\*)$/";
/** /**
* Creates a new IRI object representing a COID from a string. * Creates a new IRI object representing a COID from a string.
* Adds the "coid://" prefix if necessary and normalizes case. * Adds the "coid://" prefix if necessary and normalizes case.
* *
* @param string $coidString A COID string. * @param string $coidString A COID string.
* @return IRI * @return IRI
*/ */
public static function fromString($coidString) { public static function fromString($coidString) : IRI {
$coidPre = new IRI( $coidPre = new IRI(
(strtolower(substr($coidString, 0, 7))=='coid://') ? $coidString : 'coid://'.$coidString (strtolower(substr($coidString, 0, 7)) == 'coid://') ? $coidString : 'coid://'.$coidString
); );
// Normalize scheme and host segments to lower case // Normalize scheme and host segments to lower case
return new IRI('coid://'.strtolower($coidPre->getHost()).$coidPre->getPath()); return new IRI('coid://'.strtolower($coidPre->getHost()).$coidPre->getPath());
}
/**
* Get the type of a COID.
*
* @param IRI $coid
* @return int|null
*/
public static function getType(IRI $coid) {
if ($coid->getScheme()!='coid' || $coid->getHost()==''
|| preg_match(self::REGEX_HOSTNAME, $coid->getHost()) != 1)
return self::COID_INVALID;
if ($coid->getPath()=='' || $coid->getPath()=='/')
return self::COID_ROOT;
$segments = explode('/', $coid->getPath());
switch (count($segments)) {
case 2:
return (preg_match(self::REGEX_SEGMENT, $segments[1]) == 1)
? self::COID_UNVERSIONED
: self::COID_INVALID;
case 3:
if (preg_match(self::REGEX_SEGMENT, $segments[1]) != 1)
return self::COID_INVALID;
if (preg_match(self::REGEX_SEGMENT, $segments[2]) == 1)
return self::COID_VERSIONED;
else
if (preg_match(self::REGEX_VERSION_WILDCARD, $segments[2]) == 1)
return self::COID_VERSION_WILDCARD;
else
return self::COID_INVALID;
default:
return self::COID_INVALID;
} }
}
/** /**
* Checks whether the given IRI object is a valid COID. * Get the type of a COID.
* *
* @param IRI $coid * @param IRI $coid
* @return boolean * @return int|null
*/ */
public static function isValidCOID(IRI $coid) { public static function getType(IRI $coid) : ?int {
return (self::getType($coid)!=self::COID_INVALID); if ($coid->getScheme()!='coid' || $coid->getHost()==''
} || preg_match(self::REGEX_HOSTNAME, $coid->getHost()) != 1)
return self::COID_INVALID;
/** if ($coid->getPath()=='' || $coid->getPath()=='/')
* Get the name segment of a valid COID or null if not available. return self::COID_ROOT;
*
* @param IRI $coid
* @return string|null
*/
public static function getName(IRI $coid) {
if (self::getType($coid)!=self::COID_INVALID
&& self::getType($coid)!=self::COID_ROOT) {
$segments = explode('/', $coid->getPath());
return $segments[1];
} else
return null;
}
/** $segments = explode('/', $coid->getPath());
* Get the version segment of a valid, versioned COID or null if not available. switch (count($segments)) {
* case 2:
* @param IRI $coid return (preg_match(self::REGEX_SEGMENT, $segments[1]) == 1)
* @return string|null ? self::COID_UNVERSIONED
*/ : self::COID_INVALID;
public static function getVersion(IRI $coid) { case 3:
if (self::getType($coid)==self::COID_VERSIONED) { if (preg_match(self::REGEX_SEGMENT, $segments[1]) != 1)
$segments = explode('/', $coid->getPath()); return self::COID_INVALID;
return $segments[2];
} else
return null;
}
/** if (preg_match(self::REGEX_SEGMENT, $segments[2]) == 1)
* Get the version segment of a versioned or version wildcard COID or return self::COID_VERSIONED;
* null if not available. else
* if (preg_match(self::REGEX_VERSION_WILDCARD, $segments[2]) == 1)
* @param IRI $coid return self::COID_VERSION_WILDCARD;
* @return string|null else
*/ return self::COID_INVALID;
public static function getVersionWildcard(IRI $coid) { default:
if (self::getType($coid)==self::COID_VERSION_WILDCARD) { return self::COID_INVALID;
$segments = explode('/', $coid->getPath()); }
return $segments[2]; }
} else
return null; /**
} * Checks whether the given IRI object is a valid COID.
*
/** * @param IRI $coid
* Returns the COID itself if it is a root COID or a new IRI object * @return bool
* representing the namespace underlying the given COID. */
* public static function isValidCOID(IRI $coid) : bool {
* @param IRI $coid return (self::getType($coid)!=self::COID_INVALID);
* @return IRI|null }
*/
public static function getNamespaceCOID(IRI $coid) { /**
switch (self::getType($coid)) { * Get the name segment of a valid COID or null if not available.
case self::COID_ROOT: *
return $coid; * @param IRI $coid
case self::COID_UNVERSIONED: * @return string|null
case self::COID_VERSIONED: */
case self::COID_VERSION_WILDCARD: public static function getName(IRI $coid) : ?string {
return new IRI('coid://'.$coid->getHost()); if (self::getType($coid)!=self::COID_INVALID
default: && self::getType($coid)!=self::COID_ROOT) {
return null; $segments = explode('/', $coid->getPath());
return $segments[1];
} else
return null;
}
/**
* Get the version segment of a valid, versioned COID or null if not available.
*
* @param IRI $coid
* @return string|null
*/
public static function getVersion(IRI $coid) : ?string {
if (self::getType($coid)==self::COID_VERSIONED) {
$segments = explode('/', $coid->getPath());
return $segments[2];
} else
return null;
}
/**
* Get the version segment of a versioned or version wildcard COID or
* null if not available.
*
* @param IRI $coid
* @return string|null
*/
public static function getVersionWildcard(IRI $coid) : ?string {
if (self::getType($coid)==self::COID_VERSION_WILDCARD) {
$segments = explode('/', $coid->getPath());
return $segments[2];
} else
return null;
}
/**
* Returns the COID itself if it is a root COID or a new IRI object
* representing the namespace underlying the given COID.
*
* @param IRI $coid
* @return IRI|null
*/
public static function getNamespaceCOID(IRI $coid) : ?IRI {
switch (self::getType($coid)) {
case self::COID_ROOT:
return $coid;
case self::COID_UNVERSIONED:
case self::COID_VERSIONED:
case self::COID_VERSION_WILDCARD:
return new IRI('coid://'.$coid->getHost());
default:
return null;
}
} }
}
} }

View File

@@ -166,4 +166,11 @@ class CloudObject {
return $this->getString(Constants::PROPERTY_REVISION); return $this->getString(Constants::PROPERTY_REVISION);
} }
/**
* Get the label of the object.
*/
public function getLabel() : ?string {
return $this->getString(Constants::RDFS_LABEL);
}
} }

View File

@@ -9,5 +9,6 @@ namespace CloudObjects\SDK;
class Constants { class Constants {
const PROPERTY_REVISION = 'coid://cloudobjects.io/isAtRevision'; const PROPERTY_REVISION = 'coid://cloudobjects.io/isAtRevision';
const RDFS_LABEL = 'http://www.w3.org/2000/01/rdf-schema#label';
} }

View File

@@ -7,6 +7,7 @@
namespace CloudObjects\SDK\Helpers; namespace CloudObjects\SDK\Helpers;
use Exception; use Exception;
use CloudObjects\SDK\Exceptions\InvalidSDKConfigurationException;
use CloudObjects\SDK\NodeReader, CloudObjects\SDK\ObjectRetriever; use CloudObjects\SDK\NodeReader, CloudObjects\SDK\ObjectRetriever;
/** /**
@@ -27,20 +28,53 @@ class SDKLoader {
$this->reader = new NodeReader; $this->reader = new NodeReader;
} }
/**
* Initialize the SDK with the given name and options; used for
* SDKs that are initialized with a function and not a class name.
*
* @param string $sdkName The name of the SDK to initialize.
* @param array $options Additional options for the SDK (if necessary).
* @return mixed The return value of the SDK initialization, which may vary depending on the SDK.
* @throws Exception If the SDK is not supported or cannot be initialized.
*/
public function init(string $sdkName, array $options = []) : mixed {
$namespace = $this->objectRetriever->getAuthenticatingNamespaceCloudObject();
if (!$namespace)
throw new InvalidSDKConfigurationException("The authenticating namespace object could not be retrieved.");
switch (strtolower($sdkName)) {
case "sentry":
// --- Sentry (https://sentry.io/) ---
$initFunction = '\Sentry\init';
return $initFunction(array_merge([
'dsn' => $namespace->getString('coid://sentry.io.3rd-party.co/DSN')
], $options));
default:
throw new Exception("No rules defined to initialize SDK with name <".$sdkName.">.");
}
}
/** /**
* Initialize and return the SDK with the given classname. * Initialize and return the SDK with the given classname.
* Throws Exception if the SDK is not supported. * Throws Exception if the SDK is not supported.
* *
* @param $classname Classname for the SDK's main class * @param string $classname Classname for the SDK's main class.
* @param array $options Additional options for the SDK (if necessary) * @param array $options Additional options for the SDK (if necessary).
* @return mixed The initialized SDK instance.
* @throws Exception If the SDK is not supported or cannot be initialized.
*/ */
public function get($classname, array $options) { public function get(string $classname, array $options) : mixed {
if (!class_exists($classname)) if (!class_exists($classname))
throw new Exception("<".$classname."> is not a valid classname."); throw new Exception("<".$classname."> is not a valid classname.");
$hashkey = md5($classname.serialize($options)); $hashkey = md5($classname.serialize($options));
if (!isset($this->classes[$hashkey])) { if (!isset($this->classes[$hashkey])) {
$nsNode = $this->objectRetriever->getAuthenticatingNamespaceObject(); $nsNode = $this->objectRetriever->getAuthenticatingNamespaceObjectNode();
if (!$nsNode)
throw new InvalidSDKConfigurationException("The authenticating namespace object could not be retrieved.");
// --- Amazon Web Services (https://aws.amazon.com/) --- // --- Amazon Web Services (https://aws.amazon.com/) ---
// has multiple classnames, so check for common superclass // has multiple classnames, so check for common superclass

View File

@@ -206,7 +206,7 @@ class ObjectRetriever implements CustomCacheAndLogInterface {
/** /**
* Get an object description and return a CloudObject. * Get an object description and return a CloudObject.
*/ */
public function getCloudObject(IRI $coid) : CloudObject { public function getCloudObject(IRI $coid) : ?CloudObject {
$node = $this->getObjectNode($coid); $node = $this->getObjectNode($coid);
if (!$node) { if (!$node) {
// Object not found // Object not found
@@ -239,7 +239,7 @@ class ObjectRetriever implements CustomCacheAndLogInterface {
* @param IRI $coid COID of the object * @param IRI $coid COID of the object
* @return Node|null * @return Node|null
*/ */
public function getObjectNode(IRI $coid) : Node { public function getObjectNode(IRI $coid) : ?Node {
if (!COIDParser::isValidCOID($coid)) if (!COIDParser::isValidCOID($coid))
throw new Exception("Not a valid COID."); throw new Exception("Not a valid COID.");
@@ -536,7 +536,7 @@ class ObjectRetriever implements CustomCacheAndLogInterface {
* @deprecated Use getAuthenticatingNamespaceObjectNode() instead * @deprecated Use getAuthenticatingNamespaceObjectNode() instead
* @return Node * @return Node
*/ */
public function getAuthenticatingNamespaceObject() : Node { public function getAuthenticatingNamespaceObject() : ?Node {
return $this->getObject($this->assertAuthenticatingNamespaceAndGetId()); return $this->getObject($this->assertAuthenticatingNamespaceAndGetId());
} }
@@ -546,7 +546,7 @@ class ObjectRetriever implements CustomCacheAndLogInterface {
* *
* @return Node * @return Node
*/ */
public function getAuthenticatingNamespaceObjectNode() : Node { public function getAuthenticatingNamespaceObjectNode() : ?Node {
return $this->getObject($this->assertAuthenticatingNamespaceAndGetId()); return $this->getObject($this->assertAuthenticatingNamespaceAndGetId());
} }
@@ -556,7 +556,7 @@ class ObjectRetriever implements CustomCacheAndLogInterface {
* *
* @return CloudObject * @return CloudObject
*/ */
public function getAuthenticatingNamespaceCloudObject() : CloudObject { public function getAuthenticatingNamespaceCloudObject() : ?CloudObject {
return $this->getCloudObject($this->assertAuthenticatingNamespaceAndGetId()); return $this->getCloudObject($this->assertAuthenticatingNamespaceAndGetId());
} }

View File

@@ -0,0 +1,80 @@
<?php
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
namespace CloudObjects\SDK;
use Exception;
use ML\IRI\IRI;
use ML\JsonLD\Node;
/**
* Static facade for ObjectRetriever. Call setObjectRetriever() once to
* initialize; all subsequent static calls are forwarded to that instance.
*/
class ObjectRetrieverFacade {
private static ?ObjectRetriever $instance = null;
public static function setObjectRetriever(ObjectRetriever $retriever) : void {
self::$instance = $retriever;
}
private static function getInstance() : ObjectRetriever {
if (self::$instance === null)
throw new Exception('ObjectRetrieverFacade has not been initialized. Call setObjectRetriever() first.');
return self::$instance;
}
public static function setDefaultReader(NodeReader $reader) : ObjectRetriever {
return self::getInstance()->setDefaultReader($reader);
}
public static function getDefaultReader() : NodeReader {
return self::getInstance()->getDefaultReader();
}
public static function getClient() {
return self::getInstance()->getClient();
}
public static function getCloudObject(IRI $coid) : ?CloudObject {
return self::getInstance()->getCloudObject($coid);
}
public static function getObjectNode(IRI $coid) : ?Node {
return self::getInstance()->getObjectNode($coid);
}
public static function fetchObjectsInNamespaceWithType(IRI $namespaceCoid, $type) : array {
return self::getInstance()->fetchObjectsInNamespaceWithType($namespaceCoid, $type);
}
public static function fetchAllObjectsInNamespace(IRI $namespaceCoid) : array {
return self::getInstance()->fetchAllObjectsInNamespace($namespaceCoid);
}
public static function getCOIDListForNamespace(IRI $namespaceCoid) : array {
return self::getInstance()->getCOIDListForNamespace($namespaceCoid);
}
public static function getCOIDListForNamespaceWithType(IRI $namespaceCoid, $type) : array {
return self::getInstance()->getCOIDListForNamespaceWithType($namespaceCoid, $type);
}
public static function getAttachment(IRI $coid, $filename) {
return self::getInstance()->getAttachment($coid, $filename);
}
public static function getAuthenticatingNamespaceObjectNode() : ?Node {
return self::getInstance()->getAuthenticatingNamespaceObjectNode();
}
public static function getAuthenticatingNamespaceCloudObject() : ?CloudObject {
return self::getInstance()->getAuthenticatingNamespaceCloudObject();
}
}

View File

@@ -0,0 +1,23 @@
<?php
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use ML\IRI\IRI;
use CloudObjects\SDK\CloudObject,
CloudObjects\SDK\ObjectRetrieverFacade;
if (!function_exists('cloudobjects_get_object')) {
/**
* Retrieve a CloudObject by COID. Accepts a COID as an IRI object or a string.
*/
function cloudobjects_get_object($coid) : ?CloudObject {
if (is_string($coid))
$coid = new IRI($coid);
elseif (!($coid instanceof IRI))
throw new InvalidArgumentException('COID must be a string or an IRI object.');
return ObjectRetrieverFacade::getCloudObject($coid);
}
}

View File

@@ -21,7 +21,10 @@
"autoload": { "autoload": {
"psr-0": { "psr-0": {
"CloudObjects\\SDK" : "" "CloudObjects\\SDK" : ""
} },
"files": [
"CloudObjects/SDK/functions.php"
]
}, },
"require-dev" : { "require-dev" : {
"phpunit/phpunit": "^10", "phpunit/phpunit": "^10",

View File

@@ -10,121 +10,139 @@ use ML\IRI\IRI;
class COIDParserTest extends \PHPUnit\Framework\TestCase { class COIDParserTest extends \PHPUnit\Framework\TestCase {
public function testRootCOID() { public function testRootCOID() {
$coid = new IRI('coid://example.com'); $coid = new IRI('coid://example.com');
$this->assertEquals(COIDParser::COID_ROOT, COIDParser::getType($coid)); $this->assertEquals(COIDParser::COID_ROOT, COIDParser::getType($coid));
}
public function testInvalidRootCOID() { $coid = new IRI('coid://subdomain.example.com');
$coid = new IRI('coid://example'); $this->assertEquals(COIDParser::COID_ROOT, COIDParser::getType($coid));
$this->assertEquals(COIDParser::COID_INVALID, COIDParser::getType($coid));
$coid = new IRI('coid://exämple.com');
$this->assertEquals(COIDParser::COID_INVALID, COIDParser::getType($coid));
$coid = new IRI('coid://ex&mple.com');
$this->assertEquals(COIDParser::COID_INVALID, COIDParser::getType($coid));
}
public function testInvalidCOID() { $coid = new IRI('coid://anotherlevel.subdomain.example.com');
$coid = new IRI('http://example.com'); $this->assertEquals(COIDParser::COID_ROOT, COIDParser::getType($coid));
$this->assertEquals(COIDParser::COID_INVALID, COIDParser::getType($coid)); }
$coid = new IRI('example.com');
$this->assertEquals(COIDParser::COID_INVALID, COIDParser::getType($coid));
$coid = new IRI('COID://example.com');
$this->assertEquals(COIDParser::COID_INVALID, COIDParser::getType($coid));
$coid = new IRI('Coid://example.com');
$this->assertEquals(COIDParser::COID_INVALID, COIDParser::getType($coid));
$coid = new IRI('coid://EXAMPLE.COM');
$this->assertEquals(COIDParser::COID_INVALID, COIDParser::getType($coid));
$coid = new IRI('coid://exAMPle.CoM');
$this->assertEquals(COIDParser::COID_INVALID, COIDParser::getType($coid));
}
public function testUnversionedCOID() { public function testInvalidRootCOID() {
$coid = new IRI('coid://example.com/Example'); $coid = new IRI('coid://example');
$this->assertEquals(COIDParser::COID_UNVERSIONED, COIDParser::getType($coid)); $this->assertEquals(COIDParser::COID_INVALID, COIDParser::getType($coid));
}
public function testInvalidUnversionedCOID() { $coid = new IRI('coid://exämple.com');
$coid = new IRI('coid://example.com/Exümple'); $this->assertEquals(COIDParser::COID_INVALID, COIDParser::getType($coid));
$this->assertEquals(COIDParser::COID_INVALID, COIDParser::getType($coid));
$coid = new IRI('coid://example.com/Examp%e');
$this->assertEquals(COIDParser::COID_INVALID, COIDParser::getType($coid));
}
public function testVersionedCOID() { $coid = new IRI('coid://ex&mple.com');
$coid = new IRI('coid://example.com/Example/1.0'); $this->assertEquals(COIDParser::COID_INVALID, COIDParser::getType($coid));
$this->assertEquals(COIDParser::COID_VERSIONED, COIDParser::getType($coid)); }
$coid = new IRI('coid://example.com/Example/alpha');
$this->assertEquals(COIDParser::COID_VERSIONED, COIDParser::getType($coid));
}
public function testInvalidVersionedCOID() { public function testInvalidCOID() {
$coid = new IRI('coid://example.com/Example/1.$'); $coid = new IRI('http://example.com');
$this->assertEquals(COIDParser::COID_INVALID, COIDParser::getType($coid)); $this->assertEquals(COIDParser::COID_INVALID, COIDParser::getType($coid));
}
public function testVersionWildcardCOID() { $coid = new IRI('example.com');
$coid = new IRI('coid://example.com/Example/^1.0'); $this->assertEquals(COIDParser::COID_INVALID, COIDParser::getType($coid));
$this->assertEquals(COIDParser::COID_VERSION_WILDCARD, COIDParser::getType($coid));
$coid = new IRI('coid://example.com/Example/~1.0');
$this->assertEquals(COIDParser::COID_VERSION_WILDCARD, COIDParser::getType($coid));
$coid = new IRI('coid://example.com/Example/1.*');
$this->assertEquals(COIDParser::COID_VERSION_WILDCARD, COIDParser::getType($coid));
}
public function testInvalidVersionWildcardCOID() { $coid = new IRI('COID://example.com');
$coid = new IRI('coid://example.com/Example/^1.*'); $this->assertEquals(COIDParser::COID_INVALID, COIDParser::getType($coid));
$this->assertEquals(COIDParser::COID_INVALID, COIDParser::getType($coid));
$coid = new IRI('coid://example.com/Example/1.a.*');
$this->assertEquals(COIDParser::COID_INVALID, COIDParser::getType($coid));
}
public function testIRICaseSensitivity() { $coid = new IRI('Coid://example.com');
$coid1 = new IRI('coid://example.com/example/1.0'); $this->assertEquals(COIDParser::COID_INVALID, COIDParser::getType($coid));
$coid2 = new IRI('coid://example.com/Example/1.0');
$this->assertFalse($coid1->equals($coid2));
}
public function testRootFromString() { $coid = new IRI('coid://EXAMPLE.COM');
$coid1 = new IRI('coid://example.com'); $this->assertEquals(COIDParser::COID_INVALID, COIDParser::getType($coid));
$coid2 = COIDParser::fromString('coid://example.com');
$coid3 = COIDParser::fromString('example.com'); $coid = new IRI('coid://exAMPle.CoM');
$this->assertTrue($coid1->equals($coid2)); $this->assertEquals(COIDParser::COID_INVALID, COIDParser::getType($coid));
$this->assertTrue($coid1->equals($coid3)); }
}
public function testUnversionedFromString() { public function testUnversionedCOID() {
$coid1 = new IRI('coid://example.com/Example'); $coid = new IRI('coid://subdomain.example.com/Example');
$coid2 = COIDParser::fromString('coid://example.com/Example'); $this->assertEquals(COIDParser::COID_UNVERSIONED, COIDParser::getType($coid));
$coid3 = COIDParser::fromString('example.com/Example'); }
$this->assertTrue($coid1->equals($coid2));
$this->assertTrue($coid1->equals($coid3));
}
public function testVersionedFromString() { public function testInvalidUnversionedCOID() {
$coid1 = new IRI('coid://example.com/Example/1.0'); $coid = new IRI('coid://example.com/Exümple');
$coid2 = COIDParser::fromString('coid://example.com/Example/1.0'); $this->assertEquals(COIDParser::COID_INVALID, COIDParser::getType($coid));
$coid3 = COIDParser::fromString('example.com/Example/1.0');
$this->assertTrue($coid1->equals($coid2));
$this->assertTrue($coid1->equals($coid3));
}
public function testNormalizeRootFromString() { $coid = new IRI('coid://example.com/Examp%e');
$coid1 = new IRI('coid://example.com'); $this->assertEquals(COIDParser::COID_INVALID, COIDParser::getType($coid));
$coid2 = COIDParser::fromString('COID://example.com'); }
$coid3 = COIDParser::fromString('ExAmple.COM');
$this->assertTrue($coid1->equals($coid2));
$this->assertTrue($coid1->equals($coid3));
}
public function testNormalizeNonRootFromString() { public function testVersionedCOID() {
$coid1 = new IRI('coid://example.com/Example'); $coid = new IRI('coid://anotherlevel.subdomain.example.com/Example/1.0');
$coid2 = COIDParser::fromString('COID://example.com/Example'); $this->assertEquals(COIDParser::COID_VERSIONED, COIDParser::getType($coid));
$coid3 = COIDParser::fromString('ExAmple.COM/Example');
$coid4 = COIDParser::fromString('ExAmple.COM/EXample'); $coid = new IRI('coid://subdomain.example.com/Example/alpha');
$this->assertTrue($coid1->equals($coid2)); $this->assertEquals(COIDParser::COID_VERSIONED, COIDParser::getType($coid));
$this->assertTrue($coid1->equals($coid3)); }
$this->assertFalse($coid1->equals($coid4));
} public function testInvalidVersionedCOID() {
$coid = new IRI('coid://example.com/Example/1.$');
$this->assertEquals(COIDParser::COID_INVALID, COIDParser::getType($coid));
}
public function testVersionWildcardCOID() {
$coid = new IRI('coid://example.com/Example/^1.0');
$this->assertEquals(COIDParser::COID_VERSION_WILDCARD, COIDParser::getType($coid));
$coid = new IRI('coid://example.com/Example/~1.0');
$this->assertEquals(COIDParser::COID_VERSION_WILDCARD, COIDParser::getType($coid));
$coid = new IRI('coid://example.com/Example/1.*');
$this->assertEquals(COIDParser::COID_VERSION_WILDCARD, COIDParser::getType($coid));
}
public function testInvalidVersionWildcardCOID() {
$coid = new IRI('coid://example.com/Example/^1.*');
$this->assertEquals(COIDParser::COID_INVALID, COIDParser::getType($coid));
$coid = new IRI('coid://example.com/Example/1.a.*');
$this->assertEquals(COIDParser::COID_INVALID, COIDParser::getType($coid));
}
public function testIRICaseSensitivity() {
$coid1 = new IRI('coid://example.com/example/1.0');
$coid2 = new IRI('coid://example.com/Example/1.0');
$this->assertFalse($coid1->equals($coid2));
}
public function testRootFromString() {
$coid1 = new IRI('coid://example.com');
$coid2 = COIDParser::fromString('coid://example.com');
$coid3 = COIDParser::fromString('example.com');
$this->assertTrue($coid1->equals($coid2));
$this->assertTrue($coid1->equals($coid3));
}
public function testUnversionedFromString() {
$coid1 = new IRI('coid://example.com/Example');
$coid2 = COIDParser::fromString('coid://example.com/Example');
$coid3 = COIDParser::fromString('example.com/Example');
$this->assertTrue($coid1->equals($coid2));
$this->assertTrue($coid1->equals($coid3));
}
public function testVersionedFromString() {
$coid1 = new IRI('coid://example.com/Example/1.0');
$coid2 = COIDParser::fromString('coid://example.com/Example/1.0');
$coid3 = COIDParser::fromString('example.com/Example/1.0');
$this->assertTrue($coid1->equals($coid2));
$this->assertTrue($coid1->equals($coid3));
}
public function testNormalizeRootFromString() {
$coid1 = new IRI('coid://example.com');
$coid2 = COIDParser::fromString('COID://example.com');
$coid3 = COIDParser::fromString('ExAmple.COM');
$this->assertTrue($coid1->equals($coid2));
$this->assertTrue($coid1->equals($coid3));
}
public function testNormalizeNonRootFromString() {
$coid1 = new IRI('coid://example.com/Example');
$coid2 = COIDParser::fromString('COID://example.com/Example');
$coid3 = COIDParser::fromString('ExAmple.COM/Example');
$coid4 = COIDParser::fromString('ExAmple.COM/EXample');
$this->assertTrue($coid1->equals($coid2));
$this->assertTrue($coid1->equals($coid3));
$this->assertFalse($coid1->equals($coid4));
}
} }

View File

@@ -0,0 +1,63 @@
<?php
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
namespace CloudObjects\SDK;
use ML\IRI\IRI;
use ReflectionProperty,
InvalidArgumentException;
class FunctionsTest extends \PHPUnit\Framework\TestCase {
protected function setUp(): void {
$prop = new ReflectionProperty(ObjectRetrieverFacade::class, 'instance');
$prop->setAccessible(true);
$prop->setValue(null, null);
}
private function makeMockRetrieverReturning(?CloudObject $object) : ObjectRetriever {
$mock = $this->createMock(ObjectRetriever::class);
$mock->method('getCloudObject')->willReturn($object);
return $mock;
}
public function testAcceptsIRI(): void {
$coid = new IRI('coid://cloudobjects.io');
$mockObject = $this->createMock(CloudObject::class);
$mockRetriever = $this->createMock(ObjectRetriever::class);
$mockRetriever->expects($this->once())
->method('getCloudObject')
->with($coid)
->willReturn($mockObject);
ObjectRetrieverFacade::setObjectRetriever($mockRetriever);
$this->assertSame($mockObject, cloudobjects_get_object($coid));
}
public function testConvertsStringToIRI(): void {
$mockObject = $this->createMock(CloudObject::class);
$mockRetriever = $this->createMock(ObjectRetriever::class);
$mockRetriever->expects($this->once())
->method('getCloudObject')
->with($this->callback(fn($arg) => $arg instanceof IRI && (string)$arg === 'coid://cloudobjects.io'))
->willReturn($mockObject);
ObjectRetrieverFacade::setObjectRetriever($mockRetriever);
$this->assertSame($mockObject, cloudobjects_get_object('coid://cloudobjects.io'));
}
public function testThrowsOnInvalidArgumentType(): void {
ObjectRetrieverFacade::setObjectRetriever($this->makeMockRetrieverReturning(null));
$this->expectException(InvalidArgumentException::class);
cloudobjects_get_object(42);
}
}

View File

@@ -126,6 +126,15 @@ class NodeReaderMockTest extends \PHPUnit\Framework\TestCase {
$this->assertEquals('coid://cloudobjects.io/Public', $object->getNode('co:isVisibleTo')->getId()); $this->assertEquals('coid://cloudobjects.io/Public', $object->getNode('co:isVisibleTo')->getId());
} }
public function testConstants() {
$coid = new IRI('coid://cloudobjects.io');
$this->useRootResourceMock();
$object = $this->retriever->getCloudObject($coid);
$this->assertEquals('6-fbea0c90b2c5e5300e4039ed99be9b2d', $object->getRevision());
$this->assertEquals('CloudObjects', $object->getLabel());
}
public function testGetAllValuesString1() { public function testGetAllValuesString1() {
$coid = new IRI('coid://cloudobjects.io'); $coid = new IRI('coid://cloudobjects.io');
$this->useRootResourceMock(); $this->useRootResourceMock();

View File

@@ -0,0 +1,76 @@
<?php
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
namespace CloudObjects\SDK;
use Exception;
use ML\IRI\IRI;
use ML\JsonLD\Node;
use ReflectionProperty;
class ObjectRetrieverFacadeTest extends \PHPUnit\Framework\TestCase {
protected function setUp(): void {
$prop = new ReflectionProperty(ObjectRetrieverFacade::class, 'instance');
$prop->setAccessible(true);
$prop->setValue(null, null);
}
public function testThrowsExceptionBeforeInitialization(): void {
$this->expectException(Exception::class);
ObjectRetrieverFacade::getObjectNode(new IRI('coid://cloudobjects.io'));
}
public function testForwardsGetObjectNodeToInstance(): void {
$coid = new IRI('coid://cloudobjects.io');
$mockNode = $this->createMock(Node::class);
$mockRetriever = $this->createMock(ObjectRetriever::class);
$mockRetriever->expects($this->once())
->method('getObjectNode')
->with($coid)
->willReturn($mockNode);
ObjectRetrieverFacade::setObjectRetriever($mockRetriever);
$this->assertSame($mockNode, ObjectRetrieverFacade::getObjectNode($coid));
}
public function testForwardsGetCloudObjectToInstance(): void {
$coid = new IRI('coid://cloudobjects.io');
$mockCloudObject = $this->createMock(CloudObject::class);
$mockRetriever = $this->createMock(ObjectRetriever::class);
$mockRetriever->expects($this->once())
->method('getCloudObject')
->with($coid)
->willReturn($mockCloudObject);
ObjectRetrieverFacade::setObjectRetriever($mockRetriever);
$this->assertSame($mockCloudObject, ObjectRetrieverFacade::getCloudObject($coid));
}
public function testReplacingRetrieverUsesNewInstance(): void {
$coid = new IRI('coid://cloudobjects.io');
$mockNode = $this->createMock(Node::class);
$firstRetriever = $this->createMock(ObjectRetriever::class);
$firstRetriever->expects($this->never())->method('getObjectNode');
$secondRetriever = $this->createMock(ObjectRetriever::class);
$secondRetriever->expects($this->once())
->method('getObjectNode')
->with($coid)
->willReturn($mockNode);
ObjectRetrieverFacade::setObjectRetriever($firstRetriever);
ObjectRetrieverFacade::setObjectRetriever($secondRetriever);
$this->assertSame($mockNode, ObjectRetrieverFacade::getObjectNode($coid));
}
}