openstax_accounts 3.1.1 → 4.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 (43) hide show
  1. checksums.yaml +8 -8
  2. data/app/assets/stylesheets/openstax/accounts/dev.css.scss +4 -0
  3. data/app/controllers/openstax/accounts/dev/accounts_controller.rb +2 -2
  4. data/app/handlers/openstax/accounts/accounts_search.rb +66 -0
  5. data/app/handlers/openstax/accounts/dev/accounts_search.rb +10 -21
  6. data/app/models/openstax/accounts/group.rb +12 -8
  7. data/app/representers/openstax/accounts/api/v1/account_representer.rb +5 -0
  8. data/app/representers/openstax/accounts/api/v1/account_search_representer.rb +12 -17
  9. data/app/representers/openstax/accounts/api/v1/application_account_search_representer.rb +3 -1
  10. data/app/routines/openstax/accounts/dev/create_account.rb +1 -1
  11. data/app/routines/openstax/accounts/search_accounts.rb +16 -171
  12. data/app/routines/openstax/accounts/search_local_accounts.rb +101 -0
  13. data/app/views/openstax/accounts/dev/accounts/_search_results.html.erb +19 -45
  14. data/app/views/openstax/accounts/dev/accounts/index.html.erb +11 -8
  15. data/app/views/openstax/accounts/shared/accounts/_search.html.erb +9 -6
  16. data/db/migrate/{20140811182433_create_openstax_accounts_groups.rb → 1_create_openstax_accounts_groups.rb} +0 -0
  17. data/db/migrate/{20140811182505_create_openstax_accounts_group_members.rb → 2_create_openstax_accounts_group_members.rb} +0 -0
  18. data/db/migrate/{20140811182527_create_openstax_accounts_group_owners.rb → 3_create_openstax_accounts_group_owners.rb} +0 -0
  19. data/db/migrate/{20140811182553_create_openstax_accounts_group_nestings.rb → 4_create_openstax_accounts_group_nestings.rb} +0 -0
  20. data/lib/openstax/accounts/engine.rb +1 -0
  21. data/lib/openstax/accounts/has_many_through_groups.rb +4 -2
  22. data/lib/openstax/accounts/version.rb +1 -1
  23. data/lib/openstax_accounts.rb +11 -5
  24. data/spec/controllers/openstax/accounts/dev/accounts_controller_spec.rb +1 -1
  25. data/spec/controllers/openstax/accounts/sessions_controller_spec.rb +1 -1
  26. data/spec/dummy/app/access_policies/account_access_policy.rb +11 -0
  27. data/spec/dummy/app/controllers/oauth_controller.rb +2 -0
  28. data/spec/dummy/config/environments/production.rb +1 -1
  29. data/spec/dummy/config/environments/test.rb +1 -1
  30. data/spec/dummy/config/initializers/access_policies.rb +1 -0
  31. data/spec/dummy/config/initializers/doorkeeper.rb +75 -0
  32. data/spec/dummy/config/initializers/openstax_accounts.rb +2 -1
  33. data/spec/dummy/db/migrate/{1_create_users.rb → 5_create_users.rb} +0 -0
  34. data/spec/dummy/db/migrate/{2_create_ownerships.rb → 6_create_ownerships.rb} +0 -0
  35. data/spec/dummy/db/schema.rb +1 -1
  36. data/spec/factories/openstax_accounts_account.rb +1 -1
  37. data/spec/factories/openstax_accounts_group.rb +1 -1
  38. data/spec/handlers/openstax/accounts/accounts_search_spec.rb +63 -0
  39. data/spec/handlers/openstax/accounts/dev/accounts_search_spec.rb +55 -0
  40. data/spec/lib/openstax_accounts_spec.rb +66 -66
  41. data/spec/routines/openstax/accounts/search_accounts_spec.rb +23 -23
  42. metadata +60 -15
  43. data/app/routines/openstax/accounts/dev/search_accounts.rb +0 -27
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- MWU2MDQwOTJlMzhjYzQ5M2FlYmVkYmVmYzUzYzQ1MTZiOWUyMWY3Ng==
4
+ OWY3YWZhMGEwYzY0NjgyZGNlOTUyOGY0MmNmOWJhOWI3ODJlZDc5NQ==
5
5
  data.tar.gz: !binary |-
6
- ZjZjMTA3ZGM3YTUyZTUyZTEzZmFkMTg1ZjE0ZTJkZDg3NDUwNGFjOA==
6
+ MzE3MmRlOTlkNTdiM2FkNmUyYzQzYTI2YzA2NDA3NWU4MDRiZGQ3Nw==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- MDg0ZjY1ZTM1NDIyYzkyNTY3NzFlMzBhOWQ5YmI1Y2Y4ZjE0ZTRjZWEwYTU1
10
- MTY4OTY3N2I3MDgwNDUzZTM4MTZkMDBlMjdiY2U4MTcwMjY1YmNjMjkwNDUx
11
- YjY1MjEyZDMxMDkzODkzMjViNWE1N2Q5YmI2N2RlMzYzZDYyZWM=
9
+ NjYxMmNmZmViMjBlMTU3MjJlOGRlYzA1NGNkNThlMDM1ZmU3ZWVhY2VkMmYx
10
+ YzBmZGY3Yjg0ZGI5ODI0MmQ5MDg0ZDZlMmI0Njg1NjVjZTk4OGI1ZDk5M2M1
11
+ Y2Q3MzhhMTU2NGRkZjVmYTM2NTE4ZDM0YzNlNDdjZDBhYjg2ODk=
12
12
  data.tar.gz: !binary |-
13
- MzVmOGY3NjlkYWM2ZjUzZDY1YmM3NGEzYjY3MTI5N2JkMGE3MWFiNjk4YmI0
14
- ODA1ODhkMmY0ZjkxNWU1OTNmYmQwOWMwNjNlOGU2NDdiMzYxNzI4ZDIzODQw
15
- MDQ0MzJmOWNkODRiOTRmNTg2OWIzNGFmYjYyYjQ1YjdmYjdkY2Y=
13
+ ZjM4YTlkMzhmM2IxNGY1NGQ3OTllZjdlMjMwYjE4NWI0ZTQ0NWU3NWUyYjlk
14
+ YjQ4N2MzMzdjNzQ3MzQ0N2I4Zjg1ZjAxMTI3Nzk1NTk0MjY3YThhOGQ2NDk0
15
+ NTYxMjY1YmQ1OWM2N2NjOWEyY2ExZDBlMTYyNDk0M2JjOGMxNGQ=
@@ -29,3 +29,7 @@
29
29
  font-style: italic;
30
30
  }
31
31
  }
32
+
33
+ #search-results-list {
34
+ text-align: center;
35
+ }
@@ -12,11 +12,11 @@ module OpenStax
12
12
 
13
13
  def create
14
14
  handle_with(AccountsCreate,
15
- complete: lambda { redirect_back })
15
+ complete: lambda { redirect_to dev_accounts_path })
16
16
  end
17
17
 
18
18
  def become
19
- @account = Account.find(params[:id])
19
+ @account = Account.find_by(openstax_uid: params[:id])
20
20
  sign_in(@account)
21
21
  redirect_back
22
22
  end
@@ -0,0 +1,66 @@
1
+ module OpenStax
2
+ module Accounts
3
+ class AccountsSearch
4
+
5
+ lev_handler
6
+
7
+ paramify :search do
8
+ attribute :type, type: String
9
+ attribute :query, type: String
10
+ attribute :order_by, type: String
11
+ attribute :page, type: Integer
12
+ attribute :per_page, type: Integer
13
+ end
14
+
15
+ protected
16
+
17
+ def initialize
18
+ @min_characters = \
19
+ OpenStax::Accounts.configuration.min_search_characters
20
+ @max_items = OpenStax::Accounts.configuration.max_search_items
21
+ end
22
+
23
+ def authorized?
24
+ OSU::AccessPolicy.action_allowed?(:search, caller, Account)
25
+ end
26
+
27
+ def handle
28
+ case search_params.type
29
+ when 'Username'
30
+ query = "username:\"#{search_params.query}\""
31
+ when 'Name'
32
+ query = "name:\"#{search_params.query}\""
33
+ when 'First Name'
34
+ query = "first_name:\"#{search_params.query}\""
35
+ when 'Last Name'
36
+ query = "last_name:\"#{search_params.query}\""
37
+ when 'Email'
38
+ query = "email:\"#{search_params.query}\""
39
+ else
40
+ query = search_params.query || ''
41
+ end
42
+
43
+ fatal_error(code: :query_too_short,
44
+ message: "The provided query is too short (minimum #{
45
+ @min_characters} characters).") \
46
+ if !@min_characters.nil? && query.length < @min_characters
47
+
48
+ params = {query: search_params.query,
49
+ order_by: search_params.order_by,
50
+ page: search_params.page,
51
+ per_page: search_params.per_page}
52
+ out = run(OpenStax::Accounts::SearchAccounts, params).outputs
53
+ outputs[:total_count] = out[:total_count]
54
+
55
+ if !@max_items.nil? && outputs[:total_count] > @max_items
56
+ fatal_error(code: :too_many_items,
57
+ message: "The number of matches exceeded the allowed limit of #{
58
+ @max_items} matches. Please refine your query and try again.")
59
+ end
60
+
61
+ outputs[:items] = out[:items].to_a
62
+ end
63
+
64
+ end
65
+ end
66
+ end
@@ -1,39 +1,28 @@
1
1
  module OpenStax
2
2
  module Accounts
3
3
  module Dev
4
- class AccountsSearch
5
-
6
- lev_handler transaction: :no_transaction
4
+ class AccountsSearch < OpenStax::Accounts::AccountsSearch
7
5
 
8
6
  paramify :search do
9
- attribute :terms, type: String
10
7
  attribute :type, type: String
8
+ attribute :query, type: String
9
+ attribute :order_by, type: String
11
10
  attribute :page, type: Integer
11
+ attribute :per_page, type: Integer
12
12
  end
13
13
 
14
- uses_routine OpenStax::Accounts::Dev::SearchAccounts,
15
- as: :search_accounts,
16
- translations: { outputs: {type: :verbatim} }
17
-
18
14
  protected
19
15
 
20
- def authorized?
21
- !Rails.env.production?
16
+ def initialize
17
+ @min_characters = nil
18
+ @max_items = nil
22
19
  end
23
20
 
24
- def handle
25
- case search_params.type
26
- when 'Name'
27
- query = "name:#{search_params.terms.gsub(/\s/,',')}"
28
- when 'Username'
29
- query = "username:#{search_params.terms.gsub(/\s/,',')}"
30
- else
31
- query = search_params.terms || ''
32
- end
33
- run(:search_accounts, query, page: search_params.page || 0)
21
+ def authorized?
22
+ !Rails.env.production?
34
23
  end
35
24
 
36
25
  end
37
26
  end
38
27
  end
39
- end
28
+ end
@@ -84,10 +84,12 @@ module OpenStax::Accounts
84
84
  return [] unless persisted?
85
85
  reload
86
86
 
87
- gids = [id] + (Group.includes(:member_group_nestings)
88
- .where(member_group_nestings: {member_group_id: openstax_uid})
89
- .first.try(:supertree_group_ids) || [])
90
- update_column(:cached_supertree_group_ids, gids.to_yaml)
87
+ gids = [openstax_uid] + (Group.includes(:member_group_nestings)
88
+ .where(member_group_nestings: {
89
+ member_group_id: openstax_uid
90
+ })
91
+ .first.try(:supertree_group_ids) || [])
92
+ update_column(:cached_supertree_group_ids, gids)
91
93
  self.cached_supertree_group_ids = gids
92
94
  end
93
95
 
@@ -96,10 +98,12 @@ module OpenStax::Accounts
96
98
  return [] unless persisted?
97
99
  reload
98
100
 
99
- gids = [id] + Group.includes(:container_group_nesting)
100
- .where(container_group_nesting: {container_group_id: openstax_uid})
101
- .collect{|g| g.subtree_group_ids}.flatten
102
- update_column(:cached_subtree_group_ids, gids.to_yaml)
101
+ gids = [openstax_uid] + Group.includes(:container_group_nesting)
102
+ .where(container_group_nesting: {
103
+ container_group_id: openstax_uid
104
+ })
105
+ .collect{|g| g.subtree_group_ids}.flatten
106
+ update_column(:cached_subtree_group_ids, gids)
103
107
  self.cached_subtree_group_ids = gids
104
108
  end
105
109
 
@@ -1,3 +1,8 @@
1
+ # A representer for Accounts
2
+ #
3
+ # This representer can be used directly or subclassed for an object that delegates
4
+ # openstax_uid, username, first_name, last_name, full_name and title to an account
5
+
1
6
  module OpenStax
2
7
  module Accounts
3
8
  module Api
@@ -2,26 +2,21 @@ module OpenStax
2
2
  module Accounts
3
3
  module Api
4
4
  module V1
5
- class AccountSearchRepresenter < Roar::Decorator
6
- include Roar::Representer::JSON
5
+ class AccountSearchRepresenter < OpenStax::Api::V1::AbstractSearchRepresenter
7
6
 
8
- property :num_matching_accounts,
9
- as: :num_matching_users,
10
- type: Integer
7
+ property :total_count,
8
+ inherit: true,
9
+ schema_info: {
10
+ description: "The number of accounts matching the query; can be more than the number returned"
11
+ }
11
12
 
12
- property :page,
13
- type: Integer
14
-
15
- property :per_page,
16
- type: Integer
17
-
18
- property :order_by,
19
- type: String
20
-
21
- collection :accounts,
22
- as: :users,
13
+ collection :items,
14
+ inherit: true,
23
15
  class: Account,
24
- decorator: AccountRepresenter
16
+ decorator: Api::V1::AccountRepresenter,
17
+ schema_info: {
18
+ description: "The accounts matching the query or a subset thereof when paginating"
19
+ }
25
20
 
26
21
  end
27
22
  end
@@ -8,8 +8,10 @@ module OpenStax
8
8
  as: :application_users,
9
9
  class: OpenStax::Accounts::ApplicationAccount,
10
10
  decorator: ApplicationAccountRepresenter,
11
+ writeable: true,
12
+ readable: true,
11
13
  schema_info: {
12
- description: "The matching accounts that have used this app",
14
+ description: "The accounts of matching users that have used this app",
13
15
  minItems: 0
14
16
  }
15
17
 
@@ -20,7 +20,7 @@ module OpenStax
20
20
 
21
21
  account = OpenStax::Accounts::Account.new
22
22
 
23
- account.openstax_uid = -SecureRandom.hex.to_i(16)
23
+ account.openstax_uid = -SecureRandom.hex(4).to_i(16)/2
24
24
  account.access_token = SecureRandom.hex.to_s
25
25
  account.username = username
26
26
 
@@ -1,191 +1,36 @@
1
- # Routine for searching for accounts
2
- #
3
- # Caller provides a query and some options. The query follows the rules of
4
- # https://github.com/bruce/keyword_search , e.g.:
5
- #
6
- # "username:jps,richb" --> returns the "jps" and "richb" accounts
7
- # "name:John" --> returns accounts with first, last, or full name
8
- # starting with "John"
9
- #
10
- # Query terms can be combined, e.g. "username:jp first_name:john"
11
- #
12
- # There are currently two options to control query pagination:
13
- #
14
- # :per_page -- the max number of results to return per page (default: 20)
15
- # :page -- the zero-indexed page to return (default: 0)
16
- #
17
- # There is also an option to control the ordering:
18
- #
19
- # :order_by -- comma-separated list of fields to sort by, with an optional
20
- # space-separated sort direction (default: "username ASC")
21
- #
22
- # Finally, you can also specify a maximum allowed number of results:
23
- #
24
- # :max_matching_accounts -- the max number of results allowed (default: 10)
25
- #
26
- # This routine will return an empty relation if the number of results exceeds
27
- # max_matching_accounts. You can tell that this situation happened when
28
- # the result has an empty accounts array, but a non-zero num_matching_accounts.
29
-
30
1
  module OpenStax
31
2
  module Accounts
32
3
  class SearchAccounts
33
4
 
34
5
  lev_routine transaction: :no_transaction
6
+
7
+ uses_routine SearchLocalAccounts,
8
+ as: :local_search,
9
+ translations: { outputs: { type: :verbatim } }
35
10
 
36
11
  protected
37
-
38
- SORTABLE_FIELDS = ['username', 'first_name', 'last_name', 'id']
39
- SORT_ASCENDING = 'ASC'
40
- SORT_DESCENDING = 'DESC'
41
12
 
42
- def exec(query, options={})
13
+ def exec(*args)
14
+ params = args.last.is_a?(Hash) ? args.pop : {}
15
+ params[:q] ||= args[0]
16
+ params[:ob] ||= args[1]
17
+ params[:pp] ||= args[2]
18
+ params[:p] ||= args[3]
43
19
 
44
- if !OpenStax::Accounts.configuration.enable_stubbing? &&\
45
- KeywordSearch.search(query).values_at('email', :default).compact.any?
20
+ query = params[:query] || params[:q]
21
+ if !OpenStax::Accounts.configuration.enable_stubbing? && \
22
+ query =~ /email:/
46
23
  # Delegate to Accounts
47
-
48
24
  response = OpenStax::Accounts.search_application_accounts(query)
49
-
50
-
51
- search = OpenStruct.new
52
- search_rep = OpenStax::Accounts::Api::V1::AccountSearchRepresenter.new(search)
53
- search_rep.from_json(response.body)
54
-
55
- # Need to query local database in order to obtain ID's (primary keys)
56
- outputs[:accounts] = OpenStax::Accounts::Account.where {
57
- openstax_uid.in search.accounts.collect{ |u| u.openstax_uid }
58
- }
59
- outputs[:query] = search.q
60
- outputs[:per_page] = search.per_page
61
- outputs[:page] = search.page
62
- outputs[:order_by] = search.order_by
63
- outputs[:num_matching_accounts] = search.num_matching_accounts
64
-
25
+ OpenStax::Accounts::Api::V1::AccountSearchRepresenter \
26
+ .new(outputs).from_json(response.body)
65
27
  else
66
-
67
28
  # Local search
68
- accounts = OpenStax::Accounts::Account.all
69
-
70
- KeywordSearch.search(query) do |with|
71
-
72
- with.default_keyword :any
73
-
74
- with.keyword :username do |usernames|
75
- accounts = accounts.where{username
76
- .like_any my{prep_names(usernames)}}
77
- end
78
-
79
- with.keyword :first_name do |first_names|
80
- accounts = accounts.where{lower(first_name)
81
- .like_any my{prep_names(first_names)}}
82
- end
83
-
84
- with.keyword :last_name do |last_names|
85
- accounts = accounts.where{lower(last_name)
86
- .like_any my{prep_names(last_names)}}
87
- end
88
-
89
- with.keyword :full_name do |full_names|
90
- accounts = accounts.where{lower(full_name)
91
- .like_any my{prep_names(full_names)}}
92
- end
93
-
94
- with.keyword :name do |names|
95
- names = prep_names(names)
96
- accounts = accounts.where{ (lower(full_name).like_any names) |
97
- (lower(last_name).like_any names) |
98
- (lower(first_name).like_any names) }
99
- end
100
-
101
- with.keyword :id do |ids|
102
- accounts = accounts.where{openstax_uid.in ids}
103
- end
104
-
105
- with.keyword :email do |emails|
106
- accounts = OpenStax::Accounts::Account.none
107
- end
108
-
109
- # Rerun the queries above for 'any' terms (which are ones without a
110
- # prefix).
111
-
112
- with.keyword :any do |terms|
113
- names = prep_names(terms)
114
-
115
- accounts = accounts.where{
116
- (lower(username).like_any names) |
117
- (lower(first_name).like_any names) |
118
- (lower(last_name).like_any names) |
119
- (lower(full_name).like_any names) |
120
- (id.in terms)
121
- }
122
- end
123
-
124
- end
125
-
126
- # Pagination
127
-
128
- page = options[:page] || 0
129
- per_page = options[:per_page] || 20
130
-
131
- accounts = accounts.limit(per_page).offset(per_page*page)
132
-
133
- #
134
- # Ordering
135
- #
136
-
137
- # Parse the input
138
- order_bys = (options[:order_by] || 'username').split(',')
139
- .collect{|ob| ob.strip.split(' ')}
140
-
141
- # Toss out bad input, provide default direction
142
- order_bys = order_bys.collect do |order_by|
143
- field, direction = order_by
144
- next if !SORTABLE_FIELDS.include?(field)
145
- direction ||= SORT_ASCENDING
146
- next if direction != SORT_ASCENDING && direction != SORT_DESCENDING
147
- [field, direction]
148
- end
149
-
150
- order_bys.compact!
151
-
152
- # Use a default sort if none provided
153
- order_bys = ['username', SORT_ASCENDING] if order_bys.empty?
154
-
155
- # Convert to query style
156
- order_bys = order_bys.collect{|order_by| "#{order_by[0]} #{order_by[1]}"}
157
-
158
- # Make the ordering call
159
- order_bys.each do |order_by|
160
- accounts = accounts.order(order_by)
161
- end
162
-
163
- # Translate to routine outputs
164
-
165
- outputs[:accounts] = accounts
166
- outputs[:query] = query
167
- outputs[:per_page] = per_page
168
- outputs[:page] = page
169
- outputs[:order_by] = order_bys.join(', ') # Convert back to String
170
- outputs[:num_matching_accounts] = accounts
171
- .except(:offset, :limit, :order).count
172
-
29
+ run(:local_search, params)
173
30
  end
174
31
 
175
- # Return no results if query exceeds maximum allowed number of matches
176
- max_accounts = options[:max_matching_accounts] || \
177
- OpenStax::Accounts.configuration.max_matching_accounts
178
- outputs[:accounts] = OpenStax::Accounts::Account.none \
179
- if outputs[:num_matching_accounts] > max_accounts
180
-
181
- end
182
-
183
- # Downcase, remove all wildcards and put a wildcard at the end.
184
- def prep_names(names)
185
- names.collect{|name| "#{name.downcase.gsub('%', '')}%"}
186
32
  end
187
33
 
188
34
  end
189
-
190
35
  end
191
36
  end