openstax_accounts 0.3.0 → 1.0.0

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 (49) hide show
  1. checksums.yaml +15 -0
  2. data/README.md +20 -0
  3. data/app/controllers/openstax/accounts/dev/base_controller.rb +19 -0
  4. data/app/controllers/openstax/accounts/dev/users_controller.rb +10 -9
  5. data/app/handlers/openstax/accounts/dev/users_index.rb +39 -0
  6. data/app/handlers/openstax/accounts/sessions_omniauth_authenticated.rb +2 -1
  7. data/app/models/openstax/accounts/application_user.rb +7 -0
  8. data/app/models/openstax/accounts/user.rb +15 -3
  9. data/app/representers/openstax/accounts/api/v1/application_user_representer.rb +5 -1
  10. data/app/representers/openstax/accounts/api/v1/application_user_search_representer.rb +19 -0
  11. data/app/representers/openstax/accounts/api/v1/application_users_representer.rb +16 -0
  12. data/app/representers/openstax/accounts/api/v1/user_representer.rb +2 -6
  13. data/app/routines/openstax/accounts/dev/create_user.rb +4 -4
  14. data/app/routines/openstax/accounts/dev/search_users.rb +27 -0
  15. data/app/routines/openstax/accounts/search_users.rb +174 -32
  16. data/app/routines/openstax/accounts/sync_users.rb +44 -0
  17. data/app/views/openstax/accounts/dev/users/_search_results.html.erb +50 -0
  18. data/app/views/openstax/accounts/dev/users/index.js.erb +3 -0
  19. data/app/views/openstax/accounts/dev/users/login.html.erb +6 -5
  20. data/app/views/openstax/accounts/shared/_attention.html.erb +1 -1
  21. data/app/views/openstax/accounts/shared/users/_index.html.erb +24 -0
  22. data/config/routes.rb +5 -4
  23. data/lib/generators/openstax/accounts/schedule/USAGE +8 -0
  24. data/lib/generators/openstax/accounts/schedule/schedule_generator.rb +18 -0
  25. data/lib/generators/openstax/accounts/schedule/templates/schedule.rb +3 -0
  26. data/lib/omniauth/strategies/openstax.rb +1 -1
  27. data/lib/openstax/accounts/engine.rb +2 -0
  28. data/lib/openstax/accounts/version.rb +1 -1
  29. data/lib/openstax_accounts.rb +69 -26
  30. data/spec/controllers/openstax/accounts/dev/users_controller_spec.rb +4 -3
  31. data/spec/dummy/app/controllers/api/application_users_controller.rb +12 -0
  32. data/spec/dummy/config/application.rb +3 -0
  33. data/spec/dummy/config/initializers/openstax_accounts.rb +1 -0
  34. data/spec/dummy/config/routes.rb +5 -4
  35. data/spec/lib/openstax_accounts_spec.rb +19 -11
  36. data/spec/routines/openstax/accounts/dev/create_user_spec.rb +26 -0
  37. data/spec/routines/openstax/accounts/search_users_spec.rb +129 -0
  38. metadata +47 -68
  39. data/app/controllers/openstax/accounts/dev/dev_controller.rb +0 -13
  40. data/app/handlers/openstax/accounts/dev/users_search.rb +0 -38
  41. data/app/representers/openstax/accounts/api/v1/contact_info_representer.rb +0 -23
  42. data/app/representers/openstax/accounts/api/v1/email_address_representer.rb +0 -9
  43. data/app/views/openstax/accounts/dev/users/search.js.erb +0 -21
  44. data/app/views/openstax/accounts/users/_action_create_form.html.erb +0 -9
  45. data/app/views/openstax/accounts/users/_action_dialog.html.erb +0 -10
  46. data/app/views/openstax/accounts/users/_action_list.html.erb +0 -33
  47. data/app/views/openstax/accounts/users/_action_search.html.erb +0 -25
  48. data/app/views/openstax/accounts/users/action_search.js.erb +0 -8
  49. data/spec/dummy/app/controllers/api/users_controller.rb +0 -7
@@ -0,0 +1,44 @@
1
+ # Routine for getting user updates from Accounts
2
+ #
3
+ # Should be scheduled to run regularly
4
+
5
+ module OpenStax
6
+ module Accounts
7
+
8
+ class SyncUsers
9
+
10
+ SYNC_ATTRIBUTES = ['username', 'first_name', 'last_name',
11
+ 'full_name', 'title']
12
+
13
+ lev_routine transaction: :no_transaction
14
+
15
+ protected
16
+
17
+ def exec(options={})
18
+
19
+ return if OpenStax::Accounts.configuration.enable_stubbing?
20
+
21
+ response = OpenStax::Accounts.application_users_updates
22
+
23
+ app_users = []
24
+ app_users_rep = OpenStax::Accounts::Api::V1::ApplicationUsersRepresenter.new(app_users)
25
+ app_users_rep.from_json(response.body)
26
+
27
+ return if app_users.empty?
28
+
29
+ app_users_hash = {}
30
+ app_users.each do |app_user|
31
+ user = OpenStax::Accounts::User.where(:openstax_uid => app_user.user.openstax_uid).first
32
+ user.updating_from_accounts = true
33
+ next unless user.update_attributes(app_user.user.attributes.slice(*SYNC_ATTRIBUTES))
34
+ app_users_hash[app_user.id] = app_user.unread_updates
35
+ end
36
+
37
+ OpenStax::Accounts.application_users_updated(app_users_hash)
38
+
39
+ end
40
+
41
+ end
42
+
43
+ end
44
+ end
@@ -0,0 +1,50 @@
1
+ <%
2
+ page = @handler_result.outputs[:page]
3
+ num_users = @handler_result.outputs[:num_matching_users]
4
+ per_page = @handler_result.outputs[:per_page]
5
+ pages = (num_users * 1.0 / per_page).ceil
6
+ users = @handler_result.outputs[:users]
7
+ %>
8
+
9
+ <div id='search-results-pagination'>
10
+ <%= pluralize(num_users, 'user') %> found.
11
+
12
+ <% if pages > 0 %>
13
+ Page:
14
+
15
+ <%
16
+ linked_page_numbers = [page+1]
17
+ linked_page_numbers.push(1)
18
+ linked_page_numbers.push(pages)
19
+ linked_page_numbers.push(page+1-1, page+1-2, page+1+1, page+1+2)
20
+ linked_page_numbers.reject!{|pp| pp < 1 || pp > pages}
21
+ linked_page_numbers.uniq!
22
+ linked_page_numbers.sort!
23
+ %>
24
+
25
+ <% linked_page_numbers.each do |lpn| %>
26
+ <%= link_to_unless lpn == page + 1,
27
+ lpn,
28
+ dev_users_path(search: {terms: @handler_result.outputs[:query], page: lpn-1, per_page: per_page}), remote: true %>
29
+ <% end %>
30
+ <% end %>
31
+ </div>
32
+
33
+ <%= osu.action_list(
34
+ records: users,
35
+ list: {
36
+ headings: ['Username', 'First Name', 'Last Name', ''],
37
+ widths: ['25%', '25%', '25%', '25%'],
38
+ data_procs:
39
+ [
40
+ Proc.new { |user| user.username },
41
+ Proc.new { |user| user.first_name || '---' },
42
+ Proc.new { |user| user.last_name || '---' },
43
+ Proc.new { |user|
44
+ link_to 'Sign in as',
45
+ become_dev_user_path(user),
46
+ method: :post
47
+ }
48
+ ]
49
+ }
50
+ ) %>
@@ -0,0 +1,3 @@
1
+ <%= unless_errors alerts_html_id: 'dialog-local-alerts' do %>
2
+ $("#search-results-list").html("<%= j(render 'openstax/accounts/dev/users/search_results') %>");
3
+ <% end %>
@@ -1,4 +1,3 @@
1
-
2
1
  <div class="openstax-accounts development-login">
3
2
 
4
3
  <% handler_errors.each do |error| %>
@@ -7,11 +6,13 @@
7
6
 
8
7
  <%= osu.section_block "Development Login" do %>
9
8
 
10
- <p>You need to login, but we're not connected to the Accounts server. Search for a user below
11
- and click the sign in link next to him or her.</p>
9
+ <p>You need to login, but we're not connected to the Accounts server.
10
+ Search for a user below and click the sign in link next to him or her.</p>
12
11
 
13
- <%= render 'openstax/accounts/users/action_search', action_search_path: search_dev_users_path %>
12
+ <%= render 'openstax/accounts/shared/users/index',
13
+ :search_action_path => openstax_accounts.dev_users_path,
14
+ :remote => true %>
14
15
 
15
16
  <% end %>
16
17
 
17
- </div>
18
+ </div>
@@ -1,3 +1,3 @@
1
1
  <% handler_errors.each do |error| %>
2
2
  <%= error.translate %>
3
- <% end %>
3
+ <% end %>
@@ -0,0 +1,24 @@
1
+ <%
2
+ # Clients of this partial can override the following variables:
3
+ search_action_path ||= nil
4
+ remote ||= false
5
+ form_html ||= {id: 'search-form',
6
+ class: 'form-inline'}
7
+ search_types ||= ['Any', 'Username', 'Email']
8
+ %>
9
+
10
+ <%= lev_form_for :search,
11
+ url: search_action_path,
12
+ remote: remote,
13
+ html: form_html do |f| %>
14
+
15
+ Search for
16
+ <%= f.search_field :terms, style: 'width:300px' %>
17
+ in
18
+ <%= f.select :type, search_types, {}, {style: 'width: 150px'} %>
19
+
20
+ <%= f.submit 'Search', class: 'btn btn-primary' %>
21
+
22
+ <% end %>
23
+
24
+ <div id="search-results-list"></div>
data/config/routes.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  OpenStax::Accounts::Engine.routes.draw do
2
- match '/auth/openstax/callback', to: 'sessions#omniauth_authenticated' #omniauth route
2
+ get '/auth/openstax/callback', to: 'sessions#omniauth_authenticated' #omniauth route
3
3
  get '/auth/openstax', :as => 'openstax_login'
4
4
 
5
5
  get 'sessions/new', :as => 'login'
@@ -9,12 +9,13 @@ OpenStax::Accounts::Engine.routes.draw do
9
9
 
10
10
  if OpenStax::Accounts.configuration.enable_stubbing?
11
11
  namespace :dev do
12
- resources :users, :only => [] do
12
+ resources :users, :only => [:index] do
13
13
  collection do
14
14
  get 'login'
15
- post 'search'
16
- post 'become'
15
+ post 'index'
17
16
  end
17
+
18
+ post 'become', :on => :member
18
19
  end
19
20
  end
20
21
  end
@@ -0,0 +1,8 @@
1
+ Description:
2
+ Creates a "whenever" task that checks for updates from Accounts.
3
+
4
+ Example:
5
+ rails generate openstax:accounts:schedule
6
+
7
+ This will create or append to:
8
+ config/schedule.rb
@@ -0,0 +1,18 @@
1
+ # Can't use OpenStax. See https://github.com/rails/rails/issues/13856
2
+ module Openstax
3
+ module Accounts
4
+ class ScheduleGenerator < Rails::Generators::Base
5
+ source_root File.expand_path('../templates', __FILE__)
6
+
7
+ def generate_schedule
8
+ if File.exists?(File.expand_path('config/schedule.rb'))
9
+ File.open(File.expand_path('../templates/schedule.rb', __FILE__)) do |file|
10
+ append_file 'config/schedule.rb', file.read
11
+ end
12
+ else
13
+ copy_file 'schedule.rb', 'config/schedule.rb'
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,3 @@
1
+ every 5.minutes do
2
+ runner "OpenStax::Accounts::SyncUsers.call"
3
+ end
@@ -32,7 +32,7 @@ module OmniAuth
32
32
  end
33
33
 
34
34
  def raw_info
35
- @raw_info ||= access_token.get('/api/users/me.json').parsed
35
+ @raw_info ||= access_token.get('/api/user.json').parsed
36
36
  end
37
37
  end
38
38
  end
@@ -3,6 +3,8 @@ require 'omniauth/strategies/openstax'
3
3
  require 'lev'
4
4
  require 'roar/decorator'
5
5
  require 'roar/representer/json'
6
+ require 'keyword_search'
7
+ require 'squeel'
6
8
 
7
9
  ActiveSupport::Inflector.inflections do |inflect|
8
10
  inflect.acronym 'OpenStax'
@@ -1,5 +1,5 @@
1
1
  module OpenStax
2
2
  module Accounts
3
- VERSION = "0.3.0"
3
+ VERSION = "1.0.0"
4
4
  end
5
5
  end
@@ -6,12 +6,14 @@ require 'openstax/accounts/user_provider'
6
6
  require 'openstax/accounts/current_user_manager'
7
7
 
8
8
  require 'openstax_utilities'
9
- require 'uri'
10
9
  require 'oauth2'
10
+ require 'uri'
11
11
 
12
12
  module OpenStax
13
13
  module Accounts
14
14
 
15
+ DEFAULT_API_VERSION = :v1
16
+
15
17
  class << self
16
18
 
17
19
  ###########################################################################
@@ -27,9 +29,6 @@ module OpenStax
27
29
  # ...
28
30
  # end
29
31
  #
30
- # Set enable_stubbing to true iff you want this engine to fake all
31
- # interaction with the accounts site.
32
- #
33
32
 
34
33
  def configure
35
34
  yield configuration
@@ -40,26 +39,50 @@ module OpenStax
40
39
  end
41
40
 
42
41
  class Configuration
42
+ # openstax_accounts_url
43
+ # Base URL for OpenStax Accounts
44
+ attr_reader :openstax_accounts_url
45
+
46
+ # openstax_application_id
47
+ # OAuth client_id received from OpenStax Accounts
43
48
  attr_accessor :openstax_application_id
49
+
50
+ # openstax_application_secret
51
+ # OAuth client_secret received from OpenStax Accounts
44
52
  attr_accessor :openstax_application_secret
53
+
54
+ # enable_stubbing
55
+ # Set to true if you want this engine to fake all
56
+ # interaction with the accounts site.
45
57
  attr_accessor :enable_stubbing
46
- attr_reader :openstax_accounts_url
58
+
59
+ # logout_via
60
+ # HTTP method to accept for logout requests
47
61
  attr_accessor :logout_via
62
+
48
63
  attr_accessor :default_errors_partial
49
64
  attr_accessor :default_errors_html_id
50
65
  attr_accessor :default_errors_added_trigger
66
+
67
+ # security_transgression_exception
68
+ # Class to be used for security transgression exceptions
51
69
  attr_accessor :security_transgression_exception
52
70
 
53
- # See the "user_provider" discussion in the README
71
+ # See the "user_provider" discussion in the README
54
72
  attr_accessor :user_provider
55
73
 
74
+ # The maximum number of users that can be returned in a call to SearchUsers
75
+ # If more would be returned, the result will be empty instead
76
+ # Can also be passed directly to SearchUsers
77
+ attr_accessor :max_matching_users
78
+
56
79
  def openstax_accounts_url=(url)
57
80
  url.gsub!(/https|http/,'https') if !(url =~ /localhost/)
58
81
  url = url + "/" if url[url.size-1] != '/'
59
82
  @openstax_accounts_url = url
60
83
  end
61
84
 
62
- def initialize
85
+ def initialize
63
86
  @openstax_application_id = 'SET ME!'
64
87
  @openstax_application_secret = 'SET ME!'
65
88
  @openstax_accounts_url = 'https://accounts.openstax.org/'
@@ -70,6 +93,7 @@ module OpenStax
70
93
  @default_errors_added_trigger = 'openstax-accounts-errors-added'
71
94
  @security_transgression_exception = SecurityTransgression
72
95
  @user_provider = OpenStax::Accounts::UserProvider
96
+ @max_matching_users = 10
73
97
  super
74
98
  end
75
99
 
@@ -96,43 +120,62 @@ module OpenStax
96
120
 
97
121
  token_string = options.delete(:access_token)
98
122
  token = token_string.blank? ? client.client_credentials.get_token :
99
- OAuth2::AccessToken.new(client, token_string)
123
+ OAuth2::AccessToken.new(client, token_string)
100
124
 
101
125
  api_url = URI.join(configuration.openstax_accounts_url, 'api/', url)
102
126
 
103
127
  token.request(http_method, api_url, options)
104
128
  end
105
129
 
106
- # Creates an ApplicationUser in Accounts for the configured app
107
- # and the given OpenStax::Accounts::User.
108
- # Also takes an optional API version parameter. Defaults to :v1.
130
+ # Performs an ApplicationUser search in Accounts for the configured app.
131
+ # Takes a query parameter and an optional API version parameter.
132
+ # API version currently defaults to :v1 (may change in the future).
109
133
  # On failure, throws an Exception, just like api_call.
110
134
  # On success, returns an OAuth2::Response object.
111
- def create_application_user(user, version = :v1)
112
- options = {:access_token => user.access_token,
135
+ def application_users_index(query, version = DEFAULT_API_VERSION)
136
+ options = {:params => {:q => query},
113
137
  :api_version => version}
114
- api_call(:post, 'application_users', options)
138
+ api_call(:get, 'application_users', options)
115
139
  end
116
140
 
117
- # Performs a user search in Accounts for the configured app.
118
- # Takes a query parameter and an optional API version parameter.
119
- # API version currently defaults to :v1.
141
+ # Retrieves information about ApplicationUsers that have been recently updated.
120
142
  # On failure, throws an Exception, just like api_call.
121
143
  # On success, returns an OAuth2::Response object.
122
- def user_search(query, version = :v1)
123
- options = {:params => {:q => query},
124
- :api_version => version}
125
- api_call(:get, 'users/search', options)
144
+ def application_users_updates(version = DEFAULT_API_VERSION)
145
+ options = {:api_version => version}
146
+ api_call(:get, 'application_users/updates', options)
147
+ end
148
+
149
+ # Marks ApplicationUser updates as "read".
150
+ # The app_users parameter is a hash that maps ApplicationUser
151
+ # openstax_uid's to the value of the last received unread_updates.
152
+ # On failure, throws an Exception, just like api_call.
153
+ # On success, returns an OAuth2::Response object.
154
+ def application_users_updated(app_users, version = DEFAULT_API_VERSION)
155
+ options = {:api_version => version,
156
+ :body => {:application_users => app_users}}
157
+ api_call(:put, 'application_users/updated', options)
158
+ end
159
+
160
+ # Updates an OpenStax::Accounts::User in Accounts for the configured app.
161
+ # Also takes an optional API version parameter.
162
+ # API version currently defaults to :v1 (may change in the future).
163
+ # On failure, throws an Exception, just like api_call.
164
+ # On success, returns an OAuth2::Response object.
165
+ def user_update(user, version = DEFAULT_API_VERSION)
166
+ options = {:access_token => user.access_token,
167
+ :api_version => version,
168
+ :body => user.attributes.slice('username', 'first_name',
169
+ 'last_name', 'full_name', 'title').to_json}
170
+ api_call(:put, 'user', options)
126
171
  end
127
172
 
128
- protected
173
+ protected
129
174
 
130
175
  def client
131
- @client ||= OAuth2::Client.new(
132
- configuration.openstax_application_id,
176
+ @client ||= OAuth2::Client.new(configuration.openstax_application_id,
133
177
  configuration.openstax_application_secret,
134
- :site => configuration.openstax_accounts_url
135
- )
178
+ :site => configuration.openstax_accounts_url)
136
179
  end
137
180
 
138
181
  end
@@ -5,13 +5,14 @@ module OpenStax::Accounts
5
5
  describe UsersController do
6
6
  routes { OpenStax::Accounts::Engine.routes }
7
7
 
8
- let!(:user) { OpenStax::Accounts::User.create(username: 'some_user',
9
- openstax_uid: 1) }
8
+ let!(:user) { user = FactoryGirl.create :openstax_accounts_user,
9
+ username: 'some_user',
10
+ openstax_uid: 10 }
10
11
 
11
12
  it 'should allow users not in production to become other users' do
12
13
  expect(controller.current_user).to eq(OpenStax::Accounts::User.anonymous)
13
14
  expect(controller.current_user.is_anonymous?).to eq(true)
14
- get :become, user_id: user.id
15
+ post :become, id: user.id
15
16
  expect(controller.current_user).to eq(user)
16
17
  expect(controller.current_user.is_anonymous?).to eq(false)
17
18
  end
@@ -1,7 +1,19 @@
1
1
  module Api
2
2
  class ApplicationUsersController < DummyController
3
+ def index
4
+ dummy(:index)
5
+ end
6
+
3
7
  def create
4
8
  dummy(:create)
5
9
  end
10
+
11
+ def updates
12
+ dummy(:updates)
13
+ end
14
+
15
+ def updated
16
+ dummy(:updated)
17
+ end
6
18
  end
7
19
  end