Skip to content

Commit

Permalink
Add helper methods, basic tests, usage examples, and rubocop linting …
Browse files Browse the repository at this point in the history
…rules. Lint/format all code. Update minimum ruby version and versions used for testing to current supported version list. Update and add missing dependencies for upcoming ruby changes.
  • Loading branch information
duo-lfalsetta committed Mar 4, 2025
1 parent 8738f80 commit 0846914
Show file tree
Hide file tree
Showing 25 changed files with 4,985 additions and 446 deletions.
23 changes: 21 additions & 2 deletions .github/workflows/ruby-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,32 @@ on:
- master

jobs:
ruby-ci-lint:
name: Ruby CI - lint
runs-on: ubuntu-latest

strategy:
matrix:
ruby-version: [3.1]

steps:
- uses: actions/checkout@v4
- name: Set up Ruby ${{ matrix.ruby-version }}
uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ matrix.ruby-version }}
- name: Install dependencies
run: bundle install
- name: Lint
run: rake rubocop

ruby-ci:
name: Ruby CI - test
runs-on: ubuntu-latest

strategy:
matrix:
ruby-version: [3.0, 3.1, 3.2]
ruby-version: [3.0, 3.1, 3.2, 3.3, 3.4]

steps:
- uses: actions/checkout@v4
Expand All @@ -25,4 +44,4 @@ jobs:
- name: Install dependencies
run: bundle install
- name: Test
run: rake
run: rake test
74 changes: 74 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
##
# AllCops (Global)
#
AllCops:
NewCops: enable
SuggestExtensions: false
Exclude:
- vendor/**/*

##
# Layout
#
Layout/FirstHashElementIndentation:
EnforcedStyle: consistent

Layout/HashAlignment:
EnforcedLastArgumentHashStyle: ignore_implicit

Layout/SpaceBeforeBlockBraces:
EnforcedStyle: no_space

##
# Metrics
#
Metrics/AbcSize:
Max: 20
CountRepeatedAttributes: false
Exclude:
- test/**/*
AllowedMethods:
- request
- get_all

Metrics/BlockLength:
Enabled: false

Metrics/ClassLength:
Enabled: false

Metrics/CyclomaticComplexity:
AllowedMethods:
- get_all

Metrics/MethodLength:
Enabled: false

Metrics/ParameterLists:
Max: 6
CountKeywordArgs: false

Metrics/PerceivedComplexity:
Enabled: false

##
# Naming
#
Naming/AccessorMethodName:
Exclude:
- lib/duo_api/*

##
# Style
#
Style/NumericLiterals:
Enabled: false

##
# Gemspec
#
Gemspec/DevelopmentDependencies:
EnforcedStyle: gemspec

Gemspec/RequireMFA:
Enabled: false
2 changes: 2 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# frozen_string_literal: true

source 'https://rubygems.org'

gemspec
46 changes: 28 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,22 @@

**Accounts** - https://www.duosecurity.com/docs/accountsapi

## Tested Against Ruby Versions:
* 3.0
# Compatibility
While the gem should work for Ruby versions >= 2.5, tests and linting may only work properly on Ruby versions >= 3.0.

Tests are only run on currently supported Ruby versions.

### Tested Against Ruby Versions:
* 3.1
* 3.2
* 3.3
* 3.4

## TLS 1.2 and 1.3 Support
### TLS 1.2 and 1.3 Support

Duo_api_ruby uses the Ruby openssl extension for TLS operations.
duo_api_ruby uses the Ruby openssl extension for TLS operations.

All currently supported Ruby versions (2.7 and higher) support TLS 1.2 and 1.3.
All Ruby versions compatible with this gem (2.5 and higher) support TLS 1.2 and 1.3.

# Installing

Expand All @@ -45,27 +51,31 @@ gem 'duo_api', '~> 1.0'
```

# Using

TODO
- Examples of doing things [the hard way](/examples/the_hard_way.md)
- Examples of doing things [the less hard way](/examples/the_less_hard_way.md)
- Examples of doing things [the simple way](/examples/the_simple_way.md)

# Testing

###### (Testing and Linting can be done simultaneously by running `rake` without specifying a task)
```
$ rake
$ rake test
Loaded suite /usr/lib/ruby/vendor_ruby/rake/rake_test_loader
Started
........
Finished in 0.002024715 seconds.
--------------------------------------------------------------------------------------------------------
8 tests, 10 assertions, 0 failures, 0 errors, 0 pendings, 0 omissions, 0 notifications
Finished in 0.123992 seconds.
----------------------------------------------------------------------------------------------------------------------------
368 tests, 1098 assertions, 0 failures, 0 errors, 0 pendings, 0 omissions, 0 notifications
100% passed
--------------------------------------------------------------------------------------------------------
3951.17 tests/s, 4938.97 assertions/s
----------------------------------------------------------------------------------------------------------------------------
2967.93 tests/s, 8855.41 assertions/s
```

# Linting

###### (Testing and Linting can be done simultaneously by running `rake` without specifying a task)
```
$ rubocop
$ rake rubocop
Running RuboCop...
Inspecting 15 files
...............
15 files inspected, no offenses detected
```
17 changes: 13 additions & 4 deletions Rakefile
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
# frozen_string_literal: true

require 'rake/testtask'
require 'rubocop/rake_task'

Rake::TestTask.new do |t|
t.libs << 'test'
end
task default: %i[test rubocop]

desc 'Run tests'
task :default => :test
task :test do
Rake::TestTask.new{ |t| t.libs << 'test' }
end

desc 'Run rubocop'
task lint: %i[rubocop]
task :rubocop do
RuboCop::RakeTask.new
end
24 changes: 17 additions & 7 deletions duo_api.gemspec
Original file line number Diff line number Diff line change
@@ -1,19 +1,29 @@
# frozen_string_literal: true

Gem::Specification.new do |s|
s.name = 'duo_api'
s.version = '1.4.0'
s.version = '1.5.0'
s.summary = 'Duo API Ruby'
s.description = 'A Ruby implementation of the Duo API.'
s.email = '[email protected]'
s.homepage = 'https://github.com/duosecurity/duo_api_ruby'
s.license = 'BSD-3-Clause'
s.authors = ['Duo Security']
s.files = [
'ca_certs.pem',
'lib/duo_api.rb',
'ca_certs.pem'
'lib/duo_api/accounts.rb',
'lib/duo_api/admin.rb',
'lib/duo_api/auth.rb',
'lib/duo_api/client.rb',
'lib/duo_api/device.rb',
'lib/duo_api/helpers.rb'
]
s.add_development_dependency 'rake', '~> 12.0'
s.add_development_dependency 'rubocop', '~> 0.49.0'
s.add_development_dependency 'test-unit', '~> 3.2'
s.add_development_dependency 'mocha', '~> 1.8.0'
s.add_development_dependency 'ostruct', '~> 0.1.0'
s.required_ruby_version = '>= 2.5'
s.add_dependency 'base64', '~> 0.2.0'
s.add_development_dependency 'mocha', '~> 2.7.1'
s.add_development_dependency 'ostruct', '~> 0.6.1'
s.add_development_dependency 'rake', '~> 13.2.1'
s.add_development_dependency 'rubocop', '~> 1.73.1'
s.add_development_dependency 'test-unit', '~> 3.6.7'
end
39 changes: 0 additions & 39 deletions examples.rb

This file was deleted.

43 changes: 43 additions & 0 deletions examples/the_hard_way.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Doing Things The Hard Way

### Making requests using `request()`
###### - This method returns a raw `Net::HTTPResponse` object, which gives you more control at the expense of simplicity
```
require 'duo_api'
# Initialize the api
client = DuoApi.new(IKEY, SKEY, HOST)
# EXAMPLE 1: Get the first 100 users
resp = client.request('GET', '/admin/v1/users', { limit: '100', offset: '0' })
# print out some info from the response
puts resp.code # Response status code
puts resp.to_hash # Response headers hash
puts resp.message # Response message
puts resp.http_version # Response HTTP version
puts resp.body # Response body
# EXAMPLE 2: retreive the user 'john'
resp2 = client.request('GET', '/admin/v1/users', { username: 'john' })
puts resp2.body
# EXAMPLE 3: create a new user
resp3 = client.request('POST', '/admin/v1/users', { username: 'john2' })
puts resp3.body
# EXAMPLE 4: delete user with user_id: 'DUAE0W526W52YHOBMDO6'
resp4 = client.request('DELETE', '/admin/v1/users/DUAE0W526W52YHOBMDO6')
puts resp4.body
# EXAMPLE 5: Authlog V2. Pagination with next_offset.
resp5 = client.request(
'GET', '/admin/v2/logs/authentication',
{ limit: '1', mintime: '1546371049194', maxtime: '1548963049000' })
puts resp5.body
resp5_body = JSON.parse(resp5.body, symbolize_names: true)
resp6 = client.request(
'GET', '/admin/v2/logs/authentication',
{ limit: '1', mintime: '1546371049194', maxtime: '1548963049000',
next_offset: resp5_body[:response][:metadata][:next_offset].join(',') })
puts resp6.body
```
45 changes: 45 additions & 0 deletions examples/the_less_hard_way.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Doing Things The Less Hard Way

### Making requests using `get()`, `post()`, `put()`, and `delete()`
###### - These methods return a Hash (with symbol keys) of the parsed JSON response body
```
require 'duo_api'
# Initialize the api
client = DuoApi.new(IKEY, SKEY, HOST)
# EXAMPLE 1: Get single user by username
user = client.get('/admin/v1/users', { username: 'john' })[:response]
# EXAMPLE 2: Create new user
new_user = client.post('/admin/v1/users', { username: 'john2' })[:response]
TODO: MORE EXAMPLES HERE
```

### Making requests using `get_all()`
###### - This method handles paginated responses automatically and returns a Hash (with symbol keys) of the combined parsed JSON response bodies
```
require 'duo_api'
# Initialize the api
client = DuoApi.new(IKEY, SKEY, HOST)
# EXAMPLE 1: Get all users
users = client.get_all('/admin/v1/users')[:response]
TODO: MORE EXAMPLES HERE
```

### Making requests using `get_image()`
###### - This method expects an image content-type and returns the raw response body
```
require 'duo_api'
# Initialize the api
client = DuoApi.new(IKEY, SKEY, HOST)
# EXAMPLE 1: Download logo from Admin API and write to disk
image_data = client.get_image('/admin/v1/logo')
File.write('logo.png', image_data)
```
Loading

0 comments on commit 0846914

Please sign in to comment.