statelydb 0.14.0 → 0.16.0

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: a723737fdce6e8ad2bec8937cfd79f6d968df0fc3e9e747c64e18af9ce769601
4
- data.tar.gz: 1f0279a8b9507f4cffacc360ab82e2289471a40de39bd3891f0c7db5c63877c6
3
+ metadata.gz: acc7d4cbc70b66408fc0723cb627b291114ea531eabf57491d668bd4be9c6dc6
4
+ data.tar.gz: 4758fd8040faca40481b98aa713c12f711c353cbdfc2141e604227276da59e9c
5
5
  SHA512:
6
- metadata.gz: 815835070a91366b18948afe145eb548ae4f6ecbc6559ed3b8193288cd1084b7ac1a416264b0769afea93f4a8c01e17b0474facb46fe90460b0b3db803466428
7
- data.tar.gz: e0c75f31911432f2544c4097fbc0c7a1615624257e80495aef9e5b3a0dddc92dd9341683d24416c4a32ab19b8bd34a2d79abb577781d75ee41b55a02f2e2e11c
6
+ metadata.gz: 9338ba66b860a68ba965534743dc9f119c19d7db3196432f003db98d056a2ac33f4ce600064200b44e61d355750bc1c2ff1ac1fe378ac0618c5716aea19de8f0
7
+ data.tar.gz: 44c1c821cde2adc873cb223b3459744959dbe4ab625ddf9da76a9f47e7662cdcd028a6a471f0243fdefea30c419e1e8c8c28c251c776b66b8c8168675dbc27c9
data/lib/api/db/put_pb.rb CHANGED
@@ -7,7 +7,7 @@ require 'google/protobuf'
7
7
  require 'api/db/item_pb'
8
8
 
9
9
 
10
- descriptor_data = "\n\x0c\x64\x62/put.proto\x12\nstately.db\x1a\rdb/item.proto\"|\n\nPutRequest\x12\x19\n\x08store_id\x18\x01 \x01(\x04R\x07storeId\x12\'\n\x04puts\x18\x02 \x03(\x0b\x32\x13.stately.db.PutItemR\x04puts\x12*\n\x11schema_version_id\x18\x03 \x01(\rR\x0fschemaVersionId\"U\n\x07PutItem\x12$\n\x04item\x18\x01 \x01(\x0b\x32\x10.stately.db.ItemR\x04item\x12$\n\x0emust_not_exist\x18\x03 \x01(\x08R\x0cmustNotExist\"5\n\x0bPutResponse\x12&\n\x05items\x18\x01 \x03(\x0b\x32\x10.stately.db.ItemR\x05itemsBc\n\x0e\x63om.stately.dbB\x08PutProtoP\x01\xa2\x02\x03SDX\xaa\x02\nStately.Db\xca\x02\nStately\\Db\xe2\x02\x16Stately\\Db\\GPBMetadata\xea\x02\x0bStately::Dbb\x06proto3"
10
+ descriptor_data = "\n\x0c\x64\x62/put.proto\x12\nstately.db\x1a\rdb/item.proto\"|\n\nPutRequest\x12\x19\n\x08store_id\x18\x01 \x01(\x04R\x07storeId\x12\'\n\x04puts\x18\x02 \x03(\x0b\x32\x13.stately.db.PutItemR\x04puts\x12*\n\x11schema_version_id\x18\x03 \x01(\rR\x0fschemaVersionId\"\x99\x01\n\x07PutItem\x12$\n\x04item\x18\x01 \x01(\x0b\x32\x10.stately.db.ItemR\x04item\x12\x42\n\x1doverwrite_metadata_timestamps\x18\x02 \x01(\x08R\x1boverwriteMetadataTimestamps\x12$\n\x0emust_not_exist\x18\x03 \x01(\x08R\x0cmustNotExist\"5\n\x0bPutResponse\x12&\n\x05items\x18\x01 \x03(\x0b\x32\x10.stately.db.ItemR\x05itemsBc\n\x0e\x63om.stately.dbB\x08PutProtoP\x01\xa2\x02\x03SDX\xaa\x02\nStately.Db\xca\x02\nStately\\Db\xe2\x02\x16Stately\\Db\\GPBMetadata\xea\x02\x0bStately::Dbb\x06proto3"
11
11
 
12
12
  pool = Google::Protobuf::DescriptorPool.generated_pool
13
13
  pool.add_serialized_file(descriptor_data)
@@ -13,33 +13,28 @@ require_relative "../../error"
13
13
 
14
14
  LOGGER = Logger.new($stdout)
15
15
  LOGGER.level = Logger::WARN
16
- DEFAULT_GRANT_TYPE = "client_credentials"
17
16
 
18
17
  # A module for Stately Cloud auth code
19
18
  module StatelyDB
20
19
  module Common
21
20
  # A module for Stately Cloud auth code
22
21
  module Auth
23
- # Auth0TokenProvider is an implementation of the TokenProvider abstract base class
24
- # which vends tokens from auth0 with the given client_id and client_secret.
25
- # It will default to using the values of `STATELY_CLIENT_ID` and `STATELY_CLIENT_SECRET` if
26
- # no credentials are explicitly passed and will throw an error if none are found.
22
+ # AuthTokenProvider is an implementation of the TokenProvider abstract base class
23
+ # which vends tokens from the StatelyDB auth API.
24
+ # It will default to using the value of `STATELY_ACCESS_KEY` if
25
+ # no credentials are explicitly passed and will throw an error if no credentials are found.
27
26
  class AuthTokenProvider < TokenProvider
28
- # @param [String] origin The origin of the OAuth server
29
- # @param [String] audience The OAuth Audience for the token
30
- # @param [String] client_secret The StatelyDB client secret credential
31
- # @param [String] client_id The StatelyDB client ID credential
27
+ # @param [String] origin The origin of the auth server
32
28
  # @param [String] access_key The StatelyDB access key credential
29
+ # @param [Float] base_retry_backoff_secs The base retry backoff in seconds
33
30
  def initialize(
34
- origin: "https://oauth.stately.cloud",
35
- audience: "api.stately.cloud",
36
- client_secret: ENV.fetch("STATELY_CLIENT_SECRET", nil),
37
- client_id: ENV.fetch("STATELY_CLIENT_ID", nil),
38
- access_key: ENV.fetch("STATELY_ACCESS_KEY", nil)
31
+ origin: "https://api.stately.cloud",
32
+ access_key: ENV.fetch("STATELY_ACCESS_KEY", nil),
33
+ base_retry_backoff_secs: 1
39
34
  )
40
35
  super()
41
- @actor = Async::Actor.new(Actor.new(origin: origin, audience: audience,
42
- client_secret: client_secret, client_id: client_id, access_key: access_key))
36
+ @actor = Async::Actor.new(Actor.new(origin: origin, access_key: access_key,
37
+ base_retry_backoff_secs: base_retry_backoff_secs))
43
38
  # this initialization cannot happen in the constructor because it is async and must run on the event loop
44
39
  # which is not available in the constructor
45
40
  @actor.init
@@ -61,32 +56,26 @@ module StatelyDB
61
56
  # This is designed to be used with Async::Actor and run on a dedicated thread.
62
57
  class Actor
63
58
  # @param [String] origin The origin of the OAuth server
64
- # @param [String] audience The OAuth Audience for the token
65
- # @param [String] client_secret The StatelyDB client secret credential
66
- # @param [String] client_id The StatelyDB client ID credential
67
- def initialize(
68
- origin:,
69
- audience:,
70
- client_secret:,
71
- client_id:,
72
- access_key:
73
- )
59
+ # @param [String] access_key The StatelyDB access key credential
60
+ # @param [Float] base_retry_backoff_secs The base retry backoff in seconds
61
+ def initialize(origin:, access_key:, base_retry_backoff_secs:)
74
62
  super()
75
63
 
76
- @token_fetcher = nil
77
- if !access_key.nil?
78
- @token_fetcher = StatelyDB::Common::Auth::StatelyAccessTokenFetcher.new(origin: origin, access_key: access_key)
79
- elsif !client_secret.nil? && !client_id.nil?
80
- @token_fetcher = StatelyDB::Common::Auth::Auth0TokenFetcher.new(origin: origin, audience: audience,
81
- client_secret: client_secret, client_id: client_id)
82
- else
83
- raise StatelyDB::Error.new("unable to find client credentials in STATELY_ACCESS_KEY or STATELY_CLIENT_ID and " \
84
- "STATELY_CLIENT_SECRET environment variables. Either pass your credentials in " \
85
- "explicitly or set these environment variables",
86
- code: GRPC::Core::StatusCodes::UNAUTHENTICATED,
87
- stately_code: "Unauthenticated")
64
+ if access_key.nil?
65
+ raise StatelyDB::Error.new(
66
+ "Unable to find an access key in the STATELY_ACCESS_KEY " \
67
+ "environment variable. Either pass your credentials in " \
68
+ "the options when creating a client or set this environment variable.",
69
+ code: GRPC::Core::StatusCodes::UNAUTHENTICATED,
70
+ stately_code: "Unauthenticated"
71
+ )
88
72
  end
89
73
 
74
+ @token_fetcher = StatelyDB::Common::Auth::StatelyAccessTokenFetcher.new(
75
+ origin: origin,
76
+ access_key: access_key,
77
+ base_retry_backoff_secs: base_retry_backoff_secs
78
+ )
90
79
  @token_state = nil
91
80
  @pending_refresh = nil
92
81
  end
@@ -37,51 +37,24 @@ module StatelyDB
37
37
  end
38
38
  end
39
39
 
40
- # Auth0TokenFetcher is a TokenFetcher that fetches tokens from an Auth0 server
41
- class Auth0TokenFetcher < TokenFetcher
42
- # @param [String] origin The origin of the OAuth server
43
- # @param [String] audience The OAuth Audience for the token
44
- # @param [String] client_secret The StatelyDB client secret credential
45
- # @param [String] client_id The StatelyDB client ID credential
46
- def initialize(origin:, audience:, client_secret:, client_id:)
47
- super()
48
- @client = Async::HTTP::Client.new(Async::HTTP::Endpoint.parse(origin))
49
- @audience = audience
50
- @client_secret = client_secret
51
- @client_id = client_id
52
- end
53
-
54
- # Fetch a new token from auth0
55
- # @return [TokenResult] The fetched TokenResult
56
- def fetch
57
- headers = [["content-type", "application/json"]]
58
- body = JSON.dump({ "client_id" => @client_id, client_secret: @client_secret, audience: @audience,
59
- grant_type: DEFAULT_GRANT_TYPE })
60
- Sync do
61
- # TODO: Wrap this in a retry loop and parse errors like we
62
- # do in the Go SDK.
63
- response = @client.post("/oauth/token", headers, body)
64
- raise "Auth request failed" if response.status != 200
65
-
66
- resp_data = JSON.parse(response.read)
67
- TokenResult.new(token: resp_data["access_token"], expires_in_secs: resp_data["expires_in"])
68
- ensure
69
- response&.close
70
- end
71
- end
72
-
73
- def close
74
- @client&.close
75
- end
76
- end
77
-
78
40
  # StatelyAccessTokenFetcher is a TokenFetcher that fetches tokens from the StatelyDB API
79
41
  class StatelyAccessTokenFetcher < TokenFetcher
42
+ NON_RETRYABLE_ERRORS = [
43
+ GRPC::Core::StatusCodes::UNAUTHENTICATED,
44
+ GRPC::Core::StatusCodes::PERMISSION_DENIED,
45
+ GRPC::Core::StatusCodes::NOT_FOUND,
46
+ GRPC::Core::StatusCodes::UNIMPLEMENTED,
47
+ GRPC::Core::StatusCodes::INVALID_ARGUMENT
48
+ ].freeze
49
+ RETRY_ATTEMPTS = 10
50
+
80
51
  # @param [String] origin The origin of the OAuth server
81
52
  # @param [String] access_key The StatelyDB access key credential
82
- def initialize(origin:, access_key:)
53
+ # @param [Float] base_retry_backoff_secs The base backoff time in seconds
54
+ def initialize(origin:, access_key:, base_retry_backoff_secs:)
83
55
  super()
84
56
  @access_key = access_key
57
+ @base_retry_backoff_secs = base_retry_backoff_secs
85
58
  @channel = Common::Net.new_channel(endpoint: origin)
86
59
  error_interceptor = Common::ErrorInterceptor.new
87
60
  @stub = Stately::Auth::AuthService::Stub.new(nil, nil, channel_override: @channel,
@@ -91,14 +64,45 @@ module StatelyDB
91
64
  # Fetch a new token from the StatelyDB API
92
65
  # @return [TokenResult] The fetched TokenResult
93
66
  def fetch
94
- resp = @stub.get_auth_token(Stately::Auth::GetAuthTokenRequest.new(access_key: @access_key))
95
- TokenResult.new(token: resp.auth_token, expires_in_secs: resp.expires_in_s)
67
+ RETRY_ATTEMPTS.times do |i|
68
+ resp = @stub.get_auth_token(Stately::Auth::GetAuthTokenRequest.new(access_key: @access_key))
69
+ return TokenResult.new(token: resp.auth_token, expires_in_secs: resp.expires_in_s)
70
+ rescue StatelyDB::Error => e
71
+ # raise if it's the final attempt or if the error is not retryable
72
+ raise e unless self.class.retryable_error?(e) && i < RETRY_ATTEMPTS - 1
73
+
74
+ # exponential backoff
75
+ sleep(backoff(i, @base_retry_backoff_secs))
76
+ end
96
77
  end
97
78
 
98
79
  def close
99
80
  @channel&.close
100
81
  end
82
+
83
+ # Check if an error is retryable
84
+ # @param [StatelyDB::Error] err The error to check
85
+ # @return [Boolean] True if the error is retryable
86
+ def self.retryable_error?(err)
87
+ !NON_RETRYABLE_ERRORS.include?(err.code)
88
+ end
101
89
  end
102
90
  end
103
91
  end
104
92
  end
93
+
94
+ # backoff returns a duration to wait before retrying a request. `attempt` is
95
+ # the current attempt number, starting from 0 (e.g. the first attempt is 0,
96
+ # then 1, then 2...).
97
+ #
98
+ # @param [Integer] attempt The current attempt number
99
+ # @param [Float] base_backoff The base backoff time in seconds
100
+ # @return [Float] The duration in seconds to wait before retrying
101
+ def backoff(attempt, base_backoff)
102
+ # Double the base backoff time per attempt, starting with 1
103
+ exp = 2**attempt
104
+ # Add a full jitter to the backoff time, from no wait to 100% of the exponential backoff.
105
+ # See https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/
106
+ jitter = rand
107
+ (exp * jitter * base_backoff)
108
+ end
data/lib/error.rb CHANGED
@@ -41,9 +41,12 @@ module StatelyDB
41
41
  raw_detail = status.details[0]
42
42
  if raw_detail.type_url == "type.googleapis.com/stately.errors.StatelyErrorDetails"
43
43
  error_details = Stately::Errors::StatelyErrorDetails.decode(raw_detail.value)
44
+ message = error_details.message
45
+ rid = error.metadata["st-rid"]
46
+ message = "#{message} (Request ID: #{rid})" unless rid.nil?
44
47
  upstream_cause = error_details.upstream_cause.empty? ? nil : StandardError.new(error_details.upstream_cause) # rubocop:disable Metrics/BlockNesting
45
- return new(error_details.message, code: error.code, stately_code: error_details.stately_code,
46
- cause: upstream_cause)
48
+ return new(message, code: error.code, stately_code: error_details.stately_code,
49
+ cause: upstream_cause)
47
50
  end
48
51
  end
49
52
  end
data/lib/statelydb.rb CHANGED
@@ -188,12 +188,20 @@ module StatelyDB
188
188
  # `initialValue` field in its key, that initial value will automatically
189
189
  # be chosen not to conflict with existing items, so this condition only
190
190
  # applies to key paths that do not contain the `initialValue` field.
191
+ # @param overwrite_metadata_timestamps [Boolean] If set to true, the server will
192
+ # set the `createdAtTime` and/or `lastModifiedAtTime` fields based on the
193
+ # current values in this item (assuming you've mapped them to a field using
194
+ # `fromMetadata`). Without this, those fields are always ignored and the
195
+ # server sets them to the appropriate times. This option can be useful when
196
+ # migrating data from another system.
191
197
  # @return [StatelyDB::Item] the item that was stored
192
198
  #
193
199
  # @example client.data.put(my_item)
194
200
  # @example client.data.put(my_item, must_not_exist: true)
195
- def put(item, must_not_exist: false)
196
- resp = put_batch({ item:, must_not_exist: })
201
+ def put(item,
202
+ must_not_exist: false,
203
+ overwrite_metadata_timestamps: false)
204
+ resp = put_batch({ item:, must_not_exist:, overwrite_metadata_timestamps: })
197
205
 
198
206
  # Always return a single Item.
199
207
  resp.first
@@ -215,6 +223,7 @@ module StatelyDB
215
223
  item = input[:item]
216
224
  Stately::Db::PutItem.new(
217
225
  item: item.send("marshal_stately"),
226
+ overwrite_metadata_timestamps: input[:overwrite_metadata_timestamps],
218
227
  must_not_exist: input[:must_not_exist]
219
228
  )
220
229
  else
@@ -233,6 +233,12 @@ module StatelyDB
233
233
  # `initialValue` field in its key, that initial value will automatically
234
234
  # be chosen not to conflict with existing items, so this condition only
235
235
  # applies to key paths that do not contain the `initialValue` field.
236
+ # @param overwrite_metadata_timestamps [Boolean] If set to true, the server will
237
+ # set the `createdAtTime` and/or `lastModifiedAtTime` fields based on the
238
+ # current values in this item (assuming you've mapped them to a field using
239
+ # `fromMetadata`). Without this, those fields are always ignored and the
240
+ # server sets them to the appropriate times. This option can be useful when
241
+ # migrating data from another system.
236
242
  # @return [String, Integer] the id of the item
237
243
  #
238
244
  # @example
@@ -242,8 +248,10 @@ module StatelyDB
242
248
  # results.puts.each do |result|
243
249
  # puts result.key_path
244
250
  # end
245
- def put(item, must_not_exist: false)
246
- resp = put_batch({ item:, must_not_exist: })
251
+ def put(item,
252
+ must_not_exist: false,
253
+ overwrite_metadata_timestamps: false)
254
+ resp = put_batch({ item:, must_not_exist:, overwrite_metadata_timestamps: })
247
255
  resp.first
248
256
  end
249
257
 
@@ -269,6 +277,7 @@ module StatelyDB
269
277
  item = input[:item]
270
278
  Stately::Db::PutItem.new(
271
279
  item: item.send("marshal_stately"),
280
+ overwrite_metadata_timestamps: input[:overwrite_metadata_timestamps],
272
281
  must_not_exist: input[:must_not_exist]
273
282
  )
274
283
  else
data/sig/statelydb.rbi CHANGED
@@ -313,6 +313,8 @@ module StatelyDB
313
313
  #
314
314
  # _@param_ `must_not_exist` — A condition that indicates this item must not already exist at any of its key paths. If there is already an item at one of those paths, the Put operation will fail with a "ConditionalCheckFailed" error. Note that if the item has an `initialValue` field in its key, that initial value will automatically be chosen not to conflict with existing items, so this condition only applies to key paths that do not contain the `initialValue` field.
315
315
  #
316
+ # _@param_ `overwrite_metadata_timestamps` — If set to true, the server will set the `createdAtTime` and/or `lastModifiedAtTime` fields based on the current values in this item (assuming you've mapped them to a field using `fromMetadata`). Without this, those fields are always ignored and the server sets them to the appropriate times. This option can be useful when migrating data from another system.
317
+ #
316
318
  # _@return_ — the item that was stored
317
319
  #
318
320
  # client.data.put(my_item)
@@ -322,8 +324,8 @@ module StatelyDB
322
324
  # client.data.put(my_item, must_not_exist: true)
323
325
  # ```ruby
324
326
  # ```
325
- sig { params(item: StatelyDB::Item, must_not_exist: T::Boolean).returns(StatelyDB::Item) }
326
- def put(item, must_not_exist: false); end
327
+ sig { params(item: StatelyDB::Item, must_not_exist: T::Boolean, overwrite_metadata_timestamps: T::Boolean).returns(StatelyDB::Item) }
328
+ def put(item, must_not_exist: false, overwrite_metadata_timestamps: false); end
327
329
 
328
330
  # Put a batch of up to 50 Items into a StatelyDB Store.
329
331
  #
@@ -605,42 +607,24 @@ module StatelyDB
605
607
  def close; end
606
608
  end
607
609
 
608
- # Auth0TokenFetcher is a TokenFetcher that fetches tokens from an Auth0 server
609
- class Auth0TokenFetcher < StatelyDB::Common::Auth::TokenFetcher
610
- # _@param_ `origin` — The origin of the OAuth server
611
- #
612
- # _@param_ `audience` — The OAuth Audience for the token
613
- #
614
- # _@param_ `client_secret` — The StatelyDB client secret credential
615
- #
616
- # _@param_ `client_id` — The StatelyDB client ID credential
617
- sig do
618
- params(
619
- origin: String,
620
- audience: String,
621
- client_secret: String,
622
- client_id: String
623
- ).void
624
- end
625
- def initialize(origin:, audience:, client_secret:, client_id:); end
626
-
627
- # Fetch a new token from auth0
628
- #
629
- # _@return_ — The fetched TokenResult
630
- sig { returns(TokenResult) }
631
- def fetch; end
632
-
633
- sig { returns(T.untyped) }
634
- def close; end
635
- end
636
-
637
610
  # StatelyAccessTokenFetcher is a TokenFetcher that fetches tokens from the StatelyDB API
638
611
  class StatelyAccessTokenFetcher < StatelyDB::Common::Auth::TokenFetcher
612
+ NON_RETRYABLE_ERRORS = T.let([
613
+ GRPC::Core::StatusCodes::UNAUTHENTICATED,
614
+ GRPC::Core::StatusCodes::PERMISSION_DENIED,
615
+ GRPC::Core::StatusCodes::NOT_FOUND,
616
+ GRPC::Core::StatusCodes::UNIMPLEMENTED,
617
+ GRPC::Core::StatusCodes::INVALID_ARGUMENT
618
+ ].freeze, T.untyped)
619
+ RETRY_ATTEMPTS = T.let(10, T.untyped)
620
+
639
621
  # _@param_ `origin` — The origin of the OAuth server
640
622
  #
641
623
  # _@param_ `access_key` — The StatelyDB access key credential
642
- sig { params(origin: String, access_key: String).void }
643
- def initialize(origin:, access_key:); end
624
+ #
625
+ # _@param_ `base_retry_backoff_secs` — The base backoff time in seconds
626
+ sig { params(origin: String, access_key: String, base_retry_backoff_secs: Float).void }
627
+ def initialize(origin:, access_key:, base_retry_backoff_secs:); end
644
628
 
645
629
  # Fetch a new token from the StatelyDB API
646
630
  #
@@ -650,6 +634,14 @@ module StatelyDB
650
634
 
651
635
  sig { returns(T.untyped) }
652
636
  def close; end
637
+
638
+ # Check if an error is retryable
639
+ #
640
+ # _@param_ `err` — The error to check
641
+ #
642
+ # _@return_ — True if the error is retryable
643
+ sig { params(err: StatelyDB::Error).returns(T::Boolean) }
644
+ def self.retryable_error?(err); end
653
645
  end
654
646
 
655
647
  # TokenProvider is an abstract base class that should be extended
@@ -668,30 +660,18 @@ module StatelyDB
668
660
  def close; end
669
661
  end
670
662
 
671
- # Auth0TokenProvider is an implementation of the TokenProvider abstract base class
672
- # which vends tokens from auth0 with the given client_id and client_secret.
673
- # It will default to using the values of `STATELY_CLIENT_ID` and `STATELY_CLIENT_SECRET` if
674
- # no credentials are explicitly passed and will throw an error if none are found.
663
+ # AuthTokenProvider is an implementation of the TokenProvider abstract base class
664
+ # which vends tokens from the StatelyDB auth API.
665
+ # It will default to using the value of `STATELY_ACCESS_KEY` if
666
+ # no credentials are explicitly passed and will throw an error if no credentials are found.
675
667
  class AuthTokenProvider < StatelyDB::Common::Auth::TokenProvider
676
- # _@param_ `origin` — The origin of the OAuth server
677
- #
678
- # _@param_ `audience` — The OAuth Audience for the token
679
- #
680
- # _@param_ `client_secret` — The StatelyDB client secret credential
681
- #
682
- # _@param_ `client_id` — The StatelyDB client ID credential
668
+ # _@param_ `origin` — The origin of the auth server
683
669
  #
684
670
  # _@param_ `access_key` — The StatelyDB access key credential
685
- sig do
686
- params(
687
- origin: String,
688
- audience: String,
689
- client_secret: String,
690
- client_id: String,
691
- access_key: String
692
- ).void
693
- end
694
- def initialize(origin: "https://oauth.stately.cloud", audience: "api.stately.cloud", client_secret: ENV.fetch("STATELY_CLIENT_SECRET", nil), client_id: ENV.fetch("STATELY_CLIENT_ID", nil), access_key: ENV.fetch("STATELY_ACCESS_KEY", nil)); end
671
+ #
672
+ # _@param_ `base_retry_backoff_secs` — The base retry backoff in seconds
673
+ sig { params(origin: String, access_key: String, base_retry_backoff_secs: Float).void }
674
+ def initialize(origin: "https://api.stately.cloud", access_key: ENV.fetch("STATELY_ACCESS_KEY", nil), base_retry_backoff_secs: 1); end
695
675
 
696
676
  # Close the token provider and kill any background operations
697
677
  # This just invokes the close method on the actor which should do the cleanup
@@ -709,21 +689,11 @@ module StatelyDB
709
689
  class Actor
710
690
  # _@param_ `origin` — The origin of the OAuth server
711
691
  #
712
- # _@param_ `audience` — The OAuth Audience for the token
692
+ # _@param_ `access_key` — The StatelyDB access key credential
713
693
  #
714
- # _@param_ `client_secret` — The StatelyDB client secret credential
715
- #
716
- # _@param_ `client_id` The StatelyDB client ID credential
717
- sig do
718
- params(
719
- origin: String,
720
- audience: String,
721
- client_secret: String,
722
- client_id: String,
723
- access_key: T.untyped
724
- ).void
725
- end
726
- def initialize(origin:, audience:, client_secret:, client_id:, access_key:); end
694
+ # _@param_ `base_retry_backoff_secs` — The base retry backoff in seconds
695
+ sig { params(origin: String, access_key: String, base_retry_backoff_secs: Float).void }
696
+ def initialize(origin:, access_key:, base_retry_backoff_secs:); end
727
697
 
728
698
  # Initialize the actor. This runs on the actor thread which means
729
699
  # we can dispatch async operations here.
@@ -973,6 +943,8 @@ module StatelyDB
973
943
  #
974
944
  # _@param_ `must_not_exist` — A condition that indicates this item must not already exist at any of its key paths. If there is already an item at one of those paths, the Put operation will fail with a "ConditionalCheckFailed" error. Note that if the item has an `initialValue` field in its key, that initial value will automatically be chosen not to conflict with existing items, so this condition only applies to key paths that do not contain the `initialValue` field.
975
945
  #
946
+ # _@param_ `overwrite_metadata_timestamps` — If set to true, the server will set the `createdAtTime` and/or `lastModifiedAtTime` fields based on the current values in this item (assuming you've mapped them to a field using `fromMetadata`). Without this, those fields are always ignored and the server sets them to the appropriate times. This option can be useful when migrating data from another system.
947
+ #
976
948
  # _@return_ — the id of the item
977
949
  #
978
950
  # ```ruby
@@ -980,8 +952,8 @@ module StatelyDB
980
952
  # txn.put(my_item)
981
953
  # end
982
954
  # ```
983
- sig { params(item: StatelyDB::Item, must_not_exist: T::Boolean).returns(T.any(String, Integer)) }
984
- def put(item, must_not_exist: false); end
955
+ sig { params(item: StatelyDB::Item, must_not_exist: T::Boolean, overwrite_metadata_timestamps: T::Boolean).returns(T.any(String, Integer)) }
956
+ def put(item, must_not_exist: false, overwrite_metadata_timestamps: false); end
985
957
 
986
958
  # Put a batch of up to 50 Items into a StatelyDB Store. Results are not
987
959
  # returned until the transaction is committed and will be available in the
data/sig/statelydb.rbs CHANGED
@@ -267,6 +267,8 @@ module StatelyDB
267
267
  #
268
268
  # _@param_ `must_not_exist` — A condition that indicates this item must not already exist at any of its key paths. If there is already an item at one of those paths, the Put operation will fail with a "ConditionalCheckFailed" error. Note that if the item has an `initialValue` field in its key, that initial value will automatically be chosen not to conflict with existing items, so this condition only applies to key paths that do not contain the `initialValue` field.
269
269
  #
270
+ # _@param_ `overwrite_metadata_timestamps` — If set to true, the server will set the `createdAtTime` and/or `lastModifiedAtTime` fields based on the current values in this item (assuming you've mapped them to a field using `fromMetadata`). Without this, those fields are always ignored and the server sets them to the appropriate times. This option can be useful when migrating data from another system.
271
+ #
270
272
  # _@return_ — the item that was stored
271
273
  #
272
274
  # client.data.put(my_item)
@@ -276,7 +278,7 @@ module StatelyDB
276
278
  # client.data.put(my_item, must_not_exist: true)
277
279
  # ```ruby
278
280
  # ```
279
- def put: (StatelyDB::Item item, ?must_not_exist: bool) -> StatelyDB::Item
281
+ def put: (StatelyDB::Item item, ?must_not_exist: bool, ?overwrite_metadata_timestamps: bool) -> StatelyDB::Item
280
282
 
281
283
  # Put a batch of up to 50 Items into a StatelyDB Store.
282
284
  #
@@ -524,36 +526,17 @@ module StatelyDB
524
526
  def close: () -> untyped
525
527
  end
526
528
 
527
- # Auth0TokenFetcher is a TokenFetcher that fetches tokens from an Auth0 server
528
- class Auth0TokenFetcher < StatelyDB::Common::Auth::TokenFetcher
529
- # _@param_ `origin` — The origin of the OAuth server
530
- #
531
- # _@param_ `audience` — The OAuth Audience for the token
532
- #
533
- # _@param_ `client_secret` — The StatelyDB client secret credential
534
- #
535
- # _@param_ `client_id` — The StatelyDB client ID credential
536
- def initialize: (
537
- origin: String,
538
- audience: String,
539
- client_secret: String,
540
- client_id: String
541
- ) -> void
542
-
543
- # Fetch a new token from auth0
544
- #
545
- # _@return_ — The fetched TokenResult
546
- def fetch: () -> TokenResult
547
-
548
- def close: () -> untyped
549
- end
550
-
551
529
  # StatelyAccessTokenFetcher is a TokenFetcher that fetches tokens from the StatelyDB API
552
530
  class StatelyAccessTokenFetcher < StatelyDB::Common::Auth::TokenFetcher
531
+ NON_RETRYABLE_ERRORS: untyped
532
+ RETRY_ATTEMPTS: untyped
533
+
553
534
  # _@param_ `origin` — The origin of the OAuth server
554
535
  #
555
536
  # _@param_ `access_key` — The StatelyDB access key credential
556
- def initialize: (origin: String, access_key: String) -> void
537
+ #
538
+ # _@param_ `base_retry_backoff_secs` — The base backoff time in seconds
539
+ def initialize: (origin: String, access_key: String, base_retry_backoff_secs: Float) -> void
557
540
 
558
541
  # Fetch a new token from the StatelyDB API
559
542
  #
@@ -561,6 +544,13 @@ module StatelyDB
561
544
  def fetch: () -> TokenResult
562
545
 
563
546
  def close: () -> untyped
547
+
548
+ # Check if an error is retryable
549
+ #
550
+ # _@param_ `err` — The error to check
551
+ #
552
+ # _@return_ — True if the error is retryable
553
+ def self.retryable_error?: (StatelyDB::Error err) -> bool
564
554
  end
565
555
 
566
556
  # TokenProvider is an abstract base class that should be extended
@@ -577,27 +567,17 @@ module StatelyDB
577
567
  def close: () -> untyped
578
568
  end
579
569
 
580
- # Auth0TokenProvider is an implementation of the TokenProvider abstract base class
581
- # which vends tokens from auth0 with the given client_id and client_secret.
582
- # It will default to using the values of `STATELY_CLIENT_ID` and `STATELY_CLIENT_SECRET` if
583
- # no credentials are explicitly passed and will throw an error if none are found.
570
+ # AuthTokenProvider is an implementation of the TokenProvider abstract base class
571
+ # which vends tokens from the StatelyDB auth API.
572
+ # It will default to using the value of `STATELY_ACCESS_KEY` if
573
+ # no credentials are explicitly passed and will throw an error if no credentials are found.
584
574
  class AuthTokenProvider < StatelyDB::Common::Auth::TokenProvider
585
- # _@param_ `origin` — The origin of the OAuth server
586
- #
587
- # _@param_ `audience` — The OAuth Audience for the token
588
- #
589
- # _@param_ `client_secret` — The StatelyDB client secret credential
590
- #
591
- # _@param_ `client_id` — The StatelyDB client ID credential
575
+ # _@param_ `origin` — The origin of the auth server
592
576
  #
593
577
  # _@param_ `access_key` — The StatelyDB access key credential
594
- def initialize: (
595
- ?origin: String,
596
- ?audience: String,
597
- ?client_secret: String,
598
- ?client_id: String,
599
- ?access_key: String
600
- ) -> void
578
+ #
579
+ # _@param_ `base_retry_backoff_secs` — The base retry backoff in seconds
580
+ def initialize: (?origin: String, ?access_key: String, ?base_retry_backoff_secs: Float) -> void
601
581
 
602
582
  # Close the token provider and kill any background operations
603
583
  # This just invokes the close method on the actor which should do the cleanup
@@ -613,18 +593,10 @@ module StatelyDB
613
593
  class Actor
614
594
  # _@param_ `origin` — The origin of the OAuth server
615
595
  #
616
- # _@param_ `audience` — The OAuth Audience for the token
617
- #
618
- # _@param_ `client_secret` — The StatelyDB client secret credential
596
+ # _@param_ `access_key` — The StatelyDB access key credential
619
597
  #
620
- # _@param_ `client_id` — The StatelyDB client ID credential
621
- def initialize: (
622
- origin: String,
623
- audience: String,
624
- client_secret: String,
625
- client_id: String,
626
- access_key: untyped
627
- ) -> void
598
+ # _@param_ `base_retry_backoff_secs` — The base retry backoff in seconds
599
+ def initialize: (origin: String, access_key: String, base_retry_backoff_secs: Float) -> void
628
600
 
629
601
  # Initialize the actor. This runs on the actor thread which means
630
602
  # we can dispatch async operations here.
@@ -838,6 +810,8 @@ module StatelyDB
838
810
  #
839
811
  # _@param_ `must_not_exist` — A condition that indicates this item must not already exist at any of its key paths. If there is already an item at one of those paths, the Put operation will fail with a "ConditionalCheckFailed" error. Note that if the item has an `initialValue` field in its key, that initial value will automatically be chosen not to conflict with existing items, so this condition only applies to key paths that do not contain the `initialValue` field.
840
812
  #
813
+ # _@param_ `overwrite_metadata_timestamps` — If set to true, the server will set the `createdAtTime` and/or `lastModifiedAtTime` fields based on the current values in this item (assuming you've mapped them to a field using `fromMetadata`). Without this, those fields are always ignored and the server sets them to the appropriate times. This option can be useful when migrating data from another system.
814
+ #
841
815
  # _@return_ — the id of the item
842
816
  #
843
817
  # ```ruby
@@ -845,7 +819,7 @@ module StatelyDB
845
819
  # txn.put(my_item)
846
820
  # end
847
821
  # ```
848
- def put: (StatelyDB::Item item, ?must_not_exist: bool) -> (String | Integer)
822
+ def put: (StatelyDB::Item item, ?must_not_exist: bool, ?overwrite_metadata_timestamps: bool) -> (String | Integer)
849
823
 
850
824
  # Put a batch of up to 50 Items into a StatelyDB Store. Results are not
851
825
  # returned until the transaction is committed and will be available in the
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: statelydb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.14.0
4
+ version: 0.16.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stately Cloud, Inc.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-12-12 00:00:00.000000000 Z
11
+ date: 2024-12-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: async