New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fixes #22624 - User selectable columns model + api #5262
Conversation
Issues: #22624 |
class UserColumnsController < V2::BaseController | ||
include Api::Version2 | ||
|
||
api :GET, "/user_columns/", N_("List of User columns for a user") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
user, capitalization should be consistent
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about delete and update? Also I feel like GET /user_columns
should return all the columns for each resource (would it ever make sense to get that info?) and GET /user_columns/:resource
should return them for a resource
Would it be possible to replace https://github.com/GregSutcliffe/foreman_column_view with this? |
param :resource, String, :required => true | ||
param :columns, Array, :desc => N_("List of user selected columns") | ||
def create | ||
User.current.user_columns.where(:resource => params[:resource]).destroy_all |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CREATE "updates" via this destroy_all?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point will figure out a better way.
app/models/user_column.rb
Outdated
|
||
# Returns user columns representation as the hash object used in memory | ||
def to_hash | ||
{ :columns => columns, :resource => resource } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
attr_exportable might be usable here instead, take a look at the exportable concern
0efb615
to
7ab182e
Compare
@parthaa do we already have some PR or branch with a table that uses this new functionality? I'm curious about how the UI part looks. |
@tstrachota we will be using this on the RH subscriptions page: Due to time constraints we aren't planning on using this anywhere in foreman yet. |
Here are the Actions implemented so far
|
Note: I have listed the intended workflow with the Katello subscriptions UI here |
This approach looks good to me. I would like to see more +1 from on this one, to make sure this doesn't go against any other plans folks have, as it's introducing new API point, that might be complicated to update later. Any other thoughts, @theforeman/core ? |
This doesn't have any way to have default columns defined in any way, which means:
i think there needs to be a registry where the app or plugin can define what columns are available for a resource (and the default enablement for them). |
4fb9177
to
bffd011
Compare
Created a registry and a plugin api to make this work. Added validation and updated controller tests for this to work. |
Generally, the idea is very interesting. A couple of thoughts on the subject:
|
Good points. However this is not meant to replace graphql in terms of optimization. I would think this will work well in conjunction with graphql whenever that aspect is implemented in a mainstream way. For example if we have information on what columns we want, the api javascript will tell server what it exactly wants using GraphQL. This is purely for the UI (and possibly hammer in the future.) It is merely intended to provide and store enough information for that. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks @parthaa, some comments inline.
I do agree with @timogoebel we should consider local storage, however i guess that we still need the definition of the columns somewhere..?
api :GET, "/user_columns/:resource", N_("List of user columns for a resource") | ||
param :resource, String, :required => true | ||
def columns | ||
render :json => { root_node_name => UserColumn.formatted_columns(params[:resource])} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
does it make more sense to reuse rabl?
|
||
api :GET, "/user_columns/:resource", N_("List of user columns for a resource") | ||
param :resource, String, :required => true | ||
def columns |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why deviate from REST? (index, create, update, show, destroy)?
api :DELETE, "/user_columns", N_("Delete columns for a resource") | ||
param :resource, String, :required => true, :desc => N_("name of the resource") | ||
def reset | ||
process_response @user_column.destroy_all |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this will always remove all? just like destroy_all?
in general I would prefer to have one destroy method that get an array of ids, so it can delete one, many or all.
private | ||
|
||
def find_user_column | ||
@user_column = User.current.user_columns.where(:resource => params[:resource]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
does it make sense to move to a scope? I see we use it in multiple places.
app/models/user_column.rb
Outdated
def ensure_columns_valid | ||
valid_columns = ::UserColumnRegistry::Manager.columns(resource) || [] | ||
valid_column_names = valid_columns.map(&:name) | ||
invalid_columns = columns.select{ |col| !valid_column_names.include? col.to_s } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
instead of looping over all columns, you can use something like
invalid = columns - valid_common_names
app/models/user_column.rb
Outdated
current_user_columns = nil | ||
|
||
if User.current.present? | ||
uc = User.current.user_columns.where(:resource => resource).first |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nitpick: I find variable uc
name confusing, how about simple columns?
consider using scope instead of where.
app/models/user_column.rb
Outdated
|
||
if User.current.present? | ||
uc = User.current.user_columns.where(:resource => resource).first | ||
current_user_columns = uc.columns if uc && uc.columns |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can use uc.try(:columns)
app/models/user_column.rb
Outdated
|
||
registry_columns.map do |column| | ||
formatted_column = column.to_hash | ||
formatted_column[:enabled] = if current_user_columns.nil? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
since columns is serialized, empty string will probably be better to use .blank?
vs .nil?
@@ -720,4 +720,8 @@ | |||
map.permission :revoke_personal_access_tokens, | |||
:"api/v2/personal_access_tokens" => [:destroy] | |||
end | |||
|
|||
permission_set.security_block :user_columns do |map| | |||
map.permission :access_user_columns, {:"api/v2/user_columns" => [:index, :create, :columns, :reset, :destroy_all]} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
does it make more sense to be api/v2/user/id/columns
?
@timogoebel @ohadlevy persisting the column selections gives a better user experience because it would be rather confusing for a user to have to select their preferred columns again after initially setting them if they user another browser or otherwise clear the local storage. Also if we store this in the browser's local storage then the columns will be different in the CLI. We also want to expand this in the future to support other table configuration features such as column ordering. Not persisting these items would be frustrating to users who want to customize their table view once and have it shown the same from thereon out. |
/cc @Rohoover |
f9886d3
to
4b6450c
Compare
@timogoebel @ares @ohadlevy had a chat with @tbrisker and decided to remove the access_table_preferences permission. At this point it seems unnecessary since only User.current is allowed to mess with this table for their own preferences (similar to dashboard). Hope that works for everyone. If permissions is indeed desired in the future we can always add them later. |
Works for me. :-) |
app/models/user.rb
Outdated
@@ -42,7 +42,7 @@ class User < ApplicationRecord | |||
has_many :widgets, :dependent => :destroy | |||
has_many :ssh_keys, :dependent => :destroy | |||
has_many :personal_access_tokens, :dependent => :destroy | |||
|
|||
has_many :table_preferences, :dependent => :destroy |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about adding inverse_of
here?
@parthaa looks like |
@tbrisker updated. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not from my side, I'm fine with the last change |
@table_preferences = @user.table_preferences | ||
end | ||
|
||
api :GET, "/users/:user_id/table_preferences/:name", N_("List of table preferences for a table") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I find the description confusing, how about "Table preferences details" ? (but not a list of table for table)
@@ -0,0 +1,10 @@ | |||
class TablePreference < ApplicationRecord |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we care about auditing or search?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think so, this is just UI preferences, not anything that has impact on the system. It's similar to the dashboard widget settings which we don't audit or search.
Adding initial bindings to user selectable columns. This model + api is going to be used in work related Katello plugin's subscription pages. The main intesnt of this PR is to provide a basic model where a user can store "desirable or interested columns for a resource" When the user for example says "I want to only see name column in subscriptions" , we need model that would store that information (similar to widgets or preferences.) This commit facilitates that.
@ohadlevy all good now? :) |
Thanks! |
Gist connected to Usage -> https://gist.github.com/parthaa/36ead0ff6962d6dac33412a1c38e94f7 |
Adding initial bindings to user selectable columns. This model + api is
going to be used in work related Katello plugin's subscription pages.
The main intent of this PR is to provide a basic model where a user can
store "desirable or interested columns for a resource"
When the user for example says "I want to only see name column in
subscriptions" , we need model that would store that information
(similar to widgets or preferences.)
This commit facilitates that.