neo4j-http 1.0.0 → 1.0.2.ben

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1a6215260814ae93f5003a9013d59d709819710e8908f2646ff8e7e3555c5d40
4
- data.tar.gz: 976402f3c84d5662d61c78d8db1637232d8b6925fe2f1282943064cd8ab9ca05
3
+ metadata.gz: 5d96303c02ea752544432e28e5510fbe6c88adc8478068cab9924f523dec5c3d
4
+ data.tar.gz: c905e048958cfd2dd2a4b22c03b2a0cc7d015389689198124a02566242f47a20
5
5
  SHA512:
6
- metadata.gz: '09bfdc57bd1e10d932c4fd874b5edcc7753943fecc465fbd96aad8e32a54993da33d8f24d7cd84d1a372374cb81eeacd4125b12e42a933f552b4b2896316872f'
7
- data.tar.gz: 95f18a7b9ecd1d9ca0d2f51ca80fb310d650aa6463281daf056d626874620912bc65d36c65462ba0b48315950b9874ed88fdc6e5ba682d0664c6e6ff834cc10c
6
+ metadata.gz: 82acf63f31d626badac509a0018aec7777165caf8507253f7ce88f145f8c235255da55d6277a340d971ec3740202e7ed4dbed4671ffec0d22e76f5b53fc909d9
7
+ data.tar.gz: d8abb8918f0ee93487928ea09d8983fa7ee8e9be62fa0f5f59f4ab8213282157ddaa2146b600992edbe3f9d12f3b281e2a3d254dd31e02d2ae1414d6fc95663d
data/README.md CHANGED
@@ -27,21 +27,43 @@ Or install it yourself as:
27
27
 
28
28
  The client is configured by default via a set of environment variables from [Neo4j::Http::Configuration](https://github.com/doximity/neo4j-http/blob/master/lib/neo4j/http/configuration.rb):
29
29
 
30
- * `NEO4J_URL` - The base URL to connect to Neo4j at
31
- * `NEO4J_USER` - The user name to use when authenticating to neo4j
32
- * `NEO4J_PASSWORD` - The password of the user to be used for authentication
33
- * `NEO4J_DATABASE` - The database name to be used when connecting. By default this will be nil and the path used for connecting to Neo4j wil be `/db/data/transaction/commit` to make it compliant with v3.5 of neo4j
34
- * `NEO4J_HTTP_USER_AGENT` - The user agent name provided in the request - defaults to `Ruby Neo4j Http Client`
35
- * `NEO4J_REQUEST_TIMEOUT_IN_SECONDS` - The number of seconds for the http request to time out if provided, defaults to nil
30
+ * `NEO4J_URL` - The base URL to connect to Neo4j at - defaults to `"http://localhost:7474"`
31
+ * `NEO4J_USER` - The user name to use when authenticating to neo4j - defaults to `""`
32
+ * `NEO4J_PASSWORD` - The password of the user to be used for authentication - defaults to `""`
33
+ * `NEO4J_DATABASE` - The database name to be used when connecting. By default this will be `nil`.
34
+ * `NEO4J_HTTP_USER_AGENT` - The user agent name provided in the request - defaults to `"Ruby Neo4j Http Client"`
35
+ * `NEO4J_REQUEST_TIMEOUT_IN_SECONDS` - The number of seconds for the http request to time out if provided - defaults to `nil`
36
+ * `ACCESS_MODE` - "WRITE", or "READ" for read only instances of Neo4j clients - defaults to `"WRITE"`
36
37
 
37
- These configuration values can also be set during initalization like:
38
+ These configuration values can also be set during initalization, and take precedence over the environment variables:
38
39
 
39
40
  ```ruby
40
41
  Neo4j::Http.configure do |config|
41
- config.request_timeout_in_seconds = 42
42
+ config.uri = "http://localhost:7474"
43
+ config.user = ""
44
+ config.password = ""
45
+ config.database_name = nil
46
+ config.user_agent = "Ruby Neo4j Http Client"
47
+ config.request_timeout_in_seconds = nil
48
+ config.access_mode = "WRITE"
42
49
  end
43
50
  ```
44
51
 
52
+ ### Multiple databases
53
+
54
+ The HTTP API endpoints [follow the pattern](https://neo4j.com/docs/upgrade-migration-guide/current/migration/surface-changes/http-api/) `/db/<NEO4J_DATABASE>/tx`
55
+
56
+ To route to a different database, set a value for `NEO4J_DATABASE`. If no value is supplied, or this ENV is unset, the URI defaults to `/db/data/transaction/commit`
57
+
58
+ This can be used for testing by setting up a test environment only variable using a gem like [dotenv-rails](https://github.com/bkeepers/dotenv):
59
+
60
+ ```
61
+ # .env.testing
62
+ NEO4J_DATABASE=test
63
+ ```
64
+
65
+ All testing operations are now routed to the URI `/db/test/tx/commit`.
66
+
45
67
  ## Usage
46
68
 
47
69
  The core interface can be directly accessed on `Neo4::Http::Client` -
@@ -9,6 +9,7 @@ module Neo4j
9
9
  attr_accessor :uri
10
10
  attr_accessor :user
11
11
  attr_accessor :user_agent
12
+ attr_accessor :access_mode
12
13
 
13
14
  def initialize(options = ENV)
14
15
  @uri = options.fetch("NEO4J_URL", "http://localhost:7474")
@@ -17,8 +18,11 @@ module Neo4j
17
18
  @database_name = options.fetch("NEO4J_DATABASE", nil)
18
19
  @user_agent = options.fetch("NEO4J_HTTP_USER_AGENT", "Ruby Neo4j Http Client")
19
20
  @request_timeout_in_seconds = options.fetch("NEO4J_REQUEST_TIMEOUT_IN_SECONDS", nil)
21
+ @access_mode = options.fetch("NEO4J_ACCESS_MODE", "WRITE")
20
22
  end
21
23
 
24
+ # https://neo4j.com/developer/manage-multiple-databases/
25
+ # https://neo4j.com/docs/upgrade-migration-guide/current/migration/surface-changes/http-api/
22
26
  def transaction_path
23
27
  # v3.5 - /db/data/transaction/commit
24
28
  # v4.x - /db/#{database_name}/tx/commit
@@ -12,26 +12,36 @@ module Neo4j
12
12
  @default ||= new(Neo4j::Http.config)
13
13
  end
14
14
 
15
- def initialize(configuration)
15
+ def initialize(configuration, injected_connection = nil)
16
16
  @configuration = configuration
17
+ @injected_connection = injected_connection
17
18
  end
18
19
 
20
+ # Executes a cypher query, passing in the cypher statement, with parameters as an optional hash
21
+ # e.g. Neo4j::Http::Cypherclient.execute_cypher("MATCH (n { foo: $foo }) LIMIT 1 RETURN n", { foo: "bar" })
19
22
  def execute_cypher(cypher, parameters = {})
23
+ # By default the access mode is set to "WRITE", but can be set to "READ"
24
+ # for improved routing performance on read only queries
25
+ access_mode = parameters.delete(:access_mode) || @configuration.access_mode
26
+
20
27
  request_body = {
21
28
  statements: [
22
- {statement: cypher,
23
- parameters: parameters.as_json}
29
+ {
30
+ statement: cypher,
31
+ parameters: parameters.as_json
32
+ }
24
33
  ]
25
34
  }
26
35
 
27
- response = connection.post(transaction_path, request_body)
36
+ @connection = @injected_connection || connection(access_mode)
37
+ response = @connection.post(transaction_path, request_body)
28
38
  results = check_errors!(cypher, response, parameters)
29
39
 
30
40
  Neo4j::Http::Results.parse(results&.first || {})
31
41
  end
32
42
 
33
- def connection
34
- build_connection
43
+ def connection(access_mode)
44
+ build_connection(access_mode)
35
45
  end
36
46
 
37
47
  protected
@@ -39,6 +49,10 @@ module Neo4j
39
49
  delegate :auth_token, :transaction_path, to: :@configuration
40
50
  def check_errors!(cypher, response, parameters)
41
51
  raise Neo4j::Http::Errors::InvalidConnectionUrl, response.status if response.status == 404
52
+ if response.body["errors"].any? { |error| error["message"][/Routing WRITE queries is not supported/] }
53
+ raise Neo4j::Http::Errors::ReadOnlyError
54
+ end
55
+
42
56
  body = response.body || {}
43
57
  errors = body.fetch("errors", [])
44
58
  return body.fetch("results", {}) unless errors.present?
@@ -61,8 +75,10 @@ module Neo4j
61
75
  Neo4j::Http::Errors::Neo4jCodedError
62
76
  end
63
77
 
64
- def build_connection
65
- Faraday.new(url: @configuration.uri, headers: build_http_headers, request: build_request_options) do |f|
78
+ def build_connection(access_mode)
79
+ # https://neo4j.com/docs/http-api/current/actions/transaction-configuration/
80
+ headers = build_http_headers.merge({"access-mode" => access_mode})
81
+ Faraday.new(url: @configuration.uri, headers: headers, request: build_request_options) do |f|
66
82
  f.request :json # encode req bodies as JSON
67
83
  f.request :retry # retry transient failures
68
84
  f.response :json # decode response bodies as JSON
@@ -4,6 +4,7 @@ module Neo4j
4
4
  Neo4jError = Class.new(StandardError)
5
5
  InvalidConnectionUrl = Class.new(Neo4jError)
6
6
  Neo4jCodedError = Class.new(Neo4jError)
7
+ ReadOnlyError = Class.new(Neo4jError)
7
8
 
8
9
  # These are specific Errors Neo4j can raise
9
10
  module Neo
@@ -41,15 +41,16 @@ module Neo4j
41
41
  def find_node_by(label:, **attributes)
42
42
  selectors = attributes.map { |key, value| "#{key}: $attributes.#{key}" }.join(", ")
43
43
  cypher = "MATCH (node:#{label} { #{selectors} }) RETURN node LIMIT 1"
44
- results = @cypher_client.execute_cypher(cypher, attributes: attributes)
44
+ results = @cypher_client.execute_cypher(cypher, attributes: attributes.merge(access_mode: "READ"))
45
45
  return if results.empty?
46
+
46
47
  results.first&.fetch("node")
47
48
  end
48
49
 
49
50
  def find_nodes_by(label:, attributes:, limit: 100)
50
51
  selectors = build_selectors(attributes)
51
52
  cypher = "MATCH (node:#{label}) where #{selectors} RETURN node LIMIT #{limit}"
52
- results = @cypher_client.execute_cypher(cypher, attributes: attributes)
53
+ results = @cypher_client.execute_cypher(cypher, attributes: attributes.merge(access_mode: "READ"))
53
54
  results.map { |result| result["node"] }
54
55
  end
55
56
 
@@ -52,7 +52,13 @@ module Neo4j
52
52
  RETURN from, to, relationship
53
53
  CYPHER
54
54
 
55
- results = @cypher_client.execute_cypher(cypher, from: from, to: to, relationship: relationship)
55
+ results = @cypher_client.execute_cypher(
56
+ cypher,
57
+ from: from,
58
+ to: to,
59
+ relationship: relationship,
60
+ access_mode: "READ"
61
+ )
56
62
  results&.first
57
63
  end
58
64
 
@@ -4,7 +4,10 @@ module Neo4j
4
4
  module Http
5
5
  class Results
6
6
  # Example result set:
7
- # [{"columns"=>["n"], "data"=>[{"row"=>[{"name"=>"Foo", "uuid"=>"8c7dcfda-d848-4937-a91a-2e6debad2dd6"}], "meta"=>[{"id"=>242, "type"=>"node", "deleted"=>false}]}]}]
7
+ # [{"columns"=>["n"],
8
+ # "data"=>
9
+ # [{"row"=>[{"name"=>"Foo", "uuid"=>"8c7dcfda-d848-4937-a91a-2e6debad2dd6"}],
10
+ # "meta"=>[{"id"=>242, "type"=>"node", "deleted"=>false}]}]}]
8
11
  #
9
12
  def self.parse(results)
10
13
  columns = results["columns"]
@@ -1,5 +1,5 @@
1
1
  module Neo4j
2
2
  module Http
3
- VERSION = "1.0.0"
3
+ VERSION = "1.0.2.ben"
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: neo4j-http
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.0.2.ben
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ryan Stawarz
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-04-29 00:00:00.000000000 Z
11
+ date: 2022-07-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -73,7 +73,7 @@ dependencies:
73
73
  - - ">="
74
74
  - !ruby/object:Gem::Version
75
75
  version: '0'
76
- type: :development
76
+ type: :runtime
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
@@ -121,9 +121,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
121
121
  version: 2.3.0
122
122
  required_rubygems_version: !ruby/object:Gem::Requirement
123
123
  requirements:
124
- - - ">="
124
+ - - ">"
125
125
  - !ruby/object:Gem::Version
126
- version: '0'
126
+ version: 1.3.1
127
127
  requirements: []
128
128
  rubygems_version: 3.3.11
129
129
  signing_key: