|
1 | 1 | module Neo4j
|
2 | 2 | module Server
|
| 3 | + # The CypherTransaction object lifecycle is as follows: |
| 4 | + # * It is initialized with the transactional endpoint URL and the connection object to use for communication. It does not communicate with the server to create this. |
| 5 | + # * The first query within the transaction sets the commit and execution addresses, :commit_url and :exec_url. |
| 6 | + # * At any time, `failure` can be called to mark a transaction failed and trigger a rollback upon closure. |
| 7 | + # * `close` is called to end the transaction. It calls `_commit_tx` or `_delete_tx`. |
| 8 | + # |
| 9 | + # If a transaction is created and then closed without performing any queries, an OpenStruct is returned that behaves like a successfully closed query. |
3 | 10 | class CypherTransaction
|
4 | 11 | include Neo4j::Transaction::Instance
|
5 | 12 | include Neo4j::Core::CypherTranslator
|
6 | 13 | include Resource
|
7 | 14 |
|
8 |
| - attr_reader :commit_url, :exec_url |
| 15 | + attr_reader :commit_url, :exec_url, :base_url, :connection |
9 | 16 |
|
10 |
| - class CypherError < StandardError |
11 |
| - attr_reader :code, :status |
12 |
| - def initialize(code, status, message) |
13 |
| - super(message) |
14 |
| - @code = code |
15 |
| - @status = status |
16 |
| - end |
17 |
| - end |
18 |
| - |
19 |
| - def initialize(response, url, connection) |
20 |
| - @connection = connection |
21 |
| - @commit_url = response.body['commit'] |
22 |
| - @exec_url = response.headers['Location'] |
23 |
| - fail "NO ENDPOINT URL #{@connection} : HEAD: #{response.headers.inspect}" if !@exec_url || @exec_url.empty? |
24 |
| - init_resource_data(response.body, url) |
25 |
| - expect_response_code(response, 201) |
| 17 | + def initialize(url, session_connection) |
| 18 | + @base_url = url |
| 19 | + @connection = session_connection |
26 | 20 | register_instance
|
27 | 21 | end
|
28 | 22 |
|
| 23 | + ROW_REST = %w(row REST) |
29 | 24 | def _query(cypher_query, params = nil)
|
30 |
| - statement = {statement: cypher_query, parameters: params, resultDataContents: %w(row REST)} |
| 25 | + fail 'Transaction expired, unable to perform query' if expired? |
| 26 | + statement = {statement: cypher_query, parameters: params, resultDataContents: ROW_REST} |
31 | 27 | body = {statements: [statement]}
|
32 |
| - response = @connection.post(@exec_url, body) |
| 28 | + |
| 29 | + response = exec_url && commit_url ? connection.post(exec_url, body) : register_urls(body) |
33 | 30 | _create_cypher_response(response)
|
34 | 31 | end
|
35 | 32 |
|
| 33 | + def _delete_tx |
| 34 | + _tx_query(:delete, exec_url, headers: resource_headers) |
| 35 | + end |
| 36 | + |
| 37 | + def _commit_tx |
| 38 | + _tx_query(:post, commit_url, nil) |
| 39 | + end |
| 40 | + |
| 41 | + private |
| 42 | + |
| 43 | + def _tx_query(action, endpoint, headers = {}) |
| 44 | + return empty_response if !commit_url || expired? |
| 45 | + response = connection.send(action, endpoint, headers) |
| 46 | + expect_response_code(response, 200) |
| 47 | + response |
| 48 | + end |
| 49 | + |
| 50 | + def register_urls(body) |
| 51 | + response = connection.post(base_url, body) |
| 52 | + @commit_url = response.body['commit'] |
| 53 | + @exec_url = response.headers['Location'] |
| 54 | + fail "NO ENDPOINT URL #{connection} : HEAD: #{response.headers.inspect}" if !exec_url || exec_url.empty? |
| 55 | + init_resource_data(response.body, base_url) |
| 56 | + expect_response_code(response, 201) |
| 57 | + response |
| 58 | + end |
| 59 | + |
36 | 60 | def _create_cypher_response(response)
|
37 | 61 | first_result = response.body['results'][0]
|
38 | 62 |
|
39 | 63 | cr = CypherResponse.new(response, true)
|
40 |
| - if !response.body['errors'].empty? |
| 64 | + if response.body['errors'].empty? |
| 65 | + cr.set_data(first_result['data'], first_result['columns']) |
| 66 | + else |
41 | 67 | first_error = response.body['errors'].first
|
| 68 | + expired if first_error['message'].match(/Unrecognized transaction id/) |
42 | 69 | cr.set_error(first_error['message'], first_error['code'], first_error['code'])
|
43 |
| - else |
44 |
| - cr.set_data(first_result['data'], first_result['columns']) |
45 | 70 | end
|
46 | 71 | cr
|
47 | 72 | end
|
48 | 73 |
|
49 |
| - def _delete_tx |
50 |
| - response = @connection.delete(@exec_url, headers: resource_headers) |
51 |
| - expect_response_code(response, 200) |
52 |
| - response |
53 |
| - end |
54 |
| - |
55 |
| - def _commit_tx |
56 |
| - response = @connection.post(@commit_url) |
57 |
| - |
58 |
| - expect_response_code(response, 200) |
59 |
| - response |
| 74 | + def empty_response |
| 75 | + OpenStruct.new(status: 200, body: '') |
60 | 76 | end
|
61 | 77 | end
|
62 | 78 | end
|
|
0 commit comments