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. Definitions
|
||||||
--------------
|
|
||||||
|
|
||||||
1.1. "Contributor"
|
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"
|
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"
|
1.3. "Contribution"
|
||||||
means Covered Software of a particular Contributor.
|
|
||||||
|
means Covered Software of a particular Contributor.
|
||||||
|
|
||||||
1.4. "Covered Software"
|
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
|
means Source Code Form to which the initial Contributor has attached the
|
||||||
Form, and Modifications of such Source Code Form, in each case
|
notice in Exhibit A, the Executable Form of such Source Code Form, and
|
||||||
including portions thereof.
|
Modifications of such Source Code Form, in each case including portions
|
||||||
|
thereof.
|
||||||
|
|
||||||
1.5. "Incompatible With Secondary Licenses"
|
1.5. "Incompatible With Secondary Licenses"
|
||||||
means
|
means
|
||||||
|
|
||||||
(a) that the initial Contributor has attached the notice described
|
a. that the initial Contributor has attached the notice described in
|
||||||
in Exhibit B to the Covered Software; or
|
Exhibit B to the Covered Software; or
|
||||||
|
|
||||||
(b) that the Covered Software was made available under the terms of
|
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
|
version 1.1 or earlier of the License, but not also under the terms of
|
||||||
terms of a Secondary License.
|
a Secondary License.
|
||||||
|
|
||||||
1.6. "Executable Form"
|
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"
|
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"
|
1.8. "License"
|
||||||
means this document.
|
|
||||||
|
means this document.
|
||||||
|
|
||||||
1.9. "Licensable"
|
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
|
means having the right to grant, to the maximum extent possible, whether
|
||||||
all of the rights conveyed by this License.
|
at the time of the initial grant or subsequently, any and all of the
|
||||||
|
rights conveyed by this License.
|
||||||
|
|
||||||
1.10. "Modifications"
|
1.10. "Modifications"
|
||||||
means any of the following:
|
|
||||||
|
|
||||||
(a) any file in Source Code Form that results from an addition to,
|
means any of the following:
|
||||||
deletion from, or modification of the contents of Covered
|
|
||||||
Software; or
|
|
||||||
|
|
||||||
(b) any new file in Source Code Form that contains any Covered
|
a. any file in Source Code Form that results from an addition to,
|
||||||
Software.
|
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
|
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
|
means any patent claim(s), including without limitation, method,
|
||||||
Contributor that would be infringed, but for the grant of the
|
process, and apparatus claims, in any patent Licensable by such
|
||||||
License, by the making, using, selling, offering for sale, having
|
Contributor that would be infringed, but for the grant of the License,
|
||||||
made, import, or transfer of either its Contributions or its
|
by the making, using, selling, offering for sale, having made, import,
|
||||||
Contributor Version.
|
or transfer of either its Contributions or its Contributor Version.
|
||||||
|
|
||||||
1.12. "Secondary License"
|
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
|
means either the GNU General Public License, Version 2.0, the GNU Lesser
|
||||||
Public License, Version 3.0, or any later versions of those
|
General Public License, Version 2.1, the GNU Affero General Public
|
||||||
licenses.
|
License, Version 3.0, or any later versions of those licenses.
|
||||||
|
|
||||||
1.13. "Source Code Form"
|
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")
|
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
|
means an individual or a legal entity exercising rights under this
|
||||||
controls, is controlled by, or is under common control with You. For
|
License. For legal entities, "You" includes any entity that controls, is
|
||||||
purposes of this definition, "control" means (a) the power, direct
|
controlled by, or is under common control with You. For purposes of this
|
||||||
or indirect, to cause the direction or management of such entity,
|
definition, "control" means (a) the power, direct or indirect, to cause
|
||||||
whether by contract or otherwise, or (b) ownership of more than
|
the direction or management of such entity, whether by contract or
|
||||||
fifty percent (50%) of the outstanding shares or beneficial
|
otherwise, or (b) ownership of more than fifty percent (50%) of the
|
||||||
ownership of such entity.
|
outstanding shares or beneficial ownership of such entity.
|
||||||
|
|
||||||
|
|
||||||
2. License Grants and Conditions
|
2. License Grants and Conditions
|
||||||
--------------------------------
|
|
||||||
|
|
||||||
2.1. Grants
|
2.1. Grants
|
||||||
|
|
||||||
Each Contributor hereby grants You a world-wide, royalty-free,
|
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||||
non-exclusive license:
|
non-exclusive license:
|
||||||
|
|
||||||
(a) under intellectual property rights (other than patent or trademark)
|
a. under intellectual property rights (other than patent or trademark)
|
||||||
Licensable by such Contributor to use, reproduce, make available,
|
Licensable by such Contributor to use, reproduce, make available,
|
||||||
modify, display, perform, distribute, and otherwise exploit its
|
modify, display, perform, distribute, and otherwise exploit its
|
||||||
Contributions, either on an unmodified basis, with Modifications, or
|
Contributions, either on an unmodified basis, with Modifications, or
|
||||||
as part of a Larger Work; and
|
as part of a Larger Work; and
|
||||||
|
|
||||||
(b) under Patent Claims of such Contributor to make, use, sell, offer
|
b. under Patent Claims of such Contributor to make, use, sell, offer for
|
||||||
for sale, have made, import, and otherwise transfer either its
|
sale, have made, import, and otherwise transfer either its
|
||||||
Contributions or its Contributor Version.
|
Contributions or its Contributor Version.
|
||||||
|
|
||||||
2.2. Effective Date
|
2.2. Effective Date
|
||||||
|
|
||||||
The licenses granted in Section 2.1 with respect to any Contribution
|
The licenses granted in Section 2.1 with respect to any Contribution
|
||||||
become effective for each Contribution on the date the Contributor first
|
become effective for each Contribution on the date the Contributor first
|
||||||
distributes such Contribution.
|
distributes such Contribution.
|
||||||
|
|
||||||
2.3. Limitations on Grant Scope
|
2.3. Limitations on Grant Scope
|
||||||
|
|
||||||
The licenses granted in this Section 2 are the only rights granted under
|
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
|
this License. No additional rights or licenses will be implied from the
|
||||||
distribution or licensing of Covered Software under this License.
|
distribution or licensing of Covered Software under this License.
|
||||||
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||||
Contributor:
|
Contributor:
|
||||||
|
|
||||||
(a) for any code that a Contributor has removed from Covered Software;
|
a. for any code that a Contributor has removed from Covered Software; or
|
||||||
or
|
|
||||||
|
|
||||||
(b) for infringements caused by: (i) Your and any other third party's
|
b. for infringements caused by: (i) Your and any other third party's
|
||||||
modifications of Covered Software, or (ii) the combination of its
|
modifications of Covered Software, or (ii) the combination of its
|
||||||
Contributions with other software (except as part of its Contributor
|
Contributions with other software (except as part of its Contributor
|
||||||
Version); or
|
Version); or
|
||||||
|
|
||||||
(c) under Patent Claims infringed by Covered Software in the absence of
|
c. under Patent Claims infringed by Covered Software in the absence of
|
||||||
its Contributions.
|
its Contributions.
|
||||||
|
|
||||||
This License does not grant any rights in the trademarks, service marks,
|
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
|
or logos of any Contributor (except as may be necessary to comply with
|
||||||
the notice requirements in Section 3.4).
|
the notice requirements in Section 3.4).
|
||||||
|
|
||||||
2.4. Subsequent Licenses
|
2.4. Subsequent Licenses
|
||||||
|
|
||||||
No Contributor makes additional grants as a result of Your choice to
|
No Contributor makes additional grants as a result of Your choice to
|
||||||
distribute the Covered Software under a subsequent version of this
|
distribute the Covered Software under a subsequent version of this
|
||||||
License (see Section 10.2) or under the terms of a Secondary License (if
|
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||||
permitted under the terms of Section 3.3).
|
permitted under the terms of Section 3.3).
|
||||||
|
|
||||||
2.5. Representation
|
2.5. Representation
|
||||||
|
|
||||||
Each Contributor represents that the Contributor believes its
|
Each Contributor represents that the Contributor believes its
|
||||||
Contributions are its original creation(s) or it has sufficient rights
|
Contributions are its original creation(s) or it has sufficient rights to
|
||||||
to grant the rights to its Contributions conveyed by this License.
|
grant the rights to its Contributions conveyed by this License.
|
||||||
|
|
||||||
2.6. Fair Use
|
2.6. Fair Use
|
||||||
|
|
||||||
This License is not intended to limit any rights You have under
|
This License is not intended to limit any rights You have under
|
||||||
applicable copyright doctrines of fair use, fair dealing, or other
|
applicable copyright doctrines of fair use, fair dealing, or other
|
||||||
equivalents.
|
equivalents.
|
||||||
|
|
||||||
2.7. Conditions
|
2.7. Conditions
|
||||||
|
|
||||||
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
|
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in
|
||||||
in Section 2.1.
|
Section 2.1.
|
||||||
|
|
||||||
|
|
||||||
3. Responsibilities
|
3. Responsibilities
|
||||||
-------------------
|
|
||||||
|
|
||||||
3.1. Distribution of Source Form
|
3.1. Distribution of Source Form
|
||||||
|
|
||||||
All distribution of Covered Software in Source Code Form, including any
|
All distribution of Covered Software in Source Code Form, including any
|
||||||
Modifications that You create or to which You contribute, must be under
|
Modifications that You create or to which You contribute, must be under
|
||||||
the terms of this License. You must inform recipients that the Source
|
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
|
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
|
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
|
attempt to alter or restrict the recipients' rights in the Source Code
|
||||||
Form.
|
Form.
|
||||||
|
|
||||||
3.2. Distribution of Executable 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
|
a. such Covered Software must also be made available in Source Code Form,
|
||||||
Form, as described in Section 3.1, and You must inform recipients of
|
as described in Section 3.1, and You must inform recipients of the
|
||||||
the Executable Form how they can obtain a copy of such Source Code
|
Executable Form how they can obtain a copy of such Source Code Form by
|
||||||
Form by reasonable means in a timely manner, at a charge no more
|
reasonable means in a timely manner, at a charge no more than the cost
|
||||||
than the cost of distribution to the recipient; and
|
of distribution to the recipient; and
|
||||||
|
|
||||||
(b) You may distribute such Executable Form under the terms of this
|
b. You may distribute such Executable Form under the terms of this
|
||||||
License, or sublicense it under different terms, provided that the
|
License, or sublicense it under different terms, provided that the
|
||||||
license for the Executable Form does not attempt to limit or alter
|
license for the Executable Form does not attempt to limit or alter the
|
||||||
the recipients' rights in the Source Code Form under this License.
|
recipients' rights in the Source Code Form under this License.
|
||||||
|
|
||||||
3.3. Distribution of a Larger Work
|
3.3. Distribution of a Larger Work
|
||||||
|
|
||||||
You may create and distribute a Larger Work under terms of Your choice,
|
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
|
provided that You also comply with the requirements of this License for
|
||||||
the Covered Software. If the Larger Work is a combination of Covered
|
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
|
Software with a work governed by one or more Secondary Licenses, and the
|
||||||
Covered Software is not Incompatible With Secondary Licenses, this
|
Covered Software is not Incompatible With Secondary Licenses, this
|
||||||
License permits You to additionally distribute such Covered Software
|
License permits You to additionally distribute such Covered Software
|
||||||
under the terms of such Secondary License(s), so that the recipient of
|
under the terms of such Secondary License(s), so that the recipient of
|
||||||
the Larger Work may, at their option, further distribute the Covered
|
the Larger Work may, at their option, further distribute the Covered
|
||||||
Software under the terms of either this License or such Secondary
|
Software under the terms of either this License or such Secondary
|
||||||
License(s).
|
License(s).
|
||||||
|
|
||||||
3.4. Notices
|
3.4. Notices
|
||||||
|
|
||||||
You may not remove or alter the substance of any license notices
|
You may not remove or alter the substance of any license notices
|
||||||
(including copyright notices, patent notices, disclaimers of warranty,
|
(including copyright notices, patent notices, disclaimers of warranty, or
|
||||||
or limitations of liability) contained within the Source Code Form of
|
limitations of liability) contained within the Source Code Form of the
|
||||||
the Covered Software, except that You may alter any license notices to
|
Covered Software, except that You may alter any license notices to the
|
||||||
the extent required to remedy known factual inaccuracies.
|
extent required to remedy known factual inaccuracies.
|
||||||
|
|
||||||
3.5. Application of Additional Terms
|
3.5. Application of Additional Terms
|
||||||
|
|
||||||
You may choose to offer, and to charge a fee for, warranty, support,
|
You may choose to offer, and to charge a fee for, warranty, support,
|
||||||
indemnity or liability obligations to one or more recipients of Covered
|
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
|
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
|
behalf of any Contributor. You must make it absolutely clear that any
|
||||||
such warranty, support, indemnity, or liability obligation is offered by
|
such warranty, support, indemnity, or liability obligation is offered by
|
||||||
You alone, and You hereby agree to indemnify every Contributor for any
|
You alone, and You hereby agree to indemnify every Contributor for any
|
||||||
liability incurred by such Contributor as a result of warranty, support,
|
liability incurred by such Contributor as a result of warranty, support,
|
||||||
indemnity or liability terms You offer. You may include additional
|
indemnity or liability terms You offer. You may include additional
|
||||||
disclaimers of warranty and limitations of liability specific to any
|
disclaimers of warranty and limitations of liability specific to any
|
||||||
jurisdiction.
|
jurisdiction.
|
||||||
|
|
||||||
4. Inability to Comply Due to Statute or Regulation
|
4. Inability to Comply Due to Statute or Regulation
|
||||||
---------------------------------------------------
|
|
||||||
|
|
||||||
If it is impossible for You to comply with any of the terms of this
|
If it is impossible for You to comply with any of the terms of this License
|
||||||
License with respect to some or all of the Covered Software due to
|
with respect to some or all of the Covered Software due to statute,
|
||||||
statute, judicial order, or regulation then You must: (a) comply with
|
judicial order, or regulation then You must: (a) comply with the terms of
|
||||||
the terms of this License to the maximum extent possible; and (b)
|
this License to the maximum extent possible; and (b) describe the
|
||||||
describe the limitations and the code they affect. Such description must
|
limitations and the code they affect. Such description must be placed in a
|
||||||
be placed in a text file included with all distributions of the Covered
|
text file included with all distributions of the Covered Software under
|
||||||
Software under this License. Except to the extent prohibited by statute
|
this License. Except to the extent prohibited by statute or regulation,
|
||||||
or regulation, such description must be sufficiently detailed for a
|
such description must be sufficiently detailed for a recipient of ordinary
|
||||||
recipient of ordinary skill to be able to understand it.
|
skill to be able to understand it.
|
||||||
|
|
||||||
5. Termination
|
5. Termination
|
||||||
--------------
|
|
||||||
|
|
||||||
5.1. The rights granted under this License will terminate automatically
|
5.1. The rights granted under this License will terminate automatically if You
|
||||||
if You fail to comply with any of its terms. However, if You become
|
fail to comply with any of its terms. However, if You become compliant,
|
||||||
compliant, then the rights granted under this License from a particular
|
then the rights granted under this License from a particular Contributor
|
||||||
Contributor are reinstated (a) provisionally, unless and until such
|
are reinstated (a) provisionally, unless and until such Contributor
|
||||||
Contributor explicitly and finally terminates Your grants, and (b) on an
|
explicitly and finally terminates Your grants, and (b) on an ongoing
|
||||||
ongoing basis, if such Contributor fails to notify You of the
|
basis, if such Contributor fails to notify You of the non-compliance by
|
||||||
non-compliance by some reasonable means prior to 60 days after You have
|
some reasonable means prior to 60 days after You have come back into
|
||||||
come back into compliance. Moreover, Your grants from a particular
|
compliance. Moreover, Your grants from a particular Contributor are
|
||||||
Contributor are reinstated on an ongoing basis if such Contributor
|
reinstated on an ongoing basis if such Contributor notifies You of the
|
||||||
notifies You of the non-compliance by some reasonable means, this is the
|
non-compliance by some reasonable means, this is the first time You have
|
||||||
first time You have received notice of non-compliance with this License
|
received notice of non-compliance with this License from such
|
||||||
from such Contributor, and You become compliant prior to 30 days after
|
Contributor, and You become compliant prior to 30 days after Your receipt
|
||||||
Your receipt of the notice.
|
of the notice.
|
||||||
|
|
||||||
5.2. If You initiate litigation against any entity by asserting a patent
|
5.2. If You initiate litigation against any entity by asserting a patent
|
||||||
infringement claim (excluding declaratory judgment actions,
|
infringement claim (excluding declaratory judgment actions,
|
||||||
counter-claims, and cross-claims) alleging that a Contributor Version
|
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||||
directly or indirectly infringes any patent, then the rights granted to
|
directly or indirectly infringes any patent, then the rights granted to
|
||||||
You by any and all Contributors for the Covered Software under Section
|
You by any and all Contributors for the Covered Software under Section
|
||||||
2.1 of this License shall terminate.
|
2.1 of this License shall terminate.
|
||||||
|
|
||||||
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
|
5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user
|
||||||
end user license agreements (excluding distributors and resellers) which
|
license agreements (excluding distributors and resellers) which have been
|
||||||
have been validly granted by You or Your distributors under this License
|
validly granted by You or Your distributors under this License prior to
|
||||||
prior to termination shall survive termination.
|
termination shall survive termination.
|
||||||
|
|
||||||
************************************************************************
|
6. Disclaimer of Warranty
|
||||||
* *
|
|
||||||
* 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. *
|
|
||||||
* *
|
|
||||||
************************************************************************
|
|
||||||
|
|
||||||
************************************************************************
|
Covered Software is provided under this License on an "as is" basis,
|
||||||
* *
|
without warranty of any kind, either expressed, implied, or statutory,
|
||||||
* 7. Limitation of Liability *
|
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
|
||||||
* Under no circumstances and under no legal theory, whether tort *
|
is with You. Should any Covered Software prove defective in any respect,
|
||||||
* (including negligence), contract, or otherwise, shall any *
|
You (not any Contributor) assume the cost of any necessary servicing,
|
||||||
* Contributor, or anyone who distributes Covered Software as *
|
repair, or correction. This disclaimer of warranty constitutes an essential
|
||||||
* permitted above, be liable to You for any direct, indirect, *
|
part of this License. No use of any Covered Software is authorized under
|
||||||
* special, incidental, or consequential damages of any character *
|
this License except under this disclaimer.
|
||||||
* including, without limitation, damages for lost profits, loss of *
|
|
||||||
* goodwill, work stoppage, computer failure or malfunction, or any *
|
7. Limitation of Liability
|
||||||
* and all other commercial damages or losses, even if such party *
|
|
||||||
* shall have been informed of the possibility of such damages. This *
|
Under no circumstances and under no legal theory, whether tort (including
|
||||||
* limitation of liability shall not apply to liability for death or *
|
negligence), contract, or otherwise, shall any Contributor, or anyone who
|
||||||
* personal injury resulting from such party's negligence to the *
|
distributes Covered Software as permitted above, be liable to You for any
|
||||||
* extent applicable law prohibits such limitation. Some *
|
direct, indirect, special, incidental, or consequential damages of any
|
||||||
* jurisdictions do not allow the exclusion or limitation of *
|
character including, without limitation, damages for lost profits, loss of
|
||||||
* incidental or consequential damages, so this exclusion and *
|
goodwill, work stoppage, computer failure or malfunction, or any and all
|
||||||
* limitation may not apply to You. *
|
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
|
8. Litigation
|
||||||
-------------
|
|
||||||
|
|
||||||
Any litigation relating to this License may be brought only in the
|
Any litigation relating to this License may be brought only in the courts
|
||||||
courts of a jurisdiction where the defendant maintains its principal
|
of a jurisdiction where the defendant maintains its principal place of
|
||||||
place of business and such litigation shall be governed by laws of that
|
business and such litigation shall be governed by laws of that
|
||||||
jurisdiction, without reference to its conflict-of-law provisions.
|
jurisdiction, without reference to its conflict-of-law provisions. Nothing
|
||||||
Nothing in this Section shall prevent a party's ability to bring
|
in this Section shall prevent a party's ability to bring cross-claims or
|
||||||
cross-claims or counter-claims.
|
counter-claims.
|
||||||
|
|
||||||
9. Miscellaneous
|
9. Miscellaneous
|
||||||
----------------
|
|
||||||
|
|
||||||
This License represents the complete agreement concerning the subject
|
This License represents the complete agreement concerning the subject
|
||||||
matter hereof. If any provision of this License is held to be
|
matter hereof. If any provision of this License is held to be
|
||||||
unenforceable, such provision shall be reformed only to the extent
|
unenforceable, such provision shall be reformed only to the extent
|
||||||
necessary to make it enforceable. Any law or regulation which provides
|
necessary to make it enforceable. Any law or regulation which provides that
|
||||||
that the language of a contract shall be construed against the drafter
|
the language of a contract shall be construed against the drafter shall not
|
||||||
shall not be used to construe this License against a Contributor.
|
be used to construe this License against a Contributor.
|
||||||
|
|
||||||
|
|
||||||
10. Versions of the License
|
10. Versions of the License
|
||||||
---------------------------
|
|
||||||
|
|
||||||
10.1. New Versions
|
10.1. New Versions
|
||||||
|
|
||||||
Mozilla Foundation is the license steward. Except as provided in Section
|
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
|
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
|
publish new versions of this License. Each version will be given a
|
||||||
distinguishing version number.
|
distinguishing version number.
|
||||||
|
|
||||||
10.2. Effect of New Versions
|
10.2. Effect of New Versions
|
||||||
|
|
||||||
You may distribute the Covered Software under the terms of the version
|
You may distribute the Covered Software under the terms of the version
|
||||||
of the License under which You originally received the Covered Software,
|
of the License under which You originally received the Covered Software,
|
||||||
or under the terms of any subsequent version published by the license
|
or under the terms of any subsequent version published by the license
|
||||||
steward.
|
steward.
|
||||||
|
|
||||||
10.3. Modified Versions
|
10.3. Modified Versions
|
||||||
|
|
||||||
If you create software not governed by this License, and you want to
|
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
|
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
|
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
|
any references to the name of the license steward (except to note that
|
||||||
such modified license differs from this License).
|
such modified license differs from this License).
|
||||||
|
|
||||||
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
||||||
Licenses
|
Licenses If You choose to distribute Source Code Form that is
|
||||||
|
Incompatible With Secondary Licenses under the terms of this version of
|
||||||
If You choose to distribute Source Code Form that is Incompatible With
|
the License, the notice described in Exhibit B of this License must be
|
||||||
Secondary Licenses under the terms of this version of the License, the
|
attached.
|
||||||
notice described in Exhibit B of this License must be attached.
|
|
||||||
|
|
||||||
Exhibit A - Source Code Form License Notice
|
Exhibit A - Source Code Form License Notice
|
||||||
-------------------------------------------
|
|
||||||
|
|
||||||
This Source Code Form is subject to the terms of the Mozilla Public
|
This Source Code Form is subject to the
|
||||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
terms of the Mozilla Public License, v.
|
||||||
file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
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
|
If it is not possible or desirable to put the notice in a particular file,
|
||||||
file, then You may include the notice in a location (such as a LICENSE
|
then You may include the notice in a location (such as a LICENSE file in a
|
||||||
file in a relevant directory) where a recipient would be likely to look
|
relevant directory) where a recipient would be likely to look for such a
|
||||||
for such a notice.
|
notice.
|
||||||
|
|
||||||
You may add additional accurate notices of copyright ownership.
|
You may add additional accurate notices of copyright ownership.
|
||||||
|
|
||||||
Exhibit B - "Incompatible With Secondary Licenses" Notice
|
Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||||
---------------------------------------------------------
|
|
||||||
|
|
||||||
This Source Code Form is "Incompatible With Secondary Licenses", as
|
This Source Code Form is "Incompatible
|
||||||
defined by the Mozilla Public License, v. 2.0.
|
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