Skip to content

Commit 32629a3

Browse files
committed
Add helper methods, basic tests, usage examples, and rubocop linting 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.
1 parent 8738f80 commit 32629a3

23 files changed

+4766
-446
lines changed

.github/workflows/ruby-ci.yml

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,32 @@ on:
88
- master
99

1010
jobs:
11+
ruby-ci-lint:
12+
name: Ruby CI - lint
13+
runs-on: ubuntu-latest
14+
15+
strategy:
16+
matrix:
17+
ruby-version: [3.1]
18+
19+
steps:
20+
- uses: actions/checkout@v4
21+
- name: Set up Ruby ${{ matrix.ruby-version }}
22+
uses: ruby/setup-ruby@v1
23+
with:
24+
ruby-version: ${{ matrix.ruby-version }}
25+
- name: Install dependencies
26+
run: bundle install
27+
- name: Lint
28+
run: rake rubocop
29+
1130
ruby-ci:
1231
name: Ruby CI - test
1332
runs-on: ubuntu-latest
1433

1534
strategy:
1635
matrix:
17-
ruby-version: [3.0, 3.1, 3.2]
36+
ruby-version: [3.0, 3.1, 3.2, 3.3, 3.4]
1837

1938
steps:
2039
- uses: actions/checkout@v4
@@ -25,4 +44,4 @@ jobs:
2544
- name: Install dependencies
2645
run: bundle install
2746
- name: Test
28-
run: rake
47+
run: rake test

.rubocop.yml

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
##
2+
# AllCops (Global)
3+
#
4+
AllCops:
5+
NewCops: enable
6+
SuggestExtensions: false
7+
Exclude:
8+
- vendor/**/*
9+
10+
##
11+
# Layout
12+
#
13+
Layout/FirstHashElementIndentation:
14+
EnforcedStyle: consistent
15+
16+
Layout/HashAlignment:
17+
EnforcedLastArgumentHashStyle: ignore_implicit
18+
19+
Layout/SpaceBeforeBlockBraces:
20+
EnforcedStyle: no_space
21+
22+
##
23+
# Metrics
24+
#
25+
Metrics/AbcSize:
26+
Max: 20
27+
CountRepeatedAttributes: false
28+
Exclude:
29+
- test/**/*
30+
AllowedMethods:
31+
- request
32+
- get_all
33+
34+
Metrics/BlockLength:
35+
Enabled: false
36+
37+
Metrics/ClassLength:
38+
Enabled: false
39+
40+
Metrics/CyclomaticComplexity:
41+
AllowedMethods:
42+
- get_all
43+
44+
Metrics/MethodLength:
45+
Enabled: false
46+
47+
Metrics/ParameterLists:
48+
Max: 6
49+
CountKeywordArgs: false
50+
51+
Metrics/PerceivedComplexity:
52+
Enabled: false
53+
54+
##
55+
# Naming
56+
#
57+
Naming/AccessorMethodName:
58+
Exclude:
59+
- lib/duo_api/*
60+
61+
##
62+
# Style
63+
#
64+
Style/NumericLiterals:
65+
Enabled: false
66+
67+
##
68+
# Gemspec
69+
#
70+
Gemspec/DevelopmentDependencies:
71+
EnforcedStyle: gemspec
72+
73+
Gemspec/RequireMFA:
74+
Enabled: false

Gemfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
# frozen_string_literal: true
2+
13
source 'https://rubygems.org'
24

35
gemspec

README.md

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,22 @@
1212

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

15-
## Tested Against Ruby Versions:
16-
* 3.0
15+
# Compatibility
16+
While the gem should work for Ruby versions >= 2.5, tests and linting may only work properly on Ruby versions >= 3.0.
17+
18+
Tests are only run on currently supported Ruby versions.
19+
20+
### Tested Against Ruby Versions:
1721
* 3.1
1822
* 3.2
23+
* 3.3
24+
* 3.4
1925

20-
## TLS 1.2 and 1.3 Support
26+
### TLS 1.2 and 1.3 Support
2127

22-
Duo_api_ruby uses the Ruby openssl extension for TLS operations.
28+
duo_api_ruby uses the Ruby openssl extension for TLS operations.
2329

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

2632
# Installing
2733

@@ -45,27 +51,31 @@ gem 'duo_api', '~> 1.0'
4551
```
4652

4753
# Using
48-
49-
TODO
54+
- Examples of doing things [the hard way](/examples/the_hard_way.md)
55+
- Examples of doing things [the less hard way](/examples/the_less_hard_way.md)
56+
- Examples of doing things [the simple way](/examples/the_simple_way.md)
5057

5158
# Testing
52-
59+
###### (Testing and Linting can be done simultaneously by running `rake` without specifying a task)
5360
```
54-
$ rake
61+
$ rake test
5562
Loaded suite /usr/lib/ruby/vendor_ruby/rake/rake_test_loader
5663
Started
57-
........
58-
59-
Finished in 0.002024715 seconds.
60-
--------------------------------------------------------------------------------------------------------
61-
8 tests, 10 assertions, 0 failures, 0 errors, 0 pendings, 0 omissions, 0 notifications
64+
Finished in 0.123992 seconds.
65+
----------------------------------------------------------------------------------------------------------------------------
66+
368 tests, 1098 assertions, 0 failures, 0 errors, 0 pendings, 0 omissions, 0 notifications
6267
100% passed
63-
--------------------------------------------------------------------------------------------------------
64-
3951.17 tests/s, 4938.97 assertions/s
68+
----------------------------------------------------------------------------------------------------------------------------
69+
2967.93 tests/s, 8855.41 assertions/s
6570
```
6671

6772
# Linting
68-
73+
###### (Testing and Linting can be done simultaneously by running `rake` without specifying a task)
6974
```
70-
$ rubocop
75+
$ rake rubocop
76+
Running RuboCop...
77+
Inspecting 15 files
78+
...............
79+
80+
15 files inspected, no offenses detected
7181
```

Rakefile

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,17 @@
1+
# frozen_string_literal: true
2+
13
require 'rake/testtask'
4+
require 'rubocop/rake_task'
25

3-
Rake::TestTask.new do |t|
4-
t.libs << 'test'
5-
end
6+
task default: %i[test rubocop]
67

78
desc 'Run tests'
8-
task :default => :test
9+
task :test do
10+
Rake::TestTask.new{ |t| t.libs << 'test' }
11+
end
12+
13+
desc 'Run rubocop'
14+
task lint: %i[rubocop]
15+
task :rubocop do
16+
RuboCop::RakeTask.new
17+
end

duo_api.gemspec

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,28 @@
1+
# frozen_string_literal: true
2+
13
Gem::Specification.new do |s|
24
s.name = 'duo_api'
3-
s.version = '1.4.0'
5+
s.version = '1.5.0'
46
s.summary = 'Duo API Ruby'
57
s.description = 'A Ruby implementation of the Duo API.'
68
s.email = '[email protected]'
79
s.homepage = 'https://github.com/duosecurity/duo_api_ruby'
810
s.license = 'BSD-3-Clause'
911
s.authors = ['Duo Security']
1012
s.files = [
13+
'ca_certs.pem',
1114
'lib/duo_api.rb',
12-
'ca_certs.pem'
15+
'lib/duo_api/accounts.rb',
16+
'lib/duo_api/admin.rb',
17+
'lib/duo_api/auth.rb',
18+
'lib/duo_api/client.rb',
19+
'lib/duo_api/helpers.rb'
1320
]
14-
s.add_development_dependency 'rake', '~> 12.0'
15-
s.add_development_dependency 'rubocop', '~> 0.49.0'
16-
s.add_development_dependency 'test-unit', '~> 3.2'
17-
s.add_development_dependency 'mocha', '~> 1.8.0'
18-
s.add_development_dependency 'ostruct', '~> 0.1.0'
21+
s.required_ruby_version = '>= 2.5'
22+
s.add_dependency 'base64', '~> 0.2.0'
23+
s.add_development_dependency 'mocha', '~> 2.7.1'
24+
s.add_development_dependency 'ostruct', '~> 0.6.1'
25+
s.add_development_dependency 'rake', '~> 13.2.1'
26+
s.add_development_dependency 'rubocop', '~> 1.73.1'
27+
s.add_development_dependency 'test-unit', '~> 3.6.7'
1928
end

examples.rb

Lines changed: 0 additions & 39 deletions
This file was deleted.

examples/the_hard_way.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# Doing Things The Hard Way
2+
3+
### Making requests using `request()`
4+
###### - This method returns a raw `Net::HTTPResponse` object, which gives you more control at the expense of simplicity
5+
```
6+
require 'duo_api'
7+
8+
# Initialize the api
9+
client = DuoApi.new(IKEY, SKEY, HOST)
10+
11+
# EXAMPLE 1: Get the first 100 users
12+
resp = client.request('GET', '/admin/v1/users', { limit: '100', offset: '0' })
13+
# print out some info from the response
14+
puts resp.code # Response status code
15+
puts resp.to_hash # Response headers hash
16+
puts resp.message # Response message
17+
puts resp.http_version # Response HTTP version
18+
puts resp.body # Response body
19+
20+
# EXAMPLE 2: retreive the user 'john'
21+
resp2 = client.request('GET', '/admin/v1/users', { username: 'john' })
22+
puts resp2.body
23+
24+
# EXAMPLE 3: create a new user
25+
resp3 = client.request('POST', '/admin/v1/users', { username: 'john2' })
26+
puts resp3.body
27+
28+
# EXAMPLE 4: delete user with user_id: 'DUAE0W526W52YHOBMDO6'
29+
resp4 = client.request('DELETE', '/admin/v1/users/DUAE0W526W52YHOBMDO6')
30+
puts resp4.body
31+
32+
# EXAMPLE 5: Authlog V2. Pagination with next_offset.
33+
resp5 = client.request(
34+
'GET', '/admin/v2/logs/authentication',
35+
{ limit: '1', mintime: '1546371049194', maxtime: '1548963049000' })
36+
puts resp5.body
37+
resp5_body = JSON.parse(resp5.body, symbolize_names: true)
38+
resp6 = client.request(
39+
'GET', '/admin/v2/logs/authentication',
40+
{ limit: '1', mintime: '1546371049194', maxtime: '1548963049000',
41+
next_offset: resp5_body[:response][:metadata][:next_offset].join(',') })
42+
puts resp6.body
43+
```

examples/the_less_hard_way.md

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# Doing Things The Less Hard Way
2+
3+
### Making requests using `get()`, `post()`, `put()`, and `delete()`
4+
###### - These methods return a Hash (with symbol keys) of the parsed JSON response body
5+
```
6+
require 'duo_api'
7+
8+
# Initialize the api
9+
client = DuoApi.new(IKEY, SKEY, HOST)
10+
11+
# EXAMPLE 1: Get single user by username
12+
user = client.get('/admin/v1/users', { username: 'john' })[:response]
13+
14+
# EXAMPLE 2: Create new user
15+
new_user = client.post('/admin/v1/users', { username: 'john2' })[:response]
16+
17+
TODO: MORE EXAMPLES HERE
18+
```
19+
20+
### Making requests using `get_all()`
21+
###### - This method handles paginated responses automatically and returns a Hash (with symbol keys) of the combined parsed JSON response bodies
22+
```
23+
require 'duo_api'
24+
25+
# Initialize the api
26+
client = DuoApi.new(IKEY, SKEY, HOST)
27+
28+
# EXAMPLE 1: Get all users
29+
users = client.get_all('/admin/v1/users')[:response]
30+
31+
TODO: MORE EXAMPLES HERE
32+
```
33+
34+
### Making requests using `get_image()`
35+
###### - This method expects an image content-type and returns the raw response body
36+
```
37+
require 'duo_api'
38+
39+
# Initialize the api
40+
client = DuoApi.new(IKEY, SKEY, HOST)
41+
42+
# EXAMPLE 1: Download logo from Admin API and write to disk
43+
image_data = client.get_image('/admin/v1/logo')
44+
File.write('logo.png', image_data)
45+
```

0 commit comments

Comments
 (0)