Skip to content

Commit 0522030

Browse files
authored
Merge pull request #129 from mizoR/support-oauth
Support OAuth
2 parents 15a4188 + 4b3c17a commit 0522030

File tree

8 files changed

+181
-15
lines changed

8 files changed

+181
-15
lines changed

README.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ gem 'line-bot-api'
2323

2424
And then execute:
2525

26-
```ruby
26+
```sh
2727
bundle
2828
```
2929

@@ -44,6 +44,7 @@ require 'line/bot'
4444

4545
def client
4646
@client ||= Line::Bot::Client.new { |config|
47+
config.channel_id = ENV["LINE_CHANNEL_ID"]
4748
config.channel_secret = ENV["LINE_CHANNEL_SECRET"]
4849
config.channel_token = ENV["LINE_CHANNEL_TOKEN"]
4950
}

examples/kitchensink/app.rb

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
def client
1111
@client ||= Line::Bot::Client.new do |config|
12+
config.channel_id = ENV["LINE_CHANNEL_ID"]
1213
config.channel_secret = ENV["LINE_CHANNEL_SECRET"]
1314
config.channel_token = ENV["LINE_CHANNEL_TOKEN"]
1415
config.http_options = {

lib/line/bot/client.rb

+53-1
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,13 @@
1616
require 'base64'
1717
require 'net/http'
1818
require 'openssl'
19+
require 'uri'
1920

2021
module Line
2122
module Bot
2223
class Client
2324
# @return [String]
24-
attr_accessor :channel_token, :channel_secret, :endpoint
25+
attr_accessor :channel_token, :channel_id, :channel_secret, :endpoint
2526

2627
# @return [Object]
2728
attr_accessor :httpclient
@@ -56,6 +57,49 @@ def credentials
5657
}
5758
end
5859

60+
# Issue channel access token
61+
#
62+
# @param grant_type [String] Grant type
63+
#
64+
# @return [Net::HTTPResponse]
65+
def issue_channel_token(grant_type = 'client_credentials')
66+
channel_id_required
67+
channel_secret_required
68+
69+
payload = URI.encode_www_form(
70+
grant_type: grant_type,
71+
client_id: channel_id,
72+
client_secret: channel_secret
73+
)
74+
75+
request = Request.new do |config|
76+
config.httpclient = httpclient
77+
config.endpoint = endpoint
78+
config.endpoint_path = '/oauth/accessToken'
79+
config.content_type = 'application/x-www-form-urlencoded'
80+
config.payload = payload
81+
end
82+
83+
request.post
84+
end
85+
86+
# Revoke channel access token
87+
#
88+
# @return [Net::HTTPResponse]
89+
def revoke_channel_token(access_token)
90+
payload = URI.encode_www_form(access_token: access_token)
91+
92+
request = Request.new do |config|
93+
config.httpclient = httpclient
94+
config.endpoint = endpoint
95+
config.endpoint_path = '/oauth/revoke'
96+
config.content_type = 'application/x-www-form-urlencoded'
97+
config.payload = payload
98+
end
99+
100+
request.post
101+
end
102+
59103
# Push messages to line server and to user.
60104
#
61105
# @param user_id [String] User's identifiers
@@ -575,6 +619,14 @@ def secure_compare(a, b)
575619
def channel_token_required
576620
raise ArgumentError, '`channel_token` is not configured' unless channel_token
577621
end
622+
623+
def channel_id_required
624+
raise ArgumentError, '`channel_id` is not configured' unless channel_id
625+
end
626+
627+
def channel_secret_required
628+
raise ArgumentError, '`channel_secret` is not configured' unless channel_secret
629+
end
578630
end
579631
end
580632
end

lib/line/bot/request.rb

+26-13
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ module Bot
2222
class Request
2323
attr_accessor :endpoint, :endpoint_path, :credentials, :to, :reply_token, :messages, :httpclient, :payload, :file
2424

25+
attr_writer :content_type
26+
2527
# Initializes a new Request
2628
#
2729
# @return [Line::Bot::Request]
@@ -44,27 +46,22 @@ def payload
4446

4547
# @return [Hash]
4648
def header
47-
content_type =
48-
if file.is_a? File
49-
case file.path
50-
when /\.png\z/i then 'image/png'
51-
when /\.jpe?g\z/i then 'image/jpeg'
52-
else
53-
raise ArgumentError.new("invalid file extension: #{file.path}")
54-
end
55-
else
56-
'application/json; charset=UTF-8'
57-
end
58-
5949
header = {
6050
'Content-Type' => content_type,
6151
'User-Agent' => "LINE-BotSDK-Ruby/#{Line::Bot::API::VERSION}",
6252
}
63-
hash = credentials.inject({}) { |h, (k, v)| h[k] = v.to_s; h }
53+
hash = (credentials || {}).inject({}) { |h, (k, v)| h[k] = v.to_s; h }
6454

6555
header.merge(hash)
6656
end
6757

58+
# @return [String]
59+
def content_type
60+
return @content_type if @content_type
61+
62+
guess_content_type
63+
end
64+
6865
# Get content of specified URL.
6966
#
7067
# @return [Net::HTTPResponse]
@@ -99,6 +96,22 @@ def assert_for_posting_message
9996
def assert_for_deleting_message
10097
raise ArgumentError, 'Wrong argument type `endpoint_path`' unless endpoint_path.is_a?(String)
10198
end
99+
100+
private
101+
102+
# @return [String]
103+
def guess_content_type
104+
if file.is_a? File
105+
case file.path
106+
when /\.png\z/i then 'image/png'
107+
when /\.jpe?g\z/i then 'image/jpeg'
108+
else
109+
raise ArgumentError.new("invalid file extension: #{file.path}")
110+
end
111+
else
112+
'application/json; charset=UTF-8'
113+
end
114+
end
102115
end
103116
end
104117
end

spec/fixtures/line/bot/preview.jpg

80.2 KB
Loading

spec/fixtures/line/bot/video.mp4

1.73 MB
Binary file not shown.
+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
require 'spec_helper'
2+
require 'webmock/rspec'
3+
require 'json'
4+
5+
OAUTH_CHANNEL_TOKEN_ISSUE_CONTENT = <<"EOS"
6+
{
7+
"access_token": "W1TeHCgfH2Liwaxxxxx1",
8+
"expires_in": 2592000,
9+
"token_type": "Bearer"
10+
}
11+
EOS
12+
13+
WebMock.allow_net_connect!
14+
15+
describe Line::Bot::Client do
16+
def dummy_config
17+
{
18+
channel_id: 'channel id',
19+
channel_secret: 'channel secret',
20+
}
21+
end
22+
23+
def generate_client
24+
Line::Bot::Client.new do |config|
25+
config.channel_id = dummy_config[:channel_id]
26+
config.channel_secret = dummy_config[:channel_secret]
27+
end
28+
end
29+
30+
it 'issues an oauth access token' do
31+
uri_template = Addressable::Template.new Line::Bot::API::DEFAULT_ENDPOINT + '/oauth/accessToken'
32+
stub_request(:post, uri_template).to_return { |request| {body: OAUTH_CHANNEL_TOKEN_ISSUE_CONTENT, status: 200} }
33+
34+
client = generate_client
35+
response = client.issue_channel_token
36+
37+
expect(response).to be_a(Net::HTTPOK)
38+
result = JSON.parse(response.body)
39+
expect(result['access_token']).to eq 'W1TeHCgfH2Liwaxxxxx1'
40+
expect(result['expires_in']).to eq 2592000
41+
expect(result['token_type']).to eq 'Bearer'
42+
end
43+
44+
it 'revokes the oauth access token' do
45+
uri_template = Addressable::Template.new Line::Bot::API::DEFAULT_ENDPOINT + '/oauth/revoke'
46+
stub_request(:post, uri_template).to_return { |request| {body: '', status: 200} }
47+
48+
client = generate_client
49+
50+
response = client.revoke_channel_token('W1TeHCgfH2Liwaxxxxx1')
51+
52+
expect(response).to be_a(Net::HTTPOK)
53+
end
54+
end

spec/line/bot/request_spec.rb

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
require 'spec_helper'
2+
3+
describe Line::Bot::Request do
4+
describe '#content_type' do
5+
subject { request.content_type }
6+
7+
context 'content type is not specified' do
8+
let(:request) { described_class.new }
9+
10+
it { is_expected.to eq 'application/json; charset=UTF-8' }
11+
end
12+
13+
context 'content type is specified' do
14+
let(:request) do
15+
described_class.new do |config|
16+
config.content_type = 'application/x-www-form-urlencoded'
17+
end
18+
end
19+
20+
it { is_expected.to eq 'application/x-www-form-urlencoded' }
21+
end
22+
23+
context 'jpeg file is assigned' do
24+
let(:request) do
25+
described_class.new do |config|
26+
config.file = File.open('spec/fixtures/line/bot/preview.jpg')
27+
end
28+
end
29+
30+
it { is_expected.to eq 'image/jpeg' }
31+
end
32+
33+
context 'unsupported file is assigned' do
34+
let(:request) do
35+
described_class.new do |config|
36+
config.file = File.open('spec/fixtures/line/bot/video.mp4')
37+
end
38+
end
39+
40+
it do
41+
expect { subject }.to raise_error(ArgumentError)
42+
end
43+
end
44+
end
45+
end

0 commit comments

Comments
 (0)