Imported code from old repository
This commit is contained in:
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
vendor
|
||||
build
|
||||
cache
|
||||
*.phar
|
||||
79
CloudObjects/SDK/AccountGateway/AAUIDParser.php
Normal file
79
CloudObjects/SDK/AccountGateway/AAUIDParser.php
Normal file
@@ -0,0 +1,79 @@
|
||||
<?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\AccountGateway;
|
||||
|
||||
use ML\IRI\IRI;
|
||||
|
||||
class AAUIDParser {
|
||||
|
||||
const AAUID_INVALID = 0;
|
||||
|
||||
const AAUID_ACCOUNT = 1;
|
||||
const AAUID_CONNECTION = 2;
|
||||
const AAUID_CONNECTED_ACCOUNT = 3;
|
||||
|
||||
const REGEX_AAUID = "/^[a-z0-9]{16}$/";
|
||||
const REGEX_QUALIFIER = "/^[A-Z]{2}$/";
|
||||
|
||||
/**
|
||||
* Creates a new IRI object representing a AAUID from a string.
|
||||
* Adds the "aauid:" prefix if necessary.
|
||||
*
|
||||
* @param string $aauidString An AAUID string.
|
||||
* @return IRI
|
||||
*/
|
||||
public static function fromString($aauidString) {
|
||||
return new IRI(
|
||||
(substr($aauidString, 0, 6)=='aauid:') ? $aauidString : 'aauid:'.$aauidString
|
||||
);
|
||||
}
|
||||
|
||||
public static function getType(IRI $iri) {
|
||||
if ($iri->getScheme()!='aauid' || $iri->getPath()=='')
|
||||
return self::AAUID_INVALID;
|
||||
|
||||
$segments = explode(':', $iri->getPath());
|
||||
switch (count($segments)) {
|
||||
case 1:
|
||||
return (preg_match(self::REGEX_AAUID, $segments[0]) == 1)
|
||||
? self::AAUID_ACCOUNT
|
||||
: self::AAUID_INVALID;
|
||||
case 3;
|
||||
if (preg_match(self::REGEX_AAUID, $segments[0]) != 1
|
||||
|| preg_match(self::REGEX_QUALIFIER, $segments[2]) != 1)
|
||||
return self::AAUID_INVALID;
|
||||
switch ($segments[1]) {
|
||||
case "connection":
|
||||
return self::AAUID_CONNECTION;
|
||||
case "account":
|
||||
return self::AAUID_CONNECTED_ACCOUNT;
|
||||
default:
|
||||
return self::AAUID_INVALID;
|
||||
}
|
||||
default:
|
||||
return self::AAUID_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
public static function getAAUID(IRI $iri) {
|
||||
if (self::getType($iri)!=self::AAUID_INVALID) {
|
||||
$segments = explode(':', $iri->getPath());
|
||||
return $segments[0];
|
||||
} else
|
||||
return null;
|
||||
}
|
||||
|
||||
public static function getQualifier(IRI $iri) {
|
||||
if (self::getType($iri)==self::AAUID_CONNECTION
|
||||
|| self::getType($iri)==self::AAUID_CONNECTED_ACCOUNT) {
|
||||
$segments = explode(':', $iri->getPath());
|
||||
return $segments[2];
|
||||
} else
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
369
CloudObjects/SDK/AccountGateway/AccountContext.php
Normal file
369
CloudObjects/SDK/AccountGateway/AccountContext.php
Normal file
@@ -0,0 +1,369 @@
|
||||
<?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\AccountGateway;
|
||||
|
||||
use ML\IRI\IRI;
|
||||
use ML\JsonLD\Document, ML\JsonLD\JsonLD, ML\JsonLD\Node;
|
||||
use Symfony\Component\HttpFoundation\Request, Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Bridge\PsrHttpMessage\Factory\DiactorosFactory;
|
||||
use GuzzleHttp\Client, GuzzleHttp\HandlerStack, GuzzleHttp\Middleware;
|
||||
use Psr\Http\Message\RequestInterface, Psr\Http\Message\ResponseInterface;
|
||||
|
||||
/**
|
||||
* The context of an request for an account.
|
||||
*/
|
||||
class AccountContext {
|
||||
|
||||
private $agwBaseUrl = 'https://{aauid}.aauid.net';
|
||||
private $aauid;
|
||||
private $accessToken;
|
||||
private $dataLoader;
|
||||
|
||||
private $accountDomain = null;
|
||||
private $connectionQualifier = null;
|
||||
private $installQualifier = null;
|
||||
private $accessor = null;
|
||||
private $latestAccessorVersionCOID = null;
|
||||
|
||||
private $request; // optional
|
||||
|
||||
private $document;
|
||||
private $client;
|
||||
|
||||
private $logCode = null;
|
||||
|
||||
/**
|
||||
* Create a new context using an AAUID and an OAuth 2.0 bearer access token.
|
||||
*/
|
||||
public function __construct(IRI $aauid, $accessToken, DataLoader $dataLoader = null) {
|
||||
if (AAUIDParser::getType($aauid) != AAUIDParser::AAUID_ACCOUNT)
|
||||
throw new \Exception("Not a valid AAUID");
|
||||
|
||||
$this->aauid = $aauid;
|
||||
$this->accessToken = $accessToken;
|
||||
if ($dataLoader) {
|
||||
$this->dataLoader = $dataLoader;
|
||||
} else {
|
||||
$this->dataLoader = new DataLoader;
|
||||
}
|
||||
}
|
||||
|
||||
private function parseHeaderIntoNode($headerName, Node $node) {
|
||||
$keyValuePairs = explode(',', $this->request->getHeaderLine($headerName));
|
||||
foreach ($keyValuePairs as $pair) {
|
||||
$keyValue = explode('=', $pair);
|
||||
$node->addPropertyValue($keyValue[0], urldecode($keyValue[1]));
|
||||
}
|
||||
}
|
||||
|
||||
private function parsePsrRequest(RequestInterface $request) {
|
||||
$this->request = $request;
|
||||
|
||||
if ($request->hasHeader('C-Accessor')) {
|
||||
// Store COID of Accessor
|
||||
$this->accessor = new IRI($request->getHeaderLine('C-Accessor'));
|
||||
}
|
||||
|
||||
if ($request->hasHeader('C-Account-Domain')) {
|
||||
// Store account domain
|
||||
$this->accountDomain = $request->getHeaderLine('C-Account-Domain');
|
||||
}
|
||||
|
||||
if ($request->hasHeader('C-Accessor-Latest-Version')) {
|
||||
// A new version of thie accessor is available, store its COID
|
||||
$this->latestAccessorVersionCOID = new IRI($request
|
||||
->getHeaderLine('C-Accessor-Latest-Version'));
|
||||
}
|
||||
|
||||
if ($request->hasHeader('C-Account-Connection')) {
|
||||
// For access from connected accounts, store qualifier
|
||||
$this->connectionQualifier = $request->getHeaderLine('C-Account-Connection');
|
||||
}
|
||||
|
||||
if ($request->hasHeader('C-Install-Connection')) {
|
||||
// For access from applications, store qualifier
|
||||
$this->installQualifier = $request->getHeaderLine('C-Install-Connection');
|
||||
}
|
||||
|
||||
if ($request->hasHeader('C-Connection-Data')) {
|
||||
// Copy Data into document
|
||||
if (!$this->document) $this->document = new Document();
|
||||
$this->parseHeaderIntoNode('C-Connection-Data',
|
||||
$this->document->getGraph()->createNode('aauid:'.$this->getAAUID().':connection:'.$this->connectionQualifier));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new context from the current request.
|
||||
*
|
||||
* @param Request $request
|
||||
*/
|
||||
public static function fromSymfonyRequest(Request $request) {
|
||||
if (!$request->headers->has('C-AAUID') || !$request->headers->has('C-Access-Token'))
|
||||
return null;
|
||||
|
||||
$context = new AccountContext(
|
||||
new IRI('aauid:'.$request->headers->get('C-AAUID')),
|
||||
$request->headers->get('C-Access-Token'));
|
||||
|
||||
$psr7Factory = new DiactorosFactory;
|
||||
$context->parsePsrRequest($psr7Factory->createRequest($request));
|
||||
|
||||
return $context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new context from the current request.
|
||||
*
|
||||
* @param RequestInterface $request
|
||||
*/
|
||||
public static function fromPsrRequest(RequestInterface $request) {
|
||||
if (!$request->hasHeader('C-AAUID') || !$request->hasHeader('C-Access-Token'))
|
||||
return null;
|
||||
|
||||
$context = new AccountContext(
|
||||
new IRI('aauid:'.$request->getHeaderLine('C-AAUID')),
|
||||
$request->getHeaderLine('C-Access-Token'));
|
||||
|
||||
$context->parsePsrRequest($request);
|
||||
|
||||
return $context;
|
||||
}
|
||||
|
||||
public function getAAUID() {
|
||||
return $this->aauid;
|
||||
}
|
||||
|
||||
public function getAccessToken() {
|
||||
return $this->accessToken;
|
||||
}
|
||||
|
||||
public function getRequest() {
|
||||
return $this->request;
|
||||
}
|
||||
|
||||
public function getDataLoader() {
|
||||
return $this->dataLoader;
|
||||
}
|
||||
|
||||
private function getDocument() {
|
||||
if (!$this->document) {
|
||||
$this->document = $this->dataLoader->fetchAccountGraphDataDocument($this);
|
||||
}
|
||||
|
||||
return $this->document;
|
||||
}
|
||||
|
||||
public function getAccount() {
|
||||
return $this->getDocument()->getGraph()->getNode($this->getAAUID());
|
||||
}
|
||||
|
||||
public function getPerson() {
|
||||
return $this->getDocument()->getGraph()->getNode($this->getAAUID().':person');
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the context uses an account connection, which is the case when an API
|
||||
* is requested by a connected account on another service.
|
||||
*/
|
||||
public function usesAccountConnection() {
|
||||
return ($this->connectionQualifier !== null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the qualifier of the account connection used for accessing the API.
|
||||
*/
|
||||
public function getConnectionQualifier() {
|
||||
return $this->connectionQualifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the qualifier for the connection to the platform service.
|
||||
* Only available when the accessor is an application.
|
||||
*/
|
||||
public function getInstallQualifier() {
|
||||
return $this->installQualifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the accessor.
|
||||
*/
|
||||
public function getAccessorCOID() {
|
||||
return $this->accessor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the account's domain.
|
||||
* Only set from external API requests, null otherwise.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getAccountDomain() {
|
||||
return $this->accountDomain;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a connected account.
|
||||
* @param $qualifier The qualifier for the account connection. If not specified, uses the connection qualifier.
|
||||
*/
|
||||
public function getConnectedAccount($qualifier = null) {
|
||||
if (!$qualifier) $qualifier = $this->getConnectionQualifier();
|
||||
if (!$qualifier) return null;
|
||||
return $this->getDocument()->getGraph()->getNode($this->getAAUID().':account:'.$qualifier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an account connection.
|
||||
* @param $qualifier The qualifier for the account connection. If not specified, uses the connection qualifier.
|
||||
*/
|
||||
public function getAccountConnection($qualifier = null) {
|
||||
if (!$qualifier) $qualifier = $this->getConnectionQualifier();
|
||||
if (!$qualifier) return null;
|
||||
return $this->getDocument()->getGraph()->getNode($this->getAAUID().':connection:'.$qualifier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the connected account for a service.
|
||||
* @param $service COID of the service
|
||||
*/
|
||||
public function getConnectedAccountForService($service) {
|
||||
$accounts = $this->getDocument()->getGraph()->getNodesByType('coid://aauid.net/Account');
|
||||
foreach ($accounts as $a) {
|
||||
if ($a->getProperty('coid://aauid.net/isForService')
|
||||
&& $a->getProperty('coid://aauid.net/isForService')->getId()==$service) return $a;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all account connections.
|
||||
*/
|
||||
public function getAllAccountConnections() {
|
||||
$connections = $this->getAccount()->getProperty('coid://aauid.net/hasConnection');
|
||||
if (!is_array($connections)) $connections = array($connections);
|
||||
return $connections;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all connected accounts.
|
||||
*/
|
||||
public function getAllConnectedAccounts() {
|
||||
$accounts = array();
|
||||
foreach ($this->getAllAccountConnections() as $ac) {
|
||||
$accounts[] = $ac->getProperty('coid://aauid.net/connectsTo');
|
||||
}
|
||||
return $accounts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pushes changes on the Account Graph into the Account Graph.
|
||||
*/
|
||||
public function pushGraphUpdates() {
|
||||
$this->getClient()->post('/~/', [
|
||||
'headers' => ['Content-Type' => 'application/ld+json'],
|
||||
'body' => JsonLD::toString($this->getDocument()->toJsonLd())
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies a template for the Account Gateway Base URL. Must be a valid URL that
|
||||
* may contain an {aauid} placeholder. Call this if you want to redirect traffic
|
||||
* through a proxy or a staging or mock instance of an Account Gateway. Most users
|
||||
* of this SDK should never call this function.
|
||||
*/
|
||||
public function setAccountGatewayBaseURLTemplate($baseUrl) {
|
||||
$this->agwBaseUrl = $baseUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a preconfigured Guzzle client to access the Account Gateway.
|
||||
* @return Client
|
||||
*/
|
||||
public function getClient() {
|
||||
if (!$this->client) {
|
||||
// Create custom handler stack with middlewares
|
||||
$stack = HandlerStack::create();
|
||||
|
||||
$context = $this;
|
||||
$stack->push(Middleware::mapResponse(function (ResponseInterface $response) use ($context) {
|
||||
// If a new version of this accessor is available, store its COID
|
||||
if ($response->hasHeader('C-Accessor-Latest-Version'))
|
||||
$context->setLatestAccessorVersionCOID(
|
||||
new IRI($response->getHeaderLine('C-Accessor-Latest-Version')));
|
||||
return $response;
|
||||
}));
|
||||
|
||||
// Prepare client options
|
||||
$options = [
|
||||
'base_uri' => str_replace('{aauid}', AAUIDParser::getAAUID($this->getAAUID()), $this->agwBaseUrl),
|
||||
'headers' => [
|
||||
'Authorization' => 'Bearer '.$this->getAccessToken()
|
||||
],
|
||||
'handler' => $stack
|
||||
];
|
||||
if (isset($this->request) && $this->request->hasHeader('X-Forwarded-For')) {
|
||||
$options['headers']['X-Forwarded-For'] = $this->request->getHeaderLine('X-Forwarded-For');
|
||||
}
|
||||
|
||||
// Create client
|
||||
$this->client = new Client($options);
|
||||
}
|
||||
return $this->client;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a custom code for the current request in the Account Gateway logs.
|
||||
*/
|
||||
public function setLogCode($logCode) {
|
||||
if (!$this->request) {
|
||||
throw new \Exception('Not in a request context.');
|
||||
}
|
||||
$this->logCode = $logCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a response and add headers if applicable.
|
||||
*/
|
||||
public function processResponse(Response $response) {
|
||||
if ($this->logCode) {
|
||||
$response->headers->set('C-Code-For-Logger', $this->logCode);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a new version of the accessor is available. This information
|
||||
* is updated from incoming and outgoing requests. If no request was executed,
|
||||
* returns false.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isNewAccessorVersionAvailable() {
|
||||
return isset($this->latestAccessorVersionCOID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the COID of the latest accessor version, if one is available, or
|
||||
* null otherwise. This information is updated from incoming and outgoing
|
||||
* requests. If no request was executed, returns null.
|
||||
*
|
||||
* @return IRI|null
|
||||
*/
|
||||
public function getLatestAccessorVersionCOID() {
|
||||
return $this->latestAccessorVersionCOID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the COID of the latest accessor version. This method should only
|
||||
* called from request processing codes. Most developers should not use it.
|
||||
*
|
||||
* @param IRI $latestAccessorVersionCOID
|
||||
*/
|
||||
public function setLatestAccessorVersionCOID(IRI $latestAccessorVersionCOID) {
|
||||
$this->latestAccessorVersionCOID = $latestAccessorVersionCOID;
|
||||
}
|
||||
|
||||
}
|
||||
81
CloudObjects/SDK/AccountGateway/DataLoader.php
Normal file
81
CloudObjects/SDK/AccountGateway/DataLoader.php
Normal file
@@ -0,0 +1,81 @@
|
||||
<?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\AccountGateway;
|
||||
|
||||
use Doctrine\Common\Cache\Cache;
|
||||
use ML\JsonLD\JsonLD;
|
||||
use GuzzleHttp\Psr7\Request;
|
||||
|
||||
class DataLoader {
|
||||
|
||||
const CACHE_TTL = 172800; // cache at most 48 hours
|
||||
|
||||
private $cache;
|
||||
private $cachePrefix = 'accdata:';
|
||||
private $mountPointName = '~';
|
||||
|
||||
public function getCache() {
|
||||
return $this->cache;
|
||||
}
|
||||
|
||||
public function setCache(Cache $cache) {
|
||||
$this->cache = $cache;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCachePrefix() {
|
||||
return $this->cachePrefix;
|
||||
}
|
||||
|
||||
public function setCachePrefix($cachePrefix) {
|
||||
$this->cachePrefix = $cachePrefix;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getMountPointName() {
|
||||
return $this->mountPointName;
|
||||
}
|
||||
|
||||
public function setMountPointName($mountPointName) {
|
||||
$this->mountPointName = $mountPointName;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function fetchAccountGraphDataDocument(AccountContext $accountContext) {
|
||||
$dataRequest = new Request('GET', '/'.$this->mountPointName.'/',
|
||||
['Accept' => 'application/ld+json']);
|
||||
|
||||
if (!$this->cache || !$accountContext->getRequest()
|
||||
|| !$accountContext->getRequest()->hasHeader('C-Data-Updated')) {
|
||||
// No cache or no timestamp available, so always fetch from Account Gateway
|
||||
$dataString = (string)$accountContext->getClient()->send($dataRequest)->getBody();
|
||||
} else {
|
||||
$key = $this->cachePrefix.$accountContext->getAAUID();
|
||||
$remoteTimestamp = $accountContext->getRequest()->getHeaderLine('C-Data-Updated');
|
||||
if ($this->cache->contains($key)) {
|
||||
// Check timestamp
|
||||
$cacheEntry = $this->cache->fetch($key);
|
||||
$timestamp = substr($cacheEntry, 0, strpos($cacheEntry, '|'));
|
||||
if ($timestamp==$remoteTimestamp) {
|
||||
// Cache data is up to date, can be returned
|
||||
$dataString = substr($cacheEntry, strpos($cacheEntry, '|')+1);
|
||||
} else {
|
||||
// Fetch from Account Gateway and update cache entry
|
||||
$dataString = (string)$accountContext->getClient()->send($dataRequest)->getBody();
|
||||
$this->cache->save($key, $remoteTimestamp.'|'.$dataString, self::CACHE_TTL);
|
||||
}
|
||||
} else {
|
||||
// Fetch from Account Gateway and store in cache
|
||||
$dataString = (string)$accountContext->getClient()->send($dataRequest)->getBody();
|
||||
$this->cache->save($key, $remoteTimestamp.'|'.$dataString, self::CACHE_TTL);
|
||||
}
|
||||
}
|
||||
|
||||
return JsonLD::getDocument($dataString);
|
||||
}
|
||||
|
||||
}
|
||||
152
CloudObjects/SDK/COIDParser.php
Normal file
152
CloudObjects/SDK/COIDParser.php
Normal file
@@ -0,0 +1,152 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* The COIDParser can be used to validate COIDs and extract information.
|
||||
*/
|
||||
class COIDParser {
|
||||
|
||||
const COID_INVALID = 0;
|
||||
|
||||
const COID_ROOT = 1;
|
||||
const COID_UNVERSIONED = 2;
|
||||
const COID_VERSIONED = 3;
|
||||
const COID_VERSION_WILDCARD = 4;
|
||||
|
||||
const REGEX_HOSTNAME = "/^([a-z0-9-]+\.)?[a-z0-9-]+\.[a-z]+$/";
|
||||
const REGEX_SEGMENT = "/^[A-Za-z-_0-9\.]+$/";
|
||||
const REGEX_VERSION_WILDCARD = "/^((\^|~)(\d+\.)?\d|(\d+\.){1,2}\*)$/";
|
||||
|
||||
/**
|
||||
* Creates a new IRI object representing a COID from a string.
|
||||
* Adds the "coid://" prefix if necessary and normalizes case.
|
||||
*
|
||||
* @param string $coidString A COID string.
|
||||
* @return IRI
|
||||
*/
|
||||
public static function fromString($coidString) {
|
||||
$coidPre = new IRI(
|
||||
(strtolower(substr($coidString, 0, 7))=='coid://') ? $coidString : 'coid://'.$coidString
|
||||
);
|
||||
// Normalize scheme and host segments to lower case
|
||||
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.
|
||||
*
|
||||
* @param IRI $coid
|
||||
* @return boolean
|
||||
*/
|
||||
public static function isValidCOID(IRI $coid) {
|
||||
return (self::getType($coid)!=self::COID_INVALID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name segment of a valid COID or null if not available.
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
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) {
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
72
CloudObjects/SDK/Common/CryptoHelper.php
Normal file
72
CloudObjects/SDK/Common/CryptoHelper.php
Normal file
@@ -0,0 +1,72 @@
|
||||
<?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\Common;
|
||||
|
||||
use Exception;
|
||||
use ML\IRI\IRI;
|
||||
use CloudObjects\SDK\NodeReader;
|
||||
use Defuse\Crypto\Key, Defuse\Crypto\Crypto;
|
||||
use CloudObjects\SDK\COIDParser, CloudObjects\SDK\ObjectRetriever;
|
||||
use CloudObjects\SDK\Exceptions\InvalidObjectConfigurationException;
|
||||
|
||||
/**
|
||||
* The crypto helper can be used to encrypt or decrypt data with
|
||||
* the defuse PHP encryption library.
|
||||
*/
|
||||
class CryptoHelper {
|
||||
|
||||
private $objectRetriever;
|
||||
private $namespace;
|
||||
private $reader;
|
||||
|
||||
/**
|
||||
* Gets a key based on the coid://common.cloudobjects.io/usesSharedEncryptionKey value
|
||||
* for the default namespace.
|
||||
*/
|
||||
public function getSharedEncryptionKey() {
|
||||
$keyValue = $this->reader->getFirstValueString($this->namespace, 'common:usesSharedEncryptionKey');
|
||||
if (!isset($keyValue))
|
||||
throw new InvalidObjectConfigurationException("The namespace doesn't have an encryption key.");
|
||||
|
||||
return Key::loadFromAsciiSafeString($keyValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypt data with the default namespace's shared encryption key.
|
||||
*/
|
||||
public function encryptWithSharedEncryptionKey($data) {
|
||||
return Crypto::encrypt($data, $this->getSharedEncryptionKey());
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypt data with the default namespace's shared encryption key.
|
||||
*/
|
||||
public function decryptWithSharedEncryptionKey($data) {
|
||||
return Crypto::decrypt($data, $this->getSharedEncryptionKey());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ObjectRetriever $objectRetriever An initialized and authenticated object retriever.
|
||||
* @param IRI|null $namespaceCoid The namespace used to retrieve keys. If this parameter is not provided, the namespace provided with the "auth_ns" configuration option from the object retriever is used.
|
||||
*/
|
||||
public function __construct(ObjectRetriever $objectRetriever, IRI $namespaceCoid = null) {
|
||||
if (!class_exists('Defuse\Crypto\Crypto'))
|
||||
throw new Exception("Run composer require defuse/php-encryption before using CryptoHelper.");
|
||||
|
||||
$this->objectRetriever = $objectRetriever;
|
||||
$this->namespace = isset($namespaceCoid)
|
||||
? $objectRetriever->getObject($namespaceCoid)
|
||||
: $objectRetriever->getAuthenticatingNamespaceObject();
|
||||
|
||||
$this->reader = new NodeReader([
|
||||
'prefixes' => [
|
||||
'common' => 'coid://common.cloudobjects.io/'
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
||||
14
CloudObjects/SDK/Exceptions/CoreAPIException.php
Normal file
14
CloudObjects/SDK/Exceptions/CoreAPIException.php
Normal file
@@ -0,0 +1,14 @@
|
||||
<?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\Exceptions;
|
||||
|
||||
/**
|
||||
* An Exception that is thrown when the Core API returned an error.
|
||||
*/
|
||||
class CoreAPIException extends \Exception {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<?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\Exceptions;
|
||||
|
||||
/**
|
||||
* An Exception that is thrown when an object's configuration
|
||||
* doesn't match the client's expectations.
|
||||
*/
|
||||
class InvalidObjectConfigurationException extends \Exception {
|
||||
|
||||
}
|
||||
85
CloudObjects/SDK/Helpers/SDKLoader.php
Normal file
85
CloudObjects/SDK/Helpers/SDKLoader.php
Normal file
@@ -0,0 +1,85 @@
|
||||
<?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\Helpers;
|
||||
|
||||
use Exception;
|
||||
use CloudObjects\SDK\NodeReader, CloudObjects\SDK\ObjectRetriever;
|
||||
|
||||
/**
|
||||
* The SDKLoader helper allows developers to quickly load common PHP SDKs
|
||||
* from API providers and apply configuration stored in CloudObjects.
|
||||
*/
|
||||
class SDKLoader {
|
||||
|
||||
private $objectRetriever;
|
||||
private $reader;
|
||||
private $classes = [];
|
||||
|
||||
/**
|
||||
* @param ObjectRetriever $objectRetriever An initialized and authenticated object retriever.
|
||||
*/
|
||||
public function __construct(ObjectRetriever $objectRetriever) {
|
||||
$this->objectRetriever = $objectRetriever;
|
||||
$this->reader = new NodeReader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize and return the SDK with the given classname.
|
||||
* Throws Exception if the SDK is not supported.
|
||||
*
|
||||
* @param $classname Classname for the SDK's main class
|
||||
* @param array $options Additional options for the SDK (if necessary)
|
||||
*/
|
||||
public function get($classname, array $options) {
|
||||
if (!class_exists($classname))
|
||||
throw new Exception("<".$classname."> is not a valid classname.");
|
||||
|
||||
$hashkey = md5($classname.serialize($options));
|
||||
if (!isset($this->classes[$hashkey])) {
|
||||
$nsNode = $this->objectRetriever->getAuthenticatingNamespaceObject();
|
||||
|
||||
// --- Amazon Web Services (https://aws.amazon.com/) ---
|
||||
// has multiple classnames, so check for common superclass
|
||||
if (is_a($classname, 'Aws\AwsClient', true)) {
|
||||
$class = new $classname(array_merge($options, [
|
||||
'credentials' => [
|
||||
'key' => $this->reader->getFirstValueString($nsNode, 'coid://aws.3rd-party.co/accessKeyId'),
|
||||
'secret' => $this->reader->getFirstValueString($nsNode, 'coid://aws.3rd-party.co/secretAccessKey')
|
||||
]
|
||||
]));
|
||||
} else {
|
||||
switch ($classname) {
|
||||
|
||||
// --- stream (https://getstream.io/) ---
|
||||
case "GetStream\Stream\Client":
|
||||
$class = new $classname(
|
||||
$this->reader->getFirstValueString($nsNode, 'coid://getstreamio.3rd-party.co/key'),
|
||||
$this->reader->getFirstValueString($nsNode, 'coid://getstreamio.3rd-party.co/secret')
|
||||
);
|
||||
break;
|
||||
|
||||
// --- Pusher (https://pusher.com/) ---
|
||||
case "Pusher":
|
||||
$class = new $classname(
|
||||
$this->reader->getFirstValueString($nsNode, 'coid://pusher.3rd-party.co/key'),
|
||||
$this->reader->getFirstValueString($nsNode, 'coid://pusher.3rd-party.co/secret'),
|
||||
$this->reader->getFirstValueString($nsNode, 'coid://pusher.3rd-party.co/appId'),
|
||||
$options
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($class))
|
||||
throw new Exception("No rules defined to initialize <".$classname.">.");
|
||||
|
||||
$this->classes[$hashkey] = $class;
|
||||
return $this->classes[$hashkey];
|
||||
}
|
||||
|
||||
}
|
||||
110
CloudObjects/SDK/Helpers/SharedSecretAuthentication.php
Normal file
110
CloudObjects/SDK/Helpers/SharedSecretAuthentication.php
Normal file
@@ -0,0 +1,110 @@
|
||||
<?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\Helpers;
|
||||
|
||||
use ML\IRI\IRI;
|
||||
use CloudObjects\SDK\COIDParser, CloudObjects\SDK\NodeReader, CloudObjects\SDK\ObjectRetriever;
|
||||
|
||||
/**
|
||||
* The SharedSecretAuthentication helper allows developers to quickly
|
||||
* implement authentication based on CloudObjects shared secrets.
|
||||
*/
|
||||
class SharedSecretAuthentication {
|
||||
|
||||
const RESULT_OK = 0;
|
||||
const RESULT_INVALID_USERNAME = 1;
|
||||
const RESULT_INVALID_PASSWORD = 2;
|
||||
const RESULT_NAMESPACE_NOT_FOUND = 3;
|
||||
const RESULT_SHARED_SECRET_NOT_RETRIEVABLE = 4;
|
||||
const RESULT_SHARED_SECRET_INCORRECT = 5;
|
||||
|
||||
private $objectRetriever;
|
||||
|
||||
/**
|
||||
* @param ObjectRetriever $objectRetriever An initialized and authenticated object retriever.
|
||||
*/
|
||||
public function __construct(ObjectRetriever $objectRetriever) {
|
||||
$this->objectRetriever = $objectRetriever;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies credentials.
|
||||
* @deprecated
|
||||
*
|
||||
* @param ObjectRetriever $retriever Provides access to CloudObjects.
|
||||
* @param string $username Username; a domain.
|
||||
* @param string $password Password; a shared secret.
|
||||
*
|
||||
* @return integer A result constant, RESULT_OK if successful.
|
||||
*/
|
||||
public static function verifyCredentials(ObjectRetriever $retriever, $username, $password) {
|
||||
// Validate input
|
||||
$namespaceCoid = new IRI('coid://'.$username);
|
||||
if (COIDParser::getType($namespaceCoid) != COIDParser::COID_ROOT)
|
||||
return self::RESULT_INVALID_USERNAME;
|
||||
if (strlen($password) != 40)
|
||||
return self::RESULT_INVALID_PASSWORD;
|
||||
|
||||
// Retrieve namespace
|
||||
$namespace = $retriever->getObject($namespaceCoid);
|
||||
if (!isset($namespace))
|
||||
return self::RESULT_NAMESPACE_NOT_FOUND;
|
||||
|
||||
// Read and validate shared secret
|
||||
$reader = new NodeReader([
|
||||
'prefixes' => [
|
||||
'co' => 'coid://cloudobjects.io/'
|
||||
]
|
||||
]);
|
||||
$sharedSecret = $reader->getAllValuesNode($namespace, 'co:hasSharedSecret');
|
||||
if (count($sharedSecret) != 1)
|
||||
return self::RESULT_SHARED_SECRET_NOT_RETRIEVABLE;
|
||||
|
||||
if ($reader->getFirstValueString($sharedSecret[0], 'co:hasTokenValue') == $password)
|
||||
return self::RESULT_OK;
|
||||
else
|
||||
return self::RESULT_SHARED_SECRET_INCORRECT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies credentials.
|
||||
*
|
||||
* @param string $username Username; a domain.
|
||||
* @param string $password Password; a shared secret.
|
||||
*
|
||||
* @return integer A result constant, RESULT_OK if successful.
|
||||
*/
|
||||
public function verify($username, $password) {
|
||||
// Validate input
|
||||
$namespaceCoid = new IRI('coid://'.$username);
|
||||
if (COIDParser::getType($namespaceCoid) != COIDParser::COID_ROOT)
|
||||
return self::RESULT_INVALID_USERNAME;
|
||||
if (strlen($password) != 40)
|
||||
return self::RESULT_INVALID_PASSWORD;
|
||||
|
||||
// Retrieve namespace
|
||||
$namespace = $this->objectRetriever->getObject($namespaceCoid);
|
||||
if (!isset($namespace))
|
||||
return self::RESULT_NAMESPACE_NOT_FOUND;
|
||||
|
||||
// Read and validate shared secret
|
||||
$reader = new NodeReader([
|
||||
'prefixes' => [
|
||||
'co' => 'coid://cloudobjects.io/'
|
||||
]
|
||||
]);
|
||||
$sharedSecret = $reader->getAllValuesNode($namespace, 'co:hasSharedSecret');
|
||||
if (count($sharedSecret) != 1)
|
||||
return self::RESULT_SHARED_SECRET_NOT_RETRIEVABLE;
|
||||
|
||||
if ($reader->getFirstValueString($sharedSecret[0], 'co:hasTokenValue') == $password)
|
||||
return self::RESULT_OK;
|
||||
else
|
||||
return self::RESULT_SHARED_SECRET_INCORRECT;
|
||||
}
|
||||
|
||||
}
|
||||
76
CloudObjects/SDK/JSON/SchemaValidator.php
Normal file
76
CloudObjects/SDK/JSON/SchemaValidator.php
Normal 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\JSON;
|
||||
|
||||
use ML\IRI\IRI;
|
||||
use ML\JsonLD\Node;
|
||||
use Webmozart\Assert\Assert;
|
||||
use CloudObjects\SDK\ObjectRetriever, CloudObjects\SDK\NodeReader;
|
||||
|
||||
/**
|
||||
* The schema validator enables the validation of data against
|
||||
* JSON schemas in the CloudObjects RDF format.
|
||||
*/
|
||||
class SchemaValidator {
|
||||
|
||||
private $objectRetriever;
|
||||
private $reader;
|
||||
|
||||
/**
|
||||
* @param ObjectRetriever $objectRetriever An initialized and authenticated object retriever.
|
||||
*/
|
||||
public function __construct(ObjectRetriever $objectRetriever) {
|
||||
$this->objectRetriever = $objectRetriever;
|
||||
$this->reader = new NodeReader([
|
||||
'prefixes' => [
|
||||
'json' => 'coid://json.co-n.net/'
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate data against an element specification in an RDF node.
|
||||
*
|
||||
* @param mixed $data The data to validate.
|
||||
* @param Node $node The specification to validate against.
|
||||
*/
|
||||
public function validateAgainstNode($data, Node $node) {
|
||||
if ($this->reader->hasType($node, 'json:String'))
|
||||
Assert::string($data);
|
||||
elseif ($this->reader->hasType($node, 'json:Boolean'))
|
||||
Assert::boolean($data);
|
||||
elseif ($this->reader->hasType($node, 'json:Number'))
|
||||
Assert::numeric($data);
|
||||
elseif ($this->reader->hasType($node, 'json:Integer'))
|
||||
Assert::integer($data);
|
||||
elseif ($this->reader->hasType($node, 'json:Array'))
|
||||
Assert::isArray($data);
|
||||
elseif ($this->reader->hasType($node, 'json:Object')) {
|
||||
Assert::isArrayAccessible($data);
|
||||
foreach ($this->reader->getAllValuesNode($node, 'json:hasProperty') as $prop) {
|
||||
$key = $this->reader->getFirstValueString($prop, 'json:hasKey');
|
||||
if ($this->reader->getFirstValueBool($prop, 'json:isRequired') == true)
|
||||
Assert::keyExists($data, $key);
|
||||
if (isset($data[$key]))
|
||||
$this->validateAgainstNode($data[$key], $prop);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate data against a specification stored in CloudObjects.
|
||||
*
|
||||
* @param mixed $data The data to validate.
|
||||
* @param Node $node The COID of the specification.
|
||||
*/
|
||||
public function validateAgainstCOID($data, IRI $coid) {
|
||||
$object = $this->objectRetriever->getObject($coid);
|
||||
Assert::true($this->reader->hasType($object, 'json:Element'),
|
||||
"You can only validate data against JSON elements!");
|
||||
$this->validateAgainstNode($data, $object);
|
||||
}
|
||||
}
|
||||
364
CloudObjects/SDK/NodeReader.php
Normal file
364
CloudObjects/SDK/NodeReader.php
Normal file
@@ -0,0 +1,364 @@
|
||||
<?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\JsonLD\Node;
|
||||
use ML\IRI\IRI;
|
||||
|
||||
/**
|
||||
* The NodeReader provides some convenience methods for reading information
|
||||
* from an object graph node.
|
||||
*/
|
||||
class NodeReader {
|
||||
|
||||
private $prefixes = [];
|
||||
|
||||
public function __construct(array $options = []) {
|
||||
if (isset($options['prefixes']))
|
||||
$this->prefixes = $options['prefixes'];
|
||||
}
|
||||
|
||||
private function expand($uri) {
|
||||
if (!is_string($uri)) $uri = (string)$uri;
|
||||
$scheme = parse_url($uri, PHP_URL_SCHEME);
|
||||
if (isset($scheme) && isset($this->prefixes[$scheme]))
|
||||
return str_replace($scheme.':', $this->prefixes[$scheme], $uri);
|
||||
else
|
||||
return $uri;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a node has a certain type.
|
||||
*
|
||||
* @param Node $node The node to work on.
|
||||
* @param string|object $type The type to check for.
|
||||
* @return boolean
|
||||
*/
|
||||
public function hasType(Node $node = null, $type) {
|
||||
if (!isset($node))
|
||||
return false;
|
||||
$type = $this->expand($type);
|
||||
$typesFromNode = $node->getType();
|
||||
if (!isset($typesFromNode))
|
||||
return false;
|
||||
if (is_array($typesFromNode)) {
|
||||
foreach ($typesFromNode as $t)
|
||||
if (is_a($t, 'ML\JsonLD\Node')
|
||||
&& $t->getId() == $type)
|
||||
return true;
|
||||
} else
|
||||
if (is_a($typesFromNode, 'ML\JsonLD\Node')
|
||||
&& $typesFromNode->getId() == $type)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function getFirstValue(Node $node = null, $property, $default = null) {
|
||||
if (!isset($node))
|
||||
return $default;
|
||||
$valueFromNode = $node->getProperty($this->expand($property));
|
||||
if (!isset($valueFromNode))
|
||||
return $default;
|
||||
if (is_array($valueFromNode))
|
||||
return $valueFromNode[0];
|
||||
|
||||
return $valueFromNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a property from a node and converts it into a string.
|
||||
* If the property has multiple values only the first is returned.
|
||||
* If no value is found or the node is null, the default is returned.
|
||||
*
|
||||
* @param Node $node The node to work on.
|
||||
* @param string|object $property The property to read.
|
||||
* @param $default The default that is returned if no value for the property exists on the node.
|
||||
* @return string|null
|
||||
*/
|
||||
public function getFirstValueString(Node $node = null, $property, $default = null) {
|
||||
$valueFromNode = $this->getFirstValue($node, $property, $default);
|
||||
if ($valueFromNode == $default)
|
||||
return $default;
|
||||
|
||||
if (is_a($valueFromNode, 'ML\JsonLD\Node'))
|
||||
return $valueFromNode->getId();
|
||||
else
|
||||
return $valueFromNode->getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a property from a node and converts it into a boolean.
|
||||
* If the property has multiple values only the first is returned.
|
||||
* If no value is found or the node is null, the default is returned.
|
||||
*
|
||||
* @param Node $node The node to work on.
|
||||
* @param string|object $property The property to read.
|
||||
* @param $default The default that is returned if no value for the property exists on the node.
|
||||
* @return bool|null
|
||||
*/
|
||||
public function getFirstValueBool(Node $node = null, $property, $default = null) {
|
||||
return (in_array(
|
||||
$this->getFirstValueString($node, $property, $default),
|
||||
[ '1', 'true' ]
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a property from a node and converts it into an integer.
|
||||
* If the property has multiple values only the first is returned.
|
||||
* If no value is found or the node is null, the default is returned.
|
||||
*
|
||||
* @param Node $node The node to work on.
|
||||
* @param string|object $property The property to read.
|
||||
* @param $default The default that is returned if no value for the property exists on the node.
|
||||
* @return int|null
|
||||
*/
|
||||
public function getFirstValueInt(Node $node = null, $property, $default = null) {
|
||||
$value = $this->getFirstValueString($node, $property);
|
||||
if (is_numeric($value))
|
||||
return (int)($value);
|
||||
|
||||
return $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a property from a node and converts it into an float.
|
||||
* If the property has multiple values only the first is returned.
|
||||
* If no value is found or the node is null, the default is returned.
|
||||
*
|
||||
* @param Node $node The node to work on.
|
||||
* @param string|object $property The property to read.
|
||||
* @param $default The default that is returned if no value for the property exists on the node.
|
||||
* @return float|null
|
||||
*/
|
||||
public function getFirstValueFloat(Node $node = null, $property, $default = null) {
|
||||
$value = $this->getFirstValueString($node, $property);
|
||||
if (is_numeric($value))
|
||||
return (float)($value);
|
||||
|
||||
return $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a property from a node and converts it into a IRI.
|
||||
* If the property has multiple values only the first is returned.
|
||||
* If no value is found, value is a literal or the node is null, the default is returned.
|
||||
*
|
||||
* @param Node $node The node to work on.
|
||||
* @param string|object $property The property to read.
|
||||
* @param $default The default that is returned if no value for the property exists on the node.
|
||||
* @return string|null
|
||||
*/
|
||||
public function getFirstValueIRI(Node $node = null, $property, IRI $default = null) {
|
||||
$valueFromNode = $this->getFirstValue($node, $property, $default);
|
||||
if ($valueFromNode == $default)
|
||||
return $default;
|
||||
|
||||
if (is_a($valueFromNode, 'ML\JsonLD\Node'))
|
||||
return new IRI($valueFromNode->getId());
|
||||
else
|
||||
return $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a property from a node and returns it as a Node.
|
||||
* If the property has multiple values only the first is returned.
|
||||
* If no value is found, value is a literal or the node is null, the default is returned.
|
||||
*
|
||||
* @param Node $node The node to work on.
|
||||
* @param string|object $property The property to read.
|
||||
* @param $default The default that is returned if no value for the property exists on the node.
|
||||
* @return string|null
|
||||
*/
|
||||
public function getFirstValueNode(Node $node = null, $property, Node $default = null) {
|
||||
$valueFromNode = $this->getFirstValue($node, $property, $default);
|
||||
if ($valueFromNode == $default)
|
||||
return $default;
|
||||
|
||||
if (is_a($valueFromNode, 'ML\JsonLD\Node'))
|
||||
return $valueFromNode;
|
||||
else
|
||||
return $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a node has a specific value for a property.
|
||||
*
|
||||
* @param Node $node The node to work on.
|
||||
* @param string|object $property The property to read.
|
||||
* @param string|object $value The expected value.
|
||||
* @return boolean
|
||||
*/
|
||||
public function hasPropertyValue(Node $node = null, $property, $value) {
|
||||
if (!isset($node))
|
||||
return false;
|
||||
$valuesFromNode = $node->getProperty($this->expand($property));
|
||||
if (!isset($valuesFromNode))
|
||||
return false;
|
||||
if (!is_array($valuesFromNode))
|
||||
$valuesFromNode = array($valuesFromNode);
|
||||
|
||||
foreach ($valuesFromNode as $v) {
|
||||
if (is_a($v, 'ML\JsonLD\Node')) {
|
||||
if ($v->getId() == $this->expand($value))
|
||||
return true;
|
||||
} else {
|
||||
if ($v->getValue() == $value)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the node has at least one value for a property.
|
||||
*
|
||||
* @param Node $node The node to work on.
|
||||
* @param string|object $property The property to read.
|
||||
* @return boolean
|
||||
*/
|
||||
public function hasProperty(Node $node = null, $property) {
|
||||
if (!isset($node))
|
||||
return false;
|
||||
|
||||
return ($node->getProperty($this->expand($property)) != null);
|
||||
}
|
||||
|
||||
private function getAllValues(Node $node = null, $property) {
|
||||
if (!isset($node))
|
||||
return [];
|
||||
|
||||
$valueFromNode = $node->getProperty($this->expand($property));
|
||||
if (!isset($valueFromNode))
|
||||
return [];
|
||||
if (!is_array($valueFromNode))
|
||||
$valueFromNode = [$valueFromNode];
|
||||
return $valueFromNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the language-tagged-string for the property in the specified language.
|
||||
* If no value is found for the specified language, the default is returned.
|
||||
*/
|
||||
public function getLocalizedString(Node $node = null, $property, $language, $default = null) {
|
||||
$values = $this->getAllValues($node, $property);
|
||||
foreach ($values as $v) {
|
||||
if (is_a($v, 'ML\JsonLD\LanguageTaggedString') && $v->getLanguage() == $language)
|
||||
return $v->getValue();
|
||||
}
|
||||
|
||||
return $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads all values from a node and returns them as a string array.
|
||||
*
|
||||
* @param Node $node The node to work on.
|
||||
* @param string|object $property The property to read.
|
||||
* @return array<string>
|
||||
*/
|
||||
public function getAllValuesString(Node $node = null, $property) {
|
||||
$allValues = $this->getAllValues($node, $property);
|
||||
$output = [];
|
||||
foreach ($allValues as $a)
|
||||
if (is_a($a, 'ML\JsonLD\Node'))
|
||||
$output[] = $a->getId();
|
||||
else
|
||||
$output[] = $a->getValue();
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads all values from a node and returns them as a boolean array.
|
||||
*
|
||||
* @param Node $node The node to work on.
|
||||
* @param string|object $property The property to read.
|
||||
* @return array<bool>
|
||||
*/
|
||||
public function getAllValuesBool(Node $node = null, $property) {
|
||||
$allValues = $this->getAllValuesString($node, $property);
|
||||
$output = [];
|
||||
foreach ($allValues as $a)
|
||||
$output = in_array($a, [ '1', 'true' ]);
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads all values from a node and returns them as an integer array.
|
||||
*
|
||||
* @param Node $node The node to work on.
|
||||
* @param string|object $property The property to read.
|
||||
* @return array<bool>
|
||||
*/
|
||||
public function getAllValuesInt(Node $node = null, $property) {
|
||||
$allValues = $this->getAllValuesString($node, $property);
|
||||
$output = [];
|
||||
foreach ($allValues as $a)
|
||||
$output = (int)$a;
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads all values from a node and returns them as a float array.
|
||||
*
|
||||
* @param Node $node The node to work on.
|
||||
* @param string|object $property The property to read.
|
||||
* @return array<bool>
|
||||
*/
|
||||
public function getAllValuesFloat(Node $node = null, $property) {
|
||||
$allValues = $this->getAllValuesString($node, $property);
|
||||
$output = [];
|
||||
foreach ($allValues as $a)
|
||||
$output = (float)$a;
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads all values from a node and returns them as a IRI array.
|
||||
* Only converts the Node IDs of nodes into IRI, literal values are skipped.
|
||||
*
|
||||
* @param Node $node The node to work on.
|
||||
* @param string|object $property The property to read.
|
||||
* @return array<IRI>
|
||||
*/
|
||||
public function getAllValuesIRI(Node $node = null, $property) {
|
||||
$allValues = $this->getAllValues($node, $property);
|
||||
$output = [];
|
||||
foreach ($allValues as $a)
|
||||
if (is_a($a, 'ML\JsonLD\Node'))
|
||||
$output[] = new IRI($a->getId());
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads all values from a node and returns them as a Node array.
|
||||
* Returns only nodes, literal values are skipped.
|
||||
*
|
||||
* @param Node $node The node to work on.
|
||||
* @param string|object $property The property to read.
|
||||
* @return array<Node>
|
||||
*/
|
||||
public function getAllValuesNode(Node $node = null, $property) {
|
||||
$allValues = $this->getAllValues($node, $property);
|
||||
$output = [];
|
||||
foreach ($allValues as $a)
|
||||
if (is_a($a, 'ML\JsonLD\Node'))
|
||||
$output[] = $a;
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
}
|
||||
464
CloudObjects/SDK/ObjectRetriever.php
Normal file
464
CloudObjects/SDK/ObjectRetriever.php
Normal file
@@ -0,0 +1,464 @@
|
||||
<?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, ML\JsonLD\JsonLD;
|
||||
use Doctrine\Common\Cache\RedisCache;
|
||||
use Psr\Log\LoggerInterface, Psr\Log\LoggerAwareTrait;
|
||||
use GuzzleHttp\ClientInterface, GuzzleHttp\Client, GuzzleHttp\HandlerStack;
|
||||
use GuzzleHttp\Exception\RequestException;
|
||||
use Kevinrob\GuzzleCache\CacheMiddleware, Kevinrob\GuzzleCache\Storage\DoctrineCacheStorage;
|
||||
use Kevinrob\GuzzleCache\Strategy\PrivateCacheStrategy;
|
||||
use CloudObjects\SDK\Exceptions\CoreAPIException;
|
||||
use CloudObjects\SDK\AccountGateway\AccountContext;
|
||||
|
||||
/**
|
||||
* The ObjectRetriever provides access to objects on CloudObjects.
|
||||
*/
|
||||
class ObjectRetriever {
|
||||
|
||||
use LoggerAwareTrait;
|
||||
|
||||
private $client;
|
||||
private $prefix;
|
||||
private $options;
|
||||
private $cache;
|
||||
private $objects;
|
||||
|
||||
const CO_API_URL = 'https://api.cloudobjects.net/';
|
||||
|
||||
const REVISION_PROPERTY = 'coid://cloudobjects.io/isAtRevision';
|
||||
|
||||
public function __construct($options = []) {
|
||||
// Merge options with defaults
|
||||
$this->options = array_merge([
|
||||
'cache_provider' => 'none',
|
||||
'cache_prefix' => 'clobj:',
|
||||
'cache_ttl' => 60,
|
||||
'static_config_path' => null,
|
||||
'auth_ns' => null,
|
||||
'auth_secret' => null,
|
||||
'api_base_url' => null,
|
||||
'logger' => null,
|
||||
'timeout' => 20,
|
||||
'connect_timeout' => 5
|
||||
], $options);
|
||||
|
||||
// Set up object cache
|
||||
switch ($this->options['cache_provider']) {
|
||||
case 'none':
|
||||
// no caching
|
||||
$this->cache = null;
|
||||
break;
|
||||
case 'redis':
|
||||
// caching with Redis
|
||||
$redis = new \Redis();
|
||||
$redis->pconnect(
|
||||
isset($this->options['cache_provider.redis.host']) ? $this->options['cache_provider.redis.host'] : '127.0.0.1',
|
||||
isset($this->options['cache_provider.redis.port']) ? $this->options['cache_provider.redis.port'] : 6379);
|
||||
|
||||
$this->cache = new RedisCache();
|
||||
$this->cache->setRedis($redis);
|
||||
break;
|
||||
case 'file':
|
||||
// caching on the filesystem
|
||||
$this->cache = new \Doctrine\Common\Cache\FilesystemCache(
|
||||
isset($this->options['cache_provider.file.directory']) ? $this->options['cache_provider.file.directory'] : sys_get_temp_dir()
|
||||
);
|
||||
break;
|
||||
default:
|
||||
throw new Exception('Valid values for cache_provider are: none, redis, file');
|
||||
}
|
||||
|
||||
// Set up logger
|
||||
if (is_a($this->options['logger'], LoggerInterface::class))
|
||||
$this->setLogger($this->options['logger']);
|
||||
|
||||
// Set up handler stack
|
||||
$stack = HandlerStack::create();
|
||||
|
||||
// Add HTTP cache if specified
|
||||
if (isset($this->cache)) {
|
||||
$stack->push(
|
||||
new CacheMiddleware(
|
||||
new PrivateCacheStrategy(
|
||||
new DoctrineCacheStorage($this->cache)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Initialize client
|
||||
$options = [
|
||||
'base_uri' => isset($options['api_base_url']) ? $options['api_base_url'] : self::CO_API_URL,
|
||||
'handler' => $stack,
|
||||
'connect_timeout' => $this->options['connect_timeout'],
|
||||
'timeout' => $this->options['timeout']
|
||||
];
|
||||
|
||||
if (isset($this->options['auth_ns']) && isset($this->options['auth_secret']))
|
||||
$options['auth'] = [$this->options['auth_ns'], $this->options['auth_secret']];
|
||||
|
||||
$this->client = new Client($options);
|
||||
}
|
||||
|
||||
private function logInfoWithTime($message, $ts) {
|
||||
if (isset($this->logger))
|
||||
$this->logger->info($message, [ 'elapsed_ms' => round((microtime(true) - $ts) * 1000) ]);
|
||||
}
|
||||
|
||||
private function getFromCache($id) {
|
||||
return (isset($this->cache) && $this->cache->contains($this->options['cache_prefix'].$id))
|
||||
? $this->cache->fetch($this->options['cache_prefix'].$id) : null;
|
||||
}
|
||||
|
||||
private function putIntoCache($id, $data, $ttl) {
|
||||
if (isset($this->cache))
|
||||
$this->cache->save($this->options['cache_prefix'].$id, $data, $ttl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the HTTP client that is used to access the API.
|
||||
*/
|
||||
public function getClient() {
|
||||
return $this->client;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the HTTP client that is used to access the API.
|
||||
*
|
||||
* @param ClientInterface $client The HTTP client.
|
||||
* @param string $prefix An optional prefix (e.g. an AccountGateway mountpoint)
|
||||
*/
|
||||
public function setClient(ClientInterface $client, $prefix = null) {
|
||||
$this->client = $client;
|
||||
$this->prefix = $prefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a clone of this object retriever that uses the given account
|
||||
* context to access the API as a developer API. Cache settings are inherited
|
||||
* but the prefix is extended to keep cache content specific to account.
|
||||
*
|
||||
* @param AccountContext $accountContext
|
||||
* @param string $mountpointName The name for the API mountpoint.
|
||||
*/
|
||||
public function withAccountContext(AccountContext $accountContext, string $mountpointName) {
|
||||
$newRetriever = new self($this->options);
|
||||
$newRetriever->options['cache_prefix'] .= (string)$accountContext->getAAUID();
|
||||
$newRetriever->client = $accountContext->getClient();
|
||||
$newRetriever->prefix = '/'.$mountpointName.'/';
|
||||
|
||||
return $newRetriever;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an object description from CloudObjects. Attempts to get object
|
||||
* from in-memory cache first, stored static configurations next,
|
||||
* configured external cache third, and finally calls the Object API
|
||||
* on CloudObjects Core. Returns null if the object was not found.
|
||||
*
|
||||
* @param IRI $coid COID of the object
|
||||
* @return Node|null
|
||||
*/
|
||||
public function getObject(IRI $coid) {
|
||||
if (!COIDParser::isValidCOID($coid))
|
||||
throw new Exception("Not a valid COID.");
|
||||
|
||||
$uriString = (string)$coid;
|
||||
|
||||
if (isset($this->objects[$uriString]))
|
||||
// Return from in-memory cache if it exists
|
||||
return $this->objects[$uriString];
|
||||
|
||||
$ts = microtime(true);
|
||||
|
||||
if (isset($this->options['static_config_path'])) {
|
||||
$location = realpath($this->options['static_config_path'].DIRECTORY_SEPARATOR.
|
||||
$coid->getHost().str_replace('/', DIRECTORY_SEPARATOR, $coid->getPath())
|
||||
.DIRECTORY_SEPARATOR.'object.jsonld');
|
||||
|
||||
if ($location && file_exists($location)) {
|
||||
$object = $location;
|
||||
$this->logInfoWithTime('Fetched <'.$uriString.'> from static configuration.', $ts);
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($object)) {
|
||||
$object = $this->getFromCache($uriString);
|
||||
if (isset($object))
|
||||
$this->logInfoWithTime('Fetched <'.$uriString.'> from object cache.', $ts);
|
||||
}
|
||||
|
||||
if (!isset($object)) {
|
||||
try {
|
||||
$response = $this->client
|
||||
->get((isset($this->prefix) ? $this->prefix : '').$coid->getHost().$coid->getPath().'/object',
|
||||
['headers' => ['Accept' => 'application/ld+json']]);
|
||||
|
||||
$object = (string)$response->getBody();
|
||||
$this->putIntoCache($uriString, $object, $this->options['cache_ttl']);
|
||||
$this->logInfoWithTime('Fetched <'.$uriString.'> from Core API ['.$response->getStatusCode().'].', $ts);
|
||||
} catch (RequestException $e) {
|
||||
if ($e->hasResponse())
|
||||
$this->logInfoWithTime('Object <'.$uriString.'> not found in Core API ['.$e->getResponse()->getStatusCode().'].', $ts);
|
||||
else
|
||||
$this->logInfoWithTime('Object <'.$uriString.'> could not be retrieved from Core API.', $ts);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
$document = JsonLD::getDocument($object);
|
||||
$this->objects[$uriString] = $document->getGraph()->getNode($uriString);
|
||||
return $this->objects[$uriString];
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch all object descriptions for objects in a specific namespace
|
||||
* and with a certain type from CloudObjects. Adds individual objects
|
||||
* to cache and returns a list of COIDs (as IRI) for them. The list
|
||||
* itself is not cached, which means that every call of this function
|
||||
* goes to the Object API.
|
||||
*
|
||||
* @param IRI $namespaceCoid COID of the namespace
|
||||
* @param $type RDF type that objects should have
|
||||
* @return array<IRI>
|
||||
*/
|
||||
public function fetchObjectsInNamespaceWithType(IRI $namespaceCoid, $type) {
|
||||
if (COIDParser::getType($namespaceCoid) != COIDParser::COID_ROOT)
|
||||
throw new Exception("Not a valid namespace COID.");
|
||||
|
||||
$ts = microtime(true);
|
||||
$type = (string)$type;
|
||||
|
||||
try {
|
||||
$response = $this->client
|
||||
->get((isset($this->prefix) ? $this->prefix : '').$namespaceCoid->getHost().'/all',
|
||||
[
|
||||
'headers' => [ 'Accept' => 'application/ld+json' ],
|
||||
'query' => [ 'type' => $type ]
|
||||
]);
|
||||
|
||||
$document = JsonLD::getDocument((string)$response->getBody());
|
||||
$allObjects = $document->getGraph()->getNodesByType($type);
|
||||
$allIris = [];
|
||||
foreach ($allObjects as $object) {
|
||||
$iri = new IRI($object->getId());
|
||||
if (!COIDParser::isValidCOID($iri)) continue;
|
||||
if ($iri->getHost() != $namespaceCoid->getHost()) continue;
|
||||
|
||||
$this->objects[$object->getId()] = $object;
|
||||
$this->putIntoCache($object->getId(), $object, $this->options['cache_ttl']);
|
||||
$allIris[] = $iri;
|
||||
}
|
||||
|
||||
$this->logInfoWithTime('Fetched all objects with <'.$type.'> for <'.$namespaceCoid->getHost().'> from Core API ['.$response->getStatusCode().'].', $ts);
|
||||
} catch (Exception $e) {
|
||||
throw new CoreAPIException;
|
||||
}
|
||||
|
||||
return $allIris;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch all object descriptions for objects in a specific namespace
|
||||
* from CloudObjects. Adds individual objects to cache and returns a
|
||||
* list of COIDs (as IRI) for them. The list itself is not cached,
|
||||
* which means that every call of this function goes to the Object API.
|
||||
*
|
||||
* @param IRI $namespaceCoid COID of the namespace
|
||||
* @return array<IRI>
|
||||
*/
|
||||
public function fetchAllObjectsInNamespace(IRI $namespaceCoid) {
|
||||
if (COIDParser::getType($namespaceCoid) != COIDParser::COID_ROOT)
|
||||
throw new Exception("Not a valid namespace COID.");
|
||||
|
||||
$ts = microtime(true);
|
||||
|
||||
try {
|
||||
$response = $this->client
|
||||
->get((isset($this->prefix) ? $this->prefix : '').$namespaceCoid->getHost().'/all',
|
||||
[ 'headers' => [ 'Accept' => 'application/ld+json' ] ]);
|
||||
|
||||
$document = JsonLD::getDocument((string)$response->getBody());
|
||||
$allObjects = $document->getGraph()->getNodes();
|
||||
$allIris = [];
|
||||
foreach ($allObjects as $object) {
|
||||
$iri = new IRI($object->getId());
|
||||
if (!COIDParser::isValidCOID($iri)) continue;
|
||||
if ($iri->getHost() != $namespaceCoid->getHost()) continue;
|
||||
|
||||
$this->objects[$object->getId()] = $object;
|
||||
$this->putIntoCache($object->getId(), $object, $this->options['cache_ttl']);
|
||||
$allIris[] = $iri;
|
||||
}
|
||||
|
||||
$this->logInfoWithTime('Fetched all objects for <'.$namespaceCoid->getHost().'> from Core API ['.$response->getStatusCode().'].', $ts);
|
||||
|
||||
} catch (Exception $e) {
|
||||
throw new CoreAPIException;
|
||||
}
|
||||
|
||||
return $allIris;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a list of COIDs for all objects in a specific namespace
|
||||
* from CloudObjects, but not the objects itself. The list is not cached,
|
||||
* which means that every call of this function goes to the Object API.
|
||||
*
|
||||
* @param IRI $namespaceCoid COID of the namespace
|
||||
* @return array<IRI>
|
||||
*/
|
||||
public function getCOIDListForNamespace(IRI $namespaceCoid) {
|
||||
if (COIDParser::getType($namespaceCoid) != COIDParser::COID_ROOT)
|
||||
throw new Exception("Not a valid namespace COID.");
|
||||
|
||||
$ts = microtime(true);
|
||||
|
||||
try {
|
||||
$response = $this->client
|
||||
->get((isset($this->prefix) ? $this->prefix : '').$namespaceCoid->getHost().'/coids',
|
||||
[ 'headers' => [ 'Accept' => 'application/ld+json' ] ]);
|
||||
|
||||
$document = JsonLD::getDocument((string)$response->getBody());
|
||||
$containerNode = $document->getGraph()->getNode('co-namespace-members://'.$namespaceCoid->getHost());
|
||||
|
||||
$reader = new NodeReader([ 'prefixes' => [ 'rdfs' => 'http://www.w3.org/2000/01/rdf-schema#' ]]);
|
||||
$allIris = $reader->getAllValuesIRI($containerNode, 'rdfs:member');
|
||||
|
||||
$this->logInfoWithTime('Fetched object list for <'.$namespaceCoid->getHost().'> from Core API ['.$response->getStatusCode().'].', $ts);
|
||||
|
||||
} catch (Exception $e) {
|
||||
throw new CoreAPIException;
|
||||
}
|
||||
|
||||
return $allIris;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a list of COIDs for all objects in a specific namespace
|
||||
* from CloudObjects, but not the objects itself. The list is not cached,
|
||||
* which means that every call of this function goes to the Object API.
|
||||
*
|
||||
* @param IRI $namespaceCoid COID of the namespace
|
||||
* @param $type RDF type that objects should have
|
||||
* @return array<IRI>
|
||||
*/
|
||||
public function getCOIDListForNamespaceWithType(IRI $namespaceCoid, $type) {
|
||||
if (COIDParser::getType($namespaceCoid) != COIDParser::COID_ROOT)
|
||||
throw new Exception("Not a valid namespace COID.");
|
||||
|
||||
$ts = microtime(true);
|
||||
$type = (string)$type;
|
||||
|
||||
try {
|
||||
$response = $this->client
|
||||
->get((isset($this->prefix) ? $this->prefix : '').$namespaceCoid->getHost().'/coids',
|
||||
[
|
||||
'headers' => [ 'Accept' => 'application/ld+json' ],
|
||||
'query' => [ 'type' => $type ]
|
||||
]);
|
||||
|
||||
$document = JsonLD::getDocument((string)$response->getBody());
|
||||
$containerNode = $document->getGraph()->getNode('co-namespace-members://'.$namespaceCoid->getHost());
|
||||
|
||||
$reader = new NodeReader([ 'prefixes' => [ 'rdfs' => 'http://www.w3.org/2000/01/rdf-schema#' ]]);
|
||||
$allIris = $reader->getAllValuesIRI($containerNode, 'rdfs:member');
|
||||
|
||||
$this->logInfoWithTime('Fetched object list with <'.$type.'> for <'.$namespaceCoid->getHost().'> from Core API ['.$response->getStatusCode().'].', $ts);
|
||||
|
||||
} catch (Exception $e) {
|
||||
throw new CoreAPIException;
|
||||
}
|
||||
|
||||
return $allIris;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an object description from CloudObjects. Shorthand method for
|
||||
* "getObject" which allows passing the COID as string instead of IRI.
|
||||
*
|
||||
* @param any $coid
|
||||
* @return Node|null
|
||||
*/
|
||||
public function get($coid) {
|
||||
if (is_string($coid))
|
||||
return $this->getObject(new IRI($coid));
|
||||
|
||||
if (is_object($coid) && get_class($coid)=='ML\IRI\IRI')
|
||||
return $this->getObject($coid);
|
||||
|
||||
throw new Exception('COID must be passed as a string or an IRI object.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a object's attachment.
|
||||
*
|
||||
* @param IRI $coid
|
||||
* @param string $filename
|
||||
*/
|
||||
public function getAttachment(IRI $coid, $filename) {
|
||||
$object = $this->getObject($coid);
|
||||
|
||||
if (!$object)
|
||||
// Cannot get attachment for non-existing object
|
||||
return null;
|
||||
|
||||
$ts = microtime(true);
|
||||
|
||||
$cacheId = $object->getId().'#'.$filename;
|
||||
$fileData = $this->getFromCache($cacheId);
|
||||
|
||||
// Parse cached data into revision and content
|
||||
if (isset($fileData)) {
|
||||
$this->logInfoWithTime('Fetched attachment <'.$filename.'> for <'.$object->getId().'> from object cache.', $ts);
|
||||
list($fileRevision, $fileContent) = explode('#', $fileData, 2);
|
||||
}
|
||||
|
||||
if (!isset($fileData)
|
||||
|| $fileRevision!=$object->getProperty(self::REVISION_PROPERTY)->getValue()) {
|
||||
|
||||
// Does not exist in cache or is outdated, fetch from CloudObjects
|
||||
try {
|
||||
$response = $this->client->get((isset($this->prefix) ? $this->prefix : '').$coid->getHost().$coid->getPath()
|
||||
.'/'.basename($filename));
|
||||
|
||||
$fileContent = $response->getBody()->getContents();
|
||||
$fileData = $object->getProperty(self::REVISION_PROPERTY)->getValue().'#'.$fileContent;
|
||||
$this->putIntoCache($cacheId, $fileData, 0);
|
||||
|
||||
$this->logInfoWithTime('Fetched attachment <'.$filename.'> for <'.$object->getId().'> from Core API ['.$response->getStatusCode().'].', $ts);
|
||||
} catch (Exception $e) {
|
||||
$this->logInfoWithTime('Attachment <'.$filename.'> for <'.$object->getId().'> not found in Core API ['.$e->getResponse()->getStatusCode().'].', $ts);
|
||||
// ignore exception - treat as non-existing file
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return $fileContent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the object that describes the namespace provided with the "auth_ns"
|
||||
* configuration option.
|
||||
*
|
||||
* @return Node
|
||||
*/
|
||||
public function getAuthenticatingNamespaceObject() {
|
||||
if (!isset($this->options['auth_ns']))
|
||||
throw new Exception("Missing 'auth_ns' configuration option.");
|
||||
|
||||
$namespaceCoid = COIDParser::fromString($this->options['auth_ns']);
|
||||
if (COIDParser::getType($namespaceCoid) != COIDParser::COID_ROOT)
|
||||
throw new Exception("The 'auth_ns' configuration option is not a valid namespace/root COID.");
|
||||
|
||||
return $this->getObject($namespaceCoid);
|
||||
}
|
||||
|
||||
}
|
||||
224
CloudObjects/SDK/WebAPI/APIClientFactory.php
Normal file
224
CloudObjects/SDK/WebAPI/APIClientFactory.php
Normal file
@@ -0,0 +1,224 @@
|
||||
<?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\WebAPI;
|
||||
|
||||
use Exception;
|
||||
use ML\IRI\IRI;
|
||||
use ML\JsonLD\Node;
|
||||
use CloudObjects\SDK\NodeReader;
|
||||
use GuzzleHttp\Client, GuzzleHttp\HandlerStack, GuzzleHttp\Middleware;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use CloudObjects\SDK\COIDParser, CloudObjects\SDK\ObjectRetriever;
|
||||
use CloudObjects\SDK\Exceptions\InvalidObjectConfigurationException,
|
||||
CloudObjects\SDK\Exceptions\CoreAPIException;
|
||||
|
||||
/**
|
||||
* The APIClientFactory can be used to create a preconfigured Guzzle HTTP API client
|
||||
* based on the configuration data available for an API on CloudObjects.
|
||||
*/
|
||||
class APIClientFactory {
|
||||
|
||||
const DEFAULT_CONNECT_TIMEOUT = 5;
|
||||
const DEFAULT_TIMEOUT = 20;
|
||||
|
||||
private $objectRetriever;
|
||||
private $namespace;
|
||||
private $reader;
|
||||
private $apiClients = [];
|
||||
|
||||
private function configureAPIKeyAuthentication(Node $api, array $clientConfig) {
|
||||
// see also: https://coid.link/webapis.co-n.net/APIKeyAuthentication
|
||||
|
||||
$apiKey = $this->reader->getFirstValueString($api, 'wa:hasFixedAPIKey');
|
||||
|
||||
if (!isset($apiKey)) {
|
||||
$apiKeyProperty = $this->reader->getFirstValueString($api, 'wa:usesAPIKeyFrom');
|
||||
if (!isset($apiKeyProperty))
|
||||
throw new InvalidObjectConfigurationException("An API must have either a fixed API key or a defined API key property.");
|
||||
$apiKey = $this->reader->getFirstValueString($this->namespace, $apiKeyProperty);
|
||||
if (!isset($apiKey))
|
||||
throw new InvalidObjectConfigurationException("The namespace does not have a value for <".$apiKeyProperty.">.");
|
||||
}
|
||||
|
||||
$parameter = $this->reader->getFirstValueNode($api, 'wa:usesAuthenticationParameter');
|
||||
|
||||
if (!isset($parameter) || !$this->reader->hasProperty($parameter, 'wa:hasKey'))
|
||||
throw new InvalidObjectConfigurationException("The API does not declare a parameter for inserting the API key.");
|
||||
|
||||
$parameterName = $this->reader->getFirstValueString($parameter, 'wa:hasKey');
|
||||
|
||||
if ($this->reader->hasType($parameter, 'wa:HeaderParameter'))
|
||||
$clientConfig['headers'][$parameterName] = $apiKey;
|
||||
|
||||
elseif ($this->reader->hasType($parameter, 'wa:QueryParameter')) {
|
||||
// Guzzle currently doesn't merge query strings from default options and the request itself,
|
||||
// therefore we're implementing this behavior with a custom middleware
|
||||
$handler = HandlerStack::create();
|
||||
$handler->push(Middleware::mapRequest(function (RequestInterface $request) use ($parameterName, $apiKey) {
|
||||
$uri = $request->getUri();
|
||||
$uri = $uri->withQuery(
|
||||
(!empty($uri->getQuery()) ? $uri->getQuery().'&' : '')
|
||||
. urlencode($parameterName).'='.urlencode($apiKey)
|
||||
);
|
||||
return $request->withUri($uri);
|
||||
}));
|
||||
$clientConfig['handler'] = $handler;
|
||||
}
|
||||
|
||||
else
|
||||
throw new InvalidObjectConfigurationException("The authentication parameter must be either <wa:HeaderParameter> or <wa:QueryParameter>.");
|
||||
|
||||
return $clientConfig;
|
||||
}
|
||||
|
||||
private function configureBearerTokenAuthentication(Node $api, array $clientConfig) {
|
||||
// see also: https://coid.link/webapis.co-n.net/HTTPBasicAuthentication
|
||||
|
||||
$accessToken = $this->reader->getFirstValueString($api, 'oauth2:hasFixedBearerToken');
|
||||
|
||||
if (!isset($accessToken)) {
|
||||
$tokenProperty = $this->reader->getFirstValueString($api, 'oauth2:usesFixedBearerTokenFrom');
|
||||
if (!isset($tokenProperty))
|
||||
throw new InvalidObjectConfigurationException("An API must have either a fixed access token or a defined token property.");
|
||||
$accessToken = $this->reader->getFirstValueString($this->namespace, $tokenProperty);
|
||||
if (!isset($accessToken))
|
||||
throw new InvalidObjectConfigurationException("The namespace does not have a value for <".$tokenProperty.">.");
|
||||
}
|
||||
|
||||
$clientConfig['headers']['Authorization'] = 'Bearer ' . $accessToken;
|
||||
|
||||
return $clientConfig;
|
||||
}
|
||||
|
||||
private function configureBasicAuthentication(Node $api, array $clientConfig) {
|
||||
// see also: https://coid.link/webapis.co-n.net/HTTPBasicAuthentication
|
||||
|
||||
$username = $this->reader->getFirstValueString($api, 'wa:hasFixedUsername');
|
||||
$password = $this->reader->getFirstValueString($api, 'wa:hasFixedPassword');
|
||||
|
||||
if (!isset($username)) {
|
||||
$usernameProperty = $this->reader->getFirstValueString($api, 'wa:usesUsernameFrom');
|
||||
if (!isset($usernameProperty))
|
||||
throw new InvalidObjectConfigurationException("An API must have either a fixed username or a defined username property.");
|
||||
$username = $this->reader->getFirstValueString($this->namespace, $usernameProperty);
|
||||
if (!isset($username))
|
||||
throw new InvalidObjectConfigurationException("The namespace does not have a value for <".$usernameProperty.">.");
|
||||
}
|
||||
|
||||
if (!isset($password)) {
|
||||
$passwordProperty = $this->reader->getFirstValueString($api, 'wa:usesPasswordFrom');
|
||||
if (!isset($passwordProperty))
|
||||
throw new InvalidObjectConfigurationException("An API must have either a fixed password or a defined password property.");
|
||||
$password = $this->reader->getFirstValueString($this->namespace, $passwordProperty);
|
||||
if (!isset($password))
|
||||
throw new InvalidObjectConfigurationException("The namespace does not have a value for <".$passwordProperty.">.");
|
||||
}
|
||||
|
||||
$clientConfig['auth'] = [$username, $password];
|
||||
return $clientConfig;
|
||||
}
|
||||
|
||||
private function configureSharedSecretBasicAuthentication(Node $api, array $clientConfig) {
|
||||
// see also: https://coid.link/webapis.co-n.net/SharedSecretAuthenticationViaHTTPBasic
|
||||
|
||||
$username = COIDParser::fromString($this->namespace->getId())->getHost();
|
||||
|
||||
$apiCoid = COIDParser::fromString($api->getId());
|
||||
$providerNamespaceCoid = COIDParser::getNamespaceCOID($apiCoid);
|
||||
$providerNamespace = $this->objectRetriever->get($providerNamespaceCoid);
|
||||
$sharedSecret = $this->reader->getAllValuesNode($providerNamespace, 'co:hasSharedSecret');
|
||||
if (count($sharedSecret) != 1)
|
||||
throw new CoreAPIException("Could not retrieve the shared secret.");
|
||||
|
||||
$password = $this->reader->getFirstValueString($sharedSecret[0], 'co:hasTokenValue');
|
||||
|
||||
$clientConfig['auth'] = [$username, $password];
|
||||
return $clientConfig;
|
||||
}
|
||||
|
||||
private function createClient(Node $api, bool $specificClient = false) {
|
||||
if (!$this->reader->hasType($api, 'wa:HTTPEndpoint'))
|
||||
throw new InvalidObjectConfigurationException("The API node must have the type <coid://webapis.co-n.net/HTTPEndpoint>.");
|
||||
|
||||
$baseUrl = $this->reader->getFirstValueString($api, 'wa:hasBaseURL');
|
||||
if (!isset($baseUrl))
|
||||
throw new InvalidObjectConfigurationException("The API must have a base URL.");
|
||||
|
||||
$clientConfig = [
|
||||
'base_uri' => $baseUrl,
|
||||
'connect_timeout' => self::DEFAULT_CONNECT_TIMEOUT,
|
||||
'timeout' => self::DEFAULT_TIMEOUT
|
||||
];
|
||||
|
||||
if ($this->reader->hasPropertyValue($api, 'wa:supportsAuthenticationMechanism',
|
||||
'wa:APIKeyAuthentication'))
|
||||
$clientConfig = $this->configureAPIKeyAuthentication($api, $clientConfig);
|
||||
|
||||
elseif ($this->reader->hasPropertyValue($api, 'wa:supportsAuthenticationMechanism',
|
||||
'oauth2:FixedBearerTokenAuthentication'))
|
||||
$clientConfig = $this->configureBearerTokenAuthentication($api, $clientConfig);
|
||||
|
||||
elseif ($this->reader->hasPropertyValue($api, 'wa:supportsAuthenticationMechanism',
|
||||
'wa:HTTPBasicAuthentication'))
|
||||
$clientConfig = $this->configureBasicAuthentication($api, $clientConfig);
|
||||
|
||||
elseif ($this->reader->hasPropertyValue($api, 'wa:supportsAuthenticationMechanism',
|
||||
'wa:SharedSecretAuthenticationViaHTTPBasic'))
|
||||
$clientConfig = $this->configureSharedSecretBasicAuthentication($api, $clientConfig);
|
||||
|
||||
if ($specificClient == false)
|
||||
return new Client($clientConfig);
|
||||
|
||||
if ($this->reader->hasType($api, 'wa:GraphQLEndpoint')) {
|
||||
if (!class_exists('GraphQL\Client'))
|
||||
throw new Exception("Install the gmostafa/php-graphql-client package to retrieve a specific client for wa:GraphQLEndpoint objects.");
|
||||
|
||||
return new \GraphQL\Client($clientConfig['base_uri'],
|
||||
isset($clientConfig['headers']) ? $clientConfig['headers'] : []);
|
||||
} else
|
||||
return new Client($clientConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ObjectRetriever $objectRetriever An initialized and authenticated object retriever.
|
||||
* @param IRI|null $namespaceCoid The namespace of the API client. Used to retrieve credentials. If this parameter is not provided, the namespace provided with the "auth_ns" configuration option from the object retriever is used.
|
||||
*/
|
||||
public function __construct(ObjectRetriever $objectRetriever, IRI $namespaceCoid = null) {
|
||||
$this->objectRetriever = $objectRetriever;
|
||||
$this->namespace = isset($namespaceCoid)
|
||||
? $objectRetriever->getObject($namespaceCoid)
|
||||
: $objectRetriever->getAuthenticatingNamespaceObject();
|
||||
|
||||
$this->reader = new NodeReader([
|
||||
'prefixes' => [
|
||||
'co' => 'coid://cloudobjects.io/',
|
||||
'wa' => 'coid://webapis.co-n.net/',
|
||||
'oauth2' => 'coid://oauth2.co-n.net/'
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an API client for the WebAPI with the specified COID.
|
||||
*
|
||||
* @param IRI $apiCoid WebAPI COID
|
||||
* @param boolean $specificClient If TRUE, returns a specific client class based on the API type. If FALSE, always returns a Guzzle client. Defaults to FALSE.
|
||||
* @return Client
|
||||
*/
|
||||
public function getClientWithCOID(IRI $apiCoid, bool $specificClient = false) {
|
||||
$idString = (string)$apiCoid.(string)$specificClient;
|
||||
if (!isset($this->apiClients[$idString])) {
|
||||
$object = $this->objectRetriever->getObject($apiCoid);
|
||||
if (!isset($object))
|
||||
throw new CoreAPIException("Could not retrieve API <".(string)$apiCoid.">.");
|
||||
$this->apiClients[$idString] = $this->createClient($object, $specificClient);
|
||||
}
|
||||
|
||||
return $this->apiClients[$idString];
|
||||
}
|
||||
|
||||
}
|
||||
494
LICENSE
494
LICENSE
@@ -1,373 +1,363 @@
|
||||
Mozilla Public License Version 2.0
|
||||
==================================
|
||||
Mozilla Public License, version 2.0
|
||||
|
||||
1. Definitions
|
||||
--------------
|
||||
|
||||
1.1. "Contributor"
|
||||
means each individual or legal entity that creates, contributes to
|
||||
the creation of, or owns Covered Software.
|
||||
|
||||
means each individual or legal entity that creates, contributes to the
|
||||
creation of, or owns Covered Software.
|
||||
|
||||
1.2. "Contributor Version"
|
||||
means the combination of the Contributions of others (if any) used
|
||||
by a Contributor and that particular Contributor's Contribution.
|
||||
|
||||
means the combination of the Contributions of others (if any) used by a
|
||||
Contributor and that particular Contributor's Contribution.
|
||||
|
||||
1.3. "Contribution"
|
||||
means Covered Software of a particular Contributor.
|
||||
|
||||
means Covered Software of a particular Contributor.
|
||||
|
||||
1.4. "Covered Software"
|
||||
means Source Code Form to which the initial Contributor has attached
|
||||
the notice in Exhibit A, the Executable Form of such Source Code
|
||||
Form, and Modifications of such Source Code Form, in each case
|
||||
including portions thereof.
|
||||
|
||||
means Source Code Form to which the initial Contributor has attached the
|
||||
notice in Exhibit A, the Executable Form of such Source Code Form, and
|
||||
Modifications of such Source Code Form, in each case including portions
|
||||
thereof.
|
||||
|
||||
1.5. "Incompatible With Secondary Licenses"
|
||||
means
|
||||
means
|
||||
|
||||
(a) that the initial Contributor has attached the notice described
|
||||
in Exhibit B to the Covered Software; or
|
||||
a. that the initial Contributor has attached the notice described in
|
||||
Exhibit B to the Covered Software; or
|
||||
|
||||
(b) that the Covered Software was made available under the terms of
|
||||
version 1.1 or earlier of the License, but not also under the
|
||||
terms of a Secondary License.
|
||||
b. that the Covered Software was made available under the terms of
|
||||
version 1.1 or earlier of the License, but not also under the terms of
|
||||
a Secondary License.
|
||||
|
||||
1.6. "Executable Form"
|
||||
means any form of the work other than Source Code Form.
|
||||
|
||||
means any form of the work other than Source Code Form.
|
||||
|
||||
1.7. "Larger Work"
|
||||
means a work that combines Covered Software with other material, in
|
||||
a separate file or files, that is not Covered Software.
|
||||
|
||||
means a work that combines Covered Software with other material, in a
|
||||
separate file or files, that is not Covered Software.
|
||||
|
||||
1.8. "License"
|
||||
means this document.
|
||||
|
||||
means this document.
|
||||
|
||||
1.9. "Licensable"
|
||||
means having the right to grant, to the maximum extent possible,
|
||||
whether at the time of the initial grant or subsequently, any and
|
||||
all of the rights conveyed by this License.
|
||||
|
||||
means having the right to grant, to the maximum extent possible, whether
|
||||
at the time of the initial grant or subsequently, any and all of the
|
||||
rights conveyed by this License.
|
||||
|
||||
1.10. "Modifications"
|
||||
means any of the following:
|
||||
|
||||
(a) any file in Source Code Form that results from an addition to,
|
||||
deletion from, or modification of the contents of Covered
|
||||
Software; or
|
||||
means any of the following:
|
||||
|
||||
(b) any new file in Source Code Form that contains any Covered
|
||||
Software.
|
||||
a. any file in Source Code Form that results from an addition to,
|
||||
deletion from, or modification of the contents of Covered Software; or
|
||||
|
||||
b. any new file in Source Code Form that contains any Covered Software.
|
||||
|
||||
1.11. "Patent Claims" of a Contributor
|
||||
means any patent claim(s), including without limitation, method,
|
||||
process, and apparatus claims, in any patent Licensable by such
|
||||
Contributor that would be infringed, but for the grant of the
|
||||
License, by the making, using, selling, offering for sale, having
|
||||
made, import, or transfer of either its Contributions or its
|
||||
Contributor Version.
|
||||
|
||||
means any patent claim(s), including without limitation, method,
|
||||
process, and apparatus claims, in any patent Licensable by such
|
||||
Contributor that would be infringed, but for the grant of the License,
|
||||
by the making, using, selling, offering for sale, having made, import,
|
||||
or transfer of either its Contributions or its Contributor Version.
|
||||
|
||||
1.12. "Secondary License"
|
||||
means either the GNU General Public License, Version 2.0, the GNU
|
||||
Lesser General Public License, Version 2.1, the GNU Affero General
|
||||
Public License, Version 3.0, or any later versions of those
|
||||
licenses.
|
||||
|
||||
means either the GNU General Public License, Version 2.0, the GNU Lesser
|
||||
General Public License, Version 2.1, the GNU Affero General Public
|
||||
License, Version 3.0, or any later versions of those licenses.
|
||||
|
||||
1.13. "Source Code Form"
|
||||
means the form of the work preferred for making modifications.
|
||||
|
||||
means the form of the work preferred for making modifications.
|
||||
|
||||
1.14. "You" (or "Your")
|
||||
means an individual or a legal entity exercising rights under this
|
||||
License. For legal entities, "You" includes any entity that
|
||||
controls, is controlled by, or is under common control with You. For
|
||||
purposes of this definition, "control" means (a) the power, direct
|
||||
or indirect, to cause the direction or management of such entity,
|
||||
whether by contract or otherwise, or (b) ownership of more than
|
||||
fifty percent (50%) of the outstanding shares or beneficial
|
||||
ownership of such entity.
|
||||
|
||||
means an individual or a legal entity exercising rights under this
|
||||
License. For legal entities, "You" includes any entity that controls, is
|
||||
controlled by, or is under common control with You. For purposes of this
|
||||
definition, "control" means (a) the power, direct or indirect, to cause
|
||||
the direction or management of such entity, whether by contract or
|
||||
otherwise, or (b) ownership of more than fifty percent (50%) of the
|
||||
outstanding shares or beneficial ownership of such entity.
|
||||
|
||||
|
||||
2. License Grants and Conditions
|
||||
--------------------------------
|
||||
|
||||
2.1. Grants
|
||||
|
||||
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||
non-exclusive license:
|
||||
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||
non-exclusive license:
|
||||
|
||||
(a) under intellectual property rights (other than patent or trademark)
|
||||
Licensable by such Contributor to use, reproduce, make available,
|
||||
modify, display, perform, distribute, and otherwise exploit its
|
||||
Contributions, either on an unmodified basis, with Modifications, or
|
||||
as part of a Larger Work; and
|
||||
a. under intellectual property rights (other than patent or trademark)
|
||||
Licensable by such Contributor to use, reproduce, make available,
|
||||
modify, display, perform, distribute, and otherwise exploit its
|
||||
Contributions, either on an unmodified basis, with Modifications, or
|
||||
as part of a Larger Work; and
|
||||
|
||||
(b) under Patent Claims of such Contributor to make, use, sell, offer
|
||||
for sale, have made, import, and otherwise transfer either its
|
||||
Contributions or its Contributor Version.
|
||||
b. under Patent Claims of such Contributor to make, use, sell, offer for
|
||||
sale, have made, import, and otherwise transfer either its
|
||||
Contributions or its Contributor Version.
|
||||
|
||||
2.2. Effective Date
|
||||
|
||||
The licenses granted in Section 2.1 with respect to any Contribution
|
||||
become effective for each Contribution on the date the Contributor first
|
||||
distributes such Contribution.
|
||||
The licenses granted in Section 2.1 with respect to any Contribution
|
||||
become effective for each Contribution on the date the Contributor first
|
||||
distributes such Contribution.
|
||||
|
||||
2.3. Limitations on Grant Scope
|
||||
|
||||
The licenses granted in this Section 2 are the only rights granted under
|
||||
this License. No additional rights or licenses will be implied from the
|
||||
distribution or licensing of Covered Software under this License.
|
||||
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||
Contributor:
|
||||
The licenses granted in this Section 2 are the only rights granted under
|
||||
this License. No additional rights or licenses will be implied from the
|
||||
distribution or licensing of Covered Software under this License.
|
||||
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||
Contributor:
|
||||
|
||||
(a) for any code that a Contributor has removed from Covered Software;
|
||||
or
|
||||
a. for any code that a Contributor has removed from Covered Software; or
|
||||
|
||||
(b) for infringements caused by: (i) Your and any other third party's
|
||||
modifications of Covered Software, or (ii) the combination of its
|
||||
Contributions with other software (except as part of its Contributor
|
||||
Version); or
|
||||
b. for infringements caused by: (i) Your and any other third party's
|
||||
modifications of Covered Software, or (ii) the combination of its
|
||||
Contributions with other software (except as part of its Contributor
|
||||
Version); or
|
||||
|
||||
(c) under Patent Claims infringed by Covered Software in the absence of
|
||||
its Contributions.
|
||||
c. under Patent Claims infringed by Covered Software in the absence of
|
||||
its Contributions.
|
||||
|
||||
This License does not grant any rights in the trademarks, service marks,
|
||||
or logos of any Contributor (except as may be necessary to comply with
|
||||
the notice requirements in Section 3.4).
|
||||
This License does not grant any rights in the trademarks, service marks,
|
||||
or logos of any Contributor (except as may be necessary to comply with
|
||||
the notice requirements in Section 3.4).
|
||||
|
||||
2.4. Subsequent Licenses
|
||||
|
||||
No Contributor makes additional grants as a result of Your choice to
|
||||
distribute the Covered Software under a subsequent version of this
|
||||
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||
permitted under the terms of Section 3.3).
|
||||
No Contributor makes additional grants as a result of Your choice to
|
||||
distribute the Covered Software under a subsequent version of this
|
||||
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||
permitted under the terms of Section 3.3).
|
||||
|
||||
2.5. Representation
|
||||
|
||||
Each Contributor represents that the Contributor believes its
|
||||
Contributions are its original creation(s) or it has sufficient rights
|
||||
to grant the rights to its Contributions conveyed by this License.
|
||||
Each Contributor represents that the Contributor believes its
|
||||
Contributions are its original creation(s) or it has sufficient rights to
|
||||
grant the rights to its Contributions conveyed by this License.
|
||||
|
||||
2.6. Fair Use
|
||||
|
||||
This License is not intended to limit any rights You have under
|
||||
applicable copyright doctrines of fair use, fair dealing, or other
|
||||
equivalents.
|
||||
This License is not intended to limit any rights You have under
|
||||
applicable copyright doctrines of fair use, fair dealing, or other
|
||||
equivalents.
|
||||
|
||||
2.7. Conditions
|
||||
|
||||
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
|
||||
in Section 2.1.
|
||||
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in
|
||||
Section 2.1.
|
||||
|
||||
|
||||
3. Responsibilities
|
||||
-------------------
|
||||
|
||||
3.1. Distribution of Source Form
|
||||
|
||||
All distribution of Covered Software in Source Code Form, including any
|
||||
Modifications that You create or to which You contribute, must be under
|
||||
the terms of this License. You must inform recipients that the Source
|
||||
Code Form of the Covered Software is governed by the terms of this
|
||||
License, and how they can obtain a copy of this License. You may not
|
||||
attempt to alter or restrict the recipients' rights in the Source Code
|
||||
Form.
|
||||
All distribution of Covered Software in Source Code Form, including any
|
||||
Modifications that You create or to which You contribute, must be under
|
||||
the terms of this License. You must inform recipients that the Source
|
||||
Code Form of the Covered Software is governed by the terms of this
|
||||
License, and how they can obtain a copy of this License. You may not
|
||||
attempt to alter or restrict the recipients' rights in the Source Code
|
||||
Form.
|
||||
|
||||
3.2. Distribution of Executable Form
|
||||
|
||||
If You distribute Covered Software in Executable Form then:
|
||||
If You distribute Covered Software in Executable Form then:
|
||||
|
||||
(a) such Covered Software must also be made available in Source Code
|
||||
Form, as described in Section 3.1, and You must inform recipients of
|
||||
the Executable Form how they can obtain a copy of such Source Code
|
||||
Form by reasonable means in a timely manner, at a charge no more
|
||||
than the cost of distribution to the recipient; and
|
||||
a. such Covered Software must also be made available in Source Code Form,
|
||||
as described in Section 3.1, and You must inform recipients of the
|
||||
Executable Form how they can obtain a copy of such Source Code Form by
|
||||
reasonable means in a timely manner, at a charge no more than the cost
|
||||
of distribution to the recipient; and
|
||||
|
||||
(b) You may distribute such Executable Form under the terms of this
|
||||
License, or sublicense it under different terms, provided that the
|
||||
license for the Executable Form does not attempt to limit or alter
|
||||
the recipients' rights in the Source Code Form under this License.
|
||||
b. You may distribute such Executable Form under the terms of this
|
||||
License, or sublicense it under different terms, provided that the
|
||||
license for the Executable Form does not attempt to limit or alter the
|
||||
recipients' rights in the Source Code Form under this License.
|
||||
|
||||
3.3. Distribution of a Larger Work
|
||||
|
||||
You may create and distribute a Larger Work under terms of Your choice,
|
||||
provided that You also comply with the requirements of this License for
|
||||
the Covered Software. If the Larger Work is a combination of Covered
|
||||
Software with a work governed by one or more Secondary Licenses, and the
|
||||
Covered Software is not Incompatible With Secondary Licenses, this
|
||||
License permits You to additionally distribute such Covered Software
|
||||
under the terms of such Secondary License(s), so that the recipient of
|
||||
the Larger Work may, at their option, further distribute the Covered
|
||||
Software under the terms of either this License or such Secondary
|
||||
License(s).
|
||||
You may create and distribute a Larger Work under terms of Your choice,
|
||||
provided that You also comply with the requirements of this License for
|
||||
the Covered Software. If the Larger Work is a combination of Covered
|
||||
Software with a work governed by one or more Secondary Licenses, and the
|
||||
Covered Software is not Incompatible With Secondary Licenses, this
|
||||
License permits You to additionally distribute such Covered Software
|
||||
under the terms of such Secondary License(s), so that the recipient of
|
||||
the Larger Work may, at their option, further distribute the Covered
|
||||
Software under the terms of either this License or such Secondary
|
||||
License(s).
|
||||
|
||||
3.4. Notices
|
||||
|
||||
You may not remove or alter the substance of any license notices
|
||||
(including copyright notices, patent notices, disclaimers of warranty,
|
||||
or limitations of liability) contained within the Source Code Form of
|
||||
the Covered Software, except that You may alter any license notices to
|
||||
the extent required to remedy known factual inaccuracies.
|
||||
You may not remove or alter the substance of any license notices
|
||||
(including copyright notices, patent notices, disclaimers of warranty, or
|
||||
limitations of liability) contained within the Source Code Form of the
|
||||
Covered Software, except that You may alter any license notices to the
|
||||
extent required to remedy known factual inaccuracies.
|
||||
|
||||
3.5. Application of Additional Terms
|
||||
|
||||
You may choose to offer, and to charge a fee for, warranty, support,
|
||||
indemnity or liability obligations to one or more recipients of Covered
|
||||
Software. However, You may do so only on Your own behalf, and not on
|
||||
behalf of any Contributor. You must make it absolutely clear that any
|
||||
such warranty, support, indemnity, or liability obligation is offered by
|
||||
You alone, and You hereby agree to indemnify every Contributor for any
|
||||
liability incurred by such Contributor as a result of warranty, support,
|
||||
indemnity or liability terms You offer. You may include additional
|
||||
disclaimers of warranty and limitations of liability specific to any
|
||||
jurisdiction.
|
||||
You may choose to offer, and to charge a fee for, warranty, support,
|
||||
indemnity or liability obligations to one or more recipients of Covered
|
||||
Software. However, You may do so only on Your own behalf, and not on
|
||||
behalf of any Contributor. You must make it absolutely clear that any
|
||||
such warranty, support, indemnity, or liability obligation is offered by
|
||||
You alone, and You hereby agree to indemnify every Contributor for any
|
||||
liability incurred by such Contributor as a result of warranty, support,
|
||||
indemnity or liability terms You offer. You may include additional
|
||||
disclaimers of warranty and limitations of liability specific to any
|
||||
jurisdiction.
|
||||
|
||||
4. Inability to Comply Due to Statute or Regulation
|
||||
---------------------------------------------------
|
||||
|
||||
If it is impossible for You to comply with any of the terms of this
|
||||
License with respect to some or all of the Covered Software due to
|
||||
statute, judicial order, or regulation then You must: (a) comply with
|
||||
the terms of this License to the maximum extent possible; and (b)
|
||||
describe the limitations and the code they affect. Such description must
|
||||
be placed in a text file included with all distributions of the Covered
|
||||
Software under this License. Except to the extent prohibited by statute
|
||||
or regulation, such description must be sufficiently detailed for a
|
||||
recipient of ordinary skill to be able to understand it.
|
||||
If it is impossible for You to comply with any of the terms of this License
|
||||
with respect to some or all of the Covered Software due to statute,
|
||||
judicial order, or regulation then You must: (a) comply with the terms of
|
||||
this License to the maximum extent possible; and (b) describe the
|
||||
limitations and the code they affect. Such description must be placed in a
|
||||
text file included with all distributions of the Covered Software under
|
||||
this License. Except to the extent prohibited by statute or regulation,
|
||||
such description must be sufficiently detailed for a recipient of ordinary
|
||||
skill to be able to understand it.
|
||||
|
||||
5. Termination
|
||||
--------------
|
||||
|
||||
5.1. The rights granted under this License will terminate automatically
|
||||
if You fail to comply with any of its terms. However, if You become
|
||||
compliant, then the rights granted under this License from a particular
|
||||
Contributor are reinstated (a) provisionally, unless and until such
|
||||
Contributor explicitly and finally terminates Your grants, and (b) on an
|
||||
ongoing basis, if such Contributor fails to notify You of the
|
||||
non-compliance by some reasonable means prior to 60 days after You have
|
||||
come back into compliance. Moreover, Your grants from a particular
|
||||
Contributor are reinstated on an ongoing basis if such Contributor
|
||||
notifies You of the non-compliance by some reasonable means, this is the
|
||||
first time You have received notice of non-compliance with this License
|
||||
from such Contributor, and You become compliant prior to 30 days after
|
||||
Your receipt of the notice.
|
||||
5.1. The rights granted under this License will terminate automatically if You
|
||||
fail to comply with any of its terms. However, if You become compliant,
|
||||
then the rights granted under this License from a particular Contributor
|
||||
are reinstated (a) provisionally, unless and until such Contributor
|
||||
explicitly and finally terminates Your grants, and (b) on an ongoing
|
||||
basis, if such Contributor fails to notify You of the non-compliance by
|
||||
some reasonable means prior to 60 days after You have come back into
|
||||
compliance. Moreover, Your grants from a particular Contributor are
|
||||
reinstated on an ongoing basis if such Contributor notifies You of the
|
||||
non-compliance by some reasonable means, this is the first time You have
|
||||
received notice of non-compliance with this License from such
|
||||
Contributor, and You become compliant prior to 30 days after Your receipt
|
||||
of the notice.
|
||||
|
||||
5.2. If You initiate litigation against any entity by asserting a patent
|
||||
infringement claim (excluding declaratory judgment actions,
|
||||
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||
directly or indirectly infringes any patent, then the rights granted to
|
||||
You by any and all Contributors for the Covered Software under Section
|
||||
2.1 of this License shall terminate.
|
||||
infringement claim (excluding declaratory judgment actions,
|
||||
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||
directly or indirectly infringes any patent, then the rights granted to
|
||||
You by any and all Contributors for the Covered Software under Section
|
||||
2.1 of this License shall terminate.
|
||||
|
||||
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
|
||||
end user license agreements (excluding distributors and resellers) which
|
||||
have been validly granted by You or Your distributors under this License
|
||||
prior to termination shall survive termination.
|
||||
5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user
|
||||
license agreements (excluding distributors and resellers) which have been
|
||||
validly granted by You or Your distributors under this License prior to
|
||||
termination shall survive termination.
|
||||
|
||||
************************************************************************
|
||||
* *
|
||||
* 6. Disclaimer of Warranty *
|
||||
* ------------------------- *
|
||||
* *
|
||||
* Covered Software is provided under this License on an "as is" *
|
||||
* basis, without warranty of any kind, either expressed, implied, or *
|
||||
* statutory, including, without limitation, warranties that the *
|
||||
* Covered Software is free of defects, merchantable, fit for a *
|
||||
* particular purpose or non-infringing. The entire risk as to the *
|
||||
* quality and performance of the Covered Software is with You. *
|
||||
* Should any Covered Software prove defective in any respect, You *
|
||||
* (not any Contributor) assume the cost of any necessary servicing, *
|
||||
* repair, or correction. This disclaimer of warranty constitutes an *
|
||||
* essential part of this License. No use of any Covered Software is *
|
||||
* authorized under this License except under this disclaimer. *
|
||||
* *
|
||||
************************************************************************
|
||||
6. Disclaimer of Warranty
|
||||
|
||||
************************************************************************
|
||||
* *
|
||||
* 7. Limitation of Liability *
|
||||
* -------------------------- *
|
||||
* *
|
||||
* Under no circumstances and under no legal theory, whether tort *
|
||||
* (including negligence), contract, or otherwise, shall any *
|
||||
* Contributor, or anyone who distributes Covered Software as *
|
||||
* permitted above, be liable to You for any direct, indirect, *
|
||||
* special, incidental, or consequential damages of any character *
|
||||
* including, without limitation, damages for lost profits, loss of *
|
||||
* goodwill, work stoppage, computer failure or malfunction, or any *
|
||||
* and all other commercial damages or losses, even if such party *
|
||||
* shall have been informed of the possibility of such damages. This *
|
||||
* limitation of liability shall not apply to liability for death or *
|
||||
* personal injury resulting from such party's negligence to the *
|
||||
* extent applicable law prohibits such limitation. Some *
|
||||
* jurisdictions do not allow the exclusion or limitation of *
|
||||
* incidental or consequential damages, so this exclusion and *
|
||||
* limitation may not apply to You. *
|
||||
* *
|
||||
************************************************************************
|
||||
Covered Software is provided under this License on an "as is" basis,
|
||||
without warranty of any kind, either expressed, implied, or statutory,
|
||||
including, without limitation, warranties that the Covered Software is free
|
||||
of defects, merchantable, fit for a particular purpose or non-infringing.
|
||||
The entire risk as to the quality and performance of the Covered Software
|
||||
is with You. Should any Covered Software prove defective in any respect,
|
||||
You (not any Contributor) assume the cost of any necessary servicing,
|
||||
repair, or correction. This disclaimer of warranty constitutes an essential
|
||||
part of this License. No use of any Covered Software is authorized under
|
||||
this License except under this disclaimer.
|
||||
|
||||
7. Limitation of Liability
|
||||
|
||||
Under no circumstances and under no legal theory, whether tort (including
|
||||
negligence), contract, or otherwise, shall any Contributor, or anyone who
|
||||
distributes Covered Software as permitted above, be liable to You for any
|
||||
direct, indirect, special, incidental, or consequential damages of any
|
||||
character including, without limitation, damages for lost profits, loss of
|
||||
goodwill, work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses, even if such party shall have been
|
||||
informed of the possibility of such damages. This limitation of liability
|
||||
shall not apply to liability for death or personal injury resulting from
|
||||
such party's negligence to the extent applicable law prohibits such
|
||||
limitation. Some jurisdictions do not allow the exclusion or limitation of
|
||||
incidental or consequential damages, so this exclusion and limitation may
|
||||
not apply to You.
|
||||
|
||||
8. Litigation
|
||||
-------------
|
||||
|
||||
Any litigation relating to this License may be brought only in the
|
||||
courts of a jurisdiction where the defendant maintains its principal
|
||||
place of business and such litigation shall be governed by laws of that
|
||||
jurisdiction, without reference to its conflict-of-law provisions.
|
||||
Nothing in this Section shall prevent a party's ability to bring
|
||||
cross-claims or counter-claims.
|
||||
Any litigation relating to this License may be brought only in the courts
|
||||
of a jurisdiction where the defendant maintains its principal place of
|
||||
business and such litigation shall be governed by laws of that
|
||||
jurisdiction, without reference to its conflict-of-law provisions. Nothing
|
||||
in this Section shall prevent a party's ability to bring cross-claims or
|
||||
counter-claims.
|
||||
|
||||
9. Miscellaneous
|
||||
----------------
|
||||
|
||||
This License represents the complete agreement concerning the subject
|
||||
matter hereof. If any provision of this License is held to be
|
||||
unenforceable, such provision shall be reformed only to the extent
|
||||
necessary to make it enforceable. Any law or regulation which provides
|
||||
that the language of a contract shall be construed against the drafter
|
||||
shall not be used to construe this License against a Contributor.
|
||||
This License represents the complete agreement concerning the subject
|
||||
matter hereof. If any provision of this License is held to be
|
||||
unenforceable, such provision shall be reformed only to the extent
|
||||
necessary to make it enforceable. Any law or regulation which provides that
|
||||
the language of a contract shall be construed against the drafter shall not
|
||||
be used to construe this License against a Contributor.
|
||||
|
||||
|
||||
10. Versions of the License
|
||||
---------------------------
|
||||
|
||||
10.1. New Versions
|
||||
|
||||
Mozilla Foundation is the license steward. Except as provided in Section
|
||||
10.3, no one other than the license steward has the right to modify or
|
||||
publish new versions of this License. Each version will be given a
|
||||
distinguishing version number.
|
||||
Mozilla Foundation is the license steward. Except as provided in Section
|
||||
10.3, no one other than the license steward has the right to modify or
|
||||
publish new versions of this License. Each version will be given a
|
||||
distinguishing version number.
|
||||
|
||||
10.2. Effect of New Versions
|
||||
|
||||
You may distribute the Covered Software under the terms of the version
|
||||
of the License under which You originally received the Covered Software,
|
||||
or under the terms of any subsequent version published by the license
|
||||
steward.
|
||||
You may distribute the Covered Software under the terms of the version
|
||||
of the License under which You originally received the Covered Software,
|
||||
or under the terms of any subsequent version published by the license
|
||||
steward.
|
||||
|
||||
10.3. Modified Versions
|
||||
|
||||
If you create software not governed by this License, and you want to
|
||||
create a new license for such software, you may create and use a
|
||||
modified version of this License if you rename the license and remove
|
||||
any references to the name of the license steward (except to note that
|
||||
such modified license differs from this License).
|
||||
If you create software not governed by this License, and you want to
|
||||
create a new license for such software, you may create and use a
|
||||
modified version of this License if you rename the license and remove
|
||||
any references to the name of the license steward (except to note that
|
||||
such modified license differs from this License).
|
||||
|
||||
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
||||
Licenses
|
||||
|
||||
If You choose to distribute Source Code Form that is Incompatible With
|
||||
Secondary Licenses under the terms of this version of the License, the
|
||||
notice described in Exhibit B of this License must be attached.
|
||||
Licenses If You choose to distribute Source Code Form that is
|
||||
Incompatible With Secondary Licenses under the terms of this version of
|
||||
the License, the notice described in Exhibit B of this License must be
|
||||
attached.
|
||||
|
||||
Exhibit A - Source Code Form License Notice
|
||||
-------------------------------------------
|
||||
|
||||
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 https://mozilla.org/MPL/2.0/.
|
||||
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/.
|
||||
|
||||
If it is not possible or desirable to put the notice in a particular
|
||||
file, then You may include the notice in a location (such as a LICENSE
|
||||
file in a relevant directory) where a recipient would be likely to look
|
||||
for such a notice.
|
||||
If it is not possible or desirable to put the notice in a particular file,
|
||||
then You may include the notice in a location (such as a LICENSE file in a
|
||||
relevant directory) where a recipient would be likely to look for such a
|
||||
notice.
|
||||
|
||||
You may add additional accurate notices of copyright ownership.
|
||||
|
||||
Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||
---------------------------------------------------------
|
||||
|
||||
This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||
defined by the Mozilla Public License, v. 2.0.
|
||||
This Source Code Form is "Incompatible
|
||||
With Secondary Licenses", as defined by
|
||||
the Mozilla Public License, v. 2.0.
|
||||
|
||||
|
||||
66
README.md
66
README.md
@@ -1,2 +1,66 @@
|
||||
# CloudObjects-PHP-SDK
|
||||
# CloudObjects PHP SDK
|
||||
|
||||
[](https://packagist.org/packages/cloudobjects/sdk) [](https://packagist.org/packages/cloudobjects/sdk)
|
||||
|
||||
The CloudObjects PHP SDK provides simple access to [CloudObjects](https://cloudobjects.io/) from PHP-based applications. It wraps the [Object API](https://coid.link/cloudobjects.io/ObjectAPI/1.0) to fetch objects from the CloudObjects Core database and provides object-based access to their RDF description. A two-tiered caching mechanism (in-memory and Doctrine cache drivers) is included. The SDK also contains a helper class to validate COIDs.
|
||||
|
||||
## Installation
|
||||
|
||||
The SDK is [distributed through packagist](https://packagist.org/packages/cloudobjects/sdk). Add `cloudobjects/sdk` to the `require` section of your `composer.json`, like this:
|
||||
|
||||
````json
|
||||
{
|
||||
"require": {
|
||||
"cloudobjects/sdk" : ">=0.7"
|
||||
}
|
||||
}
|
||||
````
|
||||
|
||||
## Retrieving Objects
|
||||
|
||||
In order to retrieve objects from the CloudObjects Core database you need to create an instance of `CloudObjects\SDK\ObjectRetriever`. Then you can call `getObject()`. This method returns an `ML\JsonLD\Node` instance or `null` if the object is not found. You can use the object interface of the [JsonLD library](https://github.com/lanthaler/JsonLD/) to read the information from the object.
|
||||
|
||||
Here's a simple example:
|
||||
|
||||
````php
|
||||
use ML\IRI\IRI;
|
||||
use CloudObjects\SDK\ObjectRetriever;
|
||||
|
||||
/* ... */
|
||||
|
||||
$retriever = new ObjectRetriever();
|
||||
$object = $this->retriever->getObject(new IRI('coid://cloudobjects.io'));
|
||||
if (isset($object))
|
||||
echo $object->getProperty('http://www.w3.org/2000/01/rdf-schema#label')->getValue();
|
||||
else
|
||||
echo "Object not found.";
|
||||
````
|
||||
|
||||
### Configuration
|
||||
|
||||
You can pass an array of configuration options to the ObjectRetriever's constructor:
|
||||
|
||||
| Option | Description | Default |
|
||||
|---|---|---|
|
||||
| `cache_provider` | The type of cache used. Currently supports `redis`, `file` and `none`. | `none` |
|
||||
| `cache_prefix` | A prefix used for cache IDs. Normally this should not be set but might be necessary on shared caches. | `clobj:` |
|
||||
| `cache_ttl` | Determines how long objects can remain cached. | `60` |
|
||||
| `auth_ns` | The namespace of the service that this retriever acts for. If not set the API is accessed anonymously. | `null` |
|
||||
| `auth_secret` | The shared secret between the namespace in `auth_ns` and `cloudobjects.io` for authenticated. If not set the API is accessed anonymously. | `null` |
|
||||
|
||||
#### For `redis` cache:
|
||||
|
||||
| Option | Description | Default |
|
||||
|---|---|---|
|
||||
| `cache_provider.redis.host` | The hostname or IP of the Redis instance. | `127.0.0.1` |
|
||||
| `cache_provider.redis.port` | The port number of the Redis instance. | `6379` |
|
||||
|
||||
#### For `file` cache:
|
||||
|
||||
| Option | Description | Default |
|
||||
|---|---|---|
|
||||
| `cache_provider.file.directory` | The directory to store cache data in. | The system's temporary directory. |
|
||||
|
||||
## License
|
||||
|
||||
The PHP SDK is licensed under Mozilla Public License (see LICENSE file).
|
||||
39
composer.json
Normal file
39
composer.json
Normal file
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"name": "cloudobjects/sdk",
|
||||
"description": "CloudObjects SDK for PHP for working with COIDs and object descriptions from CloudObjects.",
|
||||
"keywords": ["cloudobjects", "sdk"],
|
||||
"homepage": "https://github.com/CloudObjects/CloudObjects-PHP-SDK",
|
||||
"license": "MPL-2.0",
|
||||
"require" : {
|
||||
"ml/json-ld": ">=1.0.7",
|
||||
"doctrine/common" : ">=2.6.1",
|
||||
"doctrine/cache" : "1.*",
|
||||
"guzzlehttp/guzzle" : ">=6.0",
|
||||
"psr/log": "^1.1",
|
||||
"kevinrob/guzzle-cache-middleware": "^3.2",
|
||||
"webmozart/assert": "^1.6"
|
||||
},
|
||||
"authors": [
|
||||
{
|
||||
"name": "Lukas Rosenstock"
|
||||
}
|
||||
],
|
||||
"autoload": {
|
||||
"psr-0": {
|
||||
"CloudObjects\\SDK" : ""
|
||||
}
|
||||
},
|
||||
"require-dev" : {
|
||||
"phpunit/phpunit": ">=4.8.0,<5.0",
|
||||
"symfony/http-foundation" : ">=4.0",
|
||||
"symfony/psr-http-message-bridge" : ">=1.1.0",
|
||||
"zendframework/zend-diactoros" : "~1.8.6",
|
||||
"defuse/php-encryption" : "^2.2"
|
||||
},
|
||||
"suggest" : {
|
||||
"symfony/http-foundation" : "Required to use parseSymfonyRequest() in AccountContext.",
|
||||
"symfony/psr-http-message-bridge" : "Required to use parseSymfonyRequest() in AccountContext.",
|
||||
"zendframework/zend-diactoros" : "Required to use parseSymfonyRequest() in AccountContext.",
|
||||
"defuse/php-encryption": "Required to use CryptoHelper"
|
||||
}
|
||||
}
|
||||
3206
composer.lock
generated
Normal file
3206
composer.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
23
makefile
Normal file
23
makefile
Normal file
@@ -0,0 +1,23 @@
|
||||
.PHONY: all
|
||||
|
||||
all: vendor build
|
||||
|
||||
composer.lock: composer.json
|
||||
# Updating Dependencies with Composer
|
||||
composer update -o
|
||||
|
||||
vendor: composer.lock
|
||||
# Installing Dependencies with Composer
|
||||
composer install -o
|
||||
|
||||
sami.phar:
|
||||
# Get a copy of sami (only on PHP 7)
|
||||
@if [ `php -v | awk '{ if ($$1 == "PHP") { print substr($$2,0,1) }}'` = "7" ]; then\
|
||||
wget http://get.sensiolabs.org/sami.phar;\
|
||||
fi
|
||||
|
||||
build: sami.phar
|
||||
# Building documentation with sami.phar (only on PHP 7)
|
||||
@if [ `php -v | awk '{ if ($$1 == "PHP") { print substr($$2,0,1) }}'` = "7" ]; then\
|
||||
php sami.phar update sami-config.php --force;\
|
||||
fi
|
||||
20
phpunit.xml
Normal file
20
phpunit.xml
Normal file
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit backupGlobals="false"
|
||||
backupStaticAttributes="false"
|
||||
colors="true"
|
||||
convertErrorsToExceptions="true"
|
||||
convertNoticesToExceptions="true"
|
||||
convertWarningsToExceptions="true"
|
||||
processIsolation="false"
|
||||
stopOnFailure="false"
|
||||
syntaxCheck="false"
|
||||
bootstrap="./tests/bootstrap.php">
|
||||
<testsuites>
|
||||
<testsuite name="OfflineTests">
|
||||
<directory>./tests/OfflineTests/</directory>
|
||||
</testsuite>
|
||||
<testsuite name="OnlineTests">
|
||||
<directory>./tests/OnlineTests/</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
</phpunit>
|
||||
63
tests/OfflineTests/AccountGateway/AAUIDParserTest.php
Normal file
63
tests/OfflineTests/AccountGateway/AAUIDParserTest.php
Normal 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\AccountGateway;
|
||||
|
||||
use ML\IRI\IRI;
|
||||
|
||||
class AAUIDParserTest extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
public function testValidAccountAAUID() {
|
||||
$aauid = new IRI('aauid:abcd1234abcd1234');
|
||||
$this->assertEquals(AAUIDParser::AAUID_ACCOUNT, AAUIDParser::getType($aauid));
|
||||
$this->assertEquals('abcd1234abcd1234', AAUIDParser::getAAUID($aauid));
|
||||
}
|
||||
|
||||
public function testInvalidAccountAAUID() {
|
||||
$aauid = new IRI('aauid:abcd1234abcd123');
|
||||
$this->assertEquals(AAUIDParser::AAUID_INVALID, AAUIDParser::getType($aauid));
|
||||
$this->assertNull(AAUIDParser::getAAUID($aauid));
|
||||
}
|
||||
|
||||
public function testValidAccountConnectionAAUID() {
|
||||
$aauid = new IRI('aauid:abcd1234abcd1234:connection:AA');
|
||||
$this->assertEquals(AAUIDParser::AAUID_CONNECTION, AAUIDParser::getType($aauid));
|
||||
$this->assertEquals('abcd1234abcd1234', AAUIDParser::getAAUID($aauid));
|
||||
$this->assertEquals('AA', AAUIDParser::getQualifier($aauid));
|
||||
}
|
||||
|
||||
public function testInvalidAccountConnectionAAUID() {
|
||||
$aauid = new IRI('aauid:abcd1234abcd1234:connection:AAA');
|
||||
$this->assertEquals(AAUIDParser::AAUID_INVALID, AAUIDParser::getType($aauid));
|
||||
$this->assertNull(AAUIDParser::getAAUID($aauid));
|
||||
$this->assertNull(AAUIDParser::getQualifier($aauid));
|
||||
}
|
||||
|
||||
public function testValidConnectedAccountAAUID() {
|
||||
$aauid = new IRI('aauid:abcd1234abcd1234:account:AA');
|
||||
$this->assertEquals(AAUIDParser::AAUID_CONNECTED_ACCOUNT, AAUIDParser::getType($aauid));
|
||||
$this->assertEquals('abcd1234abcd1234', AAUIDParser::getAAUID($aauid));
|
||||
$this->assertEquals('AA', AAUIDParser::getQualifier($aauid));
|
||||
}
|
||||
|
||||
public function testInvalidConnectedAccountAAUID() {
|
||||
$aauid = new IRI('aauid:abcd1234abcd1234:account:X9');
|
||||
$this->assertEquals(AAUIDParser::AAUID_INVALID, AAUIDParser::getType($aauid));
|
||||
$this->assertNull(AAUIDParser::getAAUID($aauid));
|
||||
$this->assertNull(AAUIDParser::getQualifier($aauid));
|
||||
}
|
||||
|
||||
public function testFromStringValid() {
|
||||
$aauid1 = new IRI('aauid:5678defg8765gfed');
|
||||
$aauid2 = AAUIDParser::fromString('aauid:5678defg8765gfed');
|
||||
$this->assertEquals($aauid1, $aauid2);
|
||||
|
||||
$aauid1 = new IRI('aauid:5678defg8765gfed');
|
||||
$aauid2 = AAUIDParser::fromString('5678defg8765gfed');
|
||||
$this->assertEquals($aauid1, $aauid2);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<?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\AccountGateway;
|
||||
|
||||
use GuzzleHttp\Psr7\Request as GuzzlePsrRequest;
|
||||
use Symfony\Component\HttpFoundation\Request as SymfonyRequest;
|
||||
|
||||
class AccountContextParseTest extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
public function testParsePsrRequest() {
|
||||
$request = new GuzzlePsrRequest('GET', '/', [
|
||||
'C-AAUID' => '1234123412341234', 'C-Access-Token' => 'test'
|
||||
]);
|
||||
|
||||
$context = AccountContext::fromPsrRequest($request);
|
||||
$this->assertNotNull($context);
|
||||
$this->assertEquals('1234123412341234', AAUIDParser::getAAUID($context->getAAUID()));
|
||||
}
|
||||
|
||||
public function testParseSymfonyRequest() {
|
||||
$request = SymfonyRequest::create('/', 'GET', [], [], [], [
|
||||
'HTTP_C_AAUID' => '1234123412341234', 'HTTP_C_ACCESS_TOKEN' => 'test'
|
||||
]);
|
||||
|
||||
$context = AccountContext::fromSymfonyRequest($request);
|
||||
$this->assertNotNull($context);
|
||||
$this->assertEquals('1234123412341234', AAUIDParser::getAAUID($context->getAAUID()));
|
||||
}
|
||||
|
||||
}
|
||||
33
tests/OfflineTests/AccountGateway/AccountContextTest.php
Normal file
33
tests/OfflineTests/AccountGateway/AccountContextTest.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?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\AccountGateway;
|
||||
|
||||
use ML\IRI\IRI;
|
||||
|
||||
class AccountContextTest extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
private $context;
|
||||
|
||||
protected function setUp() {
|
||||
$this->context = new AccountContext(new IRI('aauid:aaaabbbbccccdddd'), 'DUMMY');
|
||||
}
|
||||
|
||||
public function testDefaultGatewayBaseURL() {
|
||||
$this->assertEquals('https://aaaabbbbccccdddd.aauid.net', $this->context->getClient()->getConfig('base_uri'));
|
||||
}
|
||||
|
||||
public function testSetAccountGatewayBaseURLTemplateWithPlaceholder() {
|
||||
$this->context->setAccountGatewayBaseURLTemplate('http://{aauid}.localhost');
|
||||
$this->assertEquals('http://aaaabbbbccccdddd.localhost', $this->context->getClient()->getConfig('base_uri'));
|
||||
}
|
||||
|
||||
public function testSetAccountGatewayBaseURLTemplateWithoutPlaceholder() {
|
||||
$this->context->setAccountGatewayBaseURLTemplate('http://localhost');
|
||||
$this->assertEquals('http://localhost', $this->context->getClient()->getConfig('base_uri'));
|
||||
}
|
||||
|
||||
}
|
||||
130
tests/OfflineTests/COIDParserTest.php
Normal file
130
tests/OfflineTests/COIDParserTest.php
Normal file
@@ -0,0 +1,130 @@
|
||||
<?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;
|
||||
|
||||
class COIDParserTest extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
public function testRootCOID() {
|
||||
$coid = new IRI('coid://example.com');
|
||||
$this->assertEquals(COIDParser::COID_ROOT, COIDParser::getType($coid));
|
||||
}
|
||||
|
||||
public function testInvalidRootCOID() {
|
||||
$coid = new IRI('coid://example');
|
||||
$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('http://example.com');
|
||||
$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() {
|
||||
$coid = new IRI('coid://example.com/Example');
|
||||
$this->assertEquals(COIDParser::COID_UNVERSIONED, COIDParser::getType($coid));
|
||||
}
|
||||
|
||||
public function testInvalidUnversionedCOID() {
|
||||
$coid = new IRI('coid://example.com/Exümple');
|
||||
$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://example.com/Example/1.0');
|
||||
$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() {
|
||||
$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));
|
||||
}
|
||||
|
||||
}
|
||||
46
tests/OfflineTests/Common/CryptoHelperTest.php
Normal file
46
tests/OfflineTests/Common/CryptoHelperTest.php
Normal file
@@ -0,0 +1,46 @@
|
||||
<?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\Common;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use GuzzleHttp\Client, GuzzleHttp\Handler\MockHandler,
|
||||
GuzzleHttp\HandlerStack, GuzzleHttp\Psr7\Response;
|
||||
use CloudObjects\SDK\ObjectRetriever;
|
||||
|
||||
class CryptoHelperTest extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
private $retriever;
|
||||
private $graph;
|
||||
|
||||
private function setMockResponse(Response $response) {
|
||||
$mock = new MockHandler([$response]);
|
||||
$handler = HandlerStack::create($mock);
|
||||
$this->retriever->setClient(new Client(['handler' => $handler]));
|
||||
}
|
||||
|
||||
public function setUp() {
|
||||
$this->retriever = new ObjectRetriever([
|
||||
'auth_ns' => 'test.cloudobjects.io',
|
||||
'auth_secret' => 'TEST'
|
||||
]);
|
||||
}
|
||||
|
||||
public function testEncryptDecrypt() {
|
||||
$this->setMockResponse(new Response(200,
|
||||
[ 'Content-Type' => 'application/ld+json' ],
|
||||
'{"@context":{"common":"coid:\/\/common.cloudobjects.io\/"},"@id":"coid:\/\/test.cloudobjects.io","common:usesSharedEncryptionKey": "def0000092c63296feb07f6b44f323351ab2e570fb04c2dff73c3119fd1103234ea03f5af094d33e8fb5122c5cf73f745957a5f8f47b4fc3c43bc86fb631969f4c591831"}'));
|
||||
|
||||
$cryptoHelper = new CryptoHelper($this->retriever);
|
||||
|
||||
$cleartext = "CLEARTEXT";
|
||||
$ciphertext = $cryptoHelper->encryptWithSharedEncryptionKey($cleartext);
|
||||
$encryptedDecryptedText = $cryptoHelper->decryptWithSharedEncryptionKey($ciphertext);
|
||||
|
||||
$this->assertEquals($cleartext, $encryptedDecryptedText);
|
||||
}
|
||||
|
||||
}
|
||||
175
tests/OfflineTests/JSON/SchemaValidatorTest.php
Normal file
175
tests/OfflineTests/JSON/SchemaValidatorTest.php
Normal file
@@ -0,0 +1,175 @@
|
||||
<?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\JSON;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use ML\JsonLD\JsonLD;
|
||||
use CloudObjects\SDK\ObjectRetriever;
|
||||
|
||||
class SchemaValidatorTest extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
private $schemaValidator;
|
||||
private $graph;
|
||||
|
||||
public function setUp() {
|
||||
$this->schemaValidator = new SchemaValidator(new ObjectRetriever);
|
||||
$this->graph = JsonLD::getDocument('{}')->getGraph();
|
||||
}
|
||||
|
||||
public function testString() {
|
||||
$node = $this->graph->createNode();
|
||||
$node->setType($this->graph->createNode('coid://json.co-n.net/String'));
|
||||
$this->schemaValidator->validateAgainstNode("Test", $node);
|
||||
}
|
||||
|
||||
public function testNotString() {
|
||||
$this->setExpectedException(InvalidArgumentException::class);
|
||||
|
||||
$node = $this->graph->createNode();
|
||||
$node->setType($this->graph->createNode('coid://json.co-n.net/String'));
|
||||
$this->schemaValidator->validateAgainstNode(9, $node);
|
||||
}
|
||||
|
||||
public function testNumber() {
|
||||
$node = $this->graph->createNode();
|
||||
$node->setType($this->graph->createNode('coid://json.co-n.net/Number'));
|
||||
$this->schemaValidator->validateAgainstNode(3.5, $node);
|
||||
}
|
||||
|
||||
public function testNotNumber() {
|
||||
$this->setExpectedException(InvalidArgumentException::class);
|
||||
|
||||
$node = $this->graph->createNode();
|
||||
$node->setType($this->graph->createNode('coid://json.co-n.net/Number'));
|
||||
$this->schemaValidator->validateAgainstNode("ABC", $node);
|
||||
}
|
||||
|
||||
public function testInteger() {
|
||||
$node = $this->graph->createNode();
|
||||
$node->setType($this->graph->createNode('coid://json.co-n.net/Integer'));
|
||||
$this->schemaValidator->validateAgainstNode(12, $node);
|
||||
}
|
||||
|
||||
public function testNotInteger() {
|
||||
$this->setExpectedException(InvalidArgumentException::class);
|
||||
|
||||
$node = $this->graph->createNode();
|
||||
$node->setType($this->graph->createNode('coid://json.co-n.net/Integer'));
|
||||
$this->schemaValidator->validateAgainstNode(1.4, $node);
|
||||
}
|
||||
|
||||
public function testArray() {
|
||||
$node = $this->graph->createNode();
|
||||
$node->setType($this->graph->createNode('coid://json.co-n.net/Array'));
|
||||
$this->schemaValidator->validateAgainstNode([ 1, 2, "foo" ], $node);
|
||||
}
|
||||
|
||||
public function testNotArray() {
|
||||
$this->setExpectedException(InvalidArgumentException::class);
|
||||
|
||||
$node = $this->graph->createNode();
|
||||
$node->setType($this->graph->createNode('coid://json.co-n.net/Array'));
|
||||
$this->schemaValidator->validateAgainstNode("NANANA", $node);
|
||||
}
|
||||
|
||||
public function testObject() {
|
||||
$node = $this->graph->createNode();
|
||||
$node->setType($this->graph->createNode('coid://json.co-n.net/Object'));
|
||||
$this->schemaValidator->validateAgainstNode([
|
||||
'a' => 'A',
|
||||
'b' => 'B'
|
||||
], $node);
|
||||
}
|
||||
|
||||
public function testNotObject() {
|
||||
$this->setExpectedException(InvalidArgumentException::class);
|
||||
|
||||
$node = $this->graph->createNode();
|
||||
$node->setType($this->graph->createNode('coid://json.co-n.net/Object'));
|
||||
$this->schemaValidator->validateAgainstNode(5, $node);
|
||||
}
|
||||
|
||||
public function testObjectWithProperty() {
|
||||
$stringNode = $this->graph->createNode();
|
||||
$stringNode->setProperty('coid://json.co-n.net/hasKey', 'a');
|
||||
$stringNode->setType($this->graph->createNode('coid://json.co-n.net/String'));
|
||||
|
||||
$node = $this->graph->createNode();
|
||||
$node->setType($this->graph->createNode('coid://json.co-n.net/Object'));
|
||||
$node->setProperty('coid://json.co-n.net/hasProperty', $stringNode);
|
||||
$this->schemaValidator->validateAgainstNode([
|
||||
'a' => 'A',
|
||||
'b' => 'B'
|
||||
], $node);
|
||||
}
|
||||
|
||||
public function testObjectWithPropertyTypeError() {
|
||||
$this->setExpectedException(InvalidArgumentException::class);
|
||||
|
||||
$stringNode = $this->graph->createNode();
|
||||
$stringNode->setProperty('coid://json.co-n.net/hasKey', 'a');
|
||||
$stringNode->setType($this->graph->createNode('coid://json.co-n.net/String'));
|
||||
|
||||
$node = $this->graph->createNode();
|
||||
$node->setType($this->graph->createNode('coid://json.co-n.net/Object'));
|
||||
$node->setProperty('coid://json.co-n.net/hasProperty', $stringNode);
|
||||
$this->schemaValidator->validateAgainstNode([
|
||||
'a' => 0,
|
||||
'b' => 'B'
|
||||
], $node);
|
||||
}
|
||||
|
||||
public function testObjectWithRequiredProperty() {
|
||||
$stringNode = $this->graph->createNode();
|
||||
$stringNode->setProperty('coid://json.co-n.net/hasKey', 'a');
|
||||
$stringNode->setProperty('coid://json.co-n.net/isRequired', 'true');
|
||||
$stringNode->setType($this->graph->createNode('coid://json.co-n.net/String'));
|
||||
|
||||
$node = $this->graph->createNode();
|
||||
$node->setType($this->graph->createNode('coid://json.co-n.net/Object'));
|
||||
$node->setProperty('coid://json.co-n.net/hasProperty', $stringNode);
|
||||
$this->schemaValidator->validateAgainstNode([
|
||||
'a' => 'A',
|
||||
'b' => 'B'
|
||||
], $node);
|
||||
}
|
||||
|
||||
public function testObjectWithRequiredPropertyTypeError() {
|
||||
$this->setExpectedException(InvalidArgumentException::class);
|
||||
|
||||
$stringNode = $this->graph->createNode();
|
||||
$stringNode->setProperty('coid://json.co-n.net/hasKey', 'a');
|
||||
$stringNode->setProperty('coid://json.co-n.net/isRequired', 'true');
|
||||
$stringNode->setType($this->graph->createNode('coid://json.co-n.net/String'));
|
||||
|
||||
$node = $this->graph->createNode();
|
||||
$node->setType($this->graph->createNode('coid://json.co-n.net/Object'));
|
||||
$node->setProperty('coid://json.co-n.net/hasProperty', $stringNode);
|
||||
$this->schemaValidator->validateAgainstNode([
|
||||
'a' => 0,
|
||||
'b' => 'B'
|
||||
], $node);
|
||||
}
|
||||
|
||||
public function testObjectWithRequiredPropertyMissing() {
|
||||
$this->setExpectedException(InvalidArgumentException::class);
|
||||
|
||||
$stringNode = $this->graph->createNode();
|
||||
$stringNode->setProperty('coid://json.co-n.net/hasKey', 'a');
|
||||
$stringNode->setProperty('coid://json.co-n.net/isRequired', 'true');
|
||||
$stringNode->setType($this->graph->createNode('coid://json.co-n.net/String'));
|
||||
|
||||
$node = $this->graph->createNode();
|
||||
$node->setType($this->graph->createNode('coid://json.co-n.net/Object'));
|
||||
$node->setProperty('coid://json.co-n.net/hasProperty', $stringNode);
|
||||
$this->schemaValidator->validateAgainstNode([
|
||||
'b' => 'B',
|
||||
'c' => 'C'
|
||||
], $node);
|
||||
}
|
||||
|
||||
}
|
||||
156
tests/OfflineTests/NodeReaderMockTest.php
Normal file
156
tests/OfflineTests/NodeReaderMockTest.php
Normal file
@@ -0,0 +1,156 @@
|
||||
<?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 GuzzleHttp\Client, GuzzleHttp\Handler\MockHandler,
|
||||
GuzzleHttp\HandlerStack, GuzzleHttp\Psr7\Response;
|
||||
|
||||
class NodeReaderMockTest extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
private $retriever;
|
||||
private $reader;
|
||||
|
||||
private function setMockResponse(Response $response) {
|
||||
$mock = new MockHandler([$response]);
|
||||
$handler = HandlerStack::create($mock);
|
||||
$this->retriever->setClient(new Client(['handler' => $handler]));
|
||||
}
|
||||
|
||||
private function useRootResourceMock() {
|
||||
$this->setMockResponse(new Response(200,
|
||||
['Content-Type' => 'application/ld+json'],
|
||||
'{"@context":{"co":"coid:\/\/cloudobjects.io\/","rdf":"http:\/\/www.w3.org\/1999\/02\/22-rdf-syntax-ns#","agws":"coid:\/\/aauid.net\/","rdfs":"http:\/\/www.w3.org\/2000\/01\/rdf-schema#"},"@id":"coid:\/\/cloudobjects.io","@type":["agws:Service","co:Namespace"],"co:isAtRevision":"6-fbea0c90b2c5e5300e4039ed99be9b2d","co:isVisibleTo":{"@id":"co:Public"},"co:recommendsPrefix":"co","co:wasUpdatedAt":{"@type":"http:\/\/www.w3.org\/2001\/XMLSchema#dateTime","@value":"2017-01-16T17:29:22+00:00"},"rdfs:comment":"The CloudObjects namespace defines the essential objects.","rdfs:label":"CloudObjects"}'));
|
||||
}
|
||||
|
||||
protected function setUp() {
|
||||
$this->retriever = new ObjectRetriever;
|
||||
$this->reader = new NodeReader([
|
||||
'prefixes' => [
|
||||
'co' => 'coid://cloudobjects.io/',
|
||||
'rdfs' => 'http://www.w3.org/2000/01/rdf-schema#'
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
public function testHasType1() {
|
||||
$coid = new IRI('coid://cloudobjects.io');
|
||||
$this->useRootResourceMock();
|
||||
$object = $this->retriever->getObject($coid);
|
||||
|
||||
$this->assertTrue($this->reader->hasType($object, 'coid://cloudobjects.io/Namespace'));
|
||||
$this->assertTrue($this->reader->hasType($object, 'co:Namespace'));
|
||||
$this->assertFalse($this->reader->hasType($object, 'coid://cloudobjects.io/MemberRole'));
|
||||
$this->assertFalse($this->reader->hasType($object, 'co:MemberRole'));
|
||||
}
|
||||
|
||||
public function testHasPropertyValue1() {
|
||||
$coid = new IRI('coid://cloudobjects.io');
|
||||
$this->useRootResourceMock();
|
||||
$object = $this->retriever->getObject($coid);
|
||||
|
||||
$this->assertTrue($this->reader->hasPropertyValue($object, 'http://www.w3.org/2000/01/rdf-schema#label', 'CloudObjects'));
|
||||
$this->assertTrue($this->reader->hasPropertyValue($object, 'rdfs:label', 'CloudObjects'));
|
||||
}
|
||||
|
||||
public function testGetFirstValueString1() {
|
||||
$coid = new IRI('coid://cloudobjects.io');
|
||||
$this->useRootResourceMock();
|
||||
$object = $this->retriever->getObject($coid);
|
||||
|
||||
$this->assertEquals('CloudObjects', $this->reader->getFirstValueString($object, 'http://www.w3.org/2000/01/rdf-schema#label'));
|
||||
$this->assertEquals('CloudObjects', $this->reader->getFirstValueString($object, 'rdfs:label'));
|
||||
|
||||
$this->assertNull($this->reader->getFirstValueString($object, 'coid://cloudobjects.io/makesTriplesVisibleTo'));
|
||||
$this->assertNull($this->reader->getFirstValueString($object, 'co:makesTriplesVisibleTo'));
|
||||
|
||||
$this->assertEquals('theDefaultValue', $this->reader->getFirstValueString($object, 'coid://cloudobjects.io/makesTriplesVisibleTo', 'theDefaultValue'));
|
||||
$this->assertEquals('theDefaultValue', $this->reader->getFirstValueString($object, 'co:makesTriplesVisibleTo', 'theDefaultValue'));
|
||||
}
|
||||
|
||||
public function testGetFirstValueIRI1() {
|
||||
$coid = new IRI('coid://cloudobjects.io');
|
||||
$this->useRootResourceMock();
|
||||
$object = $this->retriever->getObject($coid);
|
||||
|
||||
$this->assertInstanceOf('ML\IRI\IRI', $this->reader->getFirstValueIRI($object, 'coid://cloudobjects.io/isVisibleTo'));
|
||||
$this->assertInstanceOf('ML\IRI\IRI', $this->reader->getFirstValueIRI($object, 'co:isVisibleTo'));
|
||||
|
||||
$this->assertEquals(new IRI('coid://cloudobjects.io/Public'), $this->reader->getFirstValueIRI($object, 'coid://cloudobjects.io/isVisibleTo'));
|
||||
$this->assertEquals(new IRI('coid://cloudobjects.io/Public'), $this->reader->getFirstValueIRI($object, 'co:isVisibleTo'));
|
||||
}
|
||||
|
||||
public function testGetFirstValueNode1() {
|
||||
$coid = new IRI('coid://cloudobjects.io');
|
||||
$this->useRootResourceMock();
|
||||
$object = $this->retriever->getObject($coid);
|
||||
|
||||
$this->assertInstanceOf('ML\JsonLD\Node', $this->reader->getFirstValueNode($object, 'coid://cloudobjects.io/isVisibleTo'));
|
||||
$this->assertInstanceOf('ML\JsonLD\Node', $this->reader->getFirstValueNode($object, 'co:isVisibleTo'));
|
||||
|
||||
$this->assertEquals('coid://cloudobjects.io/Public', $this->reader->getFirstValueNode($object, 'coid://cloudobjects.io/isVisibleTo')->getId());
|
||||
$this->assertEquals('coid://cloudobjects.io/Public', $this->reader->getFirstValueNode($object, 'co:isVisibleTo')->getId());
|
||||
}
|
||||
|
||||
public function testGetAllValuesString1() {
|
||||
$coid = new IRI('coid://cloudobjects.io');
|
||||
$this->useRootResourceMock();
|
||||
$object = $this->retriever->getObject($coid);
|
||||
|
||||
$this->assertCount(1, $this->reader->getAllValuesString($object, 'http://www.w3.org/2000/01/rdf-schema#label'));
|
||||
$this->assertCount(1, $this->reader->getAllValuesString($object, 'rdfs:label'));
|
||||
|
||||
$this->assertEquals('CloudObjects', $this->reader->getAllValuesString($object, 'http://www.w3.org/2000/01/rdf-schema#label')[0]);
|
||||
$this->assertEquals('CloudObjects', $this->reader->getAllValuesString($object, 'rdfs:label')[0]);
|
||||
|
||||
$this->assertCount(0, $this->reader->getAllValuesString($object, 'coid://cloudobjects.io/makesTriplesVisibleTo'));
|
||||
$this->assertCount(0, $this->reader->getAllValuesString($object, 'co:makesTriplesVisibleTo'));
|
||||
|
||||
$this->assertCount(2, $this->reader->getAllValuesString($object, '@type'));
|
||||
}
|
||||
|
||||
public function testGetAllValuesIRI1() {
|
||||
$coid = new IRI('coid://cloudobjects.io');
|
||||
$this->useRootResourceMock();
|
||||
$object = $this->retriever->getObject($coid);
|
||||
|
||||
$this->assertCount(0, $this->reader->getAllValuesIRI($object, 'http://www.w3.org/2000/01/rdf-schema#label'));
|
||||
$this->assertCount(0, $this->reader->getAllValuesIRI($object, 'rdfs:label'));
|
||||
|
||||
$this->assertCount(1, $this->reader->getAllValuesIRI($object, 'coid://cloudobjects.io/isVisibleTo'));
|
||||
$this->assertCount(1, $this->reader->getAllValuesIRI($object, 'co:isVisibleTo'));
|
||||
|
||||
$this->assertCount(2, $this->reader->getAllValuesIRI($object, '@type'));
|
||||
|
||||
$this->assertInstanceOf('ML\IRI\IRI', $this->reader->getAllValuesIRI($object, 'coid://cloudobjects.io/isVisibleTo')[0]);
|
||||
$this->assertInstanceOf('ML\IRI\IRI', $this->reader->getAllValuesIRI($object, 'co:isVisibleTo')[0]);
|
||||
|
||||
$this->assertEquals(new IRI('coid://cloudobjects.io/Public'), $this->reader->getAllValuesIRI($object, 'coid://cloudobjects.io/isVisibleTo')[0]);
|
||||
$this->assertEquals(new IRI('coid://cloudobjects.io/Public'), $this->reader->getAllValuesIRI($object, 'co:isVisibleTo')[0]);
|
||||
}
|
||||
|
||||
public function testGetAllValuesNode1() {
|
||||
$coid = new IRI('coid://cloudobjects.io');
|
||||
$this->useRootResourceMock();
|
||||
$object = $this->retriever->getObject($coid);
|
||||
|
||||
$this->assertCount(0, $this->reader->getAllValuesNode($object, 'http://www.w3.org/2000/01/rdf-schema#label'));
|
||||
$this->assertCount(0, $this->reader->getAllValuesNode($object, 'rdfs:label'));
|
||||
|
||||
$this->assertCount(1, $this->reader->getAllValuesNode($object, 'coid://cloudobjects.io/isVisibleTo'));
|
||||
$this->assertCount(1, $this->reader->getAllValuesNode($object, 'co:isVisibleTo'));
|
||||
|
||||
$this->assertCount(2, $this->reader->getAllValuesNode($object, '@type'));
|
||||
|
||||
$this->assertInstanceOf('ML\JsonLD\Node', $this->reader->getAllValuesNode($object, 'coid://cloudobjects.io/isVisibleTo')[0]);
|
||||
$this->assertInstanceOf('ML\JsonLD\Node', $this->reader->getAllValuesNode($object, 'co:isVisibleTo')[0]);
|
||||
|
||||
$this->assertEquals('coid://cloudobjects.io/Public', $this->reader->getAllValuesNode($object, 'coid://cloudobjects.io/isVisibleTo')[0]->getId());
|
||||
$this->assertEquals('coid://cloudobjects.io/Public', $this->reader->getAllValuesNode($object, 'co:isVisibleTo')[0]->getId());
|
||||
}
|
||||
|
||||
}
|
||||
40
tests/OfflineTests/ObjectRetrieverMockTest.php
Normal file
40
tests/OfflineTests/ObjectRetrieverMockTest.php
Normal file
@@ -0,0 +1,40 @@
|
||||
<?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 GuzzleHttp\Client, GuzzleHttp\Handler\MockHandler,
|
||||
GuzzleHttp\HandlerStack, GuzzleHttp\Psr7\Response;
|
||||
|
||||
class ObjectRetrieverMockTest extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
private $retriever;
|
||||
|
||||
private function setMockResponse(Response $response) {
|
||||
$mock = new MockHandler([$response]);
|
||||
$handler = HandlerStack::create($mock);
|
||||
$this->retriever->setClient(new Client(['handler' => $handler]));
|
||||
}
|
||||
|
||||
protected function setUp() {
|
||||
$this->retriever = new ObjectRetriever;
|
||||
}
|
||||
|
||||
public function testGetRootResource() {
|
||||
$this->setMockResponse(new Response(200,
|
||||
['Content-Type' => 'application/ld+json'],
|
||||
'{"@context":{"cloudobjects":"coid:\/\/cloudobjects.io\/","rdf":"http:\/\/www.w3.org\/1999\/02\/22-rdf-syntax-ns#","rdfs":"http:\/\/www.w3.org\/2000\/01\/rdf-schema#"},"@id":"coid:\/\/cloudobjects.io","@type":"cloudobjects:Namespace","cloudobjects:hasPublicListing":"true","cloudobjects:revision":"1-325baa62b76105f56dc09386f5a2ec91","rdfs:comment":"The CloudObjects namespace defines the essential objects.","rdfs:label":"CloudObjects"}'));
|
||||
|
||||
$coid = new IRI('coid://cloudobjects.io');
|
||||
$object = $this->retriever->getObject($coid);
|
||||
$this->assertNotNull($object);
|
||||
$this->assertEquals((string)$coid, $object->getID());
|
||||
$this->assertNotNull($object->getProperty('http://www.w3.org/2000/01/rdf-schema#label'));
|
||||
$this->assertEquals('CloudObjects', $object->getProperty('http://www.w3.org/2000/01/rdf-schema#label')->getValue());
|
||||
}
|
||||
|
||||
}
|
||||
38
tests/OnlineTests/JSON/SchemaValidatorPublicTest.php
Normal file
38
tests/OnlineTests/JSON/SchemaValidatorPublicTest.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?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\JSON;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use ML\IRI\IRI;
|
||||
use CloudObjects\SDK\ObjectRetriever;
|
||||
|
||||
class SchemaValidatorPublicTest extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
private $schemaValidator;
|
||||
|
||||
public function setUp() {
|
||||
$this->schemaValidator = new SchemaValidator(new ObjectRetriever);
|
||||
}
|
||||
|
||||
public function testAddress() {
|
||||
$this->schemaValidator->validateAgainstCOID([
|
||||
'locality' => 'Frankfurt',
|
||||
'region' => 'Hessen',
|
||||
'country-name' => 'Germany'
|
||||
], new IRI('coid://json.co-n.net/Address'));
|
||||
}
|
||||
|
||||
public function testNotAddress() {
|
||||
$this->setExpectedException(InvalidArgumentException::class);
|
||||
|
||||
$this->schemaValidator->validateAgainstCOID([
|
||||
'region' => 'Hessen',
|
||||
'country-name' => 'Germany'
|
||||
], new IRI('coid://json.co-n.net/Address'));
|
||||
}
|
||||
|
||||
}
|
||||
60
tests/OnlineTests/ObjectRetrieverPublicTest.php
Normal file
60
tests/OnlineTests/ObjectRetrieverPublicTest.php
Normal file
@@ -0,0 +1,60 @@
|
||||
<?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;
|
||||
|
||||
class ObjectRetrieverTest extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
private $retriever;
|
||||
|
||||
protected function setUp() {
|
||||
$this->retriever = new ObjectRetriever;
|
||||
}
|
||||
|
||||
private function stringifyItems(array $input) {
|
||||
$output = [];
|
||||
foreach ($input as $i)
|
||||
$output[] = (string)$i;
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
public function testGetRootObject() {
|
||||
$coid = new IRI('coid://cloudobjects.io');
|
||||
$object = $this->retriever->getObject($coid);
|
||||
$this->assertNotNull($object);
|
||||
$this->assertEquals((string)$coid, $object->getID());
|
||||
$this->assertNotNull($object->getProperty('http://www.w3.org/2000/01/rdf-schema#label'));
|
||||
$this->assertEquals('CloudObjects', $object->getProperty('http://www.w3.org/2000/01/rdf-schema#label')->getValue());
|
||||
}
|
||||
|
||||
public function testGetCOIDList() {
|
||||
$coid = new IRI('coid://cloudobjects.io');
|
||||
$list = $this->stringifyItems(
|
||||
$this->retriever->getCOIDListForNamespace($coid)
|
||||
);
|
||||
$this->assertNotEmpty($list);
|
||||
|
||||
$this->assertContains('coid://cloudobjects.io/isVisibleTo', $list);
|
||||
$this->assertContains('coid://cloudobjects.io/Public', $list);
|
||||
$this->assertNotContains('coid://json.co-n.net/Element', $list);
|
||||
}
|
||||
|
||||
public function testGetFilteredCOIDList() {
|
||||
$coid = new IRI('coid://cloudobjects.io');
|
||||
$list = $this->stringifyItems(
|
||||
$this->retriever->getCOIDListForNamespaceWithType($coid, 'coid://cloudobjects.io/Audience')
|
||||
);
|
||||
$this->assertNotEmpty($list);
|
||||
|
||||
$this->assertNotContains('coid://cloudobjects.io/isVisibleTo', $list);
|
||||
$this->assertContains('coid://cloudobjects.io/Public', $list);
|
||||
$this->assertContains('coid://cloudobjects.io/Private', $list);
|
||||
}
|
||||
|
||||
}
|
||||
7
tests/bootstrap.php
Normal file
7
tests/bootstrap.php
Normal file
@@ -0,0 +1,7 @@
|
||||
<?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/. */
|
||||
|
||||
require_once __DIR__."/../vendor/autoload.php";
|
||||
Reference in New Issue
Block a user