neo4j-ruby-driver 6.0.3.alpha.0-java → 6.2.1.beta.2-java

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.
Files changed (84) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +113 -411
  3. data/lib/neo4j/driver/auth_token_manager.rb +29 -0
  4. data/lib/neo4j/driver/client_certificate_manager.rb +31 -0
  5. data/lib/neo4j/driver/exceptions/neo4j_exception.rb +16 -7
  6. data/lib/neo4j/driver/exceptions/no_such_record_exception.rb +1 -1
  7. data/lib/neo4j/driver/exceptions/protocol_exception.rb +0 -5
  8. data/lib/neo4j/driver/exceptions/security_retryable_exception.rb +14 -0
  9. data/lib/neo4j/driver/exceptions/transaction_terminated_exception.rb +13 -0
  10. data/lib/neo4j/driver/ext/auth_token.rb +17 -0
  11. data/{jruby → lib}/neo4j/driver/ext/auth_tokens.rb +5 -1
  12. data/lib/neo4j/driver/ext/bookmark_managers.rb +21 -0
  13. data/lib/neo4j/driver/ext/client_certificates.rb +21 -0
  14. data/{jruby → lib}/neo4j/driver/ext/config_converter.rb +36 -7
  15. data/{jruby → lib}/neo4j/driver/ext/exception_checkable.rb +3 -3
  16. data/lib/neo4j/driver/ext/exception_mapper.rb +173 -0
  17. data/lib/neo4j/driver/ext/graph_database.rb +25 -0
  18. data/lib/neo4j/driver/ext/internal/clock_adapter.rb +33 -0
  19. data/lib/neo4j/driver/ext/internal/cluster/routing_table_registry_impl.rb +28 -0
  20. data/lib/neo4j/driver/ext/internal/driver_factory.rb +54 -0
  21. data/lib/neo4j/driver/ext/internal/internal_notification_severity.rb +15 -0
  22. data/lib/neo4j/driver/ext/internal/reflection.rb +22 -0
  23. data/lib/neo4j/driver/ext/internal/summary/internal_gql_notification.rb +22 -0
  24. data/lib/neo4j/driver/ext/internal/summary/internal_gql_status_object.rb +18 -0
  25. data/{jruby → lib}/neo4j/driver/ext/internal/summary/internal_plan.rb +3 -5
  26. data/lib/neo4j/driver/ext/internal/summary/internal_query_profile.rb +36 -0
  27. data/lib/neo4j/driver/ext/internal/summary/internal_result_summary.rb +44 -0
  28. data/lib/neo4j/driver/ext/internal_driver.rb +102 -0
  29. data/{jruby → lib}/neo4j/driver/ext/internal_result.rb +2 -2
  30. data/{jruby → lib}/neo4j/driver/ext/internal_session.rb +5 -1
  31. data/lib/neo4j/driver/ext/logger.rb +55 -0
  32. data/lib/neo4j/driver/ext/map_converter.rb +13 -0
  33. data/lib/neo4j/driver/ext/neo_converter.rb +70 -0
  34. data/lib/neo4j/driver/ext/plain_map_converter.rb +11 -0
  35. data/{jruby → lib}/neo4j/driver/ext/ruby_converter.rb +20 -6
  36. data/lib/neo4j/driver/ext/unsupported_type.rb +14 -0
  37. data/lib/neo4j/driver/internal/deprecator.rb +9 -5
  38. data/lib/neo4j/driver/internal/duration_normalizer.rb +14 -39
  39. data/lib/neo4j/driver/internal/internal_auth_token_manager.rb +37 -0
  40. data/lib/neo4j/driver/internal/validator.rb +16 -2
  41. data/lib/neo4j/driver/summary/query_type.rb +5 -1
  42. data/lib/neo4j/driver/types/duration.rb +76 -0
  43. data/lib/neo4j/driver/types/local_date_time.rb +77 -8
  44. data/lib/neo4j/driver/types/local_time.rb +50 -7
  45. data/lib/neo4j/driver/types/offset_time.rb +78 -7
  46. data/lib/neo4j/driver/types/point.rb +38 -18
  47. data/lib/neo4j/driver/types/temporal_value.rb +44 -0
  48. data/lib/neo4j/driver/types/unresolvable_zoned_date_time.rb +38 -0
  49. data/{jruby → lib}/neo4j/driver/version.rb +1 -1
  50. data/{jruby → lib}/neo4j/driver.rb +31 -7
  51. data/lib/neo4j-ruby-driver_jars.rb +14 -0
  52. data/lib/neo4j-ruby-driver_loader.rb +34 -20
  53. data/lib/org/neo4j/driver/neo4j-java-driver-all/6.2.1/neo4j-java-driver-all-6.2.1.jar +0 -0
  54. data/lib/org/neo4j/driver/neo4j-java-driver-observation-metrics/6.2.1/neo4j-java-driver-observation-metrics-6.2.1.jar +0 -0
  55. data/lib/org/reactivestreams/reactive-streams/1.0.4/reactive-streams-1.0.4.jar +0 -0
  56. metadata +76 -226
  57. data/jruby/neo4j/driver/ext/async_converter.rb +0 -55
  58. data/jruby/neo4j/driver/ext/exception_mapper.rb +0 -92
  59. data/jruby/neo4j/driver/ext/graph_database.rb +0 -33
  60. data/jruby/neo4j/driver/ext/internal/async/internal_async_session.rb +0 -23
  61. data/jruby/neo4j/driver/ext/internal/cluster/routing_table_registry_impl.rb +0 -15
  62. data/jruby/neo4j/driver/ext/internal/cursor/disposable_async_result_cursor.rb +0 -15
  63. data/jruby/neo4j/driver/ext/internal/driver_factory.rb +0 -24
  64. data/jruby/neo4j/driver/ext/internal/summary/internal_result_summary.rb +0 -34
  65. data/jruby/neo4j/driver/ext/internal_driver.rb +0 -52
  66. data/jruby/neo4j/driver/ext/logger.rb +0 -60
  67. data/jruby/neo4j/driver/ext/map_converter.rb +0 -14
  68. data/jruby/neo4j/driver/ext/neo_converter.rb +0 -59
  69. data/lib/neo4j/driver/internal/bolt_server_address.rb +0 -97
  70. data/lib/neo4j/driver/synchronizable.rb +0 -23
  71. data/lib/neo4j/driver/types/time.rb +0 -45
  72. /data/{jruby → lib}/neo4j/driver/ext/internal/abstract_query_runner.rb +0 -0
  73. /data/{jruby → lib}/neo4j/driver/ext/internal/eager_result_value.rb +0 -0
  74. /data/{jruby → lib}/neo4j/driver/ext/internal/metrics/internal_connection_pool_metrics.rb +0 -0
  75. /data/{jruby → lib}/neo4j/driver/ext/internal/summary/internal_notification.rb +0 -0
  76. /data/{jruby → lib}/neo4j/driver/ext/internal_entity.rb +0 -0
  77. /data/{jruby → lib}/neo4j/driver/ext/internal_keys.rb +0 -0
  78. /data/{jruby → lib}/neo4j/driver/ext/internal_node.rb +0 -0
  79. /data/{jruby → lib}/neo4j/driver/ext/internal_record.rb +0 -0
  80. /data/{jruby → lib}/neo4j/driver/ext/internal_relationship.rb +0 -0
  81. /data/{jruby → lib}/neo4j/driver/ext/internal_transaction.rb +0 -0
  82. /data/{jruby → lib}/neo4j/driver/ext/query.rb +0 -0
  83. /data/{jruby → lib}/neo4j/driver/ext/run_override.rb +0 -0
  84. /data/{jruby → lib}/neo4j/driver/ext/start_end_naming.rb +0 -0
@@ -6,6 +6,9 @@ module Neo4j
6
6
  module Internal
7
7
  module Summary
8
8
  module InternalPlan
9
+ # Ruby-idiomatic args: Java Map<String, Value> → Hash with
10
+ # native Ruby values. Symmetric with MRI Plan#arguments
11
+ # (which is already a Ruby hash).
9
12
  def args
10
13
  arguments&.to_h&.transform_values(&:as_ruby_object)
11
14
  end
@@ -13,11 +16,6 @@ module Neo4j
13
16
  def identifiers
14
17
  super.to_a
15
18
  end
16
-
17
- # this part should probably be included in testkit-backend
18
- def to_h
19
- {operator_type:, args:, identifiers:, children: children&.map(&:to_h)}
20
- end
21
19
  end
22
20
  end
23
21
  end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Neo4j
4
+ module Driver
5
+ module Ext
6
+ module Internal
7
+ module Summary
8
+ # QueryProfile (driver 6.2) reports each profile stat as an
9
+ # Optional, so an absent stat is distinguishable from a real 0 —
10
+ # unlike the deprecated ProfiledPlan, whose long getters collapse
11
+ # absent to 0. Map empty -> nil (the backend omits nil) and
12
+ # present -> the value; this is what makes
13
+ # Feature:API:Summary:Profile:OptionalStats work. operator_type /
14
+ # args / identifiers / children come from the InternalPlan
15
+ # superclass unchanged.
16
+ module InternalQueryProfile
17
+ def db_hits = long(dbHits)
18
+ def records = long(rows)
19
+ def page_cache_hits = long(pageCacheHits)
20
+ def page_cache_misses = long(pageCacheMisses)
21
+ def page_cache_hit_ratio = double(pageCacheHitRatio)
22
+ def time = super.or_else(nil)&.to_nanos
23
+
24
+ private
25
+
26
+ # OptionalLong / OptionalDouble -> Integer / Float, or nil when
27
+ # empty. or_else(nil) can't be used: their orElse takes a
28
+ # primitive default, not nil.
29
+ def long(optional) = optional.present? ? optional.as_long : nil
30
+ def double(optional) = optional.present? ? optional.as_double : nil
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Neo4j
4
+ module Driver
5
+ module Ext
6
+ module Internal
7
+ module Summary
8
+ module InternalResultSummary
9
+ %i[result_available_after result_consumed_after].each do |method|
10
+ define_method(method) do
11
+ super(Java::JavaUtilConcurrent::TimeUnit::MILLISECONDS).then { |val| val unless val == -1 }
12
+ end
13
+ end
14
+
15
+ # Ruby-ify the 6.2 queryProfile() Optional -> the QueryProfile or
16
+ # nil. The presence-aware profile (Optional stats) the backend
17
+ # serializes; `profile` stays the deprecated ProfiledPlan.
18
+ def query_profile = super.or_else(nil)
19
+
20
+ # Driver::Summary::QueryType is a Ruby module in lib/shared/.
21
+ # include_package only fills missing constants on a module via
22
+ # const_missing — Java never overrides Ruby constants that
23
+ # already exist, regardless of load order. So our Ruby module
24
+ # wins on both flavours.
25
+ #
26
+ # The case selector still needs the Java enum. Fully-qualifying
27
+ # Java::OrgNeo4jDriverSummary::QueryType keeps readers from
28
+ # wondering whether `QueryType` here means the Ruby module or
29
+ # the Java enum, and avoids a java_import line whose only job
30
+ # would be to disambiguate this one method.
31
+ def query_type
32
+ case super
33
+ when Java::OrgNeo4jDriverSummary::QueryType::READ_ONLY then Driver::Summary::QueryType::READ_ONLY
34
+ when Java::OrgNeo4jDriverSummary::QueryType::READ_WRITE then Driver::Summary::QueryType::READ_WRITE
35
+ when Java::OrgNeo4jDriverSummary::QueryType::WRITE_ONLY then Driver::Summary::QueryType::WRITE_ONLY
36
+ when Java::OrgNeo4jDriverSummary::QueryType::SCHEMA_WRITE then Driver::Summary::QueryType::SCHEMA_WRITE
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Neo4j
4
+ module Driver
5
+ module Ext
6
+ module InternalDriver
7
+ extend AutoCloseable
8
+ include ConfigConverter
9
+ include ExceptionCheckable
10
+
11
+ auto_closeable :session
12
+
13
+ # See lib/mri/.../driver.rb. auth_token is part of `config` in the
14
+ # published surface, but Java's builder takes it as a separate
15
+ # setter — pull it out before handing the rest to QueryConfig.
16
+ def execute_query(query, parameters = {}, config = {})
17
+ auth_token = config.delete(:auth_token)
18
+ check do
19
+ executable_query(query)
20
+ .with_auth_token(auth_token)
21
+ .with_config(to_java_config(Neo4j::Driver::QueryConfig, **config))
22
+ .with_parameters(to_neo(parameters))
23
+ .execute
24
+ end
25
+ end
26
+
27
+ def session(auth_token: nil, **session_config)
28
+ # auth_token is a per-session override (Feature:API:Session:AuthConfig);
29
+ # Java exposes it via Driver.session(Class<T>, SessionConfig, AuthToken).
30
+ # Passing null defers to the driver default — same as the
31
+ # 2-arg overload, so we can always take the long path.
32
+ java_method(:session,
33
+ [java.lang.Class, org.neo4j.driver.SessionConfig, org.neo4j.driver.AuthToken])
34
+ .call(org.neo4j.driver.Session.java_class,
35
+ to_java_config(Neo4j::Driver::SessionConfig, **session_config),
36
+ auth_token)
37
+ end
38
+
39
+ def verify_connectivity
40
+ check { super }
41
+ end
42
+
43
+ def verify_authentication(auth_token)
44
+ check { super }
45
+ end
46
+
47
+ def supports_session_auth?
48
+ check { supports_session_auth }
49
+ end
50
+
51
+ # Routing table for `database` (testkit GetRoutingTable). Returns
52
+ # Java's RoutingTable (routers/writers/readers + expirationTimestamp);
53
+ # the path to it is the driver's business, not testkit's.
54
+ def routing_table(database)
55
+ routing_table_registry.routing_table_handler(database).routing_table
56
+ end
57
+
58
+ # Force a routing-table refresh (testkit ForcedRoutingTableUpdate).
59
+ # Drive the registry's ensureRoutingTable — a fresh/aged table is
60
+ # stale, so this triggers a real ROUTE and populates the handler.
61
+ # ImmutableObservation is an empty marker interface, so a bare impl
62
+ # is enough.
63
+ def routing_table_refresh(database, bookmarks)
64
+ db_name = Neo4j::Driver::Internal::DatabaseName.database(database)
65
+ params = Java::OrgNeo4jDriverInternalShadedBoltConnection::RoutedBoltConnectionParameters
66
+ .builder
67
+ .with_database_name(db_name)
68
+ .with_bookmarks(java.util.HashSet.new(Array(bookmarks)))
69
+ .build
70
+ routing_table_registry
71
+ .ensure_routing_table(java.util.concurrent.CompletableFuture.completed_future(db_name),
72
+ params, NOOP_OBSERVATION)
73
+ .to_completable_future.get
74
+ end
75
+
76
+ NOOP_OBSERVATION = Class.new do
77
+ include Java::OrgNeo4jDriverInternalShadedBoltConnectionObservation::ImmutableObservation
78
+ end.new
79
+
80
+ private
81
+
82
+ # Reflect from the session factory down to the RoutedBoltConnectionSource
83
+ # that owns the routing-table registry. The number of delegating
84
+ # wrappers varies by driver version (6.1.x adds a
85
+ # ProviderClosingBoltConnectionSource), so unwrap `delegate` until the
86
+ # source with a `registry` field.
87
+ def routing_table_registry
88
+ source = Internal::Reflection.field(session_factory, 'connectionSource')
89
+ until Internal::Reflection.field?(source, 'registry')
90
+ unless Internal::Reflection.field?(source, 'delegate')
91
+ raise KeyError,
92
+ "No routing-table registry in the connection-source chain (#{source.java_class.simple_name})"
93
+ end
94
+
95
+ source = Internal::Reflection.field(source, 'delegate')
96
+ end
97
+ Internal::Reflection.field(source, 'registry')
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
@@ -14,8 +14,8 @@ module Neo4j
14
14
  end
15
15
  end
16
16
 
17
- def each(&block)
18
- check { stream.for_each(&block) }
17
+ def each(&)
18
+ check { stream.for_each(&) }
19
19
  end
20
20
 
21
21
  def to_a
@@ -17,7 +17,11 @@ module Neo4j
17
17
  %i[read write].each do |mode|
18
18
  define_method("execute_#{mode}") do |**config, &block|
19
19
  check do
20
- super(->(tx) { Struct::Wrapper.new(reverse_check { block.call(tx) }) }, to_java_config(Neo4j::Driver::TransactionConfig, **config)).object
20
+ super(lambda { |tx|
21
+ Struct::Wrapper.new(reverse_check do
22
+ block.call(tx)
23
+ end)
24
+ }, to_java_config(Neo4j::Driver::TransactionConfig, **config)).object
21
25
  end
22
26
  end
23
27
  end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Neo4j
4
+ module Driver
5
+ module Ext
6
+ class Logger
7
+ include org.neo4j.driver.Logger
8
+ include org.neo4j.driver.Logging
9
+ extend Forwardable
10
+
11
+ delegate debug?: :@logger
12
+
13
+ def initialize(logger)
14
+ @logger = logger
15
+ end
16
+
17
+ def getLog(_name)
18
+ self
19
+ end
20
+ alias get_log getLog
21
+
22
+ def error(*) = log(::Logger::ERROR, *)
23
+
24
+ def info(*) = log(::Logger::INFO, *)
25
+
26
+ def warn(*) = log(::Logger::WARN, *)
27
+
28
+ def debug(*) = log(::Logger::DEBUG, *)
29
+ alias trace debug
30
+
31
+ def trace_enabled?
32
+ debug?
33
+ end
34
+
35
+ def debug_enabled
36
+ debug?
37
+ end
38
+
39
+ private
40
+
41
+ def log(level, *)
42
+ @logger.add(level) { format(*) }
43
+ end
44
+
45
+ def format(message, *args)
46
+ return message.to_s if args.empty? && !message.is_a?(java.lang.Throwable)
47
+
48
+ args.unshift(message)
49
+ args.unshift('%s%n%s') if args.last.is_a?(java.lang.Throwable)
50
+ java.lang.String.format(*args)
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Neo4j
4
+ module Driver
5
+ module Ext
6
+ module MapConverter
7
+ include PlainMapConverter
8
+
9
+ def to_h = java_method(:asMap, [java.util.function.Function]).call(&:itself).as_ruby_object
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Neo4j
4
+ module Driver
5
+ module Ext
6
+ module NeoConverter
7
+ private
8
+
9
+ def to_neo(object, skip_unknown: false)
10
+ case object
11
+ when Hash
12
+ object.map { |key, value| [key.to_s, to_neo(value)] }.to_h
13
+ when Types::Path
14
+ Exceptions::ClientException.unable_to_convert(object)
15
+ when Enumerable
16
+ object.map(&method(:to_neo))
17
+ when Types::UUID # native java.util.UUID; Values.value accepts it
18
+ object
19
+ when String
20
+ object.encoding == Encoding::BINARY ? object.to_java_bytes : object
21
+ when Types::Duration
22
+ Java::OrgNeo4jDriverInternal::InternalIsoDuration.new(object.months, object.days, object.seconds,
23
+ object.nanoseconds)
24
+ when Types::Point
25
+ Java::OrgNeo4jDriver::Values.point(object.srid, *object.coordinates)
26
+ when Types::OffsetTime
27
+ Java::JavaTime::OffsetTime.of(Java::JavaTime::LocalTime.of_nano_of_day(object.nanoseconds),
28
+ Java::JavaTime::ZoneOffset.of_total_seconds(object.tz_offset_seconds))
29
+ when Types::LocalTime
30
+ Java::JavaTime::LocalTime.of_nano_of_day(object.nanoseconds)
31
+ when Types::LocalDateTime
32
+ Java::JavaTime::LocalDateTime.of_epoch_second(object.epoch_seconds, object.nanoseconds,
33
+ Java::JavaTime::ZoneOffset::UTC)
34
+ when defined?(ActiveSupport::TimeWithZone) && ActiveSupport::TimeWithZone
35
+ to_zoned_date_time(object, object.time_zone.tzinfo.identifier)
36
+ when Time, DateTime
37
+ to_zoned_date_time(object, zone_identifier_for(object))
38
+ when Date
39
+ Java::JavaTime::LocalDate.of(object.year, object.month, object.day)
40
+ when Symbol
41
+ object.to_s
42
+ when nil, true, false, Integer, Float
43
+ object
44
+ else
45
+ raise Exceptions::ClientException.unable_to_convert(object) unless skip_unknown
46
+
47
+ object
48
+
49
+ end
50
+ end
51
+
52
+ def to_zoned_date_time(object, zone)
53
+ # Build from the instant, not the wall-clock fields: during a DST
54
+ # fall-back the local time is ambiguous and ZonedDateTime.of(...)
55
+ # would silently pick the earlier offset, dropping the object's
56
+ # actual offset. ofInstant keeps the real instant (and is identical
57
+ # for non-ambiguous times). Use to_i/nsec (the UTC instant) rather
58
+ # than to_time, which triggers an ActiveSupport localtime path.
59
+ instant = Java::JavaTime::Instant.of_epoch_second(object.to_i, object.nsec)
60
+ Java::JavaTime::ZonedDateTime.of_instant(instant, Java::JavaTime::ZoneId.of(zone))
61
+ end
62
+
63
+ def zone_identifier_for(time)
64
+ zone = time.zone
65
+ zone.respond_to?(:identifier) ? zone.identifier : time.formatted_offset
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Neo4j
4
+ module Driver
5
+ module Ext
6
+ module PlainMapConverter
7
+ def as_ruby_object = to_h.transform_values!(&:as_ruby_object).transform_keys!(&:to_sym)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -16,20 +16,25 @@ module Neo4j
16
16
  date = as_local_date
17
17
  Date.new(date.year, date.month_value, date.day_of_month)
18
18
  when Java::OrgNeo4jDriverInternalTypes::TypeConstructor::DURATION
19
- Driver::Internal::DurationNormalizer.create(*%i[months days seconds nanoseconds].map(&as_object.method(:send)))
19
+ Types::Duration.new(*%i[months days seconds nanoseconds].map(&as_object.method(:send)))
20
20
  when Java::OrgNeo4jDriverInternalTypes::TypeConstructor::POINT
21
21
  point = as_point
22
22
  Types::Point.new(srid: point.srid, x: point.x, y: point.y, z: nullable(point.z))
23
23
  when Java::OrgNeo4jDriverInternalTypes::TypeConstructor::BYTES
24
24
  String.from_java_bytes(as_byte_array).force_encoding(Encoding::BINARY)
25
25
  when Java::OrgNeo4jDriverInternalTypes::TypeConstructor::TIME
26
- Types::OffsetTime.parse(as_offset_time.to_string)
26
+ Types::OffsetTime.new(as_offset_time.to_local_time.to_nano_of_day, as_offset_time.offset.total_seconds)
27
27
  when Java::OrgNeo4jDriverInternalTypes::TypeConstructor::LOCAL_TIME
28
- Types::LocalTime.parse(as_local_time.to_string)
28
+ Types::LocalTime.new(as_local_time.to_nano_of_day)
29
29
  when Java::OrgNeo4jDriverInternalTypes::TypeConstructor::LOCAL_DATE_TIME
30
- Types::LocalDateTime.parse(as_local_date_time.to_string)
30
+ Types::LocalDateTime.new(as_local_date_time.to_epoch_second(Java::JavaTime::ZoneOffset::UTC),
31
+ as_local_date_time.nano)
31
32
  when Java::OrgNeo4jDriverInternalTypes::TypeConstructor::DATE_TIME
32
33
  to_time
34
+ when Java::OrgNeo4jDriverInternalTypes::TypeConstructor::UUID
35
+ # Bolt 6.1 UUID -> native java.util.UUID (Types::UUID on JRuby).
36
+ # asObject would raise Uncoercible (UUID is not a string).
37
+ as_uuid
33
38
  else
34
39
  as_object
35
40
  end
@@ -40,12 +45,21 @@ module Neo4j
40
45
  def to_time
41
46
  time = as_zoned_date_time
42
47
  zone_id = time.zone.id
43
- if /^Z|[+\-][0-9]{2}:[0-9]{2}$/.match?(zone_id)
48
+ if /^Z|[+-][0-9]{2}:[0-9]{2}$/.match?(zone_id)
44
49
  Time.parse(time.to_string)
45
50
  else
46
51
  instant = time.to_instant
47
- Time.at(instant.epoch_second, instant.nano, :nsec).in_time_zone(TZInfo::Timezone.get(zone_id))
52
+ Time.at(instant.epoch_second, instant.nano, :nsec, in: TZInfo::Timezone.get(zone_id))
48
53
  end
54
+ rescue Java::JavaTime::DateTimeException => e
55
+ # The server sent a zoned datetime whose zone the driver can't
56
+ # resolve (e.g. an unknown tz id); the Java value defers the
57
+ # failure to access time. Surface it as a driver exception
58
+ # (preserving the zone id in the message) instead of leaking the
59
+ # raw Java exception. cause: nil so the managed-tx reverse_check
60
+ # doesn't unwrap to (and re-raise) the raw Java DateTimeException,
61
+ # which the retry logic can't map back to a driver exception.
62
+ raise Exceptions::ClientException.new(e.message), cause: nil
49
63
  end
50
64
 
51
65
  def nullable(double)
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Neo4j
4
+ module Driver
5
+ module Ext
6
+ # Java's UnsupportedType#message returns an Optional<String>; unwrap it to
7
+ # a nil-or-String so the public API matches MRI's UnsupportedType, which
8
+ # stores a plain nilable message.
9
+ module UnsupportedType
10
+ def message = super.or_else(nil)
11
+ end
12
+ end
13
+ end
14
+ end
@@ -1,13 +1,17 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Neo4j
2
4
  module Driver
3
5
  module Internal
4
6
  module Deprecator
5
- def self.deprecator
6
- @deprecator ||= ActiveSupport::Deprecation.new('6.0', 'neo4j-ruby-driver')
7
- end
7
+ class << self
8
+ def deprecator
9
+ self
10
+ end
8
11
 
9
- def self.log_warning(old_method, new_method, version)
10
- deprecator.deprecation_warning(old_method, new_method)
12
+ def behavior=(value)
13
+ # No-op for now
14
+ end
11
15
  end
12
16
  end
13
17
  end
@@ -4,47 +4,22 @@ module Neo4j
4
4
  module Driver
5
5
  module Internal
6
6
  module DurationNormalizer
7
- class << self
8
- def normalize(object)
9
- parts = object.parts.to_h
10
- parts.default = 0
11
- months_i, months_remainder_seconds = divmod(months(parts), ActiveSupport::Duration::SECONDS_PER_MONTH)
12
- months_days, months_remainder_seconds =
13
- months_remainder_seconds.divmod(ActiveSupport::Duration::SECONDS_PER_DAY)
14
- days_i, days_remainder_seconds = divmod(months_days + days(parts), ActiveSupport::Duration::SECONDS_PER_DAY)
15
- seconds_i, nonanoseconds = divmod(months_remainder_seconds + days_remainder_seconds + seconds(parts),
16
- 1_000_000_000)
17
- [months_i, days_i, seconds_i, nonanoseconds.round]
7
+ # Convert timeout from seconds (or ActiveSupport::Duration) to
8
+ # milliseconds for the Bolt protocol. A negative timeout is
9
+ # invalid — reject it client-side (matches Java, which raises
10
+ # rather than sending a negative tx_timeout to the server).
11
+ def timeout_to_milliseconds(timeout)
12
+ return if timeout.nil?
13
+
14
+ # Check the sign before rounding: a tiny negative timeout
15
+ # (e.g. -0.4ms) would otherwise round to 0 and slip through.
16
+ ms = timeout.to_f * 1000
17
+ if ms.negative?
18
+ raise Exceptions::ClientException,
19
+ "Transaction timeout must not be negative, but was #{ms}ms"
18
20
  end
19
21
 
20
- def milliseconds(duration)
21
- duration&.in_milliseconds&.round
22
- end
23
-
24
- def create(months, days, seconds, nanoseconds)
25
- { months:, days:, seconds: seconds + (nanoseconds.zero? ? 0 : nanoseconds * BigDecimal('1e-9')) }
26
- .sum { |key, value| ActiveSupport::Duration.send(key, value) }
27
- end
28
-
29
- private
30
-
31
- def divmod(number, factor)
32
- number_i, remainder = number.divmod(1)
33
- [number_i.to_i, remainder * factor]
34
- end
35
-
36
- def months(parts)
37
- parts[:years] * 12 + parts[:months]
38
- end
39
-
40
- def days(parts)
41
- parts[:weeks] * 7 + parts[:days]
42
- end
43
-
44
- def seconds(parts)
45
- parts[:hours] * ActiveSupport::Duration::SECONDS_PER_HOUR +
46
- parts[:minutes] * ActiveSupport::Duration::SECONDS_PER_MINUTE + parts[:seconds]
47
- end
22
+ ms.round
48
23
  end
49
24
  end
50
25
  end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Neo4j
4
+ module Driver
5
+ module Internal
6
+ # Auth-token manager: anything responding to
7
+ #
8
+ # get_token -> AuthToken
9
+ # handle_security_exception(token, exception) -> Boolean
10
+ #
11
+ # is a manager. Pass an instance to `GraphDatabase.driver` via the
12
+ # `:auth_token_manager` kw arg and the driver will call back into
13
+ # it when it needs a fresh token or when the server rejects the
14
+ # current one.
15
+ #
16
+ # This concrete class is the Proc-based shortcut — supply two
17
+ # callables and you're done. Clients that need richer behaviour
18
+ # subclass and override the methods; the JRuby flavour's class
19
+ # additionally implements `org.neo4j.driver.AuthTokenManager`, so
20
+ # subclasses there automatically slot into the Java driver.
21
+ class InternalAuthTokenManager
22
+ def initialize(get_token:, handle_security_exception:)
23
+ @get_token = get_token
24
+ @handle_security_exception = handle_security_exception
25
+ end
26
+
27
+ def get_token
28
+ @get_token.call
29
+ end
30
+
31
+ def handle_security_exception(token, exception)
32
+ @handle_security_exception.call(token, exception)
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -16,12 +16,26 @@ module Neo4j
16
16
 
17
17
  def self.require_non_nil!(obj, message = nil)
18
18
  raise ArgumentError, [message, "can't be nil"].compact.join(' ') if obj.nil?
19
+
19
20
  obj
20
21
  end
21
22
 
23
+ # Matches Java's driver-side query-text validation (the JRuby
24
+ # flavour gets this for free from the Java driver). A Query
25
+ # object carries its own text, so only bare strings are checked.
26
+ def self.require_query_text!(query)
27
+ raise ArgumentError, 'Cypher query text should not be null' if query.nil?
28
+ if query.is_a?(String) && query.strip.empty?
29
+ raise ArgumentError,
30
+ 'Cypher query text should not be an empty string'
31
+ end
32
+
33
+ query
34
+ end
35
+
22
36
  def self.require_non_nil_credentials!(username, password)
23
- require_non_nil! username, "Username"
24
- require_non_nil! password, "Password"
37
+ require_non_nil! username, 'Username'
38
+ require_non_nil! password, 'Password'
25
39
  end
26
40
  end
27
41
  end
@@ -1,10 +1,14 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Neo4j
2
4
  module Driver
3
5
  module Summary
6
+ # Mirrors org.neo4j.driver.summary.QueryType — coarse classification
7
+ # of what a query did (read, write, both, or schema mutation).
4
8
  module QueryType
5
9
  READ_ONLY = 'r'
6
- READ_WRITE = 'rw'
7
10
  WRITE_ONLY = 'w'
11
+ READ_WRITE = 'rw'
8
12
  SCHEMA_WRITE = 's'
9
13
  end
10
14
  end