workosv2 2.15.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.
- checksums.yaml +7 -0
- data/.github/CODEOWNERS +5 -0
- data/.github/pull_request_template.md +11 -0
- data/.github/renovate.json +5 -0
- data/.gitignore +49 -0
- data/.rspec +1 -0
- data/.rubocop.yml +24 -0
- data/.ruby-version +1 -0
- data/.semaphore/rubygems.yml +24 -0
- data/.semaphore/semaphore.yml +51 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +126 -0
- data/Gemfile.lock.old +127 -0
- data/LICENSE +21 -0
- data/README.md +53 -0
- data/bin/build +3 -0
- data/bin/console +3 -0
- data/bin/docs +5 -0
- data/bin/publish +3 -0
- data/bin/tapioca +29 -0
- data/codecov.yml +12 -0
- data/docs/WorkOS/APIError.html +160 -0
- data/docs/WorkOS/AuditLog.html +235 -0
- data/docs/WorkOS/AuditTrail.html +235 -0
- data/docs/WorkOS/AuthenticationError.html +160 -0
- data/docs/WorkOS/Base.html +287 -0
- data/docs/WorkOS/Client.html +504 -0
- data/docs/WorkOS/InvalidRequestError.html +160 -0
- data/docs/WorkOS/Profile.html +788 -0
- data/docs/WorkOS/RequestError.html +135 -0
- data/docs/WorkOS/SSO.html +691 -0
- data/docs/WorkOS/Types/ProfileStruct.html +135 -0
- data/docs/WorkOS/Types/Provider.html +135 -0
- data/docs/WorkOS/Types.html +128 -0
- data/docs/WorkOS/WorkOSError.html +447 -0
- data/docs/WorkOS.html +324 -0
- data/docs/class_list.html +51 -0
- data/docs/css/common.css +1 -0
- data/docs/css/full_list.css +58 -0
- data/docs/css/style.css +496 -0
- data/docs/file.README.html +252 -0
- data/docs/file_list.html +56 -0
- data/docs/frames.html +17 -0
- data/docs/index.html +250 -0
- data/docs/js/app.js +314 -0
- data/docs/js/full_list.js +216 -0
- data/docs/js/jquery.js +4 -0
- data/docs/method_list.html +267 -0
- data/docs/top-level-namespace.html +110 -0
- data/lib/workosv2/audit_log_export.rb +55 -0
- data/lib/workosv2/audit_logs.rb +114 -0
- data/lib/workosv2/audit_trail.rb +111 -0
- data/lib/workosv2/challenge.rb +55 -0
- data/lib/workosv2/client.rb +186 -0
- data/lib/workosv2/configuration.rb +17 -0
- data/lib/workosv2/connection.rb +66 -0
- data/lib/workosv2/deprecated_hash_wrapper.rb +76 -0
- data/lib/workosv2/directory.rb +65 -0
- data/lib/workosv2/directory_group.rb +68 -0
- data/lib/workosv2/directory_sync.rb +218 -0
- data/lib/workosv2/directory_user.rb +97 -0
- data/lib/workosv2/errors.rb +81 -0
- data/lib/workosv2/event.rb +51 -0
- data/lib/workosv2/events.rb +52 -0
- data/lib/workosv2/factor.rb +54 -0
- data/lib/workosv2/hash_provider.rb +19 -0
- data/lib/workosv2/mfa.rb +178 -0
- data/lib/workosv2/organization.rb +57 -0
- data/lib/workosv2/organizations.rb +188 -0
- data/lib/workosv2/passwordless.rb +85 -0
- data/lib/workosv2/portal.rb +66 -0
- data/lib/workosv2/profile.rb +76 -0
- data/lib/workosv2/profile_and_token.rb +29 -0
- data/lib/workosv2/sso.rb +297 -0
- data/lib/workosv2/types/audit_log_export_struct.rb +17 -0
- data/lib/workosv2/types/challenge_struct.rb +18 -0
- data/lib/workosv2/types/connection_struct.rb +20 -0
- data/lib/workosv2/types/directory_group_struct.rb +19 -0
- data/lib/workosv2/types/directory_struct.rb +19 -0
- data/lib/workosv2/types/directory_user_struct.rb +26 -0
- data/lib/workosv2/types/event_struct.rb +15 -0
- data/lib/workosv2/types/factor_struct.rb +18 -0
- data/lib/workosv2/types/intent_enum.rb +17 -0
- data/lib/workosv2/types/list_struct.rb +13 -0
- data/lib/workosv2/types/organization_struct.rb +17 -0
- data/lib/workosv2/types/passwordless_session_struct.rb +17 -0
- data/lib/workosv2/types/profile_struct.rb +21 -0
- data/lib/workosv2/types/provider_enum.rb +15 -0
- data/lib/workosv2/types/verify_challenge_struct.rb +13 -0
- data/lib/workosv2/types/webhook_struct.rb +15 -0
- data/lib/workosv2/types.rb +25 -0
- data/lib/workosv2/verify_challenge.rb +39 -0
- data/lib/workosv2/version.rb +6 -0
- data/lib/workosv2/webhook.rb +51 -0
- data/lib/workosv2/webhooks.rb +217 -0
- data/lib/workosv2.rb +79 -0
- data/sorbet/config +2 -0
- data/sorbet/rbi/gems/addressable@2.8.0.rbi +290 -0
- data/sorbet/rbi/gems/ast@2.4.2.rbi +54 -0
- data/sorbet/rbi/gems/codecov@0.2.12.rbi +55 -0
- data/sorbet/rbi/gems/coderay@1.1.3.rbi +8 -0
- data/sorbet/rbi/gems/crack@0.4.5.rbi +57 -0
- data/sorbet/rbi/gems/diff-lcs@1.4.4.rbi +185 -0
- data/sorbet/rbi/gems/docile@1.3.5.rbi +54 -0
- data/sorbet/rbi/gems/hashdiff@1.0.1.rbi +82 -0
- data/sorbet/rbi/gems/json@2.5.1.rbi +109 -0
- data/sorbet/rbi/gems/method_source@1.0.0.rbi +8 -0
- data/sorbet/rbi/gems/parallel@1.20.1.rbi +113 -0
- data/sorbet/rbi/gems/parser@3.0.1.0.rbi +1187 -0
- data/sorbet/rbi/gems/pry@0.14.2.rbi +8 -0
- data/sorbet/rbi/gems/public_suffix@4.0.6.rbi +146 -0
- data/sorbet/rbi/gems/rainbow@3.0.0.rbi +153 -0
- data/sorbet/rbi/gems/rake@13.0.3.rbi +807 -0
- data/sorbet/rbi/gems/rbi@0.0.16.rbi +2118 -0
- data/sorbet/rbi/gems/regexp_parser@2.1.1.rbi +1117 -0
- data/sorbet/rbi/gems/rexml@3.2.5.rbi +709 -0
- data/sorbet/rbi/gems/rspec-core@3.9.3.rbi +2467 -0
- data/sorbet/rbi/gems/rspec-expectations@3.9.4.rbi +1569 -0
- data/sorbet/rbi/gems/rspec-mocks@3.9.1.rbi +1493 -0
- data/sorbet/rbi/gems/rspec-support@3.9.4.rbi +511 -0
- data/sorbet/rbi/gems/rspec@3.9.0.rbi +38 -0
- data/sorbet/rbi/gems/rubocop-ast@1.4.1.rbi +1881 -0
- data/sorbet/rbi/gems/rubocop@0.93.1.rbi +11497 -0
- data/sorbet/rbi/gems/ruby-progressbar@1.11.0.rbi +405 -0
- data/sorbet/rbi/gems/simplecov-html@0.12.3.rbi +89 -0
- data/sorbet/rbi/gems/simplecov@0.21.2.rbi +577 -0
- data/sorbet/rbi/gems/simplecov_json_formatter@0.1.2.rbi +8 -0
- data/sorbet/rbi/gems/spoom@1.1.15.rbi +1549 -0
- data/sorbet/rbi/gems/tapioca@0.7.3.rbi +1718 -0
- data/sorbet/rbi/gems/thor@1.2.1.rbi +844 -0
- data/sorbet/rbi/gems/unicode-display_width@1.7.0.rbi +22 -0
- data/sorbet/rbi/gems/unparser@0.6.2.rbi +8 -0
- data/sorbet/rbi/gems/vcr@5.0.0.rbi +699 -0
- data/sorbet/rbi/gems/webmock@3.12.2.rbi +662 -0
- data/sorbet/rbi/gems/yard-sorbet@0.8.0.rbi +268 -0
- data/sorbet/rbi/gems/yard@0.9.26.rbi +4048 -0
- data/sorbet/tapioca/config.yml +13 -0
- data/sorbet/tapioca/require.rb +4 -0
- data/spec/lib/workos/audit_logs_spec.rb +151 -0
- data/spec/lib/workos/audit_trail_spec.rb +146 -0
- data/spec/lib/workos/configuration_spec.rb +61 -0
- data/spec/lib/workos/directory_sync_spec.rb +492 -0
- data/spec/lib/workos/directory_user_spec.rb +36 -0
- data/spec/lib/workos/event_spec.rb +88 -0
- data/spec/lib/workos/mfa_spec.rb +281 -0
- data/spec/lib/workos/organizations_spec.rb +257 -0
- data/spec/lib/workos/passwordless_spec.rb +77 -0
- data/spec/lib/workos/portal_spec.rb +87 -0
- data/spec/lib/workos/sso_spec.rb +650 -0
- data/spec/lib/workos/webhooks_spec.rb +236 -0
- data/spec/spec_helper.rb +56 -0
- data/spec/support/fixtures/vcr_cassettes/audit_logs/create_event.yml +59 -0
- data/spec/support/fixtures/vcr_cassettes/audit_logs/create_event_custom_idempotency_key.yml +60 -0
- data/spec/support/fixtures/vcr_cassettes/audit_logs/create_event_invalid.yml +59 -0
- data/spec/support/fixtures/vcr_cassettes/audit_logs/create_export.yml +76 -0
- data/spec/support/fixtures/vcr_cassettes/audit_logs/create_export_with_filters.yml +77 -0
- data/spec/support/fixtures/vcr_cassettes/audit_logs/get_export.yml +73 -0
- data/spec/support/fixtures/vcr_cassettes/audit_trail/create_event.yml +65 -0
- data/spec/support/fixtures/vcr_cassettes/audit_trail/create_event_custom_idempotency_key.yml +67 -0
- data/spec/support/fixtures/vcr_cassettes/audit_trail/create_event_invalid.yml +68 -0
- data/spec/support/fixtures/vcr_cassettes/audit_trail/create_events_duplicate_idempotency_key_and_payload.yml +131 -0
- data/spec/support/fixtures/vcr_cassettes/audit_trail/create_events_duplicate_idempotency_key_different_payload.yml +134 -0
- data/spec/support/fixtures/vcr_cassettes/audit_trail/get_events.yml +61 -0
- data/spec/support/fixtures/vcr_cassettes/base/execute_request_unauthenticated.yml +66 -0
- data/spec/support/fixtures/vcr_cassettes/directory_sync/delete_directory.yml +72 -0
- data/spec/support/fixtures/vcr_cassettes/directory_sync/get_directory_with_invalid_id.yml +83 -0
- data/spec/support/fixtures/vcr_cassettes/directory_sync/get_directory_with_valid_id.yml +84 -0
- data/spec/support/fixtures/vcr_cassettes/directory_sync/get_group.yml +80 -0
- data/spec/support/fixtures/vcr_cassettes/directory_sync/get_group_with_invalid_id.yml +62 -0
- data/spec/support/fixtures/vcr_cassettes/directory_sync/get_user.yml +83 -0
- data/spec/support/fixtures/vcr_cassettes/directory_sync/get_user_with_invalid_id.yml +62 -0
- data/spec/support/fixtures/vcr_cassettes/directory_sync/list_directories/with_after.yml +87 -0
- data/spec/support/fixtures/vcr_cassettes/directory_sync/list_directories/with_before.yml +89 -0
- data/spec/support/fixtures/vcr_cassettes/directory_sync/list_directories/with_domain.yml +84 -0
- data/spec/support/fixtures/vcr_cassettes/directory_sync/list_directories/with_limit.yml +85 -0
- data/spec/support/fixtures/vcr_cassettes/directory_sync/list_directories/with_no_options.yml +93 -0
- data/spec/support/fixtures/vcr_cassettes/directory_sync/list_directories/with_search.yml +85 -0
- data/spec/support/fixtures/vcr_cassettes/directory_sync/list_groups/with_after.yml +90 -0
- data/spec/support/fixtures/vcr_cassettes/directory_sync/list_groups/with_before.yml +90 -0
- data/spec/support/fixtures/vcr_cassettes/directory_sync/list_groups/with_directory.yml +90 -0
- data/spec/support/fixtures/vcr_cassettes/directory_sync/list_groups/with_limit.yml +84 -0
- data/spec/support/fixtures/vcr_cassettes/directory_sync/list_groups/with_no_options.yml +84 -0
- data/spec/support/fixtures/vcr_cassettes/directory_sync/list_groups/with_user.yml +82 -0
- data/spec/support/fixtures/vcr_cassettes/directory_sync/list_users/with_after.yml +186 -0
- data/spec/support/fixtures/vcr_cassettes/directory_sync/list_users/with_before.yml +88 -0
- data/spec/support/fixtures/vcr_cassettes/directory_sync/list_users/with_directory.yml +194 -0
- data/spec/support/fixtures/vcr_cassettes/directory_sync/list_users/with_group.yml +186 -0
- data/spec/support/fixtures/vcr_cassettes/directory_sync/list_users/with_limit.yml +189 -0
- data/spec/support/fixtures/vcr_cassettes/directory_sync/list_users/with_no_options.yml +74 -0
- data/spec/support/fixtures/vcr_cassettes/events/list_events_with_after.yml +80 -0
- data/spec/support/fixtures/vcr_cassettes/events/list_events_with_event.yml +80 -0
- data/spec/support/fixtures/vcr_cassettes/events/list_events_with_no_options.yml +80 -0
- data/spec/support/fixtures/vcr_cassettes/events/list_events_with_range.yml +80 -0
- data/spec/support/fixtures/vcr_cassettes/mfa/challenge_factor_generic_valid.yml +82 -0
- data/spec/support/fixtures/vcr_cassettes/mfa/challenge_factor_sms_valid.yml +82 -0
- data/spec/support/fixtures/vcr_cassettes/mfa/challenge_factor_totp_valid.yml +82 -0
- data/spec/support/fixtures/vcr_cassettes/mfa/delete_factor.yml +80 -0
- data/spec/support/fixtures/vcr_cassettes/mfa/enroll_factor_generic_valid.yml +82 -0
- data/spec/support/fixtures/vcr_cassettes/mfa/enroll_factor_sms_valid.yml +82 -0
- data/spec/support/fixtures/vcr_cassettes/mfa/enroll_factor_totp_valid.yml +82 -0
- data/spec/support/fixtures/vcr_cassettes/mfa/get_factor_invalid.yml +82 -0
- data/spec/support/fixtures/vcr_cassettes/mfa/get_factor_valid.yml +82 -0
- data/spec/support/fixtures/vcr_cassettes/mfa/verify_challenge_generic_expired.yml +84 -0
- data/spec/support/fixtures/vcr_cassettes/mfa/verify_challenge_generic_invalid.yml +84 -0
- data/spec/support/fixtures/vcr_cassettes/mfa/verify_challenge_generic_valid.yml +82 -0
- data/spec/support/fixtures/vcr_cassettes/mfa/verify_challenge_generic_valid_is_false.yml +82 -0
- data/spec/support/fixtures/vcr_cassettes/organization/create.yml +84 -0
- data/spec/support/fixtures/vcr_cassettes/organization/create_invalid.yml +72 -0
- data/spec/support/fixtures/vcr_cassettes/organization/create_with_duplicate_idempotency_key_and_different_payload.yml +155 -0
- data/spec/support/fixtures/vcr_cassettes/organization/create_with_duplicate_idempotency_key_and_payload.yml +154 -0
- data/spec/support/fixtures/vcr_cassettes/organization/create_with_idempotency_key.yml +79 -0
- data/spec/support/fixtures/vcr_cassettes/organization/delete.yml +72 -0
- data/spec/support/fixtures/vcr_cassettes/organization/delete_invalid.yml +72 -0
- data/spec/support/fixtures/vcr_cassettes/organization/get.yml +84 -0
- data/spec/support/fixtures/vcr_cassettes/organization/get_invalid.yml +72 -0
- data/spec/support/fixtures/vcr_cassettes/organization/list.yml +87 -0
- data/spec/support/fixtures/vcr_cassettes/organization/update.yml +84 -0
- data/spec/support/fixtures/vcr_cassettes/passwordless/create_session.yml +72 -0
- data/spec/support/fixtures/vcr_cassettes/passwordless/create_session_invalid.yml +73 -0
- data/spec/support/fixtures/vcr_cassettes/passwordless/send_session.yml +72 -0
- data/spec/support/fixtures/vcr_cassettes/passwordless/send_session_invalid.yml +73 -0
- data/spec/support/fixtures/vcr_cassettes/portal/generate_link_audit_logs.yml +72 -0
- data/spec/support/fixtures/vcr_cassettes/portal/generate_link_dsync.yml +72 -0
- data/spec/support/fixtures/vcr_cassettes/portal/generate_link_invalid.yml +72 -0
- data/spec/support/fixtures/vcr_cassettes/portal/generate_link_sso.yml +72 -0
- data/spec/support/fixtures/vcr_cassettes/sso/delete_connection_with_invalid_id.yml +72 -0
- data/spec/support/fixtures/vcr_cassettes/sso/delete_connection_with_valid_id.yml +70 -0
- data/spec/support/fixtures/vcr_cassettes/sso/get_connection_with_invalid_id.yml +72 -0
- data/spec/support/fixtures/vcr_cassettes/sso/get_connection_with_valid_id.yml +86 -0
- data/spec/support/fixtures/vcr_cassettes/sso/list_connections/with_after.yml +83 -0
- data/spec/support/fixtures/vcr_cassettes/sso/list_connections/with_before.yml +86 -0
- data/spec/support/fixtures/vcr_cassettes/sso/list_connections/with_connection_type.yml +90 -0
- data/spec/support/fixtures/vcr_cassettes/sso/list_connections/with_domain.yml +86 -0
- data/spec/support/fixtures/vcr_cassettes/sso/list_connections/with_limit.yml +83 -0
- data/spec/support/fixtures/vcr_cassettes/sso/list_connections/with_no_options.yml +89 -0
- data/spec/support/fixtures/vcr_cassettes/sso/list_connections/with_organization_id.yml +86 -0
- data/spec/support/fixtures/vcr_cassettes/sso/profile.yml +74 -0
- data/spec/support/profile.txt +1 -0
- data/spec/support/shared_examples/client_spec.rb +30 -0
- data/spec/support/webhook_payload.txt +1 -0
- data/workosv2.gemspec +38 -0
- metadata +531 -0
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: true
|
3
|
+
|
4
|
+
module WorkOSV2
|
5
|
+
# The AuditLogExport class represents the WorkOSV2 entity created when exporting Audit Log Events.
|
6
|
+
class AuditLogExport
|
7
|
+
include HashProvider
|
8
|
+
extend T::Sig
|
9
|
+
|
10
|
+
attr_accessor :object, :id, :state, :url, :created_at, :updated_at
|
11
|
+
|
12
|
+
sig { params(json: String).void }
|
13
|
+
def initialize(json)
|
14
|
+
raw = parse_json(json)
|
15
|
+
|
16
|
+
@object = T.let(raw.object, String)
|
17
|
+
@id = T.let(raw.id, String)
|
18
|
+
@state = T.let(raw.state, String)
|
19
|
+
@url = raw.url
|
20
|
+
@created_at = T.let(raw.created_at, String)
|
21
|
+
@updated_at = T.let(raw.updated_at, String)
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_json(*)
|
25
|
+
{
|
26
|
+
object: object,
|
27
|
+
id: id,
|
28
|
+
state: state,
|
29
|
+
url: url,
|
30
|
+
created_at: created_at,
|
31
|
+
updated_at: updated_at,
|
32
|
+
}
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
sig do
|
38
|
+
params(
|
39
|
+
json_string: String,
|
40
|
+
).returns(WorkOSV2::Types::AuditLogExportStruct)
|
41
|
+
end
|
42
|
+
def parse_json(json_string)
|
43
|
+
hash = JSON.parse(json_string, symbolize_names: true)
|
44
|
+
|
45
|
+
WorkOSV2::Types::AuditLogExportStruct.new(
|
46
|
+
object: hash[:object],
|
47
|
+
id: hash[:id],
|
48
|
+
state: hash[:state],
|
49
|
+
url: hash[:url],
|
50
|
+
created_at: hash[:created_at],
|
51
|
+
updated_at: hash[:updated_at],
|
52
|
+
)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: true
|
3
|
+
|
4
|
+
require 'net/http'
|
5
|
+
require 'uri'
|
6
|
+
|
7
|
+
module WorkOSV2
|
8
|
+
# The Audit Logs module provides convenience methods for working with the
|
9
|
+
# WorkOSV2 Audit Logs platform. You'll need a valid API key.
|
10
|
+
module AuditLogs
|
11
|
+
class << self
|
12
|
+
extend T::Sig
|
13
|
+
include Client
|
14
|
+
|
15
|
+
# Create an Audit Log Event.
|
16
|
+
#
|
17
|
+
# @param [String] organization An Organization ID
|
18
|
+
# @param [Hash] event An Audit Log Event
|
19
|
+
# @param [String] idempotency_key An idempotency key
|
20
|
+
#
|
21
|
+
# @return [nil]
|
22
|
+
sig do
|
23
|
+
params(
|
24
|
+
organization: String,
|
25
|
+
event: Hash,
|
26
|
+
idempotency_key: T.nilable(String),
|
27
|
+
).void
|
28
|
+
end
|
29
|
+
def create_event(organization:, event:, idempotency_key: nil)
|
30
|
+
request = post_request(
|
31
|
+
path: '/audit_logs/events',
|
32
|
+
auth: true,
|
33
|
+
idempotency_key: idempotency_key,
|
34
|
+
body: {
|
35
|
+
organization_id: organization,
|
36
|
+
event: event,
|
37
|
+
},
|
38
|
+
)
|
39
|
+
|
40
|
+
execute_request(request: request)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Create an Export of Audit Log Events.
|
44
|
+
#
|
45
|
+
# @param [String] organization An Organization ID
|
46
|
+
# @param [String] range_start ISO-8601 datetime
|
47
|
+
# @param [String] range_end ISO-8601 datetime
|
48
|
+
# @param [Array<String>] actions A list of actions to filter by
|
49
|
+
# @param [Array<String>] @deprecated use `actor_names` instead
|
50
|
+
# @param [Array<String>] actor_names A list of actor names to filter by
|
51
|
+
# @param [Array<String>] actor_ids A list of actor ids to filter by
|
52
|
+
# @param [Array<String>] targets A list of target types to filter by
|
53
|
+
#
|
54
|
+
# @return [WorkOSV2::AuditLogExport]
|
55
|
+
sig do
|
56
|
+
params(
|
57
|
+
organization: String,
|
58
|
+
range_start: String,
|
59
|
+
range_end: String,
|
60
|
+
actions: T.nilable(T::Array[String]),
|
61
|
+
actors: T.nilable(T::Array[String]),
|
62
|
+
targets: T.nilable(T::Array[String]),
|
63
|
+
actor_names: T.nilable(T::Array[String]),
|
64
|
+
actor_ids: T.nilable(T::Array[String]),
|
65
|
+
).returns(WorkOSV2::AuditLogExport)
|
66
|
+
end
|
67
|
+
def create_export(organization:, range_start:, range_end:, actions: nil, # rubocop:disable Metrics/ParameterLists
|
68
|
+
actors: nil, targets: nil, actor_names: nil, actor_ids: nil)
|
69
|
+
body = {
|
70
|
+
organization_id: organization,
|
71
|
+
range_start: range_start,
|
72
|
+
range_end: range_end,
|
73
|
+
}
|
74
|
+
|
75
|
+
body['actions'] = actions unless actions.nil?
|
76
|
+
body['actors'] = actors unless actors.nil?
|
77
|
+
body['actor_names'] = actor_names unless actor_names.nil?
|
78
|
+
body['actor_ids'] = actor_ids unless actor_ids.nil?
|
79
|
+
body['targets'] = targets unless targets.nil?
|
80
|
+
|
81
|
+
request = post_request(
|
82
|
+
path: '/audit_logs/exports',
|
83
|
+
auth: true,
|
84
|
+
body: body,
|
85
|
+
)
|
86
|
+
|
87
|
+
response = execute_request(request: request)
|
88
|
+
|
89
|
+
WorkOSV2::AuditLogExport.new(response.body)
|
90
|
+
end
|
91
|
+
|
92
|
+
# Retrieves an Export of Audit Log Events
|
93
|
+
#
|
94
|
+
# @param [String] id An Audit Log Export ID
|
95
|
+
#
|
96
|
+
# @return [WorkOSV2::AuditLogExport]
|
97
|
+
sig do
|
98
|
+
params(
|
99
|
+
id: String,
|
100
|
+
).returns(WorkOSV2::AuditLogExport)
|
101
|
+
end
|
102
|
+
def get_export(id:)
|
103
|
+
request = get_request(
|
104
|
+
auth: true,
|
105
|
+
path: "/audit_logs/exports/#{id}",
|
106
|
+
)
|
107
|
+
|
108
|
+
response = execute_request(request: request)
|
109
|
+
|
110
|
+
WorkOSV2::AuditLogExport.new(response.body)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: true
|
3
|
+
|
4
|
+
require 'net/http'
|
5
|
+
require 'uri'
|
6
|
+
|
7
|
+
module WorkOSV2
|
8
|
+
# The Audit Trail module provides convenience methods for working with the
|
9
|
+
# WorkOSV2 Audit Trail platform. You'll need a valid API key.
|
10
|
+
#
|
11
|
+
# @see https://docs.workos.com/audit-trail/overview
|
12
|
+
module AuditTrail
|
13
|
+
class << self
|
14
|
+
extend T::Sig
|
15
|
+
include Client
|
16
|
+
|
17
|
+
# Create an Audit Trail event.
|
18
|
+
#
|
19
|
+
# @param [Hash] event An event hash
|
20
|
+
# @option event [String] group A single organization containing related
|
21
|
+
# members. This will normally be the customer of a vendor's application.
|
22
|
+
# @option event [String] location Identifier for where the event
|
23
|
+
# originated. This will be an IP address (IPv4 or IPv6), hostname, or
|
24
|
+
# device ID.
|
25
|
+
# @option event [String] action Specific activity performed by the actor.
|
26
|
+
# @option event [String] action_type Corresponding CRUD category of the
|
27
|
+
# event. Can be one of C, R, U, or D.
|
28
|
+
# @option event [String] actor_name Display name of the entity performing
|
29
|
+
# the action.
|
30
|
+
# @option event [String] actor_id Unique identifier of the entity
|
31
|
+
# performing the action.
|
32
|
+
# @option event [String] target_name Display name of the object or
|
33
|
+
# resource that is being acted upon.
|
34
|
+
# @option event [String] target_id Unique identifier of the object or
|
35
|
+
# resource being acted upon.
|
36
|
+
# @option event [String] occurred_at ISO-8601 datetime at which the event
|
37
|
+
# happened, with millisecond precision.
|
38
|
+
# @option event [Hash] metadata Arbitrary key-value data containing
|
39
|
+
# information associated with the event. Note: There is a limit of 50
|
40
|
+
# keys. Key names can be up to 40 characters long, and values can be up
|
41
|
+
# to 500 characters long.
|
42
|
+
# @param [String] idempotency_key An idempotency key
|
43
|
+
sig do
|
44
|
+
params(
|
45
|
+
event: Hash,
|
46
|
+
idempotency_key: T.nilable(String),
|
47
|
+
).returns(::T.untyped)
|
48
|
+
end
|
49
|
+
|
50
|
+
def create_event(event:, idempotency_key: nil)
|
51
|
+
request = post_request(
|
52
|
+
path: '/events',
|
53
|
+
auth: true,
|
54
|
+
idempotency_key: idempotency_key,
|
55
|
+
body: event,
|
56
|
+
)
|
57
|
+
|
58
|
+
execute_request(request: request)
|
59
|
+
end
|
60
|
+
|
61
|
+
# Retrieve Audit Trail events.
|
62
|
+
#
|
63
|
+
# @param [Hash] options An options hash
|
64
|
+
# @option options [String] before Event ID to look before
|
65
|
+
# @option options [String] after Event ID to look after
|
66
|
+
# @option options [Integer] limit Number of Events to return
|
67
|
+
# @option options [String] order The order in which to paginate records
|
68
|
+
# @option options [Array<String>] group List of Groups to filter for
|
69
|
+
# @option options [Array<String>] action List of Actions to filter for
|
70
|
+
# @option options [Array<String>] action_type List of Action Types to
|
71
|
+
# filter for
|
72
|
+
# @option options [Array<String>] actor_name List of Actor Name to filter
|
73
|
+
# for
|
74
|
+
# @option options [Array<String>] actor_id List of Actor IDs to filter for
|
75
|
+
# @option options [Array<String>] target_name List of Target Names to
|
76
|
+
# filter for
|
77
|
+
# @option options [Array<String>] target_id List of Target IDs to filter
|
78
|
+
# for
|
79
|
+
# @option options [String] occurred_at ISO-8601 datetime of when an event
|
80
|
+
# occurred
|
81
|
+
# @option options [String] occurred_at_gt ISO-8601 datetime of when an
|
82
|
+
# event occurred after
|
83
|
+
# @option options [String] occurred_at_gte ISO-8601 datetime of when an
|
84
|
+
# event occurred at or after
|
85
|
+
# @option options [String] occurred_at_lt ISO-8601 datetime of when an
|
86
|
+
# event occurred before
|
87
|
+
# @option options [String] occurred_at_lte ISO-8601 datetime of when an
|
88
|
+
# event occured at or before
|
89
|
+
# @option options [String] search Keyword search
|
90
|
+
#
|
91
|
+
# @return [Array<Hash>]
|
92
|
+
sig do
|
93
|
+
params(
|
94
|
+
options: T::Hash[Symbol, String],
|
95
|
+
).returns(T::Array[T::Hash[String, T.nilable(String)]])
|
96
|
+
end
|
97
|
+
|
98
|
+
def get_events(options = {})
|
99
|
+
response = execute_request(
|
100
|
+
request: get_request(
|
101
|
+
path: '/events',
|
102
|
+
auth: true,
|
103
|
+
params: options,
|
104
|
+
),
|
105
|
+
)
|
106
|
+
|
107
|
+
JSON.parse(response.body)['data']
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: false
|
3
|
+
|
4
|
+
module WorkOSV2
|
5
|
+
# The Challnge class provides a lightweight wrapper around
|
6
|
+
# a WorkOSV2 DirectoryUser resource. This class is not meant to be instantiated
|
7
|
+
# in DirectoryUser space, and is instantiated internally but exposed.
|
8
|
+
class Challenge
|
9
|
+
include HashProvider
|
10
|
+
extend T::Sig
|
11
|
+
|
12
|
+
attr_accessor :id, :object, :expires_at, :code, :authentication_factor_id, :updated_at, :created_at
|
13
|
+
|
14
|
+
sig { params(json: String).void }
|
15
|
+
def initialize(json)
|
16
|
+
raw = parse_json(json)
|
17
|
+
@id = T.let(raw.id, String)
|
18
|
+
@object = T.let(raw.object, String)
|
19
|
+
@expires_at = T.let(raw.expires_at, String)
|
20
|
+
@code = raw.code
|
21
|
+
@authentication_factor_id = T.let(raw.authentication_factor_id, String)
|
22
|
+
@created_at = T.let(raw.created_at, String)
|
23
|
+
@updated_at = T.let(raw.updated_at, String)
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_json(*)
|
27
|
+
{
|
28
|
+
id: id,
|
29
|
+
object: object,
|
30
|
+
expires_at: expires_at,
|
31
|
+
code: code,
|
32
|
+
authentication_factor_id: authentication_factor_id,
|
33
|
+
created_at: created_at,
|
34
|
+
updated_at: updated_at,
|
35
|
+
}
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
sig { params(json_string: String).returns(WorkOSV2::Types::ChallengeStruct) }
|
41
|
+
def parse_json(json_string)
|
42
|
+
hash = JSON.parse(json_string, symbolize_names: true)
|
43
|
+
|
44
|
+
WorkOSV2::Types::ChallengeStruct.new(
|
45
|
+
id: hash[:id],
|
46
|
+
object: hash[:object],
|
47
|
+
expires_at: hash[:expires_at],
|
48
|
+
code: hash[:code],
|
49
|
+
authentication_factor_id: hash[:authentication_factor_id],
|
50
|
+
created_at: hash[:created_at],
|
51
|
+
updated_at: hash[:updated_at],
|
52
|
+
)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,186 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: false
|
3
|
+
|
4
|
+
module WorkOSV2
|
5
|
+
# A Net::HTTP based API client for interacting with the WorkOSV2 API
|
6
|
+
module Client
|
7
|
+
extend T::Sig
|
8
|
+
include Kernel
|
9
|
+
|
10
|
+
sig { returns(Net::HTTP) }
|
11
|
+
def client
|
12
|
+
Net::HTTP.new(WorkOSV2::API_HOSTNAME, 443).tap do |http_client|
|
13
|
+
http_client.use_ssl = true
|
14
|
+
http_client.open_timeout = WorkOSV2.config.timeout
|
15
|
+
http_client.read_timeout = WorkOSV2.config.timeout
|
16
|
+
http_client.write_timeout = WorkOSV2.config.timeout if RUBY_VERSION >= '2.6.0'
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
sig do
|
21
|
+
params(
|
22
|
+
request: T.any(Net::HTTP::Get, Net::HTTP::Post, Net::HTTP::Delete, Net::HTTP::Put),
|
23
|
+
).returns(::T.untyped)
|
24
|
+
end
|
25
|
+
def execute_request(request:)
|
26
|
+
begin
|
27
|
+
response = client.request(request)
|
28
|
+
rescue Net::OpenTimeout, Net::ReadTimeout, Net::WriteTimeout
|
29
|
+
raise TimeoutError.new(
|
30
|
+
message: 'API Timeout Error',
|
31
|
+
)
|
32
|
+
end
|
33
|
+
|
34
|
+
http_status = response.code.to_i
|
35
|
+
handle_error_response(response: response) if http_status >= 400
|
36
|
+
|
37
|
+
response
|
38
|
+
end
|
39
|
+
|
40
|
+
sig do
|
41
|
+
params(
|
42
|
+
path: String,
|
43
|
+
auth: T.nilable(T::Boolean),
|
44
|
+
params: T.nilable(Hash),
|
45
|
+
access_token: T.nilable(String),
|
46
|
+
).returns(Net::HTTP::Get)
|
47
|
+
end
|
48
|
+
def get_request(path:, auth: false, params: {}, access_token: nil)
|
49
|
+
uri = URI(path)
|
50
|
+
uri.query = URI.encode_www_form(params) if params
|
51
|
+
|
52
|
+
request = Net::HTTP::Get.new(
|
53
|
+
uri.to_s,
|
54
|
+
'Content-Type' => 'application/json',
|
55
|
+
)
|
56
|
+
|
57
|
+
request['Authorization'] = "Bearer #{access_token || WorkOSV2.config.key!}" if auth
|
58
|
+
request['User-Agent'] = user_agent
|
59
|
+
request
|
60
|
+
end
|
61
|
+
|
62
|
+
sig do
|
63
|
+
params(
|
64
|
+
path: String,
|
65
|
+
auth: T.nilable(T::Boolean),
|
66
|
+
idempotency_key: T.nilable(String),
|
67
|
+
body: T.nilable(Hash),
|
68
|
+
).returns(Net::HTTP::Post)
|
69
|
+
end
|
70
|
+
def post_request(path:, auth: false, idempotency_key: nil, body: nil)
|
71
|
+
request = Net::HTTP::Post.new(path, 'Content-Type' => 'application/json')
|
72
|
+
request.body = body.to_json if body
|
73
|
+
request['Authorization'] = "Bearer #{WorkOSV2.config.key!}" if auth
|
74
|
+
request['Idempotency-Key'] = idempotency_key if idempotency_key
|
75
|
+
request['User-Agent'] = user_agent
|
76
|
+
request
|
77
|
+
end
|
78
|
+
|
79
|
+
sig do
|
80
|
+
params(
|
81
|
+
path: String,
|
82
|
+
auth: T.nilable(T::Boolean),
|
83
|
+
params: T.nilable(Hash),
|
84
|
+
).returns(Net::HTTP::Delete)
|
85
|
+
end
|
86
|
+
def delete_request(path:, auth: false, params: {})
|
87
|
+
uri = URI(path)
|
88
|
+
uri.query = URI.encode_www_form(params) if params
|
89
|
+
|
90
|
+
request = Net::HTTP::Delete.new(
|
91
|
+
uri.to_s,
|
92
|
+
'Content-Type' => 'application/json',
|
93
|
+
)
|
94
|
+
|
95
|
+
request['Authorization'] = "Bearer #{WorkOSV2.config.key!}" if auth
|
96
|
+
request['User-Agent'] = user_agent
|
97
|
+
request
|
98
|
+
end
|
99
|
+
|
100
|
+
sig do
|
101
|
+
params(
|
102
|
+
path: String,
|
103
|
+
auth: T.nilable(T::Boolean),
|
104
|
+
idempotency_key: T.nilable(String),
|
105
|
+
body: T.nilable(Hash),
|
106
|
+
).returns(Net::HTTP::Put)
|
107
|
+
end
|
108
|
+
def put_request(path:, auth: false, idempotency_key: nil, body: nil)
|
109
|
+
request = Net::HTTP::Put.new(path, 'Content-Type' => 'application/json')
|
110
|
+
request.body = body.to_json if body
|
111
|
+
request['Authorization'] = "Bearer #{WorkOSV2.config.key!}" if auth
|
112
|
+
request['Idempotency-Key'] = idempotency_key if idempotency_key
|
113
|
+
request['User-Agent'] = user_agent
|
114
|
+
request
|
115
|
+
end
|
116
|
+
|
117
|
+
sig { returns(String) }
|
118
|
+
def user_agent
|
119
|
+
engine = defined?(::RUBY_ENGINE) ? ::RUBY_ENGINE : 'Ruby'
|
120
|
+
|
121
|
+
[
|
122
|
+
'WorkOSV2',
|
123
|
+
"#{engine}/#{RUBY_VERSION}",
|
124
|
+
RUBY_PLATFORM,
|
125
|
+
"v#{WorkOSV2::VERSION}"
|
126
|
+
].join('; ')
|
127
|
+
end
|
128
|
+
|
129
|
+
# rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
130
|
+
sig { params(response: ::T.untyped).void }
|
131
|
+
def handle_error_response(response:)
|
132
|
+
http_status = response.code.to_i
|
133
|
+
json = JSON.parse(response.body)
|
134
|
+
|
135
|
+
case http_status
|
136
|
+
when 400
|
137
|
+
raise InvalidRequestError.new(
|
138
|
+
message: json['message'],
|
139
|
+
http_status: http_status,
|
140
|
+
request_id: response['x-request-id'],
|
141
|
+
code: json['code'],
|
142
|
+
errors: json['errors'],
|
143
|
+
)
|
144
|
+
when 401
|
145
|
+
raise AuthenticationError.new(
|
146
|
+
message: json['message'],
|
147
|
+
http_status: http_status,
|
148
|
+
request_id: response['x-request-id'],
|
149
|
+
)
|
150
|
+
when 404
|
151
|
+
raise APIError.new(
|
152
|
+
message: json['message'],
|
153
|
+
http_status: http_status,
|
154
|
+
request_id: response['x-request-id'],
|
155
|
+
)
|
156
|
+
when 422
|
157
|
+
message = json['message']
|
158
|
+
code = json['code']
|
159
|
+
errors = extract_error(json['errors']) if json['errors']
|
160
|
+
message += " (#{errors})" if errors
|
161
|
+
|
162
|
+
raise InvalidRequestError.new(
|
163
|
+
message: message,
|
164
|
+
http_status: http_status,
|
165
|
+
request_id: response['x-request-id'],
|
166
|
+
code: code,
|
167
|
+
)
|
168
|
+
else
|
169
|
+
raise APIError.new(
|
170
|
+
message: json['message'],
|
171
|
+
http_status: http_status,
|
172
|
+
request_id: response['x-request-id'],
|
173
|
+
)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
# rubocop:enable Metrics/MethodLength, Metrics/AbcSize
|
177
|
+
|
178
|
+
private
|
179
|
+
|
180
|
+
def extract_error(errors)
|
181
|
+
errors.map do |error|
|
182
|
+
"#{error['field']}: #{error['code']}"
|
183
|
+
end.join('; ')
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: true
|
3
|
+
|
4
|
+
module WorkOSV2
|
5
|
+
# Configuration class sets config initializer
|
6
|
+
class Configuration
|
7
|
+
attr_accessor :timeout, :key
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@timeout = 60
|
11
|
+
end
|
12
|
+
|
13
|
+
def key!
|
14
|
+
key or raise '`WorkOSV2.config.key` not set'
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: true
|
3
|
+
|
4
|
+
module WorkOSV2
|
5
|
+
# The Connection class provides a lightweight wrapper around
|
6
|
+
# a WorkOSV2 Connection resource. This class is not meant to be instantiated
|
7
|
+
# in user space, and is instantiated internally but exposed.
|
8
|
+
# Note: status is deprecated - use state instead
|
9
|
+
class Connection
|
10
|
+
include HashProvider
|
11
|
+
extend T::Sig
|
12
|
+
|
13
|
+
attr_accessor :id, :name, :connection_type, :domains, :organization_id,
|
14
|
+
:state, :status, :created_at, :updated_at
|
15
|
+
|
16
|
+
# rubocop:disable Metrics/AbcSize
|
17
|
+
sig { params(json: String).void }
|
18
|
+
def initialize(json)
|
19
|
+
raw = parse_json(json)
|
20
|
+
|
21
|
+
@id = T.let(raw.id, String)
|
22
|
+
@name = T.let(raw.name, String)
|
23
|
+
@connection_type = T.let(raw.connection_type, String)
|
24
|
+
@domains = T.let(raw.domains, Array)
|
25
|
+
@organization_id = raw.organization_id
|
26
|
+
@state = T.let(raw.state, String)
|
27
|
+
@status = T.let(raw.status, String)
|
28
|
+
@created_at = T.let(raw.created_at, String)
|
29
|
+
@updated_at = T.let(raw.updated_at, String)
|
30
|
+
end
|
31
|
+
# rubocop:enable Metrics/AbcSize
|
32
|
+
|
33
|
+
def to_json(*)
|
34
|
+
{
|
35
|
+
id: id,
|
36
|
+
name: name,
|
37
|
+
connection_type: connection_type,
|
38
|
+
domains: domains,
|
39
|
+
organization_id: organization_id,
|
40
|
+
state: state,
|
41
|
+
status: status,
|
42
|
+
created_at: created_at,
|
43
|
+
updated_at: updated_at,
|
44
|
+
}
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
sig { params(json_string: String).returns(WorkOSV2::Types::ConnectionStruct) }
|
50
|
+
def parse_json(json_string)
|
51
|
+
hash = JSON.parse(json_string, symbolize_names: true)
|
52
|
+
|
53
|
+
WorkOSV2::Types::ConnectionStruct.new(
|
54
|
+
id: hash[:id],
|
55
|
+
name: hash[:name],
|
56
|
+
connection_type: hash[:connection_type],
|
57
|
+
domains: hash[:domains],
|
58
|
+
organization_id: hash[:organization_id],
|
59
|
+
state: hash[:state],
|
60
|
+
status: hash[:status],
|
61
|
+
created_at: hash[:created_at],
|
62
|
+
updated_at: hash[:updated_at],
|
63
|
+
)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module WorkOSV2
|
4
|
+
# A Temporary wrapper class for a Hash, currently the base class for
|
5
|
+
# WorOS::DirectoryGroup and WorkOSV2::DirectoryUser. Makes all the Hash
|
6
|
+
# methods available to those classes, but will also emit a deprecation
|
7
|
+
# warning whenever any of them are used.
|
8
|
+
#
|
9
|
+
# Once we deprecate Hash compatibility, this model can be deleted.
|
10
|
+
class DeprecatedHashWrapper < Hash
|
11
|
+
(public_instance_methods - Object.methods).each do |method_name|
|
12
|
+
define_method method_name do |*args, &block|
|
13
|
+
print_deprecation_warning(method_name)
|
14
|
+
super(*args, &block)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# call the original implementation of :replace in Hash,
|
19
|
+
# so we don't show the deprecation warning
|
20
|
+
def replace_without_warning(new_hash)
|
21
|
+
method(:replace).super_method&.call(new_hash)
|
22
|
+
end
|
23
|
+
|
24
|
+
def [](attribute_name)
|
25
|
+
usage = "#{object_name}.#{attribute_name}"
|
26
|
+
warning_message = "WARNING: The Hash style access for #{class_name} attributes is deprecated
|
27
|
+
and will be removed in a future version. Please use `#{usage}` or equivalent accessor.\n"
|
28
|
+
|
29
|
+
print_deprecation_warning('[]', warning_message)
|
30
|
+
|
31
|
+
super(attribute_name.to_sym)
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def deprecation_warning(method_name)
|
37
|
+
usage = "#{object_name}.to_h.#{method_name}"
|
38
|
+
|
39
|
+
"WARNING: Hash compatibility for #{class_name} is deprecated and will be removed
|
40
|
+
in a future version. Please use `#{usage}` to access methods on the attribute Hash object.\n"
|
41
|
+
end
|
42
|
+
|
43
|
+
def print_deprecation_warning(method_name, warning_message = deprecation_warning(method_name))
|
44
|
+
if RUBY_VERSION > '3'
|
45
|
+
warn warning_message, category: :deprecated
|
46
|
+
else
|
47
|
+
warn warning_message
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def class_name
|
52
|
+
self.class.name
|
53
|
+
end
|
54
|
+
|
55
|
+
# We want to do class_name.demodulize.underscore here, but that's not available in Ruby 1.9, so
|
56
|
+
# implementing the demodulize and underscore methods here.
|
57
|
+
def object_name
|
58
|
+
i = class_name.rindex('::')
|
59
|
+
object_name = i ? class_name[(i + 2)..-1] : class_name
|
60
|
+
underscore(object_name)
|
61
|
+
end
|
62
|
+
|
63
|
+
def underscore(camel_cased_word)
|
64
|
+
return camel_cased_word.to_s unless /[A-Z-]|::/.match?(camel_cased_word)
|
65
|
+
|
66
|
+
word = camel_cased_word.to_s.gsub('::', '/')
|
67
|
+
word.gsub!(/(?:(?<=([A-Za-z\d]))|\b)((?=a)b)(?=\b|[^a-z])/) do
|
68
|
+
"#{Regexp.last_match(1) && '_'}#{Regexp.last_match(2).downcase}"
|
69
|
+
end
|
70
|
+
word.gsub!(/([A-Z]+)(?=[A-Z][a-z])|([a-z\d])(?=[A-Z])/) { (Regexp.last_match(1) || Regexp.last_match(2)) << '_' }
|
71
|
+
word.tr!('-', '_')
|
72
|
+
word.downcase!
|
73
|
+
word
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|