discourse_api 0.2.2 → 0.2.4

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.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +11 -0
  3. data/examples/invite_users.rb +3 -0
  4. data/examples/sso.rb +14 -0
  5. data/examples/topic_lists.rb +4 -0
  6. data/lib/discourse_api/api/categories.rb +1 -1
  7. data/lib/discourse_api/api/invite.rb +4 -0
  8. data/lib/discourse_api/api/notifications.rb +10 -0
  9. data/lib/discourse_api/api/private_messages.rb +10 -0
  10. data/lib/discourse_api/api/sso.rb +17 -0
  11. data/lib/discourse_api/api/topics.rb +12 -0
  12. data/lib/discourse_api/api/users.rb +3 -3
  13. data/lib/discourse_api/client.rb +6 -0
  14. data/lib/discourse_api/version.rb +1 -1
  15. data/lib/single_sign_on.rb +98 -0
  16. data/spec/discourse_api/api/categories_spec.rb +2 -8
  17. data/spec/discourse_api/api/notifications_spec.rb +24 -0
  18. data/spec/discourse_api/api/private_messages_spec.rb +22 -0
  19. data/spec/discourse_api/api/search_spec.rb +1 -5
  20. data/spec/discourse_api/api/topics_spec.rb +30 -17
  21. data/spec/discourse_api/api/users_spec.rb +48 -12
  22. data/spec/discourse_api/client_spec.rb +4 -0
  23. data/spec/fixtures/categories.json +72 -1
  24. data/spec/fixtures/category_latest_topics.json +92 -1
  25. data/spec/fixtures/hot.json +113 -1
  26. data/spec/fixtures/latest.json +115 -1
  27. data/spec/fixtures/new.json +113 -1
  28. data/spec/fixtures/notifications.json +16 -0
  29. data/spec/fixtures/private_messages.json +69 -0
  30. data/spec/fixtures/search.json +24 -1
  31. data/spec/fixtures/topic.json +739 -1
  32. data/spec/fixtures/topic_invite_user.json +3 -0
  33. data/spec/fixtures/topics_created_by.json +49 -1
  34. data/spec/fixtures/user.json +65 -1
  35. data/spec/fixtures/user_update_user.json +3 -0
  36. data/spec/fixtures/user_update_username.json +3 -0
  37. metadata +21 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7b27fccecba3357cb727bb0b241e29613ddf4d6c
4
- data.tar.gz: 9eff8ed55d066031a9ca7b875ecbae247c8f54b7
3
+ metadata.gz: 30c8f8cfc3ece69e96755f29c5765f204d505b10
4
+ data.tar.gz: 80873e28b06c2912282a2a9482d579536d97e250
5
5
  SHA512:
6
- metadata.gz: 2dc325485d107e56f286baa033ffbefaf646c32611cfbea7b285a7d628445be7d6e900d7a0626516f6d1fb428d6e8ac6363803349845f51cebba0dbf66c846df
7
- data.tar.gz: cf520bd3e1baba478c6f4ad7ece1f3419dfccdfdb8fdf6d54cec01a3a1433d06bbf254656b457bfb39e14090e329b05e7a5923518b90af185d105f7065a789e8
6
+ metadata.gz: 4fb815d8b714c4c0a90e931265b4d76a027848303c47bec066dbc79a7c3844026c4c8800b9fe88d89ffe460cf17ce97a67de9ea2dc9711e3a2f4c70f3ada1ba0
7
+ data.tar.gz: eba82e043e294893e8655fa8efd1b0c923657f03aa691daf8455739d0bf6b6841b5b5f10624cd8333248494eaac7fa1e40a64b7578a4963adc5746bcfb50c28f
data/README.md CHANGED
@@ -29,6 +29,8 @@ few endpoints available:
29
29
 
30
30
  ```ruby
31
31
  client = DiscourseApi::Client.new("http://try.discourse.org")
32
+ client.api_key = "YOUR_API_KEY"
33
+ client.api_username = "YOUR_USERNAME"
32
34
 
33
35
  # Topic endpoints
34
36
  client.latest_topics #=> Gets a list of the latest topics
@@ -43,6 +45,15 @@ client.search("sandbox") #=> Gets a list of topics that m
43
45
  # Categories endpoint
44
46
  client.categories #=> Gets a list of categories
45
47
  client.category_latest_posts("category-slug") #=> Gets a list of latest posts in a category
48
+
49
+ # SSO endpoint
50
+ client.sync_sso( #=> Synchronizes the SSO record
51
+ sso_secret: "discourse_sso_rocks",
52
+ name: "Test Name",
53
+ username: "test_name",
54
+ email: "name@example.com",
55
+ external_id: "2"
56
+ )
46
57
  ```
47
58
 
48
59
 
@@ -5,6 +5,9 @@ client = DiscourseApi::Client.new("http://localhost:3000")
5
5
  client.api_key = "YOUR_API_KEY"
6
6
  client.api_username = "YOUR_USERNAME"
7
7
 
8
+ # invite user
9
+ client.invite_user(email: "name@example.com", group_ids: "41,42")
10
+
8
11
  # invite to a topic
9
12
  client.invite_user_to_topic(email: "foo@bar.com", topic_id: 1)
10
13
 
@@ -0,0 +1,14 @@
1
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
2
+ require File.expand_path('../../lib/discourse_api', __FILE__)
3
+
4
+ client = DiscourseApi::Client.new("http://localhost:3000")
5
+ client.api_key = "YOUR_API_KEY"
6
+ client.api_username = "YOUR_USERNAME"
7
+
8
+ client.sync_sso(
9
+ sso_secret: "discourse_sso_rocks",
10
+ name: "Test Name",
11
+ username: "test_name",
12
+ email: "name@example.com",
13
+ external_id: "2"
14
+ )
@@ -7,5 +7,9 @@ client.api_username = "YOUR_USERNAME"
7
7
 
8
8
  # get latest topics
9
9
  puts client.latest_topics({})
10
+
11
+ # recategorize topic
12
+ puts client.recategorize_topic(topic_id: 108, category_id: 5)
13
+
10
14
  # get all categories
11
15
  puts client.categories({})
@@ -7,7 +7,7 @@ module DiscourseApi
7
7
  end
8
8
 
9
9
  def category_latest_topics(category_slug)
10
- response = get("/category/#{category_slug}.json")
10
+ response = get("/category/#{category_slug}/l/latest.json")
11
11
  response[:body]['topic_list']['topics']
12
12
  end
13
13
  end
@@ -1,6 +1,10 @@
1
1
  module DiscourseApi
2
2
  module API
3
3
  module Invite
4
+ def invite_user(params={})
5
+ post("/invites", params)
6
+ end
7
+
4
8
  def invite_user_to_topic(params={})
5
9
  post("/t/#{params[:topic_id]}/invite", params)
6
10
  end
@@ -0,0 +1,10 @@
1
+ module DiscourseApi
2
+ module API
3
+ module Notifications
4
+ def notifications
5
+ response = get('/notifications.json')
6
+ response[:body]
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ module DiscourseApi
2
+ module API
3
+ module PrivateMessages
4
+ def private_messages(username, *args)
5
+ response = get("topics/private-messages/#{username}.json", args)
6
+ response[:body]['topic_list']['topics']
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,17 @@
1
+ require 'single_sign_on'
2
+
3
+ module DiscourseApi
4
+ module API
5
+ module SSO
6
+ def sync_sso(params={})
7
+ sso = SingleSignOn.new
8
+ sso.sso_secret = params[:sso_secret]
9
+ sso.name = params[:name]
10
+ sso.username = params[:username]
11
+ sso.email = params[:email]
12
+ sso.external_id = params[:external_id]
13
+ post("/admin/users/sync_sso/", sso.payload)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -15,6 +15,14 @@ module DiscourseApi
15
15
  response[:body]['topic_list']['topics']
16
16
  end
17
17
 
18
+ def rename_topic(topic_id, title)
19
+ put("/t/#{topic_id}.json", { topic_id: topic_id, title: title })
20
+ end
21
+
22
+ def recategorize_topic(topic_id, category_id)
23
+ put("/t/#{topic_id}.json", { topic_id: topic_id, category_id: category_id })
24
+ end
25
+
18
26
  def topic(id, *args)
19
27
  response = get("/t/#{id}.json", args)
20
28
  response[:body]
@@ -24,6 +32,10 @@ module DiscourseApi
24
32
  response = get("/topics/created-by/#{username}.json", args)
25
33
  response[:body]['topic_list']['topics']
26
34
  end
35
+
36
+ def delete_topic(id)
37
+ delete("/t/#{id}.json")
38
+ end
27
39
  end
28
40
  end
29
41
  end
@@ -27,11 +27,11 @@ module DiscourseApi
27
27
  end
28
28
 
29
29
  # Create a user
30
- def create_user(*args)
30
+ def create_user(args={})
31
31
  # First retrieve the honeypot values
32
32
  response = get("/users/hp.json")
33
- args.first[:password_confirmation] = response[:body]['value']
34
- args.first[:challenge] = response[:body]['challenge'].reverse
33
+ args[:password_confirmation] = response[:body]['value']
34
+ args[:challenge] = response[:body]['challenge'].reverse
35
35
 
36
36
  # POST the args
37
37
  post("/users", args)
@@ -4,9 +4,12 @@ require 'json'
4
4
  require 'discourse_api/version'
5
5
  require 'discourse_api/api/categories'
6
6
  require 'discourse_api/api/search'
7
+ require 'discourse_api/api/sso'
7
8
  require 'discourse_api/api/topics'
8
9
  require 'discourse_api/api/users'
9
10
  require 'discourse_api/api/invite'
11
+ require 'discourse_api/api/private_messages'
12
+ require 'discourse_api/api/notifications'
10
13
 
11
14
  module DiscourseApi
12
15
  class Client
@@ -15,9 +18,12 @@ module DiscourseApi
15
18
 
16
19
  include DiscourseApi::API::Categories
17
20
  include DiscourseApi::API::Search
21
+ include DiscourseApi::API::SSO
18
22
  include DiscourseApi::API::Topics
19
23
  include DiscourseApi::API::Users
20
24
  include DiscourseApi::API::Invite
25
+ include DiscourseApi::API::PrivateMessages
26
+ include DiscourseApi::API::Notifications
21
27
 
22
28
  def initialize(host, api_key=nil, api_username=nil)
23
29
  @host = host
@@ -1,3 +1,3 @@
1
1
  module DiscourseApi
2
- VERSION = "0.2.2"
2
+ VERSION = "0.2.4"
3
3
  end
@@ -0,0 +1,98 @@
1
+ require 'base64'
2
+ require 'rack'
3
+ require 'openssl'
4
+
5
+ class SingleSignOn
6
+ ACCESSORS = [:nonce, :name, :username, :email, :avatar_url, :avatar_force_update,
7
+ :about_me, :external_id]
8
+ FIXNUMS = []
9
+ NONCE_EXPIRY_TIME = 600 # 10 minutes
10
+
11
+ attr_accessor(*ACCESSORS)
12
+ attr_accessor :sso_secret, :sso_url
13
+
14
+ def self.sso_secret
15
+ raise RuntimeError, "sso_secret not implemented on class, be sure to set it on instance"
16
+ end
17
+
18
+ def self.sso_url
19
+ raise RuntimeError, "sso_url not implemented on class, be sure to set it on instance"
20
+ end
21
+
22
+ def self.parse(payload, sso_secret = nil)
23
+ sso = new
24
+ sso.sso_secret = sso_secret if sso_secret
25
+
26
+ parsed = Rack::Utils.parse_query(payload)
27
+ if sso.sign(parsed["sso"]) != parsed["sig"]
28
+ raise RuntimeError, "Bad signature for payload"
29
+ end
30
+
31
+ decoded = Base64.decode64(parsed["sso"])
32
+ decoded_hash = Rack::Utils.parse_query(decoded)
33
+
34
+ ACCESSORS.each do |k|
35
+ val = decoded_hash[k.to_s]
36
+ val = val.to_i if FIXNUMS.include? k
37
+ sso.send("#{k}=", val)
38
+ end
39
+
40
+ decoded_hash.each do |k,v|
41
+ # 1234567
42
+ # custom.
43
+ #
44
+ if k[0..6] == "custom."
45
+ field = k[7..-1]
46
+ sso.custom_fields[field] = v
47
+ end
48
+ end
49
+
50
+ sso
51
+ end
52
+
53
+ def sso_secret
54
+ @sso_secret || self.class.sso_secret
55
+ end
56
+
57
+ def sso_url
58
+ @sso_url || self.class.sso_url
59
+ end
60
+
61
+ def custom_fields
62
+ @custom_fields ||= {}
63
+ end
64
+
65
+
66
+ def sign(payload)
67
+ OpenSSL::HMAC.hexdigest("sha256", sso_secret, payload)
68
+ end
69
+
70
+
71
+ def to_url(base_url=nil)
72
+ base = "#{base_url || sso_url}"
73
+ "#{base}#{base.include?('?') ? '&' : '?'}#{payload}"
74
+ end
75
+
76
+ def payload
77
+ payload = Base64.encode64(unsigned_payload)
78
+ "sso=#{CGI::escape(payload)}&sig=#{sign(payload)}"
79
+ end
80
+
81
+ def unsigned_payload
82
+ payload = {}
83
+ ACCESSORS.each do |k|
84
+ next unless (val = send k)
85
+
86
+ payload[k] = val
87
+ end
88
+
89
+ if @custom_fields
90
+ @custom_fields.each do |k,v|
91
+ payload["custom.#{k}"] = v.to_s
92
+ end
93
+ end
94
+
95
+ Rack::Utils.build_query(payload)
96
+ end
97
+
98
+ end
@@ -1,7 +1,7 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe DiscourseApi::API::Categories do
4
- subject { DiscourseApi::Client.new("http://localhost:3000") }
4
+ subject { DiscourseApi::Client.new("http://localhost:3000", "test_d7fd0429940", "test_user" )}
5
5
 
6
6
  describe "#categories" do
7
7
  before do
@@ -9,15 +9,11 @@ describe DiscourseApi::API::Categories do
9
9
  end
10
10
 
11
11
  it "requests the correct resource" do
12
- subject.api_key = 'test_d7fd0429940'
13
- subject.api_username = 'test_user'
14
12
  subject.categories
15
13
  expect(a_get("http://localhost:3000/categories.json?api_key=test_d7fd0429940&api_username=test_user")).to have_been_made
16
14
  end
17
15
 
18
16
  it "returns the requested categories" do
19
- subject.api_key = 'test_d7fd0429940'
20
- subject.api_username = 'test_user'
21
17
  categories = subject.categories
22
18
  expect(categories).to be_an Array
23
19
  expect(categories.first).to be_a Hash
@@ -26,12 +22,10 @@ describe DiscourseApi::API::Categories do
26
22
 
27
23
  describe '#category_latest_topics' do
28
24
  before do
29
- stub_get("http://localhost:3000/category/category-slug.json?api_key=test_d7fd0429940&api_username=test_user").to_return(body: fixture("category_latest_topics.json"), headers: { content_type: "application/json" })
25
+ stub_get("http://localhost:3000/category/category-slug/l/latest.json?api_key=test_d7fd0429940&api_username=test_user").to_return(body: fixture("category_latest_topics.json"), headers: { content_type: "application/json" })
30
26
  end
31
27
 
32
28
  it "returns the latest topics in a category" do
33
- subject.api_key = 'test_d7fd0429940'
34
- subject.api_username = 'test_user'
35
29
  latest_topics = subject.category_latest_topics('category-slug')
36
30
  expect(latest_topics).to be_an Array
37
31
  end
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+
3
+ describe DiscourseApi::API::Notifications do
4
+ subject { DiscourseApi::Client.new("http://localhost:3000", "test_d7fd0429940", "test_user") }
5
+
6
+ describe "#notifications" do
7
+
8
+ before do
9
+ stub_get("http://localhost:3000/notifications.json?api_key=test_d7fd0429940&api_username=test_user").to_return(body: fixture("notifications.json"), headers: { content_type: "application/json" })
10
+ end
11
+
12
+ it "requests the correct resource" do
13
+ subject.notifications
14
+ expect(a_get("http://localhost:3000/notifications.json?api_key=test_d7fd0429940&api_username=test_user")).to have_been_made
15
+ end
16
+
17
+ it "returns the requested notifications" do
18
+ notifications = subject.notifications
19
+ expect(notifications).to be_an Array
20
+ expect(notifications.first).to be_an Hash
21
+ expect(notifications[0]['notification_type']).to eq(9)
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,22 @@
1
+ require 'spec_helper'
2
+
3
+ describe DiscourseApi::API::PrivateMessages do
4
+ subject { DiscourseApi::Client.new("http://localhost:3000", "test_d7fd0429940", "test_user") }
5
+
6
+ describe "#private_messages" do
7
+ before do
8
+ stub_get("http://localhost:3000/topics/private-messages/test_user.json?api_key=test_d7fd0429940&api_username=test_user").to_return(body: fixture("private_messages.json"), headers: { content_type: "application/json" })
9
+ end
10
+
11
+ it "requests the correct resource" do
12
+ subject.private_messages('test_user')
13
+ expect(a_get("http://localhost:3000/topics/private-messages/test_user.json?api_key=test_d7fd0429940&api_username=test_user")).to have_been_made
14
+ end
15
+
16
+ it "returns the requested private messages" do
17
+ private_messages = subject.private_messages('test_user')
18
+ expect(private_messages).to be_an Array
19
+ end
20
+ end
21
+
22
+ end
@@ -1,7 +1,7 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe DiscourseApi::API::Search do
4
- subject { DiscourseApi::Client.new("http://localhost:3000") }
4
+ subject { DiscourseApi::Client.new("http://localhost:3000", "test_d7fd0429940", "test_user" )}
5
5
 
6
6
  describe "#search" do
7
7
  before do
@@ -9,15 +9,11 @@ describe DiscourseApi::API::Search do
9
9
  end
10
10
 
11
11
  it "requests the correct resource" do
12
- subject.api_key = 'test_d7fd0429940'
13
- subject.api_username = 'test_user'
14
12
  subject.search("test")
15
13
  expect(a_get("http://localhost:3000/search.json?api_key=test_d7fd0429940&api_username=test_user").with(query: { term: "test"} )).to have_been_made
16
14
  end
17
15
 
18
16
  it "returns the requested search" do
19
- subject.api_key = 'test_d7fd0429940'
20
- subject.api_username = 'test_user'
21
17
  results = subject.search("test")
22
18
  expect(results).to be_an Array
23
19
  expect(results.first).to be_a Hash
@@ -1,10 +1,22 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe DiscourseApi::API::Topics do
4
- subject { DiscourseApi::Client.new("http://localhost:3000") }
4
+ subject { DiscourseApi::Client.new("http://localhost:3000", "test_d7fd0429940", "test_user") }
5
5
 
6
6
  describe "#invite_user_to_topic" do
7
- it "needs to have a test written for it"
7
+ before do
8
+ stub_post("http://localhost:3000/t/12/invite?api_key=test_d7fd0429940&api_username=test_user").to_return(body: fixture("topic_invite_user.json"), headers: { content_type: "application/json" })
9
+ end
10
+
11
+ it "requests the correct resource" do
12
+ subject.invite_user_to_topic(email: "fake_user@example.com", topic_id: 12)
13
+ expect(a_post("http://localhost:3000/t/12/invite?api_key=test_d7fd0429940&api_username=test_user")).to have_been_made
14
+ end
15
+
16
+ it "returns success" do
17
+ response = subject.invite_user_to_topic(email: "fake_user@example.com", topic_id: 12)
18
+ expect(response[:body]['success']).to be_truthy
19
+ end
8
20
  end
9
21
 
10
22
  describe "#latest_topics" do
@@ -13,15 +25,11 @@ describe DiscourseApi::API::Topics do
13
25
  end
14
26
 
15
27
  it "requests the correct resource" do
16
- subject.api_key = 'test_d7fd0429940'
17
- subject.api_username = 'test_user'
18
28
  subject.latest_topics
19
29
  expect(a_get("http://localhost:3000/latest.json?api_key=test_d7fd0429940&api_username=test_user")).to have_been_made
20
30
  end
21
31
 
22
32
  it "returns the requested topics" do
23
- subject.api_key = 'test_d7fd0429940'
24
- subject.api_username = 'test_user'
25
33
  topics = subject.latest_topics
26
34
  expect(topics).to be_an Array
27
35
  expect(topics.first).to be_a Hash
@@ -34,14 +42,11 @@ describe DiscourseApi::API::Topics do
34
42
  end
35
43
 
36
44
  it "requests the correct resource" do
37
- subject.api_key = 'test_d7fd0429940'
38
- subject.api_username = 'test_user'
39
45
  subject.new_topics
40
46
  expect(a_get("http://localhost:3000/new.json?api_key=test_d7fd0429940&api_username=test_user")).to have_been_made
41
47
  end
42
48
 
43
49
  it "returns the requested topics" do
44
- subject.api_key = 'test_d7fd0429940'
45
50
  subject.api_username = 'test_user'
46
51
  topics = subject.new_topics
47
52
  expect(topics).to be_an Array
@@ -55,36 +60,44 @@ describe DiscourseApi::API::Topics do
55
60
  end
56
61
 
57
62
  it "requests the correct resource" do
58
- subject.api_key = 'test_d7fd0429940'
59
- subject.api_username = 'test_user'
60
63
  subject.topic(57)
61
64
  expect(a_get("http://localhost:3000/t/57.json?api_key=test_d7fd0429940&api_username=test_user")).to have_been_made
62
65
  end
63
66
 
64
67
  it "returns the requested topic" do
65
- subject.api_key = 'test_d7fd0429940'
66
- subject.api_username = 'test_user'
67
68
  topic = subject.topic(57)
68
69
  expect(topic).to be_a Hash
69
70
  expect(topic["id"]).to eq(57)
70
71
  end
71
72
  end
72
73
 
74
+ describe "#update_topic" do
75
+ before do
76
+ stub_put("http://localhost:3000/t/57.json?api_key=test_d7fd0429940&api_username=test_user").to_return(body: fixture("topic.json"), headers: { content_type: "application/json" })
77
+ end
78
+
79
+ it "renames the topic" do
80
+ subject.rename_topic(57, "A new title!")
81
+ expect(a_put("http://localhost:3000/t/57.json?api_key=test_d7fd0429940&api_username=test_user")).to have_been_made
82
+ end
83
+
84
+ it "assigns the topic a new category" do
85
+ subject.recategorize_topic(57, 3)
86
+ expect(a_put("http://localhost:3000/t/57.json?api_key=test_d7fd0429940&api_username=test_user")).to have_been_made
87
+ end
88
+ end
89
+
73
90
  describe "#topics_by" do
74
91
  before do
75
92
  stub_get("http://localhost:3000/topics/created-by/test.json?api_key=test_d7fd0429940&api_username=test_user").to_return(body: fixture("topics_created_by.json"), headers: { content_type: "application/json" })
76
93
  end
77
94
 
78
95
  it "requests the correct resource" do
79
- subject.api_key = 'test_d7fd0429940'
80
- subject.api_username = 'test_user'
81
96
  subject.topics_by('test')
82
97
  expect(a_get("http://localhost:3000/topics/created-by/test.json?api_key=test_d7fd0429940&api_username=test_user")).to have_been_made
83
98
  end
84
99
 
85
100
  it "returns the requested topics" do
86
- subject.api_key = 'test_d7fd0429940'
87
- subject.api_username = 'test_user'
88
101
  topics = subject.topics_by('test')
89
102
  expect(topics).to be_an Array
90
103
  expect(topics.first).to be_a Hash