diff --git a/lib/vonage/client.rb b/lib/vonage/client.rb index 06c2845b..199fd77a 100644 --- a/lib/vonage/client.rb +++ b/lib/vonage/client.rb @@ -145,6 +145,13 @@ def tfa @tfa ||= T.let(TFA.new(config), T.nilable(Vonage::TFA)) end + # @return [Users] + # + sig { returns(T.nilable(Vonage::Users)) } + def users + @users ||= T.let(Users.new(config), T.nilable(Vonage::Users)) + end + # @return [Verify] # sig { returns(T.nilable(Vonage::Verify)) } diff --git a/lib/vonage/users.rb b/lib/vonage/users.rb new file mode 100644 index 00000000..df30f916 --- /dev/null +++ b/lib/vonage/users.rb @@ -0,0 +1,156 @@ +# typed: strict +# frozen_string_literal: true + +module Vonage + class Users < Namespace + extend T::Sig + self.authentication = BearerToken + + self.request_body = JSON + + # Get a list of Users associated with the Vonage Application. + # + # @param [optional, Integer] :page_size + # Specifies the number of records to be returned in the response. + # + # @param [optional, String] :order + # Specifies the order in which the records should be returned. + # Must be one of `asc`, `ASC`, `desc`, or `DESC` + # + # @param [optional, String] :cursor + # Specficy a cursor point from which to start returning results, for example the values of the `next` or `prev` + # `_links` contained in a response. + # + # @param [optional, String] :name + # Specify a user name with which to filter results + # + # @return [ListResponse] + # + # @see https://developer.vonage.com/en/api/application.v2#getUsers + # + def list(**params) + request('/v1/users', params: params, response_class: ListResponse) + end + + # Get a specified User associated with the Vonage Application. + # + # @param [required, String] :id + # The unique ID or name for the user. + # + # @return [Vonage::Response] + # + # @see https://developer.vonage.com/en/api/application.v2#getUser + # + def find(id:) + request("/v1/users/#{id}") + end + + # Create a new User associated with the Vonage Application. + # + # @param [optional, String] :name + # A unique name for the user. If not provided, a name will be auto-generated. + # + # @param [optional, String] :display_name + # A string to be displayed as user name. It does not need to be unique. + # + # @param [optional, String] :image_url + # A publicly accessible URL to an image file for an image to be associated with the user. + # + # @param [optional, Hash] :properties A hash defining properties for the User. + # @option properties [Hash] :custom_data A hash of custom data as key/value pairs. + # + # @param [optional, Hash] :channels A hash defining details of various channels. + # @option channels [Array] :pstn An array containing a Hash which defines data for the pstn channel. + # @option pstn [Integer] :number The pstn number. + # @option channels [Array] :sip An array containing a Hash which defines data for the sip channel. + # @option sip [String] :uri The sip uri. + # @option sip [String] :username The sip username. + # @option sip [String] :password The sip password. + # @option channels [Array] :vbc An array containing a Hash which defines data for the vbc channel. + # @option vbc [String] :extension The vbc extension. + # @option channels [Array] :websocket An array containing a Hash which defines data for the websocket channel. + # @option websocket [String] :uri The websocket uri. + # @option websocket [String] :content-type The websocket audio type. Must be one of: `audio/l16;rate=8000`, `audio/l16;rate=16000`. + # @option websocket [Hash] :headers A hash of custom websocket headers provided as key/value pairs. + # @option channels [Array] :sms An array containing a Hash which defines data for the sms channel. + # @option sms [Integer] :number The sms number. + # @option channels [Array] :mms An array containing a Hash which defines data for the mms channel. + # @option mms [Integer] :number The mms number. + # @option channels [Array] :whatsapp An array containing a Hash which defines data for the whatsapp channel. + # @option whatsapp [Integer] :number The whatsapp number. + # @option channels [Array] :viber An array containing a Hash which defines data for the sms channel. + # @option viber [Integer] :number The viber number. + # @option channels [Array] :messenger An array containing a Hash which defines data for the messenger channel. + # @option messenger [Integer] :id The messenger id. + # + # @return [Vonage::Response] + # + # @see https://developer.vonage.com/en/api/application.v2#createUser + # + def create(**params) + request('/v1/users', params: params, type: Post) + end + + # Update an existing User associated with the Vonage Application. + # + # @param [required, String] :id + # The unique ID or name for the user to be updated. + # + # @param [optional, String] :name + # A unique name for the user. + # + # @param [optional, String] :display_name + # A string to be displayed as user name. It does not need to be unique. + # + # @param [optional, String] :image_url + # A publicly accessible URL to an image file for an image to be associated with the user. + # + # @param [optional, Hash] :properties A hash defining properties for the User. + # @option properties [Hash] :custom_data A hash of custom data as key/value pairs. + # + # @param [optional, Hash] :channels A hash defining details of various channels. + # @option channels [Array] :pstn An array containing a Hash which defines data for the pstn channel. + # @option pstn [Integer] :number The pstn number. + # @option channels [Array] :sip An array containing a Hash which defines data for the sip channel. + # @option sip [String] :uri The sip uri. + # @option sip [String] :username The sip username. + # @option sip [String] :password The sip password. + # @option channels [Array] :vbc An array containing a Hash which defines data for the vbc channel. + # @option vbc [String] :extension The vbc extension. + # @option channels [Array] :websocket An array containing a Hash which defines data for the websocket channel. + # @option websocket [String] :uri The websocket uri. + # @option websocket [String] :content-type The websocket audio type. Must be one of: `audio/l16;rate=8000`, `audio/l16;rate=16000`. + # @option websocket [Hash] :headers A hash of custom websocket headers provided as key/value pairs. + # @option channels [Array] :sms An array containing a Hash which defines data for the sms channel. + # @option sms [Integer] :number The sms number. + # @option channels [Array] :mms An array containing a Hash which defines data for the mms channel. + # @option mms [Integer] :number The mms number. + # @option channels [Array] :whatsapp An array containing a Hash which defines data for the whatsapp channel. + # @option whatsapp [Integer] :number The whatsapp number. + # @option channels [Array] :viber An array containing a Hash which defines data for the sms channel. + # @option viber [Integer] :number The viber number. + # @option channels [Array] :messenger An array containing a Hash which defines data for the messenger channel. + # @option messenger [Integer] :id The messenger id. + # + # @return [Vonage::Response] + # + # @see https://developer.vonage.com/en/api/application.v2#createUser + # + def update(id:, **params) + request("/v1/users/#{id}", params: params, type: Patch) + end + + # Delete a specified User associated with the Vonage Application. + # + # @param [required, String] :id + # The unique ID or name for the user. + # + # @return [Vonage::Response] + # + # @see https://developer.vonage.com/en/api/application.v2#deleteUser + # + def delete(id:) + request("/v1/users/#{id}", type: Delete) + end + end +end diff --git a/lib/vonage/users/list_response.rb b/lib/vonage/users/list_response.rb new file mode 100644 index 00000000..d886e6ee --- /dev/null +++ b/lib/vonage/users/list_response.rb @@ -0,0 +1,11 @@ +# typed: true + +class Vonage::Users::ListResponse < Vonage::Response + include Enumerable + + def each + return enum_for(:each) unless block_given? + + @entity._embedded.users.each { |item| yield item } + end +end diff --git a/lib/vonage/verify2.rb b/lib/vonage/verify2.rb index 3f96e8de..60e81891 100644 --- a/lib/vonage/verify2.rb +++ b/lib/vonage/verify2.rb @@ -28,7 +28,7 @@ class Verify2 < Namespace # @option opts [String] :client_ref Reference to be included in callbacks # @option opts [Boolean] If used, must be set to `false`. Will bypass a network block for a single Verify V2 request # - # @return Vomage::Response + # @return Vonage::Response # @see https://developer.vonage.com/en/api/verify.v2#newRequest # def start_verification(brand:, workflow:, **opts) diff --git a/test/vonage/client_test.rb b/test/vonage/client_test.rb index 1c944bd3..29e38b31 100644 --- a/test/vonage/client_test.rb +++ b/test/vonage/client_test.rb @@ -87,6 +87,10 @@ def test_tfa_method assert_kind_of Vonage::TFA, client.tfa end + def test_users_method + assert_kind_of Vonage::Users, client.users + end + def test_verify_method assert_kind_of Vonage::Verify, client.verify end diff --git a/test/vonage/jwt_test.rb b/test/vonage/jwt_test.rb index 51fbaf1e..667df5b8 100644 --- a/test/vonage/jwt_test.rb +++ b/test/vonage/jwt_test.rb @@ -15,7 +15,7 @@ def uuid_pattern end def sample_token - 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpYXQiOjE1OTUyNTM2MTMsImp0aSI6ImU1QmxGeDVOek5ydCIsImV4cCI6MTU5NTI1NDUxMywic3ViIjoiU3ViamVjdCIsImFwcGxpY2F0aW9uX2lkIjoieHh4eHh4eHgteHh4eC14eHh4LXh4eHgteHh4eHh4eHh4eHh4In0.Jv1flw0dzDEskyEHaK1appNUEHF2zBRJw0VWjQ8ri-MzsWguPu8ofoGVfWDTemF2xj87ukgfg8a3kTOjA0rZfCMUG4vJiGrWPJvCab7ECvy0_-vJgsDSzrG7I5MsBpbJnc1iyxv1kRu_U-EcbOceaM77yqisRLFSmwkEYuLFAOMuFeBOHZTbHYLhWYvzCOZXIU0IxDNQfGw-wXxXSMcv8aAPvhJe7bYZeRUpX8Pw0y2Qz0PxE7tB2ven_6-F_5FuOl2ARGU90GpzLho77aV5KQAKsaShwA4oqH5ETJF5JUDc9MYky-7Hbu2BmC3AqnpxGNnu7g4M6nnM-g63_5WFFg' + "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpYXQiOjE1OTUyNTM2MTMsImp0aSI6ImU1QmxGeDVOek5ydCIsImV4cCI6MTU5NTI1NDUxMywiYXBwbGljYXRpb25faWQiOiJ4eHh4eHh4eC14eHh4LXh4eHgteHh4eC14eHh4eHh4eHh4eHgifQ.DurSM2It4XstunZd_PykqDW1EceX5pCCB3s1ER2_ljMkLZbNuMcMv3GvCnTdtNKhizMEjrH8PXyVSjENdKMjUscyyMISNzLKQMYM1vsHOIIeFQY0M90D2wEaAvHf7fUtPPbBPsOUsl3zMDCrpikDSvEnMXOlxNTkc7_xxCWjv1dQI5Fu7_6VE8xEXgLi0jCv3rEaA61CYlzGwVRFkAH9kkettJa3tl7ZyHNqgR4TM-9DmuPomNQ8ARgff3ab0NalVZougF4cgxQvvMqIOWhXfxkmU-OCAs86wVGDkapJ8TJt_NuUKcVAMfhnf9eLR3USVMnsz2oTRWcezKrJM63mQQ" end def decode(token) diff --git a/test/vonage/test.rb b/test/vonage/test.rb index 65d3cd6b..132b7b38 100644 --- a/test/vonage/test.rb +++ b/test/vonage/test.rb @@ -95,7 +95,7 @@ def meetings_host def vonage_host "api-eu.vonage.com" end - + def request(body: nil, query: nil, headers: {}, auth: nil) headers['Authorization'] = auth || authorization headers['Content-Type'] = 'application/json' if body @@ -115,6 +115,10 @@ def secrets_response { body: '{"_embedded": {"secrets":[]}}', headers: response_headers } end + def users_response + { body: '{"_embedded": {"users":[]}}', headers: response_headers } + end + def numbers_response { body: '{"numbers":[]}', headers: response_headers } end diff --git a/test/vonage/users_test.rb b/test/vonage/users_test.rb new file mode 100644 index 00000000..ec58a256 --- /dev/null +++ b/test/vonage/users_test.rb @@ -0,0 +1,92 @@ +# typed: false +require_relative './test' + +class Vonage::UsersTest < Vonage::Test + def users + Vonage::Users.new(config) + end + + def users_uri + 'https://api.nexmo.com/v1/users' + end + + def user_uri + 'https://api.nexmo.com/v1/users/' + user_id + end + + def test_list_method + stub_request(:get, users_uri).with(request).to_return(users_response) + + response = users.list + + assert_kind_of Vonage::Users::ListResponse, response + response.each{|resp| assert_kind_of Vonage::Response, resp } + end + + def test_list_method_with_optional_params + stub_request(:get, users_uri + '?order=asc').with(request).to_return(users_response) + + response = users.list(order: 'asc') + + assert_kind_of Vonage::Users::ListResponse, response + response.each{|resp| assert_kind_of Vonage::Response, resp } + end + + def test_find_method + stub_request(:get, user_uri).with(request).to_return(response) + + assert_kind_of Vonage::Response, users.find(id: user_id) + end + + def test_find_method_without_id + assert_raises ArgumentError do + users.find + end + end + + def test_create_method + stub_request(:post, users_uri).with(request).to_return(response) + + assert_kind_of Vonage::Response, users.create + end + + def test_create_method_with_optional_params + params = {name: 'USR-1234'} + + stub_request(:post, users_uri).with(request(body: params)).to_return(response) + + assert_kind_of Vonage::Response, users.create(**params) + end + + def test_update_method + stub_request(:patch, user_uri).to_return(response) + + assert_kind_of Vonage::Response, users.update(id: user_id) + end + + def test_update_method_with_optional_params + params = {display_name: 'Foo'} + + stub_request(:patch, user_uri).with(request(body: params)).to_return(response) + + assert_kind_of Vonage::Response, users.update(id: user_id, **params) + end + + def test_update_method_without_id + assert_raises ArgumentError do + users.update + end + end + + def test_delete_method + stub_request(:delete, user_uri).with(request).to_return(status: 204) + + assert_kind_of Vonage::Response, users.delete(id: user_id) + end + + def test_delete_method_without_id + assert_raises ArgumentError do + users.delete + end + end +end diff --git a/vonage.gemspec b/vonage.gemspec index e985d61b..1013d3db 100644 --- a/vonage.gemspec +++ b/vonage.gemspec @@ -12,7 +12,7 @@ Gem::Specification.new do |s| s.summary = 'This is the Ruby Server SDK for Vonage APIs. To use it you\'ll need a Vonage account. Sign up for free at https://www.vonage.com' s.files = Dir.glob('lib/**/*.rb') + %w(LICENSE.txt README.md vonage.gemspec) s.required_ruby_version = '>= 2.5.0' - s.add_dependency('vonage-jwt', '~> 0.1.0') + s.add_dependency('vonage-jwt', '~> 0.1.3') s.add_dependency('zeitwerk', '~> 2', '>= 2.2') s.add_dependency('sorbet-runtime', '~> 0.5') s.add_dependency('multipart-post', '~> 2.0')