statelydb 0.12.2 → 0.14.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: 16704510255d3248c18f2f26a63e2e94debe5207e6bdb820b8be999a44aeb486
4
- data.tar.gz: 981f9a3030231adfcf253dbde5c84bb54232d64c4cec169a630d8c085194cc0e
3
+ metadata.gz: a723737fdce6e8ad2bec8937cfd79f6d968df0fc3e9e747c64e18af9ce769601
4
+ data.tar.gz: 1f0279a8b9507f4cffacc360ab82e2289471a40de39bd3891f0c7db5c63877c6
5
5
  SHA512:
6
- metadata.gz: 647935dfc070e7b871933d9f75d24c7b6e651cd95005b374a65c5af7e1d0f3271259c5bf585350b3c9065fef8e33c10e86212bb3500d87ef0647ed77cf9bb79d
7
- data.tar.gz: a5857dbb91d7f4f91e2d3518d1e0fcdaf8104e5e26b792440207c18f4c18f3d7e0a639c2d090c9ef95cf25e623b00ee8d708956d8989993a54e4a44b8f5f0947
6
+ metadata.gz: 815835070a91366b18948afe145eb548ae4f6ecbc6559ed3b8193288cd1084b7ac1a416264b0769afea93f4a8c01e17b0474facb46fe90460b0b3db803466428
7
+ data.tar.gz: e0c75f31911432f2544c4097fbc0c7a1615624257e80495aef9e5b3a0dddc92dd9341683d24416c4a32ab19b8bd34a2d79abb577781d75ee41b55a02f2e2e11c
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+ # Generated by the protocol buffer compiler. DO NOT EDIT!
3
+ # source: auth/get_auth_token.proto
4
+
5
+ require 'google/protobuf'
6
+
7
+
8
+ descriptor_data = "\n\x19\x61uth/get_auth_token.proto\x12\x0cstately.auth\"B\n\x13GetAuthTokenRequest\x12\x1f\n\naccess_key\x18\x01 \x01(\tH\x00R\taccessKeyB\n\n\x08identity\"W\n\x14GetAuthTokenResponse\x12\x1d\n\nauth_token\x18\x01 \x01(\tR\tauthToken\x12 \n\x0c\x65xpires_in_s\x18\x02 \x01(\x04R\nexpiresInSBv\n\x10\x63om.stately.authB\x11GetAuthTokenProtoP\x01\xa2\x02\x03SAX\xaa\x02\x0cStately.Auth\xca\x02\x0cStately\\Auth\xe2\x02\x18Stately\\Auth\\GPBMetadata\xea\x02\rStately::Authb\x06proto3"
9
+
10
+ pool = Google::Protobuf::DescriptorPool.generated_pool
11
+ pool.add_serialized_file(descriptor_data)
12
+
13
+ module Stately
14
+ module Auth
15
+ GetAuthTokenRequest = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("stately.auth.GetAuthTokenRequest").msgclass
16
+ GetAuthTokenResponse = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("stately.auth.GetAuthTokenResponse").msgclass
17
+ end
18
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+ # Generated by the protocol buffer compiler. DO NOT EDIT!
3
+ # source: auth/service.proto
4
+
5
+ require 'google/protobuf'
6
+
7
+ require 'api/auth/get_auth_token_pb'
8
+
9
+
10
+ descriptor_data = "\n\x12\x61uth/service.proto\x12\x0cstately.auth\x1a\x19\x61uth/get_auth_token.proto2i\n\x0b\x41uthService\x12Z\n\x0cGetAuthToken\x12!.stately.auth.GetAuthTokenRequest\x1a\".stately.auth.GetAuthTokenResponse\"\x03\x90\x02\x01\x42q\n\x10\x63om.stately.authB\x0cServiceProtoP\x01\xa2\x02\x03SAX\xaa\x02\x0cStately.Auth\xca\x02\x0cStately\\Auth\xe2\x02\x18Stately\\Auth\\GPBMetadata\xea\x02\rStately::Authb\x06proto3"
11
+
12
+ pool = Google::Protobuf::DescriptorPool.generated_pool
13
+ pool.add_serialized_file(descriptor_data)
14
+
15
+ module Stately
16
+ module Auth
17
+ end
18
+ end
@@ -0,0 +1,29 @@
1
+ # Generated by the protocol buffer compiler. DO NOT EDIT!
2
+ # Source: auth/service.proto for package 'Stately.Auth'
3
+
4
+ require 'grpc'
5
+ require 'api/auth/service_pb'
6
+
7
+ module Stately
8
+ module Auth
9
+ module AuthService
10
+ # AuthService is the service for vending access tokens used to connect to
11
+ # StatelyDB. This API is meant to be used from SDKs. Access Keys are created
12
+ # and managed from the stately.dbmanagement.UserService.
13
+ class Service
14
+
15
+ include ::GRPC::GenericService
16
+
17
+ self.marshal_class_method = :encode
18
+ self.unmarshal_class_method = :decode
19
+ self.service_name = 'stately.auth.AuthService'
20
+
21
+ # GetAuthToken returns a short-lived access token from some proof of
22
+ # identity. This operation will fail if the identity cannot be verified.
23
+ rpc :GetAuthToken, ::Stately::Auth::GetAuthTokenRequest, ::Stately::Auth::GetAuthTokenResponse
24
+ end
25
+
26
+ Stub = Service.rpc_stub_class
27
+ end
28
+ end
29
+ end
data/lib/api/db/get_pb.rb CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  require 'google/protobuf'
6
6
 
7
- require 'db/item_pb'
7
+ require 'api/db/item_pb'
8
8
 
9
9
 
10
10
  descriptor_data = "\n\x0c\x64\x62/get.proto\x12\nstately.db\x1a\rdb/item.proto\"\xa3\x01\n\nGetRequest\x12\x19\n\x08store_id\x18\x01 \x01(\x04R\x07storeId\x12\'\n\x04gets\x18\x02 \x03(\x0b\x32\x13.stately.db.GetItemR\x04gets\x12\x1f\n\x0b\x61llow_stale\x18\x03 \x01(\x08R\nallowStale\x12*\n\x11schema_version_id\x18\x05 \x01(\rR\x0fschemaVersionIdJ\x04\x08\x04\x10\x05\"$\n\x07GetItem\x12\x19\n\x08key_path\x18\x01 \x01(\tR\x07keyPath\"5\n\x0bGetResponse\x12&\n\x05items\x18\x01 \x03(\x0b\x32\x10.stately.db.ItemR\x05itemsBc\n\x0e\x63om.stately.dbB\x08GetProtoP\x01\xa2\x02\x03SDX\xaa\x02\nStately.Db\xca\x02\nStately\\Db\xe2\x02\x16Stately\\Db\\GPBMetadata\xea\x02\x0bStately::Dbb\x06proto3"
@@ -4,9 +4,9 @@
4
4
 
5
5
  require 'google/protobuf'
6
6
 
7
- require 'db/item_pb'
8
- require 'db/item_property_pb'
9
- require 'db/list_token_pb'
7
+ require 'api/db/item_pb'
8
+ require 'api/db/item_property_pb'
9
+ require 'api/db/list_token_pb'
10
10
 
11
11
 
12
12
  descriptor_data = "\n\rdb/list.proto\x12\nstately.db\x1a\rdb/item.proto\x1a\x16\x64\x62/item_property.proto\x1a\x13\x64\x62/list_token.proto\"\xbd\x02\n\x10\x42\x65ginListRequest\x12\x19\n\x08store_id\x18\x01 \x01(\x04R\x07storeId\x12&\n\x0fkey_path_prefix\x18\x02 \x01(\tR\rkeyPathPrefix\x12\x14\n\x05limit\x18\x03 \x01(\rR\x05limit\x12\x1f\n\x0b\x61llow_stale\x18\x04 \x01(\x08R\nallowStale\x12\x41\n\rsort_property\x18\x05 \x01(\x0e\x32\x1c.stately.db.SortablePropertyR\x0csortProperty\x12@\n\x0esort_direction\x18\x06 \x01(\x0e\x32\x19.stately.db.SortDirectionR\rsortDirection\x12*\n\x11schema_version_id\x18\x07 \x01(\rR\x0fschemaVersionId\"\x8b\x01\n\x0cListResponse\x12\x37\n\x06result\x18\x01 \x01(\x0b\x32\x1d.stately.db.ListPartialResultH\x00R\x06result\x12\x36\n\x08\x66inished\x18\x02 \x01(\x0b\x32\x18.stately.db.ListFinishedH\x00R\x08\x66inishedB\n\n\x08response\";\n\x11ListPartialResult\x12&\n\x05items\x18\x01 \x03(\x0b\x32\x10.stately.db.ItemR\x05items\";\n\x0cListFinished\x12+\n\x05token\x18\x01 \x01(\x0b\x32\x15.stately.db.ListTokenR\x05token*8\n\rSortDirection\x12\x12\n\x0eSORT_ASCENDING\x10\x00\x12\x13\n\x0fSORT_DESCENDING\x10\x01\x42\x64\n\x0e\x63om.stately.dbB\tListProtoP\x01\xa2\x02\x03SDX\xaa\x02\nStately.Db\xca\x02\nStately\\Db\xe2\x02\x16Stately\\Db\\GPBMetadata\xea\x02\x0bStately::Dbb\x06proto3"
data/lib/api/db/put_pb.rb CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  require 'google/protobuf'
6
6
 
7
- require 'db/item_pb'
7
+ require 'api/db/item_pb'
8
8
 
9
9
 
10
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"
@@ -4,14 +4,14 @@
4
4
 
5
5
  require 'google/protobuf'
6
6
 
7
- require 'db/continue_list_pb'
8
- require 'db/delete_pb'
9
- require 'db/get_pb'
10
- require 'db/list_pb'
11
- require 'db/put_pb'
12
- require 'db/scan_root_paths_pb'
13
- require 'db/sync_list_pb'
14
- require 'db/transaction_pb'
7
+ require 'api/db/continue_list_pb'
8
+ require 'api/db/delete_pb'
9
+ require 'api/db/get_pb'
10
+ require 'api/db/list_pb'
11
+ require 'api/db/put_pb'
12
+ require 'api/db/scan_root_paths_pb'
13
+ require 'api/db/sync_list_pb'
14
+ require 'api/db/transaction_pb'
15
15
 
16
16
 
17
17
  descriptor_data = "\n\x10\x64\x62/service.proto\x12\nstately.db\x1a\x16\x64\x62/continue_list.proto\x1a\x0f\x64\x62/delete.proto\x1a\x0c\x64\x62/get.proto\x1a\rdb/list.proto\x1a\x0c\x64\x62/put.proto\x1a\x18\x64\x62/scan_root_paths.proto\x1a\x12\x64\x62/sync_list.proto\x1a\x14\x64\x62/transaction.proto2\xee\x04\n\x0f\x44\x61tabaseService\x12;\n\x03Put\x12\x16.stately.db.PutRequest\x1a\x17.stately.db.PutResponse\"\x03\x90\x02\x02\x12;\n\x03Get\x12\x16.stately.db.GetRequest\x1a\x17.stately.db.GetResponse\"\x03\x90\x02\x01\x12\x44\n\x06\x44\x65lete\x12\x19.stately.db.DeleteRequest\x1a\x1a.stately.db.DeleteResponse\"\x03\x90\x02\x02\x12J\n\tBeginList\x12\x1c.stately.db.BeginListRequest\x1a\x18.stately.db.ListResponse\"\x03\x90\x02\x01\x30\x01\x12P\n\x0c\x43ontinueList\x12\x1f.stately.db.ContinueListRequest\x1a\x18.stately.db.ListResponse\"\x03\x90\x02\x01\x30\x01\x12L\n\x08SyncList\x12\x1b.stately.db.SyncListRequest\x1a\x1c.stately.db.SyncListResponse\"\x03\x90\x02\x01\x30\x01\x12T\n\x0bTransaction\x12\x1e.stately.db.TransactionRequest\x1a\x1f.stately.db.TransactionResponse\"\x00(\x01\x30\x01\x12Y\n\rScanRootPaths\x12 .stately.db.ScanRootPathsRequest\x1a!.stately.db.ScanRootPathsResponse\"\x03\x90\x02\x01\x42g\n\x0e\x63om.stately.dbB\x0cServiceProtoP\x01\xa2\x02\x03SDX\xaa\x02\nStately.Db\xca\x02\nStately\\Db\xe2\x02\x16Stately\\Db\\GPBMetadata\xea\x02\x0bStately::Dbb\x06proto3"
@@ -2,7 +2,7 @@
2
2
  # Source: db/service.proto for package 'Stately.Db'
3
3
 
4
4
  require 'grpc'
5
- require 'db/service_pb'
5
+ require 'api/db/service_pb'
6
6
 
7
7
  module Stately
8
8
  module Db
@@ -4,8 +4,8 @@
4
4
 
5
5
  require 'google/protobuf'
6
6
 
7
- require 'db/item_pb'
8
- require 'db/list_pb'
7
+ require 'api/db/item_pb'
8
+ require 'api/db/list_pb'
9
9
 
10
10
 
11
11
  descriptor_data = "\n\x12\x64\x62/sync_list.proto\x12\nstately.db\x1a\rdb/item.proto\x1a\rdb/list.proto\"\\\n\x0fSyncListRequest\x12\x1d\n\ntoken_data\x18\x01 \x01(\x0cR\ttokenData\x12*\n\x11schema_version_id\x18\x05 \x01(\rR\x0fschemaVersionId\"\xc8\x01\n\x10SyncListResponse\x12\x31\n\x05reset\x18\x01 \x01(\x0b\x32\x19.stately.db.SyncListResetH\x00R\x05reset\x12=\n\x06result\x18\x02 \x01(\x0b\x32#.stately.db.SyncListPartialResponseH\x00R\x06result\x12\x36\n\x08\x66inished\x18\x03 \x01(\x0b\x32\x18.stately.db.ListFinishedH\x00R\x08\x66inishedB\n\n\x08response\"\x0f\n\rSyncListReset\"\xdf\x01\n\x17SyncListPartialResponse\x12\x35\n\rchanged_items\x18\x01 \x03(\x0b\x32\x10.stately.db.ItemR\x0c\x63hangedItems\x12<\n\rdeleted_items\x18\x02 \x03(\x0b\x32\x17.stately.db.DeletedItemR\x0c\x64\x65letedItems\x12O\n%updated_item_keys_outside_list_window\x18\x03 \x03(\tR updatedItemKeysOutsideListWindow\"(\n\x0b\x44\x65letedItem\x12\x19\n\x08key_path\x18\x01 \x01(\tR\x07keyPathBh\n\x0e\x63om.stately.dbB\rSyncListProtoP\x01\xa2\x02\x03SDX\xaa\x02\nStately.Db\xca\x02\nStately\\Db\xe2\x02\x16Stately\\Db\\GPBMetadata\xea\x02\x0bStately::Dbb\x06proto3"
@@ -4,13 +4,13 @@
4
4
 
5
5
  require 'google/protobuf'
6
6
 
7
- require 'db/continue_list_pb'
8
- require 'db/delete_pb'
9
- require 'db/get_pb'
10
- require 'db/item_pb'
11
- require 'db/item_property_pb'
12
- require 'db/list_pb'
13
- require 'db/put_pb'
7
+ require 'api/db/continue_list_pb'
8
+ require 'api/db/delete_pb'
9
+ require 'api/db/get_pb'
10
+ require 'api/db/item_pb'
11
+ require 'api/db/item_property_pb'
12
+ require 'api/db/list_pb'
13
+ require 'api/db/put_pb'
14
14
  require 'google/protobuf/empty_pb'
15
15
 
16
16
 
@@ -6,8 +6,10 @@ require "async/http/internet"
6
6
  require "async/semaphore"
7
7
  require "json"
8
8
  require "logger"
9
- require "weakref"
9
+ require "grpc"
10
10
  require_relative "token_provider"
11
+ require_relative "token_fetcher"
12
+ require_relative "../../error"
11
13
 
12
14
  LOGGER = Logger.new($stdout)
13
15
  LOGGER.level = Logger::WARN
@@ -22,20 +24,22 @@ module StatelyDB
22
24
  # which vends tokens from auth0 with the given client_id and client_secret.
23
25
  # It will default to using the values of `STATELY_CLIENT_ID` and `STATELY_CLIENT_SECRET` if
24
26
  # no credentials are explicitly passed and will throw an error if none are found.
25
- class Auth0TokenProvider < TokenProvider
27
+ class AuthTokenProvider < TokenProvider
26
28
  # @param [String] origin The origin of the OAuth server
27
29
  # @param [String] audience The OAuth Audience for the token
28
30
  # @param [String] client_secret The StatelyDB client secret credential
29
31
  # @param [String] client_id The StatelyDB client ID credential
32
+ # @param [String] access_key The StatelyDB access key credential
30
33
  def initialize(
31
34
  origin: "https://oauth.stately.cloud",
32
35
  audience: "api.stately.cloud",
33
- client_secret: ENV.fetch("STATELY_CLIENT_SECRET"),
34
- client_id: ENV.fetch("STATELY_CLIENT_ID")
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)
35
39
  )
36
40
  super()
37
41
  @actor = Async::Actor.new(Actor.new(origin: origin, audience: audience,
38
- client_secret: client_secret, client_id: client_id))
42
+ client_secret: client_secret, client_id: client_id, access_key: access_key))
39
43
  # this initialization cannot happen in the constructor because it is async and must run on the event loop
40
44
  # which is not available in the constructor
41
45
  @actor.init
@@ -64,29 +68,41 @@ module StatelyDB
64
68
  origin:,
65
69
  audience:,
66
70
  client_secret:,
67
- client_id:
71
+ client_id:,
72
+ access_key:
68
73
  )
69
74
  super()
70
- @client = Async::HTTP::Client.new(Async::HTTP::Endpoint.parse(origin))
71
- @client_id = client_id
72
- @client_secret = client_secret
73
- @audience = audience
74
75
 
75
- @access_token = nil
76
- @expires_at_unix_secs = nil
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")
88
+ end
89
+
90
+ @token_state = nil
77
91
  @pending_refresh = nil
78
92
  end
79
93
 
80
94
  # Initialize the actor. This runs on the actor thread which means
81
95
  # we can dispatch async operations here.
82
96
  def init
97
+ # disable the async lib logger. We do our own error handling and propagation
98
+ Console.logger.disable(Async::Task)
83
99
  refresh_token
84
100
  end
85
101
 
86
102
  # Close the token provider and kill any background operations
87
103
  def close
88
104
  @scheduled&.stop
89
- @client&.close
105
+ @token_fetcher&.close
90
106
  end
91
107
 
92
108
  # Get the current access token
@@ -94,8 +110,7 @@ module StatelyDB
94
110
  # @return [String] The current access token
95
111
  def get_token(force: false)
96
112
  if force
97
- @access_token = nil
98
- @expires_at_unix_secs = nil
113
+ @token_state = nil
99
114
  else
100
115
  token, ok = valid_access_token
101
116
  return token if ok
@@ -107,11 +122,10 @@ module StatelyDB
107
122
  # Get the current access token and whether it is valid
108
123
  # @return [Array] The current access token and whether it is valid
109
124
  def valid_access_token
110
- return "", false if @access_token.nil?
111
- return "", false if @expires_at_unix_secs.nil?
112
- return "", false if @expires_at_unix_secs < Time.now.to_i
125
+ return "", false if @token_state.nil?
126
+ return "", false if @token_state.expires_at_unix_secs < Time.now.to_i
113
127
 
114
- [@access_token, true]
128
+ [@token_state.token, true]
115
129
  end
116
130
 
117
131
  # Refresh the access token
@@ -151,19 +165,16 @@ module StatelyDB
151
165
  # @return [String] The new access token
152
166
  def refresh_token_impl
153
167
  Sync do
154
- resp_data = make_auth0_request
155
-
156
- new_access_token = resp_data["access_token"]
157
- new_expires_in_secs = resp_data["expires_in"]
168
+ token_result = @token_fetcher.fetch
169
+ new_expires_in_secs = token_result.expires_in_secs
158
170
  new_expires_at_unix_secs = Time.now.to_i + new_expires_in_secs
159
- if @expires_at_unix_secs.nil? || new_expires_at_unix_secs > @expires_at_unix_secs
160
171
 
161
- @access_token = new_access_token
162
- @expires_at_unix_secs = new_expires_at_unix_secs
172
+ # only update the token state if the new expiry is later than the current one
173
+ if @token_state.nil? || new_expires_at_unix_secs > @token_state.expires_at_unix_secs
174
+ @token_state = TokenState.new(token: token_result.token, expires_at_unix_secs: new_expires_at_unix_secs)
163
175
  else
164
-
165
- new_access_token = @access_token
166
- new_expires_in_secs = @expires_at_unix_secs - Time.now.to_i
176
+ # otherwise use the existing expiry time for scheduling the refresh
177
+ new_expires_in_secs = @token_state.expires_at_unix_secs - Time.now.to_i
167
178
  end
168
179
 
169
180
  # Schedule a refresh of the token ahead of the expiry time
@@ -182,24 +193,21 @@ module StatelyDB
182
193
  @scheduled = nil
183
194
  end
184
195
 
185
- new_access_token
196
+ @token_state.token
186
197
  end
187
198
  end
199
+ end
188
200
 
189
- def make_auth0_request
190
- headers = [["content-type", "application/json"]]
191
- body = JSON.dump({ "client_id" => @client_id, client_secret: @client_secret, audience: @audience,
192
- grant_type: DEFAULT_GRANT_TYPE })
193
- Sync do
194
- # TODO: Wrap this in a retry loop and parse errors like we
195
- # do in the Go SDK.
196
- response = @client.post("/oauth/token", headers, body)
197
- raise "Auth request failed" if response.status != 200
198
-
199
- JSON.parse(response.read)
200
- ensure
201
- response&.close
202
- end
201
+ # Persistent state for the token provider
202
+ class TokenState
203
+ attr_reader :token, :expires_at_unix_secs
204
+
205
+ # Create a new TokenState
206
+ # @param [String] token The access token
207
+ # @param [Integer] expires_at_unix_secs The unix timestamp when the token expires
208
+ def initialize(token:, expires_at_unix_secs:)
209
+ @token = token
210
+ @expires_at_unix_secs = expires_at_unix_secs
203
211
  end
204
212
  end
205
213
  end
@@ -11,7 +11,7 @@ module StatelyDB
11
11
  class Interceptor < GRPC::ClientInterceptor
12
12
  # @param [TokenProvider] token_provider The token provider to use for authentication
13
13
  def initialize(
14
- token_provider: Auth0TokenProvider.new
14
+ token_provider: AuthTokenProvider.new
15
15
  )
16
16
  super()
17
17
  @token_provider = token_provider
@@ -0,0 +1,104 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../net/conn"
4
+ require_relative "../error_interceptor"
5
+ require_relative "../../api/auth/service_services_pb"
6
+ require "grpc"
7
+
8
+ module StatelyDB
9
+ module Common
10
+ # A module for Stately Cloud auth code
11
+ module Auth
12
+ # Result from a token fetch operation
13
+ class TokenResult
14
+ attr_reader :token, :expires_in_secs
15
+
16
+ # Create a new TokenResult
17
+ # @param [String] token The access token
18
+ # @param [Integer] expires_in_secs The number of seconds until the token expires
19
+ def initialize(token:, expires_in_secs:)
20
+ @token = token
21
+ @expires_in_secs = expires_in_secs
22
+ end
23
+ end
24
+
25
+ # TokenFetcher is an abstract base class that should be extended
26
+ # for individual token fetcher implementations
27
+ class TokenFetcher
28
+ # Get the current access token
29
+ # @return [TokenResult] The fetched TokenResult
30
+ def fetch
31
+ raise "Not Implemented"
32
+ end
33
+
34
+ # Close the token provider and kill any background operations
35
+ def close
36
+ raise "Not Implemented"
37
+ end
38
+ end
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
+ # StatelyAccessTokenFetcher is a TokenFetcher that fetches tokens from the StatelyDB API
79
+ class StatelyAccessTokenFetcher < TokenFetcher
80
+ # @param [String] origin The origin of the OAuth server
81
+ # @param [String] access_key The StatelyDB access key credential
82
+ def initialize(origin:, access_key:)
83
+ super()
84
+ @access_key = access_key
85
+ @channel = Common::Net.new_channel(endpoint: origin)
86
+ error_interceptor = Common::ErrorInterceptor.new
87
+ @stub = Stately::Auth::AuthService::Stub.new(nil, nil, channel_override: @channel,
88
+ interceptors: [error_interceptor])
89
+ end
90
+
91
+ # Fetch a new token from the StatelyDB API
92
+ # @return [TokenResult] The fetched TokenResult
93
+ 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)
96
+ end
97
+
98
+ def close
99
+ @channel&.close
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
data/lib/error.rb CHANGED
@@ -1,10 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Add the pb dir to the LOAD_PATH because generated proto imports are not relative and
4
- # we don't want the protos polluting the main namespace.
5
- # Tracking here: https://github.com/grpc/grpc/issues/6164
6
- $LOAD_PATH.unshift "#{File.dirname(__FILE__)}/api"
7
-
8
3
  require "api/errors/error_details_pb"
9
4
 
10
5
  module StatelyDB
data/lib/statelydb.rb CHANGED
@@ -1,12 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Add the pb dir to the LOAD_PATH because generated proto imports are not relative and
4
- # we don't want the protos polluting the main namespace.
5
- # Tracking here: https://github.com/grpc/grpc/issues/6164
6
- $LOAD_PATH.unshift "#{File.dirname(__FILE__)}/api"
7
-
8
3
  require "api/db/service_services_pb"
9
- require "common/auth/auth0_token_provider"
4
+ require "common/auth/auth_token_provider"
10
5
  require "common/auth/interceptor"
11
6
  require "common/net/conn"
12
7
  require "common/error_interceptor"
@@ -35,7 +30,7 @@ module StatelyDB
35
30
  # @param region [String] the region to connect to.
36
31
  def initialize(store_id:,
37
32
  schema:,
38
- token_provider: Common::Auth::Auth0TokenProvider.new,
33
+ token_provider: Common::Auth::AuthTokenProvider.new,
39
34
  endpoint: nil,
40
35
  region: nil)
41
36
  if store_id.nil?
@@ -63,6 +58,7 @@ module StatelyDB
63
58
  @allow_stale = false
64
59
  end
65
60
 
61
+ # @return [void] nil
66
62
  def close
67
63
  @channel&.close
68
64
  @token_provider&.close
data/lib/token.rb CHANGED
@@ -34,8 +34,7 @@ module StatelyDB
34
34
  end
35
35
 
36
36
  # Returns the schema version ID associated with the token.
37
- def schema_version_id
38
- !!@schema_version_id
39
- end
37
+ # @return [Integer]
38
+ attr_reader :schema_version_id
40
39
  end
41
40
  end
@@ -254,7 +254,7 @@ module StatelyDB
254
254
  #
255
255
  # @param items [StatelyDB::Item, Array<StatelyDB::Item>] the items to store. Max
256
256
  # 50 items.
257
- # @return [Array<StatelyDB::UUID, String, Integer, nil>] the ids of the items
257
+ # @return [Array<StatelyDB::UUID, String, Integer, NilClass>] the ids of the items
258
258
  #
259
259
  # @example
260
260
  # results = client.data.transaction do |txn|
data/sig/grpc.rbs ADDED
@@ -0,0 +1,80 @@
1
+ module GRPC
2
+ class ActiveCall
3
+ end
4
+
5
+ class Interceptor
6
+ end
7
+ class ClientInterceptor < Interceptor
8
+ # gRPC client unary interceptor
9
+ #
10
+ # _@param_ `request` — The request object
11
+ #
12
+ # _@param_ `call` — The active call object
13
+ #
14
+ # _@param_ `method` — The method being called
15
+ #
16
+ # _@param_ `metadata` — The metadata hash
17
+ #
18
+ # _@return_ — The response object
19
+ def request_response: (
20
+ request: Object,
21
+ call: GRPC::ActiveCall,
22
+ method: Symbol,
23
+ metadata: ::Hash[untyped, untyped]
24
+ ) -> Object
25
+
26
+ # gRPC client streaming interceptor
27
+ #
28
+ # _@param_ `requests` — The list of requests
29
+ #
30
+ # _@param_ `call` — The active call object
31
+ #
32
+ # _@param_ `method` — The method being called
33
+ #
34
+ # _@param_ `metadata` — The metadata hash
35
+ #
36
+ # _@return_ — The response enumerator
37
+ def client_streamer: (
38
+ requests: ::Enumerable[untyped],
39
+ call: GRPC::ActiveCall,
40
+ method: Symbol,
41
+ metadata: ::Hash[untyped, untyped]
42
+ ) -> ::Enumerator[untyped]
43
+
44
+ # gRPC server streaming interceptor
45
+ #
46
+ # _@param_ `request` — The request object
47
+ #
48
+ # _@param_ `call` — The active call object
49
+ #
50
+ # _@param_ `method` — The method being called
51
+ #
52
+ # _@param_ `metadata` — The metadata hash
53
+ #
54
+ # _@return_ — The response enumerator
55
+ def server_streamer: (
56
+ request: Object,
57
+ call: GRPC::ActiveCall,
58
+ method: Symbol,
59
+ metadata: ::Hash[untyped, untyped]
60
+ ) -> ::Enumerator[untyped]
61
+
62
+ # gRPC bidirectional streaming interceptor
63
+ #
64
+ # _@param_ `requests` — The list of requests
65
+ #
66
+ # _@param_ `call` — The active call object
67
+ #
68
+ # _@param_ `method` — The method being called
69
+ #
70
+ # _@param_ `metadata` — The metadata hash
71
+ #
72
+ # _@return_ — The response enumerator
73
+ def bidi_streamer: (
74
+ requests: ::Enumerable[untyped],
75
+ call: GRPC::ActiveCall,
76
+ method: Symbol,
77
+ metadata: ::Hash[untyped, untyped]
78
+ ) -> ::Enumerator[untyped]
79
+ end
80
+ end