Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
vendor
phpunit.xml
test
.phpunit.result.cache
.phpunit.result.cache
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"require": {
"guzzlehttp/guzzle": "^7.9",
"psr/http-client": "^1.0",
"ext-json": "*",
"ext-json": "*",
"php": "^8.1"
},
"require-dev": {
Expand Down
37 changes: 0 additions & 37 deletions run.php

This file was deleted.

119 changes: 119 additions & 0 deletions src/Transaction.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
<?php

namespace Neo4j\QueryAPI;

use Neo4j\QueryAPI\Exception\Neo4jException;
use Neo4j\QueryAPI\Objects\ResultCounters;
use Neo4j\QueryAPI\Results\ResultRow;
use Neo4j\QueryAPI\Results\ResultSet;
use Psr\Http\Client\ClientInterface;
use stdClass;

class Transaction
{
public function __construct(
private ClientInterface $client,
private string $clusterAffinity,
private string $transactionId
) {
}

/**
* Execute a Cypher query within the transaction.
*
* @param string $query The Cypher query to be executed.
* @param array $parameters Parameters for the query.
* @return ResultSet The result rows in ResultSet format.
* @throws Neo4jException If the response structure is invalid.
*/
public function run(string $query, array $parameters): ResultSet
{
$response = $this->client->post("/db/neo4j/query/v2/tx/{$this->transactionId}", [
'headers' => [
'neo4j-cluster-affinity' => $this->clusterAffinity,
],
'json' => [
'statement' => $query,
'parameters' => empty($parameters) ? new stdClass() : $parameters,
'includeCounters' => true
],
]);

$responseBody = $response->getBody()->getContents();
$data = json_decode($responseBody, true);

if (!isset($data['data']['fields'], $data['data']['values'])) {
throw new Neo4jException([
'message' => 'Unexpected response structure from Neo4j',
'response' => $data,
]);
}

$keys = $data['data']['fields'];
$values = $data['data']['values'];

if (empty($values)) {
return new ResultSet([], new ResultCounters(
containsUpdates: $data['counters']['containsUpdates'],
nodesCreated: $data['counters']['nodesCreated'],
nodesDeleted: $data['counters']['nodesDeleted'],
propertiesSet: $data['counters']['propertiesSet'],
relationshipsCreated: $data['counters']['relationshipsCreated'],
relationshipsDeleted: $data['counters']['relationshipsDeleted'],
labelsAdded: $data['counters']['labelsAdded'],
labelsRemoved: $data['counters']['labelsRemoved'],
indexesAdded: $data['counters']['indexesAdded'],
indexesRemoved: $data['counters']['indexesRemoved'],
constraintsAdded: $data['counters']['constraintsAdded'],
constraintsRemoved: $data['counters']['constraintsRemoved'],
containsSystemUpdates: $data['counters']['containsSystemUpdates'],
systemUpdates: $data['counters']['systemUpdates']
));
}

$ogm = new OGM();
$rows = array_map(function ($resultRow) use ($ogm, $keys) {
$data = [];
foreach ($keys as $index => $key) {
$fieldData = $resultRow[$index] ?? null;
$data[$key] = $ogm->map($fieldData);
}
return new ResultRow($data);
}, $values);

return new ResultSet($rows, new ResultCounters(
containsUpdates: $data['counters']['containsUpdates'],
nodesCreated: $data['counters']['nodesCreated'],
nodesDeleted: $data['counters']['nodesDeleted'],
propertiesSet: $data['counters']['propertiesSet'],
relationshipsCreated: $data['counters']['relationshipsCreated'],
relationshipsDeleted: $data['counters']['relationshipsDeleted'],
labelsAdded: $data['counters']['labelsAdded'],
labelsRemoved: $data['counters']['labelsRemoved'],
indexesAdded: $data['counters']['indexesAdded'],
indexesRemoved: $data['counters']['indexesRemoved'],
constraintsAdded: $data['counters']['constraintsAdded'],
constraintsRemoved: $data['counters']['constraintsRemoved'],
containsSystemUpdates: $data['counters']['containsSystemUpdates'],
systemUpdates: $data['counters']['systemUpdates']
));
}

public function commit(): void
{
$this->client->post("/db/neo4j/query/v2/tx/{$this->transactionId}/commit", [
'headers' => [
'neo4j-cluster-affinity' => $this->clusterAffinity,
],
]);
}

public function rollback(): void
{
$this->client->delete("/db/neo4j/query/v2/tx/{$this->transactionId}", [
'headers' => [
'neo4j-cluster-affinity' => $this->clusterAffinity,
],
]);
}
}
54 changes: 0 additions & 54 deletions test.php

This file was deleted.

2 changes: 0 additions & 2 deletions test_curl.php

This file was deleted.

11 changes: 6 additions & 5 deletions tests/Integration/Neo4jQueryAPIIntegrationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,26 +53,27 @@ public function testTransactionCommit(): void
$name = (string)mt_rand(1, 100000);

// Create a node within the transaction
$tsx->run('CREATE (x:Human {name: $name})', ['name' => $name]); // Pass the array here
$tsx->run("CREATE (x:Human {name: \$name})", ['name' => $name]);

// Validate that the node does not exist in the database before the transaction is committed
$results = $this->api->run('MATCH (x:Human {name: $name}) RETURN x', ['name' => $name]);
$results = $this->api->run("MATCH (x:Human {name: \$name}) RETURN x", ['name' => $name]);
$this->assertCount(0, $results);

// Validate that the node exists within the transaction
$results = $tsx->run('MATCH (x:Human {name: $name}) RETURN x', ['name' => $name]);
$results = $tsx->run("MATCH (x:Human {name: \$name}) RETURN x", ['name' => $name]);
$this->assertCount(1, $results);

// Commit the transaction
$tsx->commit();

// Validate that the node now exists in the database
$results = $this->api->run('MATCH (x:Human {name: $name}) RETURN x', ['name' => $name]);
$this->assertCount(0, $results);
$results = $this->api->run("MATCH (x:Human {name: \$name}) RETURN x", ['name' => $name]);
$this->assertCount(1, $results); // Updated to expect 1 result
}




/**
* @throws GuzzleException
*/
Expand Down
73 changes: 73 additions & 0 deletions tests/Integration/ProfileTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<?php

namespace Neo4j\QueryAPI\Tests\Integration;

use GuzzleHttp\Client;
use GuzzleHttp\Handler\MockHandler;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Psr7\Response;
use Neo4j\QueryAPI\Profile;
use PHPUnit\Framework\TestCase;

class ProfileTest extends TestCase
{
public function testExecuteQuery(): void
{
$mockResponseData = [
'result' => [
'columns' => ['name'],
'rows' => [['John Doe']],
],
'bookmarks' => ['bookmark1'],
];


$mock = new MockHandler([
new Response(200, [], json_encode($mockResponseData))
]);
$handlerStack = HandlerStack::create($mock);

$mockClient = new Client(['handler' => $handlerStack]);

$profile = new Profile('http://mock-neo4j-url', 'user', 'password');
$reflection = new \ReflectionClass(Profile::class);
$clientProperty = $reflection->getProperty('client');
$clientProperty->setValue($profile, $mockClient);

$query = 'MATCH (n:Person) RETURN n.name';
$result = $profile->executeQuery($query);

$this->assertIsArray($result);
$this->assertEquals($mockResponseData, $result);
}

public function testFormatResponse(): void
{
$mockInputData = [
'result' => [
'columns' => ['name'],
'rows' => [['John Doe']],
],
'profiledQueryPlan' => [
'plan' => 'Mock Plan',
],
'bookmarks' => ['bookmark1'],
];

$expectedOutput = [
'data' => [
'fields' => ['name'],
'values' => [['John Doe']],
],
'profiledQueryPlan' => [
'plan' => 'Mock Plan',
],
'bookmarks' => ['bookmark1'],
];

$profile = new Profile('http://mock-neo4j-url', 'user', 'password');

$formattedResponse = $profile->formatResponse($mockInputData);
$this->assertEquals($expectedOutput, $formattedResponse);
}
}
Loading