descope 1.1.0 → 1.2.0

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 (76) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yaml +4 -4
  3. data/.github/workflows/publish-gem.yaml +2 -2
  4. data/.github/workflows/release-please.yaml +1 -1
  5. data/.release-please-manifest.json +1 -1
  6. data/.rubocop.yml +1 -1
  7. data/.rubocop_todo.yml +1 -1
  8. data/.ruby-version +1 -1
  9. data/CHANGELOG.md +10 -0
  10. data/Gemfile +5 -5
  11. data/Gemfile.lock +32 -32
  12. data/README.md +233 -1
  13. data/lib/descope/api/v1/auth/enchantedlink.rb +76 -9
  14. data/lib/descope/api/v1/management/access_key.rb +8 -7
  15. data/lib/descope/api/v1/management/analytics.rb +62 -0
  16. data/lib/descope/api/v1/management/audit.rb +2 -2
  17. data/lib/descope/api/v1/management/authz.rb +15 -15
  18. data/lib/descope/api/v1/management/common.rb +76 -0
  19. data/lib/descope/api/v1/management/descoper.rb +45 -0
  20. data/lib/descope/api/v1/management/engine.rb +44 -0
  21. data/lib/descope/api/v1/management/fga.rb +64 -0
  22. data/lib/descope/api/v1/management/flow.rb +5 -5
  23. data/lib/descope/api/v1/management/group.rb +42 -0
  24. data/lib/descope/api/v1/management/jwt_template.rb +49 -0
  25. data/lib/descope/api/v1/management/lists.rb +96 -0
  26. data/lib/descope/api/v1/management/management_key.rb +54 -0
  27. data/lib/descope/api/v1/management/outbound_app.rb +112 -0
  28. data/lib/descope/api/v1/management/password.rb +2 -2
  29. data/lib/descope/api/v1/management/permission.rb +4 -4
  30. data/lib/descope/api/v1/management/project.rb +5 -5
  31. data/lib/descope/api/v1/management/role.rb +5 -5
  32. data/lib/descope/api/v1/management/scim.rb +14 -14
  33. data/lib/descope/api/v1/management/scope_claim_mapping.rb +34 -0
  34. data/lib/descope/api/v1/management/sso_application.rb +7 -7
  35. data/lib/descope/api/v1/management/sso_settings.rb +5 -5
  36. data/lib/descope/api/v1/management/tenant.rb +8 -6
  37. data/lib/descope/api/v1/management/third_party_application.rb +190 -0
  38. data/lib/descope/api/v1/management/user.rb +36 -36
  39. data/lib/descope/api/v1/management.rb +22 -0
  40. data/lib/descope/http_client.rb +30 -0
  41. data/lib/descope/mixins/common.rb +1 -0
  42. data/lib/descope/mixins/http.rb +23 -16
  43. data/lib/descope/mixins/initializer.rb +19 -9
  44. data/lib/descope/mixins/requests.rb +24 -0
  45. data/lib/descope/mixins.rb +2 -1
  46. data/lib/descope/version.rb +1 -1
  47. data/spec/integration/lib.descope/api/v1/management/user_spec.rb +1 -1
  48. data/spec/lib.descope/api/v1/auth/enchantedlink_spec.rb +163 -1
  49. data/spec/lib.descope/api/v1/management/access_key_spec.rb +7 -7
  50. data/spec/lib.descope/api/v1/management/analytics_spec.rb +82 -0
  51. data/spec/lib.descope/api/v1/management/audit_spec.rb +2 -2
  52. data/spec/lib.descope/api/v1/management/authz_spec.rb +16 -16
  53. data/spec/lib.descope/api/v1/management/descoper_spec.rb +101 -0
  54. data/spec/lib.descope/api/v1/management/engine_spec.rb +115 -0
  55. data/spec/lib.descope/api/v1/management/fga_spec.rb +169 -0
  56. data/spec/lib.descope/api/v1/management/flow_spec.rb +5 -5
  57. data/spec/lib.descope/api/v1/management/group_spec.rb +72 -0
  58. data/spec/lib.descope/api/v1/management/jwt_template_spec.rb +103 -0
  59. data/spec/lib.descope/api/v1/management/lists_spec.rb +285 -0
  60. data/spec/lib.descope/api/v1/management/management_key_spec.rb +109 -0
  61. data/spec/lib.descope/api/v1/management/outbound_app_spec.rb +236 -0
  62. data/spec/lib.descope/api/v1/management/password_spec.rb +2 -2
  63. data/spec/lib.descope/api/v1/management/permission_spec.rb +4 -4
  64. data/spec/lib.descope/api/v1/management/project_spec.rb +4 -4
  65. data/spec/lib.descope/api/v1/management/role_spec.rb +5 -5
  66. data/spec/lib.descope/api/v1/management/scim_spec.rb +14 -14
  67. data/spec/lib.descope/api/v1/management/scope_claim_mapping_spec.rb +59 -0
  68. data/spec/lib.descope/api/v1/management/sso_application_spec.rb +6 -6
  69. data/spec/lib.descope/api/v1/management/sso_settings_spec.rb +5 -5
  70. data/spec/lib.descope/api/v1/management/tenant_spec.rb +6 -6
  71. data/spec/lib.descope/api/v1/management/third_party_application_spec.rb +171 -0
  72. data/spec/lib.descope/api/v1/management/user_spec.rb +36 -36
  73. data/spec/lib.descope/auth_management_key_spec.rb +151 -0
  74. data/spec/support/client_config.rb +1 -0
  75. data/spec/support/dummy_class.rb +12 -9
  76. metadata +28 -3
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'descope/mixins/http'
4
+
5
+ module Descope
6
+ # Performs the SDK's HTTP calls. One instance is created per kind of request so that the two
7
+ # management keys are never sent together: the management client carries the management key, the
8
+ # authentication client carries the auth management key.
9
+ class HttpClient
10
+ include Descope::Mixins::HTTP
11
+
12
+ def initialize(base_uri:, project_id:, headers:, logger:, key: nil, timeout: nil, retry_count: nil)
13
+ @base_uri = base_uri
14
+ @project_id = project_id
15
+ @key = key
16
+ @headers = headers.dup
17
+ @logger = logger
18
+ @timeout = timeout
19
+ @retry_count = retry_count
20
+ end
21
+
22
+ # Bearer <project_id>[:<pswd>][:<key>] where pswd is a refresh token or access key presented by
23
+ # the caller and key is this client's management key. Blank parts are skipped, so callers do
24
+ # not have to branch on what happens to be configured.
25
+ def authorization_header(pswd = nil)
26
+ bearer = [@project_id, pswd, @key].reject { |part| part.nil? || part.to_s.empty? }.join(':')
27
+ { 'Authorization' => "Bearer #{bearer}" }
28
+ end
29
+ end
30
+ end
@@ -81,6 +81,7 @@ module Descope
81
81
  VERIFY_ENCHANTEDLINK_AUTH_PATH = '/v1/auth/enchantedlink/verify'
82
82
  GET_SESSION_ENCHANTEDLINK_AUTH_PATH = '/v1/auth/enchantedlink/pending-session'
83
83
  UPDATE_USER_EMAIL_ENCHANTEDLINK_PATH = '/v1/auth/enchantedlink/update/email'
84
+ UPDATE_USER_PHONE_ENCHANTEDLINK_PATH = '/v1/auth/enchantedlink/update/phone'
84
85
 
85
86
  # oauth
86
87
  OAUTH_START_PATH = '/v1/auth/oauth/authorize'
@@ -18,13 +18,18 @@ module Descope
18
18
  MIN_REQUEST_RETRY_DELAY = 250
19
19
  BASE_DELAY = 100
20
20
 
21
- %i[get post post_file post_form put patch delete delete_with_body].each do |method|
21
+ HTTP_METHODS = %i[get post put patch delete].freeze
22
+
23
+ HTTP_METHODS.each do |method|
22
24
  define_method(method) do |uri, body = {}, extra_headers = {}, pswd = nil|
23
25
  body = body.delete_if { |_, v| v.nil? }
24
- authorization_header(pswd) # This will set the pswd if provided, else default to the @default_pswd
26
+
27
+ # The Authorization header travels with the request rather than being merged into the
28
+ # shared @headers, so concurrent requests cannot send each other's credentials.
29
+ headers = authorization_header(pswd).merge(extra_headers || {})
25
30
 
26
31
  @logger.debug "request => method: #{method}, uri: #{uri}, body: #{body}, extra_headers: #{extra_headers}}"
27
- request_with_retry(method, uri, body, extra_headers)
32
+ request_with_retry(method, uri, body, headers)
28
33
  end
29
34
  end
30
35
 
@@ -118,6 +123,15 @@ module Descope
118
123
  @headers.merge!(h.to_hash)
119
124
  end
120
125
 
126
+ # Everything after the project ID in the bearer is either a token or a management key, so
127
+ # none of it belongs in a log line.
128
+ def mask_authorization(headers)
129
+ authorization = headers['Authorization']
130
+ return headers if authorization.nil?
131
+
132
+ headers.merge('Authorization' => authorization.sub(/\A(Bearer [^:]*):.*\z/, '\1:***'))
133
+ end
134
+
121
135
  def request_with_retry(method, uri, body = {}, extra_headers = {}, pswd = nil)
122
136
  Retryable.retryable(retry_options) do
123
137
  request(method, uri, body, extra_headers)
@@ -125,23 +139,16 @@ module Descope
125
139
  end
126
140
 
127
141
  def request(method, uri, body = {}, extra_headers = {})
128
- # @headers is getting the authorization header merged in initializer.rb
129
- headers_debug = @headers.dup
130
- if headers_debug['Authorization']
131
- headers_debug['Authorization'] = headers_debug['Authorization'].gsub(/(.{10})\z/, '***********')
132
- end
142
+ request_headers = @headers.merge(extra_headers)
133
143
 
134
144
  @logger.debug "base url: #{@base_uri}"
135
- @logger.debug "request method: #{method}, uri: #{uri}, body: #{body}, extra_headers: #{extra_headers}, headers: #{headers_debug}"
145
+ @logger.debug "request method: #{method}, uri: #{uri}, body: #{body}, " \
146
+ "headers: #{mask_authorization(request_headers)}"
136
147
  result = case method
137
- when :get
138
- get_headers = @headers.merge({ params: body }).merge(extra_headers)
139
- call(:get, encode_uri(uri), timeout, get_headers)
140
- when :delete
141
- delete_headers = @headers.merge({ params: body })
142
- call(:delete, encode_uri(uri), timeout, delete_headers)
148
+ when :get, :delete
149
+ call(method, encode_uri(uri), timeout, request_headers.merge({ params: body }))
143
150
  else
144
- call(method, encode_uri(uri), timeout, @headers, body.to_json)
151
+ call(method, encode_uri(uri), timeout, request_headers, body.to_json)
145
152
  end
146
153
 
147
154
  raise Descope::Unsupported.new('No response from server', code: 400) unless result.respond_to?(:code)
@@ -1,12 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'json'
4
+ require 'descope/http_client'
4
5
 
5
6
  module Descope
6
7
  module Mixins
7
8
  # Helper class for initializing the Descope API
8
9
  module Initializer
9
10
  attr_accessor :public_keys, :mlock
11
+ attr_reader :base_uri, :headers
10
12
 
11
13
  def initialize(config)
12
14
  options = Hash[config.map { |(k, v)| [k.to_sym, v] }]
@@ -33,7 +35,10 @@ module Descope
33
35
  @skip_verify = options[:skip_verify]
34
36
  @secure = !@skip_verify
35
37
  @management_key = options[:management_key] || ENV['DESCOPE_MANAGEMENT_KEY']
36
- @logger.debug("Management Key ID: #{@management_key}")
38
+ # Sent with every authentication request so that methods whose public access has been
39
+ # disabled can still be used. Never sent on management requests, and can - and probably
40
+ # should - be a different management key than @management_key.
41
+ @auth_management_key = options[:auth_management_key] || ENV['DESCOPE_AUTH_MANAGEMENT_KEY']
37
42
  @timeout_seconds = options[:timeout_seconds] || Common::DEFAULT_TIMEOUT_SECONDS
38
43
  @jwt_validation_leeway = options[:jwt_validation_leeway] || Common::DEFAULT_JWT_VALIDATION_LEEWAY
39
44
 
@@ -60,16 +65,21 @@ module Descope
60
65
 
61
66
  end
62
67
 
63
- def authorization_header(pswd = nil)
64
- pswd = @default_pswd if pswd.nil? || pswd.empty?
65
- bearer = "#{@project_id}:#{pswd}"
66
- add_headers('Authorization' => "Bearer #{bearer}")
67
- end
68
-
69
68
  def initialize_api(options)
70
69
  initialize_v1(options)
71
- @default_pswd = options.fetch(:management_key, ENV['DESCOPE_MANAGEMENT_KEY'])
72
- authorization_header
70
+ @auth_http = http_client(@auth_management_key)
71
+ @mgmt_http = http_client(@management_key)
72
+ end
73
+
74
+ def http_client(key)
75
+ HttpClient.new(
76
+ base_uri: @base_uri,
77
+ project_id: @project_id,
78
+ key: key,
79
+ headers: @headers,
80
+ logger: @logger,
81
+ timeout: @timeout_seconds
82
+ )
73
83
  end
74
84
 
75
85
  def initialize_v1(_options)
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'descope/mixins/http'
4
+
5
+ module Descope
6
+ module Mixins
7
+ # Routes an API call to the HTTP client that holds the right key: the bare verbs serve the
8
+ # authentication APIs and go out with the auth management key, the mgmt_ prefixed verbs serve
9
+ # the management APIs and go out with the management key.
10
+ module Requests
11
+ attr_reader :auth_http, :mgmt_http
12
+
13
+ Descope::Mixins::HTTP::HTTP_METHODS.each do |method|
14
+ define_method(method) do |*args|
15
+ @auth_http.public_send(method, *args)
16
+ end
17
+
18
+ define_method(:"mgmt_#{method}") do |*args|
19
+ @mgmt_http.public_send(method, *args)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -4,6 +4,7 @@ require 'logger'
4
4
  require 'jwt'
5
5
  require 'descope/mixins/headers'
6
6
  require 'descope/mixins/http'
7
+ require 'descope/mixins/requests'
7
8
  require 'descope/mixins/initializer'
8
9
  require 'descope/mixins/validation'
9
10
  require 'descope/mixins/logging'
@@ -15,7 +16,7 @@ module Descope
15
16
  module Mixins
16
17
  include Descope::Mixins::Common
17
18
  include Descope::Mixins::Headers
18
- include Descope::Mixins::HTTP
19
+ include Descope::Mixins::Requests
19
20
  include Descope::Mixins::Initializer
20
21
  include Descope::Mixins::Logging
21
22
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  # Current version of gem
4
4
  module Descope
5
- VERSION = '1.1.0'
5
+ VERSION = '1.2.0'
6
6
  SDK_VERSION = '1.0.0'
7
7
  end
@@ -242,7 +242,7 @@ describe Descope::Api::V1::Management::User do
242
242
  begin
243
243
  @client.expire_password(user['loginIds'][0])
244
244
  @client.password_sign_in(login_id: user['loginIds'][0], password:)
245
- rescue Descope::ServerError => e
245
+ rescue Descope::Unauthorized => e
246
246
  expect(e.message).to match(/"errorCode":"E062909"/)
247
247
  end
248
248
  end
@@ -30,7 +30,7 @@ describe Descope::Api::V1::EnchantedLink do
30
30
  expect(@instance).to receive(:post).with(
31
31
  enchanted_link_compose_signin_url,
32
32
  request_params,
33
- nil,
33
+ {},
34
34
  'refresh_token'
35
35
  )
36
36
 
@@ -60,6 +60,48 @@ describe Descope::Api::V1::EnchantedLink do
60
60
  @instance.send(:validate_refresh_token_provided, { mfa: true, stepup: true }, '')
61
61
  end.to raise_error(Descope::AuthException, 'Missing refresh token for stepup/mfa')
62
62
  end
63
+
64
+ it 'is expected to respond to sign in with phone' do
65
+ expect(@instance).to respond_to(:enchanted_link_sign_in_with_phone)
66
+ end
67
+
68
+ it 'is expected to sign in with enchanted link via sms' do
69
+ request_params = {
70
+ loginId: '+1234567890',
71
+ redirectUrl: 'https://some-uri/sms',
72
+ loginOptions: {
73
+ stepup: false,
74
+ customClaims: { 'abc': '123' },
75
+ mfa: false,
76
+ ssoAppId: 'sso-id'
77
+ },
78
+ providerId: 'provider-id',
79
+ templateId: 'template-id'
80
+ }
81
+
82
+ expect(@instance).to receive(:post).with(
83
+ '/v1/auth/enchantedlink/signin/sms',
84
+ request_params,
85
+ {},
86
+ 'refresh_token'
87
+ ).and_return({ 'linkId' => '1', 'pendingRef' => 'ref', 'maskedPhone' => '+1******890' })
88
+
89
+ res = @instance.enchanted_link_sign_in_with_phone(
90
+ login_id: '+1234567890',
91
+ uri: 'https://some-uri/sms',
92
+ login_options: {
93
+ stepup: false,
94
+ custom_claims: { 'abc': '123' },
95
+ mfa: false,
96
+ sso_app_id: 'sso-id'
97
+ },
98
+ provider_id: 'provider-id',
99
+ template_id: 'template-id',
100
+ refresh_token: 'refresh_token'
101
+ )
102
+
103
+ expect(res['maskedPhone']).to eq('+1******890')
104
+ end
63
105
  end
64
106
 
65
107
  context '.sign_up' do
@@ -88,6 +130,36 @@ describe Descope::Api::V1::EnchantedLink do
88
130
  )
89
131
  end.not_to raise_error
90
132
  end
133
+
134
+ it 'is expected to respond to sign up with phone' do
135
+ expect(@instance).to respond_to(:enchanted_link_sign_up_with_phone)
136
+ end
137
+
138
+ it 'is expected to sign up with enchanted link via sms' do
139
+ request_params = {
140
+ loginId: '+1234567890',
141
+ redirectUrl: 'https://some-uri/sms',
142
+ user: { loginId: 'user1', phone: '+1234567890' },
143
+ phone: '+1234567890',
144
+ providerId: 'provider-id',
145
+ templateId: 'template-id'
146
+ }
147
+
148
+ expect(@instance).to receive(:post).with(
149
+ '/v1/auth/enchantedlink/signup/sms',
150
+ request_params
151
+ ).and_return({ 'linkId' => '1', 'pendingRef' => 'ref', 'maskedPhone' => '+1******890' })
152
+
153
+ res = @instance.enchanted_link_sign_up_with_phone(
154
+ login_id: '+1234567890',
155
+ uri: 'https://some-uri/sms',
156
+ user: { login_id: 'user1', phone: '+1234567890' },
157
+ provider_id: 'provider-id',
158
+ template_id: 'template-id'
159
+ )
160
+
161
+ expect(res['maskedPhone']).to eq('+1******890')
162
+ end
91
163
  end
92
164
 
93
165
  context '.sign_up_or_in' do
@@ -125,6 +197,96 @@ describe Descope::Api::V1::EnchantedLink do
125
197
  )
126
198
  end.not_to raise_error
127
199
  end
200
+
201
+ it 'is expected to respond to sign up or in with phone' do
202
+ expect(@instance).to respond_to(:enchanted_link_sign_up_or_in_with_phone)
203
+ end
204
+
205
+ it 'is expected to sign up or in with enchanted link via sms' do
206
+ request_params = {
207
+ loginId: '+1234567890',
208
+ redirectUrl: 'https://some-uri/sms',
209
+ loginOptions: {
210
+ stepup: false,
211
+ customClaims: { 'abc': '123' },
212
+ mfa: false,
213
+ ssoAppId: 'sso-id'
214
+ },
215
+ providerId: 'provider-id',
216
+ templateId: 'template-id'
217
+ }
218
+
219
+ expect(@instance).to receive(:post).with(
220
+ '/v1/auth/enchantedlink/signup-in/sms',
221
+ request_params
222
+ ).and_return({ 'linkId' => '1', 'pendingRef' => 'ref', 'maskedPhone' => '+1******890' })
223
+
224
+ res = @instance.enchanted_link_sign_up_or_in_with_phone(
225
+ login_id: '+1234567890',
226
+ uri: 'https://some-uri/sms',
227
+ login_options: {
228
+ stepup: false,
229
+ custom_claims: { 'abc': '123' },
230
+ mfa: false,
231
+ sso_app_id: 'sso-id'
232
+ },
233
+ provider_id: 'provider-id',
234
+ template_id: 'template-id'
235
+ )
236
+
237
+ expect(res['maskedPhone']).to eq('+1******890')
238
+ end
239
+ end
240
+
241
+ context '.update_user_phone' do
242
+ it 'is expected to respond to update user phone' do
243
+ expect(@instance).to respond_to(:enchanted_link_update_user_phone)
244
+ end
245
+
246
+ it 'is expected to update user phone with enchanted link via sms' do
247
+ request_params = {
248
+ loginId: 'test',
249
+ phone: '+1234567890',
250
+ addToLoginIDs: true,
251
+ onMergeUseExisting: true,
252
+ redirectUrl: 'https://some-uri/sms',
253
+ providerId: 'provider-id',
254
+ templateId: 'template-id',
255
+ templateOptions: { 'abc': '123' }
256
+ }
257
+
258
+ expect(@instance).to receive(:post).with(
259
+ '/v1/auth/enchantedlink/update/phone/sms',
260
+ request_params,
261
+ {},
262
+ 'refresh_token'
263
+ ).and_return({ 'linkId' => '1', 'pendingRef' => 'ref', 'maskedPhone' => '+1******890' })
264
+
265
+ res = @instance.enchanted_link_update_user_phone(
266
+ login_id: 'test',
267
+ phone: '+1234567890',
268
+ uri: 'https://some-uri/sms',
269
+ add_to_login_ids: true,
270
+ on_merge_use_existing: true,
271
+ provider_id: 'provider-id',
272
+ template_id: 'template-id',
273
+ template_options: { 'abc': '123' },
274
+ refresh_token: 'refresh_token'
275
+ )
276
+
277
+ expect(res['maskedPhone']).to eq('+1******890')
278
+ end
279
+
280
+ it 'is expected to raise an error on an invalid phone number' do
281
+ expect do
282
+ @instance.enchanted_link_update_user_phone(
283
+ login_id: 'test',
284
+ phone: 'not-a-phone',
285
+ uri: 'https://some-uri/sms',
286
+ refresh_token: 'refresh_token'
287
+ )
288
+ end.to raise_error(Descope::AuthException)
289
+ end
128
290
  end
129
291
 
130
292
  context '.enchanted_link_verify_token' do
@@ -15,7 +15,7 @@ describe Descope::Api::V1::Management::AccessKey do
15
15
  end
16
16
 
17
17
  it 'is expected to create access key' do
18
- expect(@instance).to receive(:post).with(
18
+ expect(@instance).to receive(:mgmt_post).with(
19
19
  ACCESS_KEY_CREATE_PATH, {
20
20
  name: 'test',
21
21
  expireTime: 0,
@@ -46,7 +46,7 @@ describe Descope::Api::V1::Management::AccessKey do
46
46
  end
47
47
 
48
48
  it 'is expected to load an access key' do
49
- expect(@instance).to receive(:get).with(
49
+ expect(@instance).to receive(:mgmt_get).with(
50
50
  ACCESS_KEY_LOAD_PATH, { id: '123' }
51
51
  )
52
52
  expect { @instance.load_access_key('123') }.not_to raise_error
@@ -59,7 +59,7 @@ describe Descope::Api::V1::Management::AccessKey do
59
59
  end
60
60
 
61
61
  it 'is expected to search all access keys' do
62
- expect(@instance).to receive(:post).with(
62
+ expect(@instance).to receive(:mgmt_post).with(
63
63
  ACCESS_KEYS_SEARCH_PATH, { tenantIds: %w[123 456] }
64
64
  )
65
65
  expect { @instance.search_all_access_keys(%w[123 456]) }.not_to raise_error
@@ -72,7 +72,7 @@ describe Descope::Api::V1::Management::AccessKey do
72
72
  end
73
73
 
74
74
  it 'is expected to update an access keys' do
75
- expect(@instance).to receive(:post).with(
75
+ expect(@instance).to receive(:mgmt_post).with(
76
76
  ACCESS_KEY_UPDATE_PATH, { id: '123', name: 'test1' }
77
77
  )
78
78
  expect { @instance.update_access_key(id: '123', name: 'test1') }.not_to raise_error
@@ -85,7 +85,7 @@ describe Descope::Api::V1::Management::AccessKey do
85
85
  end
86
86
 
87
87
  it 'is expected to deactivate an access keys' do
88
- expect(@instance).to receive(:post).with(
88
+ expect(@instance).to receive(:mgmt_post).with(
89
89
  ACCESS_KEY_DEACTIVATE_PATH, { id: '123' }
90
90
  )
91
91
  expect { @instance.deactivate_access_key('123') }.not_to raise_error
@@ -98,7 +98,7 @@ describe Descope::Api::V1::Management::AccessKey do
98
98
  end
99
99
 
100
100
  it 'is expected to activate an access keys' do
101
- expect(@instance).to receive(:post).with(
101
+ expect(@instance).to receive(:mgmt_post).with(
102
102
  ACCESS_KEY_ACTIVATE_PATH, { id: '123' }
103
103
  )
104
104
  expect { @instance.activate_access_key('123') }.not_to raise_error
@@ -111,7 +111,7 @@ describe Descope::Api::V1::Management::AccessKey do
111
111
  end
112
112
 
113
113
  it 'is expected to delete an access keys' do
114
- expect(@instance).to receive(:post).with(
114
+ expect(@instance).to receive(:mgmt_post).with(
115
115
  ACCESS_KEY_DELETE_PATH, { id: '123' }
116
116
  )
117
117
  expect { @instance.delete_access_key('123') }.not_to raise_error
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Descope::Api::V1::Management::Analytics do
6
+ before(:all) do
7
+ dummy_instance = DummyClass.new
8
+ dummy_instance.extend(Descope::Api::V1::Management::Analytics)
9
+ @instance = dummy_instance
10
+ end
11
+
12
+ context '.search' do
13
+ let(:mock_response) do
14
+ {
15
+ 'analytics' => [
16
+ {
17
+ 'projectId' => 'abc',
18
+ 'action' => 'get',
19
+ 'created' => '1234567000',
20
+ 'device' => 'mobile',
21
+ 'method' => 'post',
22
+ 'geo' => 'US',
23
+ 'tenant' => 'tenant1',
24
+ 'referrer' => 'referrer1',
25
+ 'cnt' => '5'
26
+ }
27
+ ]
28
+ }
29
+ end
30
+
31
+ before do
32
+ allow(@instance).to receive(:mgmt_post).with(
33
+ ANALYTICS_SEARCH_PATH,
34
+ {
35
+ from: 1_234_567_000,
36
+ to: 123_456_789_000,
37
+ groupByAction: true,
38
+ groupByDevice: true,
39
+ groupByMethod: true,
40
+ groupByGeo: true,
41
+ groupByTenant: true,
42
+ groupByReferrer: true,
43
+ groupByCreated: true,
44
+ actions: %w[action1 action2],
45
+ excludedActions: %w[exclude1 exclude2],
46
+ devices: %w[Bot Mobile Desktop Tablet Unknown],
47
+ methods: %w[otp totp magiclink oauth saml password],
48
+ geos: %w[US IL],
49
+ tenants: %w[tenant1 tenant2]
50
+ }
51
+ ).and_return(mock_response)
52
+ end
53
+
54
+ it 'should respond to .analytics_search' do
55
+ expect(@instance).to respond_to :analytics_search
56
+ end
57
+
58
+ it 'is expected to search analytics and get records' do
59
+ res = nil # define res outside the block
60
+ expect do
61
+ res = @instance.analytics_search(
62
+ from_ts: 1_234_567_000,
63
+ to_ts: 123_456_789_000,
64
+ group_by_action: true,
65
+ group_by_device: true,
66
+ group_by_method: true,
67
+ group_by_geo: true,
68
+ group_by_tenant: true,
69
+ group_by_referrer: true,
70
+ group_by_created: true,
71
+ actions: %w[action1 action2],
72
+ excluded_actions: %w[exclude1 exclude2],
73
+ devices: %w[Bot Mobile Desktop Tablet Unknown],
74
+ methods: %w[otp totp magiclink oauth saml password],
75
+ geos: %w[US IL],
76
+ tenants: %w[tenant1 tenant2]
77
+ )
78
+ end.not_to raise_error
79
+ expect(res['analytics'][0]['projectId']).to eq('abc')
80
+ end
81
+ end
82
+ end
@@ -31,7 +31,7 @@ describe Descope::Api::V1::Management::Audit do
31
31
  end
32
32
 
33
33
  before do
34
- allow(@instance).to receive(:post).twice.with(
34
+ allow(@instance).to receive(:mgmt_post).twice.with(
35
35
  AUDIT_SEARCH,
36
36
  {
37
37
  noTenants: true,
@@ -144,7 +144,7 @@ describe Descope::Api::V1::Management::Audit do
144
144
  end
145
145
 
146
146
  it 'is expected to create an audit event' do
147
- expect(@instance).to receive(:post).with(
147
+ expect(@instance).to receive(:mgmt_post).with(
148
148
  '/v1/mgmt/audit/event',
149
149
  {
150
150
  action: 'get',