rdf_sync-1.x-dev/src/Plugin/rdf_sync/Connector/Virtuoso.php
src/Plugin/rdf_sync/Connector/Virtuoso.php
<?php
declare(strict_types=1);
namespace Drupal\rdf_sync\Plugin\rdf_sync\Connector;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Plugin\PluginFormInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\rdf_sync\Attribute\RdfSyncConnector;
use Drupal\rdf_sync\Model\VirtuosoEndpoint;
use Drupal\rdf_sync\RdfSyncConnectorPluginPluginBase;
use EasyRdf\GraphStore;
use EasyRdf\Http\Exception as EasyRdfException;
use EasyRdf\Sparql\Client;
use EasyRdf\Sparql\Result;
/**
* Defines the Virtuoso connector plugin.
*/
#[RdfSyncConnector(
id: 'virtuoso',
label: new TranslatableMarkup('Virtuoso'),
description: new TranslatableMarkup('Virtuoso OpenSource'),
website: 'https://github.com/openlink/virtuoso-opensource',
)]
class Virtuoso extends RdfSyncConnectorPluginPluginBase implements PluginFormInterface {
/**
* The EasyRdf client.
*/
protected Client $client;
/**
* {@inheritdoc}
*/
public function defaultConfiguration(): array {
return [
'endpoint' => VirtuosoEndpoint::Basic->value,
'scheme' => 'http',
'host' => 'localhost',
'port' => 8890,
'paths' => [
'query' => 'sparql',
'update' => 'sparql',
'graph_store' => 'sparql-graph-crud',
],
'credentials' => [
'user' => NULL,
'password' => NULL,
],
] + parent::defaultConfiguration();
}
/**
* {@inheritdoc}
*/
public function query(string $query): Result {
return $this->performOperation($query, 'query');
}
/**
* {@inheritdoc}
*/
public function update(string $query): Result {
return $this->performOperation($query, 'update');
}
/**
* {@inheritdoc}
*/
public function clearGraph(string $uri): void {
$this->update("CLEAR GRAPH <$uri>");
}
/**
* {@inheritdoc}
*/
public function createGraphStore(): GraphStore {
$connectionString = $this->getEndpointUrl('graph_store');
return new GraphStore($connectionString);
}
/**
* {@inheritdoc}
*/
public function buildConfigurationForm(array $form, FormStateInterface $form_state): array {
$form['endpoint'] = [
'#type' => 'radios',
'#title' => $this->t('SPARQL endpoint type'),
'#description' => $this->t('See <a href=":url">Securing SPARQL endpoints</a>. Digest authentication and OAuth are not supported yet.', [
':url' => 'https://vos.openlinksw.com/owiki/wiki/VOS/VirtTipsAndTricksGuideSPARQLEndpoints',
]),
'#options' => VirtuosoEndpoint::casesAsOptions(),
'#config_target' => "rdf_sync.settings:connector.config.endpoint",
];
// Not implemented yet.
$form['endpoint'][VirtuosoEndpoint::Digest->value]['#disabled'] = TRUE;
$form['endpoint'][VirtuosoEndpoint::OAuth->value]['#disabled'] = TRUE;
$form['scheme'] = [
'#type' => 'select',
'#title' => $this->t('HTTP protocol'),
'#options' => [
'http' => $this->t('http'),
'https' => $this->t('https'),
],
'#config_target' => "rdf_sync.settings:connector.config.scheme",
];
$form['host'] = [
'#type' => 'textfield',
'#title' => $this->t('RDF host'),
'#required' => TRUE,
'#config_target' => 'rdf_sync.settings:connector.config.host',
];
$form['port'] = [
'#type' => 'number',
'#title' => $this->t('RDF port'),
'#required' => TRUE,
'#min' => 1,
'#config_target' => 'rdf_sync.settings:connector.config.port',
];
$form['paths'] = [
'#type' => 'details',
'#title' => $this->t('Paths'),
'#open' => TRUE,
];
$form['paths']['query'] = [
'#type' => 'textfield',
'#title' => $this->t('SPARQL query path'),
'#required' => TRUE,
'#config_target' => 'rdf_sync.settings:connector.config.paths.query',
];
$form['paths']['update'] = [
'#type' => 'textfield',
'#title' => $this->t('SPARQL update path'),
'#required' => TRUE,
'#config_target' => 'rdf_sync.settings:connector.config.paths.update',
];
$form['paths']['graph_store'] = [
'#type' => 'textfield',
'#title' => $this->t('Graph store path'),
'#required' => TRUE,
'#config_target' => 'rdf_sync.settings:connector.config.paths.graph_store',
];
$condition = [
':input[name="config[endpoint]"]' => [
'value' => VirtuosoEndpoint::Basic->value,
],
];
$states = ['invisible' => $condition, 'optional' => $condition];
$form['credentials'] = [
'#type' => 'details',
'#title' => $this->t('Credentials'),
'#open' => TRUE,
'#states' => $states,
];
$form['credentials']['user'] = [
'#type' => 'textfield',
'#title' => $this->t('User'),
'#config_target' => 'rdf_sync.settings:connector.config.credentials.user',
'#states' => $states,
];
$form['credentials']['password'] = [
'#type' => 'textfield',
'#title' => $this->t('Password'),
'#config_target' => 'rdf_sync.settings:connector.config.credentials.password',
'#states' => $states,
];
return $form;
}
/**
* {@inheritdoc}
*/
public function validateConfigurationForm(array &$form, FormStateInterface $form_state): void {
$needCredentials = [VirtuosoEndpoint::Digest, VirtuosoEndpoint::OAuth];
$endpointType = VirtuosoEndpoint::from($form_state->getValue('endpoint'));
if (in_array($endpointType, $needCredentials, TRUE)) {
$user = trim($form_state->getValue(['credentials', 'user']));
$password = $form_state->getValue(['credentials', 'password']);
if (!$user) {
$form_state->setErrorByName('credentials][user', $this->t('The user name is mandatory'));
}
// User might have been trimmed.
$form_state->setValue(['credentials', 'user'], $user);
if (!$password) {
$form_state->setErrorByName('credentials][password', $this->t('The password is mandatory'));
}
}
else {
$form_state->unsetValue(['credentials', 'user']);
$form_state->unsetValue(['credentials', 'password']);
}
}
/**
* {@inheritdoc}
*/
public function submitConfigurationForm(array &$form, FormStateInterface $form_state): void {}
/**
* Provides a helper to run a query or an update.
*
* @param string $query
* The SPARQL query.
* @param string $method
* The method: 'query' or 'update'.
*
* @return \EasyRdf\Sparql\Result
* The result object.
*/
protected function performOperation(string $query, string $method): Result {
assert(in_array($method, ['query', 'update'], TRUE));
try {
return $this->getClient()->$method($query);
}
catch (EasyRdfException $exception) {
// Re-throw the exception, but with the query as message.
throw new \Exception("Execution of $method failed with message '{$exception->getBody()}'. Query: " . $query, $exception->getCode(), $exception);
}
catch (\Throwable $exception) {
throw new \Exception("Execution of $method failed with message '{$exception->getMessage()}'. Query: " . $query, $exception->getCode(), $exception);
}
}
/**
* Returns the Easy RDF SPARQL client.
*
* @return \EasyRdf\Sparql\Client
* The Easy RDF SPARQL client.
*/
protected function getClient(): Client {
if (!isset($this->client)) {
$this->client = new Client($this->getEndpointUrl('query'), $this->getEndpointUrl('update'));
}
return $this->client;
}
/**
* {@inheritdoc}
*/
public function getEndpointUrl(string $type): string {
assert(in_array($type, ['query', 'update', 'graph_store'], TRUE));
$config = $this->getConfiguration();
$path = trim($config['paths'][$type], '/');
$endpoint = VirtuosoEndpoint::from($config['endpoint']);
return match($endpoint) {
VirtuosoEndpoint::Basic => "{$config['scheme']}://{$config['host']}:{$config['port']}/$path",
default => throw new \InvalidArgumentException("The '$endpoint->value' endpoint type is not supported yet"),
};
}
}
