neo4j-http 1.0.0 → 1.0.2.ben

Sign up to get free protection for your applications and to get access to all the features.
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: