diff --git a/examples/QueryExample_Directory.php b/examples/QueryExample_Directory.php index 5a80a52..06d16e8 100644 --- a/examples/QueryExample_Directory.php +++ b/examples/QueryExample_Directory.php @@ -9,7 +9,10 @@ use Darksparrow\DeegraphInteractions\QueryBuilder\QueryBuilder; $result = QueryBuilder::Directory() ->RelativePath("{0b368d41-7c15-42c8-899c-2b178ae9d983}") ->Build() - ->RunQuery(DeegraphConnection::DB()) + ->RunQuery( + actorID: DeegraphConnection::Actor(), + server: DeegraphConnection::DB(), + ) ; echo "Directory\n"; diff --git a/examples/QueryExample_Permission.php b/examples/QueryExample_Permission.php index 8475178..e32c313 100644 --- a/examples/QueryExample_Permission.php +++ b/examples/QueryExample_Permission.php @@ -8,9 +8,11 @@ use Darksparrow\DeegraphInteractions\QueryBuilder\QueryBuilder; $permissions = QueryBuilder::Permission() ->On("{b222c121-0ed2-5819-bbf9-4db9aab85ea3}") - ->As("{b222c121-0ed2-5819-bbf9-4db9aab85ea3}") ->Build() - ->RunQuery(DeegraphConnection::DB()) + ->RunQuery( + actorID: DeegraphConnection::Actor(), + server: DeegraphConnection::DB(), + ) ; echo "Permissions\n"; diff --git a/examples/QueryExample_References.php b/examples/QueryExample_References.php index 389fef4..b1e87d5 100644 --- a/examples/QueryExample_References.php +++ b/examples/QueryExample_References.php @@ -9,7 +9,10 @@ use Darksparrow\DeegraphInteractions\QueryBuilder\QueryBuilder; $result = QueryBuilder::References() ->RelativePath("{0b368d41-7c15-42c8-899c-2b178ae9d983}") ->Build() - ->RunQuery(DeegraphConnection::DB()) + ->RunQuery( + actorID: DeegraphConnection::Actor(), + server: DeegraphConnection::DB(), + ) ; echo "References\n"; diff --git a/examples/QueryExample_Select.php b/examples/QueryExample_Select.php new file mode 100644 index 0000000..12bcee5 --- /dev/null +++ b/examples/QueryExample_Select.php @@ -0,0 +1,22 @@ +RelativePaths(["@id", "name"]) + ->From($targetNode) + ->Build() + ->RunQuery( + actorID: DeegraphConnection::Actor(), + server: DeegraphConnection::DB(), + ) +; + +var_dump($result->Rows[0]->Properties["@id"]["{$targetNode}/@id"]); +var_dump($result->Rows[0]->Properties["name"]["{$targetNode}/name"]); diff --git a/src/Core/DeegraphServer.php b/src/Core/DeegraphServer.php index 1e4339b..9ca2da4 100644 --- a/src/Core/DeegraphServer.php +++ b/src/Core/DeegraphServer.php @@ -2,30 +2,28 @@ namespace Darksparrow\DeegraphInteractions\Core; +use Auxilium\Exceptions\DatabaseConnectionException; +use Auxilium\Exceptions\DeegraphException; use Darksparrow\DeegraphInteractions\DataStructures\DeegraphNodeDetails; use Darksparrow\DeegraphInteractions\DataStructures\ServerInfo; use Darksparrow\DeegraphInteractions\DataStructures\UUID; +use Exception; class DeegraphServer { private string $Token; - private string $Actor; - - private string $ServerDomain; private int $Port; private bool $AllowSelfSignedCerts; + public function __construct( string $token, - string $actor, string $server = "localhost", int $port = 8088, bool $allowSelfSignedCerts = false) { $this->Token = $token; - $this->Actor = $actor; - $this->ServerDomain = $server; $this->Port = $port; $this->AllowSelfSignedCerts = $allowSelfSignedCerts; @@ -41,48 +39,130 @@ class DeegraphServer return new ServerInfo(response: $response); } - public function RunRawRequest(string $endpoint, string $method = "GET", string $body = null): array + /** + * Runs a request against the Deegraph server. + * + * @param string $actorID The Actor for the request. + * @param string $endpoint What endpoint on the server to target. + * @param string $method What method to use. + * @param string|null $body + * + * @return array + * + * @throws Exception + */ + private function RunRawRequest( + UUID $actorID, + string $endpoint, + string $method = "GET", + string $body = null + ): array { - $ch = curl_init("https://{$this->ServerDomain}:{$this->Port}{$endpoint}"); - curl_setopt($ch, CURLOPT_HTTPHEADER, [ - 'Content-Type: application/json', - "Authorization: Bearer {$this->Token}", - "X-Auxilium-Actor: {$this->Actor}", - ]); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + // Convert the provided method string to capital letters + $method = strtoupper($method); - if ($method == "POST") { - curl_setopt($ch, CURLOPT_POST, 1); - } + // Break the endpoint into components, and URL-encode each component to ensure safe transmission + $endpointComponents = explode("/", $endpoint); + foreach($endpointComponents as &$cmp) + $cmp = urlencode($cmp); + + // Construct the full URL using the server domain and encoded endpoint + $url = "https://" . $this->ServerDomain . implode("/", $endpointComponents); + + // Initialize a cURL session + $ch = curl_init(); + curl_setopt(handle: $ch, option: CURLOPT_URL, value: $url); + + // If the request method is POST, set the CURLOPT_POST option + if($method == "POST") + curl_setopt(handle: $ch, option: CURLOPT_POST, value: true); + + // Specify the port for the connection + curl_setopt(handle: $ch, option: CURLOPT_PORT, value: $this->Port); + + // Allow self-signed certificates if configured if($this->AllowSelfSignedCerts) { - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); - curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); + curl_setopt(handle: $ch, option: CURLOPT_SSL_VERIFYPEER, value: false); + curl_setopt(handle: $ch, option: CURLOPT_SSL_VERIFYHOST, value: false); + } + + // Ensure the Actor property is set; if not, throw an exception + if($actorID == null) + { + throw new Exception("Client's user account is invalid"); } - if ($method == "POST" || $method == "PUT") { - if ($body != null) { - curl_setopt($ch, CURLOPT_POSTFIELDS, $body); + // Set HTTP headers, including authorization and custom headers + curl_setopt(handle: $ch, option: CURLOPT_HTTPHEADER, value: [ + "Content-Type: text/plain", + "Authorization: Bearer " . $this->Token, + "X-Auxilium-Actor: " . $actorID->GetPlainUUID(), + ]); + + // Handle request body for POST and PUT methods + if($method == "POST" || $method == "PUT") + { + if($body != null) + { + curl_setopt(handle: $ch, option: CURLOPT_POSTFIELDS, value: $body); } } - $result = curl_exec($ch); + // Configure cURL to return the server response as a string + curl_setopt(handle: $ch, option: CURLOPT_RETURNTRANSFER, value: true); + + // Execute the cURL request and store the response + $serverResponse = curl_exec($ch); + + // Check if the request failed + if($serverResponse === false) + { + throw new Exception("Deegraph server did not respond to Auxilium query"); + } + // Handle specific SSL certificate errors if(curl_error($ch) == "SSL certificate problem: unable to get local issuer certificate") { throw new \Exception(); - die(); } + // Check for server-side errors (HTTP status codes 500–599) + if( + curl_getinfo($ch, CURLINFO_RESPONSE_CODE) >= 500 + && curl_getinfo($ch, CURLINFO_RESPONSE_CODE) < 600 + ) + { + throw new Exception( + message: "Deegraph server responded with an internal error code", + code: 0, + previous: null, + // trade: isset($server_output["@trace"]) ? $server_output["@trace"] : null + ); + } + + // Close the cURL session curl_close($ch); - $temp = json_decode($result, true); + // Decode the JSON response into an associative array + $temp = json_decode($serverResponse, associative: true); return $temp; } - public function GetRawNode(UUID $nodeID): DeegraphNodeDetails + public function RunQuery(UUID $actorID, string $queryString): array + { + return $this->RunRawRequest( + actorID: $actorID, + endpoint: "/api/v1/@query", + method: "POST", + body: $queryString + ); + } + + public function GetRawNode(UUID $actorID, UUID $nodeID): DeegraphNodeDetails { $response = $this->RunRawRequest( + actorID: $actorID, endpoint: "/api/v1/{$nodeID}", method: "GET", body: null, diff --git a/src/DataStructures/UUID.php b/src/DataStructures/UUID.php index 5b92a96..4467645 100644 --- a/src/DataStructures/UUID.php +++ b/src/DataStructures/UUID.php @@ -25,4 +25,9 @@ class UUID { return "{" . $this->UUID . "}"; } + + public function GetPlainUUID(): string + { + return $this->UUID; + } } diff --git a/src/QueryBuilder/DeleteQuery/DeleteQuery.php b/src/QueryBuilder/DeleteQuery/DeleteQuery.php index a7bdc50..890142e 100644 --- a/src/QueryBuilder/DeleteQuery/DeleteQuery.php +++ b/src/QueryBuilder/DeleteQuery/DeleteQuery.php @@ -3,6 +3,7 @@ namespace Darksparrow\DeegraphInteractions\QueryBuilder\DeleteQuery; use Darksparrow\DeegraphInteractions\Core\DeegraphServer; +use Darksparrow\DeegraphInteractions\DataStructures\UUID; use Darksparrow\DeegraphInteractions\QueryBuilder\DirectoryQuery\DirectoryQueryResponse; class DeleteQuery @@ -18,12 +19,11 @@ class DeleteQuery return $this->QueryString; } - public function RunQuery(DeegraphServer $server): DeleteQueryResponse + public function RunQuery(UUID $actorID, DeegraphServer $server): DeleteQueryResponse { - $response = $server->RunRawRequest( - endpoint: "/api/v1/@query", - method: "POST", - body: $this->QueryString + $response = $server->RunQuery( + actorID: $actorID, + queryString: $this->QueryString, ); return new DeleteQueryResponse($response); } diff --git a/src/QueryBuilder/DirectoryQuery/DirectoryQuery.php b/src/QueryBuilder/DirectoryQuery/DirectoryQuery.php index e05266c..1ac79cb 100644 --- a/src/QueryBuilder/DirectoryQuery/DirectoryQuery.php +++ b/src/QueryBuilder/DirectoryQuery/DirectoryQuery.php @@ -3,6 +3,7 @@ namespace Darksparrow\DeegraphInteractions\QueryBuilder\DirectoryQuery; use Darksparrow\DeegraphInteractions\Core\DeegraphServer; +use Darksparrow\DeegraphInteractions\DataStructures\UUID; final class DirectoryQuery { @@ -17,12 +18,11 @@ final class DirectoryQuery return $this->QueryString; } - public function RunQuery(DeegraphServer $server): DirectoryQueryResponse + public function RunQuery(UUID $actorID, DeegraphServer $server): DirectoryQueryResponse { - $response = $server->RunRawRequest( - endpoint: "/api/v1/@query", - method: "POST", - body: $this->QueryString + $response = $server->RunQuery( + actorID: $actorID, + queryString: $this->QueryString, ); return new DirectoryQueryResponse($response); } diff --git a/src/QueryBuilder/LinkQuery/LinkQuery.php b/src/QueryBuilder/LinkQuery/LinkQuery.php index 9450ab8..805d0b4 100644 --- a/src/QueryBuilder/LinkQuery/LinkQuery.php +++ b/src/QueryBuilder/LinkQuery/LinkQuery.php @@ -3,6 +3,7 @@ namespace Darksparrow\DeegraphInteractions\QueryBuilder\LinkQuery; use Darksparrow\DeegraphInteractions\Core\DeegraphServer; +use Darksparrow\DeegraphInteractions\DataStructures\UUID; use Darksparrow\DeegraphInteractions\QueryBuilder\DirectoryQuery\DirectoryQueryResponse; class LinkQuery @@ -18,12 +19,11 @@ class LinkQuery return $this->QueryString; } - public function RunQuery(DeegraphServer $server): LinkQueryResponse + public function RunQuery(UUID $actorID, DeegraphServer $server): LinkQueryResponse { - $response = $server->RunRawRequest( - endpoint: "/api/v1/@query", - method: "POST", - body: $this->QueryString + $response = $server->RunQuery( + actorID: $actorID, + queryString: $this->QueryString, ); return new LinkQueryResponse($response); } diff --git a/src/QueryBuilder/PermissionsQuery/PermissionQuery.php b/src/QueryBuilder/PermissionsQuery/PermissionQuery.php index 25a54fc..debddad 100644 --- a/src/QueryBuilder/PermissionsQuery/PermissionQuery.php +++ b/src/QueryBuilder/PermissionsQuery/PermissionQuery.php @@ -3,6 +3,7 @@ namespace Darksparrow\DeegraphInteractions\QueryBuilder\PermissionsQuery; use Darksparrow\DeegraphInteractions\Core\DeegraphServer; +use Darksparrow\DeegraphInteractions\DataStructures\UUID; final class PermissionQuery { @@ -17,12 +18,11 @@ final class PermissionQuery return $this->QueryString; } - public function RunQuery(DeegraphServer $server): PermissionQueryResponse + public function RunQuery(UUID $actorID, DeegraphServer $server): PermissionQueryResponse { - $response = $server->RunRawRequest( - endpoint: "/api/v1/@query", - method: "POST", - body: $this->QueryString + $response = $server->RunQuery( + actorID: $actorID, + queryString: $this->QueryString, ); return new PermissionQueryResponse($response); } diff --git a/src/QueryBuilder/ReferencesQuery/ReferencesQuery.php b/src/QueryBuilder/ReferencesQuery/ReferencesQuery.php index cc7986a..58234d0 100644 --- a/src/QueryBuilder/ReferencesQuery/ReferencesQuery.php +++ b/src/QueryBuilder/ReferencesQuery/ReferencesQuery.php @@ -3,6 +3,7 @@ namespace Darksparrow\DeegraphInteractions\QueryBuilder\ReferencesQuery; use Darksparrow\DeegraphInteractions\Core\DeegraphServer; +use Darksparrow\DeegraphInteractions\DataStructures\UUID; final class ReferencesQuery { @@ -17,12 +18,11 @@ final class ReferencesQuery return $this->QueryString; } - public function RunQuery(DeegraphServer $server): ReferencesQueryResponse + public function RunQuery(UUID $actorID, DeegraphServer $server): ReferencesQueryResponse { - $response = $server->RunRawRequest( - endpoint: "/api/v1/@query", - method: "POST", - body: $this->QueryString + $response = $server->RunQuery( + actorID: $actorID, + queryString: $this->QueryString, ); return new ReferencesQueryResponse($response); } diff --git a/src/QueryBuilder/SelectQuery/SelectQuery.php b/src/QueryBuilder/SelectQuery/SelectQuery.php index f15c2fb..2839406 100644 --- a/src/QueryBuilder/SelectQuery/SelectQuery.php +++ b/src/QueryBuilder/SelectQuery/SelectQuery.php @@ -3,6 +3,7 @@ namespace Darksparrow\DeegraphInteractions\QueryBuilder\SelectQuery; use Darksparrow\DeegraphInteractions\Core\DeegraphServer; +use Darksparrow\DeegraphInteractions\DataStructures\UUID; class SelectQuery { @@ -17,12 +18,11 @@ class SelectQuery return $this->QueryString; } - public function RunQuery(DeegraphServer $server): SelectQueryResponse + public function RunQuery(UUID $actorID, DeegraphServer $server): SelectQueryResponse { - $response = $server->RunRawRequest( - endpoint: "/api/v1/@query", - method: "POST", - body: $this->QueryString + $response = $server->RunQuery( + actorID: $actorID, + queryString: $this->QueryString, ); return new SelectQueryResponse($response); } diff --git a/src/QueryBuilder/UnlinkQuery/UnlinkQuery.php b/src/QueryBuilder/UnlinkQuery/UnlinkQuery.php index 837d44e..7967058 100644 --- a/src/QueryBuilder/UnlinkQuery/UnlinkQuery.php +++ b/src/QueryBuilder/UnlinkQuery/UnlinkQuery.php @@ -3,6 +3,7 @@ namespace Darksparrow\DeegraphInteractions\QueryBuilder\UnlinkQuery; use Darksparrow\DeegraphInteractions\Core\DeegraphServer; +use Darksparrow\DeegraphInteractions\DataStructures\UUID; use Darksparrow\DeegraphInteractions\QueryBuilder\LinkQuery\LinkQueryResponse; class UnlinkQuery @@ -18,12 +19,11 @@ class UnlinkQuery return $this->QueryString; } - public function RunQuery(DeegraphServer $server): UnlinkQueryResponse + public function RunQuery(UUID $actorID, DeegraphServer $server): UnlinkQueryResponse { - $response = $server->RunRawRequest( - endpoint: "/api/v1/@query", - method: "POST", - body: $this->QueryString + $response = $server->RunQuery( + actorID: $actorID, + queryString: $this->QueryString, ); return new UnlinkQueryResponse($response); }