Class: StatelyDB::Client

Inherits:
Object
  • Object
show all
Defined in:
lib/statelydb.rb

Overview

Client is a client for interacting with the Stately Cloud API.

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(store_id: nil, schema: StatelyDB::Types, token_provider: Common::Auth::Auth0TokenProvider.new, endpoint: nil, region: nil) ⇒ Client

Initialize a new StatelyDB Client

Parameters:

  • store_id (Integer) (defaults to: nil)

    the StatelyDB to use for all operations with this client.

  • schema (Module) (defaults to: StatelyDB::Types)

    the schema module to use for mapping StatelyDB Items.

  • token_provider (Common::Auth::TokenProvider) (defaults to: Common::Auth::Auth0TokenProvider.new)

    the token provider to use for authentication.

  • endpoint (String) (defaults to: nil)

    the endpoint to connect to.

  • region (String) (defaults to: nil)

    the region to connect to.



34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/statelydb.rb', line 34

def initialize(store_id: nil,
               schema: StatelyDB::Types,
               token_provider: Common::Auth::Auth0TokenProvider.new,
               endpoint: nil,
               region: nil)
  raise "store_id is required" if store_id.nil?
  raise "schema is required" if schema.nil?

  endpoint = self.class.make_endpoint(endpoint:, region:)

  channel = Common::Net.new_channel(endpoint:)

  auth_interceptor = Common::Auth::Interceptor.new(token_provider:)
  error_interceptor = Common::ErrorInterceptor.new

  @stub = Stately::Db::DatabaseService::Stub.new(nil, nil, channel_override: channel,
                                                           interceptors: [error_interceptor, auth_interceptor])
  @store_id = store_id.to_i
  @schema = schema
  @allow_stale = false
end

Class Method Details

.make_endpoint(endpoint: nil, region: nil) ⇒ String

Construct the API endpoint from the region and endpoint. If the endpoint is provided, it will be returned as-is. If the region is provided and the endpoint is not, then the region-specific endpoint will be returned. If neither the region nor the endpoint is provided, then the default endpoint will be returned.

Parameters:

  • endpoint (String) (defaults to: nil)

    the endpoint to connect to

  • region (Region) (defaults to: nil)

    the region to connect to

Returns:

  • (String)

    the constructed endpoint



266
267
268
269
270
271
272
273
# File 'lib/statelydb.rb', line 266

def self.make_endpoint(endpoint: nil, region: nil)
  return endpoint unless endpoint.nil?
  return "https://api.stately.cloud" if region.nil?

  region = region.sub("aws-", "") if region.start_with?("aws-")

  "https://#{region}.aws.api.stately.cloud"
end

Instance Method Details

#begin_list(prefix, limit: 100, sort_property: nil, sort_direction: :ascending) ⇒ Array<StatelyDB::Item>, StatelyDB::Token

Begin listing Items from a StatelyDB Store at the given prefix.

Examples:

client.data.begin_list("/ItemType-identifier", limit: 10, sort_direction: :ascending)

Parameters:

  • prefix (String)

    the prefix to list

  • limit (Integer) (defaults to: 100)

    the maximum number of items to return

  • sort_property (String) (defaults to: nil)

    the property to sort by

  • sort_direction (Symbol) (defaults to: :ascending)

    the direction to sort by (:ascending or :descending)

Returns:



116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/statelydb.rb', line 116

def begin_list(prefix,
               limit: 100,
               sort_property: nil,
               sort_direction: :ascending)
  sort_direction = sort_direction == :ascending ? 0 : 1

  req = Stately::Db::BeginListRequest.new(
    store_id: @store_id,
    key_path_prefix: String(prefix),
    limit:,
    sort_property:,
    sort_direction:,
    allow_stale: @allow_stale
  )
  resp = @stub.begin_list(req)
  process_list_response(resp)
end

#continue_list(token) ⇒ Array<StatelyDB::Item>, StatelyDB::Token

Continue listing Items from a StatelyDB Store using a token.

Examples:

(items, token) = client.data.begin_list("/ItemType-identifier")
client.data.continue_list(token)

Parameters:

Returns:



142
143
144
145
146
147
148
# File 'lib/statelydb.rb', line 142

def continue_list(token)
  req = Stately::Db::ContinueListRequest.new(
    token_data: token.token_data
  )
  resp = @stub.continue_list(req)
  process_list_response(resp)
end

#delete(*key_paths) ⇒ void

This method returns an undefined value.

Delete up to 50 Items from a StatelyDB Store at the given key_paths.

Examples:

client.data.delete("/ItemType-identifier", "/ItemType-identifier2")

Parameters:

  • key_paths (String, Array<String>)

    the paths to the items. Max 50 key paths.

Raises:

  • (StatelyDB::Error::InvalidParameters)

    if the parameters are invalid

  • (StatelyDB::Error::NotFound)

    if the item is not found



213
214
215
216
217
218
219
220
221
# File 'lib/statelydb.rb', line 213

def delete(*key_paths)
  key_paths = Array(key_paths).flatten
  req = Stately::Db::DeleteRequest.new(
    store_id: @store_id,
    deletes: key_paths.map { |key_path| Stately::Db::DeleteItem.new(key_path: String(key_path)) }
  )
  @stub.delete(req)
  nil
end

#get(key_path) ⇒ StatelyDB::Item, NilClass

Fetch a single Item from a StatelyDB Store at the given key_path.

Examples:

client.get("/ItemType-identifier")

Parameters:

  • key_path (String)

    the path to the item

Returns:

Raises:

  • (StatelyDB::Error)

    if the parameters are invalid or if the item is not found



76
77
78
79
80
81
# File 'lib/statelydb.rb', line 76

def get(key_path)
  resp = get_batch(key_path)

  # Always return a single Item.
  resp.first
end

#get_batch(*key_paths) ⇒ Array<StatelyDB::Item>, NilClass

Fetch a batch of up to 100 Items from a StatelyDB Store at the given key_paths.

Examples:

client.data.get_batch("/ItemType-identifier", "/ItemType-identifier2")

Parameters:

  • key_paths (String, Array<String>)

    the paths to the items. Max 100 key paths.

Returns:

Raises:

  • (StatelyDB::Error)

    if the parameters are invalid or if the item is not found



91
92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/statelydb.rb', line 91

def get_batch(*key_paths)
  key_paths = Array(key_paths).flatten
  req = Stately::Db::GetRequest.new(
    store_id: @store_id,
    gets:
      key_paths.map { |key_path| Stately::Db::GetItem.new(key_path: String(key_path)) },
    allow_stale: @allow_stale
  )

  resp = @stub.get(req)
  resp.items.map do |result|
    @schema.unmarshal_item(stately_item: result)
  end
end

#put(item) ⇒ StatelyDB::Item

Put an Item into a StatelyDB Store at the given key_path.

Examples:

client.data.put(my_item)

Parameters:

Returns:



173
174
175
176
177
178
# File 'lib/statelydb.rb', line 173

def put(item)
  resp = put_batch(item)

  # Always return a single Item.
  resp.first
end

#put_batch(*items) ⇒ Array<StatelyDB::Item>

Put a batch of up to 50 Items into a StatelyDB Store.

Examples:

client.data.put_batch(item1, item2)

Parameters:

Returns:



187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
# File 'lib/statelydb.rb', line 187

def put_batch(*items)
  items = Array(items).flatten
  req = Stately::Db::PutRequest.new(
    store_id: @store_id,
    puts: items.map do |item|
      Stately::Db::PutItem.new(
        item: item.send("marshal_stately")
      )
    end
  )
  resp = @stub.put(req)

  resp.items.map do |result|
    @schema.unmarshal_item(stately_item: result)
  end
end

#sync_list(token) ⇒ StatelyDB::SyncResult

Sync a list of Items from a StatelyDB Store.

Examples:

(items, token) = client.data.begin_list("/ItemType-identifier")
client.data.sync_list(token)

Parameters:

Returns:



158
159
160
161
162
163
164
# File 'lib/statelydb.rb', line 158

def sync_list(token)
  req = Stately::Db::SyncListRequest.new(
    token_data: token.token_data
  )
  resp = @stub.sync_list(req)
  process_sync_response(resp)
end

#transactionStatelyDB::Transaction::Transaction::Result

Transaction takes a block and executes the block within a transaction. If the block raises an exception, the transaction is rolled back. If the block completes successfully, the transaction is committed.

Examples:

client.data.transaction do |txn|
  txn.put(item: my_item)
  txn.put(item: another_item)
end

Returns:

Raises:

  • (StatelyDB::Error::InvalidParameters)

    if the parameters are invalid

  • (StatelyDB::Error::NotFound)

    if the item is not found

  • (Exception)

    if any other exception is raised



237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
# File 'lib/statelydb.rb', line 237

def transaction
  txn = StatelyDB::Transaction::Transaction.new(stub: @stub, store_id: @store_id, schema: @schema)
  txn.begin
  yield txn
  txn.commit
rescue StatelyDB::Error
  raise
# Handle any other exceptions and abort the transaction. We're rescuing Exception here
# because we want to catch all exceptions, including those that don't inherit from StandardError.
rescue Exception => e
  txn.abort

  # All gRPC errors inherit from GRPC::BadStatus. We wrap these in a StatelyDB::Error.
  raise StatelyDB::Error.from(e) if e.is_a? GRPC::BadStatus

  # Calling raise with no parameters re-raises the original exception
  raise
end

#with_allow_stale(allow_stale) ⇒ StatelyDB::Client

Set whether to allow stale results for all operations with this client. This produces a new client with the allow_stale flag set.

Examples:

client.with_allow_stale(true).get("/ItemType-identifier")

Parameters:

  • allow_stale (Boolean)

    whether to allow stale results

Returns:



62
63
64
65
66
# File 'lib/statelydb.rb', line 62

def with_allow_stale(allow_stale)
  new_client = clone
  new_client.instance_variable_set(:@allow_stale, allow_stale)
  new_client
end