Commit 8af8ad82 authored by Jiaan Louw's avatar Jiaan Louw Committed by Mark Florian

Add sort by dropdown to audit logs

This is the initial commit that adds a sort by dropdown
to audit logs.

Updates the audit log event view to add the dropdown.

First pass at implementing sorting in the audit log
event controller.
parent d8149c08
...@@ -19,6 +19,18 @@ module ExploreHelper ...@@ -19,6 +19,18 @@ module ExploreHelper
request_path_with_options(options) request_path_with_options(options)
end end
def filter_audit_path(options = {})
exist_opts = {
entity_type: params[:entity_type],
entity_id: params[:entity_id],
created_before: params[:created_before],
created_after: params[:created_after],
sort: params[:sort]
}
options = exist_opts.merge(options).delete_if { |key, value| value.blank? }
request_path_with_options(options)
end
def filter_groups_path(options = {}) def filter_groups_path(options = {})
request_path_with_options(options) request_path_with_options(options)
end end
......
...@@ -207,6 +207,13 @@ module SortingHelper ...@@ -207,6 +207,13 @@ module SortingHelper
}.merge(issuable_sort_option_overrides) }.merge(issuable_sort_option_overrides)
end end
def audit_logs_sort_order_hash
{
sort_value_recently_created => sort_title_recently_created,
sort_value_oldest_created => sort_title_oldest_created
}
end
def issuable_sort_option_title(sort_value) def issuable_sort_option_title(sort_value)
sort_value = issuable_sort_option_overrides[sort_value] || sort_value sort_value = issuable_sort_option_overrides[sort_value] || sort_value
......
...@@ -13,6 +13,8 @@ class AuditEvent < ApplicationRecord ...@@ -13,6 +13,8 @@ class AuditEvent < ApplicationRecord
scope :by_entity_type, -> (entity_type) { where(entity_type: entity_type) } scope :by_entity_type, -> (entity_type) { where(entity_type: entity_type) }
scope :by_entity_id, -> (entity_id) { where(entity_id: entity_id) } scope :by_entity_id, -> (entity_id) { where(entity_id: entity_id) }
scope :order_by_id_desc, -> { order(id: :desc) }
scope :order_by_id_asc, -> { order(id: :asc) }
after_initialize :initialize_details after_initialize :initialize_details
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
class Admin::AuditLogsController < Admin::ApplicationController class Admin::AuditLogsController < Admin::ApplicationController
include AuditEvents::EnforcesValidDateParams include AuditEvents::EnforcesValidDateParams
include AuditEvents::AuditLogsParams include AuditEvents::AuditLogsParams
include AuditEvents::Sortable
before_action :check_license_admin_audit_log_available! before_action :check_license_admin_audit_log_available!
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
module AuditEvents module AuditEvents
module AuditLogsParams module AuditLogsParams
def audit_logs_params def audit_logs_params
params.permit(:entity_type, :entity_id, :created_before, :created_after) params.permit(:entity_type, :entity_id, :created_before, :created_after, :sort)
end end
end end
end end
# frozen_string_literal: true
module AuditEvents
module Sortable
extend ActiveSupport::Concern
include SortingHelper
include SortingPreference
included do
before_action :set_sorting, only: [:index]
end
private
def default_sort_order
sort_value_recently_created
end
def set_sorting
params[:sort] = set_sort_order
end
end
end
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
class Groups::AuditEventsController < Groups::ApplicationController class Groups::AuditEventsController < Groups::ApplicationController
include AuditEvents::EnforcesValidDateParams include AuditEvents::EnforcesValidDateParams
include AuditEvents::AuditLogsParams include AuditEvents::AuditLogsParams
include AuditEvents::Sortable
before_action :authorize_admin_group! before_action :authorize_admin_group!
before_action :check_audit_events_available! before_action :check_audit_events_available!
......
...@@ -4,6 +4,7 @@ class Projects::AuditEventsController < Projects::ApplicationController ...@@ -4,6 +4,7 @@ class Projects::AuditEventsController < Projects::ApplicationController
include LicenseHelper include LicenseHelper
include AuditEvents::EnforcesValidDateParams include AuditEvents::EnforcesValidDateParams
include AuditEvents::AuditLogsParams include AuditEvents::AuditLogsParams
include AuditEvents::Sortable
before_action :authorize_admin_project! before_action :authorize_admin_project!
before_action :check_audit_events_available! before_action :check_audit_events_available!
......
...@@ -9,9 +9,10 @@ class AuditLogFinder ...@@ -9,9 +9,10 @@ class AuditLogFinder
end end
def execute def execute
audit_events = AuditEvent.order(id: :desc) # rubocop: disable CodeReuse/ActiveRecord audit_events = AuditEvent.all
audit_events = by_entity(audit_events) audit_events = by_entity(audit_events)
audit_events = by_created_at(audit_events) audit_events = by_created_at(audit_events)
audit_events = sort(audit_events)
audit_events = by_id(audit_events) audit_events = by_id(audit_events)
audit_events audit_events
...@@ -39,6 +40,12 @@ class AuditLogFinder ...@@ -39,6 +40,12 @@ class AuditLogFinder
audit_events.find_by_id(params[:id]) audit_events.find_by_id(params[:id])
end end
def sort(audit_events)
return audit_events.order_by_id_asc if params[:sort] == 'created_asc'
audit_events.order_by_id_desc
end
def valid_entity_type? def valid_entity_type?
VALID_ENTITY_TYPES.include? params[:entity_type] VALID_ENTITY_TYPES.include? params[:entity_type]
end end
......
- page_title 'Audit Log' - page_title 'Audit Log'
- entity_type = params[:entity_type]
.todos-filters .todos-filters
.row-content-block.second-block .row-content-block.second-block
= form_tag admin_audit_logs_path, method: :get, class: 'filter-form' do = form_tag admin_audit_logs_path, method: :get, class: 'filter-form' do
.filter-item.inline .filter-item.inline
- if entity_type.present? - if params[:sort]
= hidden_field_tag(:entity_type, entity_type) = hidden_field_tag(:sort, params[:sort])
- if params[:entity_type].present?
= hidden_field_tag(:entity_type, params[:entity_type])
= dropdown_tag(audit_entity_type_label(entity_type), = dropdown_tag(audit_entity_type_label(params[:entity_type]),
options: { toggle_class: 'js-type-search js-filter-submit js-type-filter', options: { toggle_class: 'js-type-search js-filter-submit js-type-filter',
dropdown_class: 'dropdown-menu-type dropdown-menu-selectable dropdown-menu-action js-filter-submit', dropdown_class: 'dropdown-menu-type dropdown-menu-selectable dropdown-menu-action js-filter-submit',
placeholder: 'Search types', placeholder: 'Search types',
...@@ -55,6 +56,8 @@ ...@@ -55,6 +56,8 @@
= submit_tag _('Search'), class: 'btn' = submit_tag _('Search'), class: 'btn'
= render 'shared/audit_events/event_sort'
- if @events.present? - if @events.present?
%table#events-table.table %table#events-table.table
%thead %thead
......
...@@ -4,4 +4,5 @@ ...@@ -4,4 +4,5 @@
%p.light Events in #{@group.name} %p.light Events in #{@group.name}
= render 'shared/audit_events/event_filter', path: group_audit_events_path(@group) = render 'shared/audit_events/event_filter', path: group_audit_events_path(@group)
= render 'shared/audit_events/event_sort'
= render 'shared/audit_events/event_table', events: @events = render 'shared/audit_events/event_table', events: @events
...@@ -9,4 +9,5 @@ ...@@ -9,4 +9,5 @@
- if feature_available - if feature_available
= render 'shared/audit_events/event_filter', path: project_audit_events_path(@project) = render 'shared/audit_events/event_filter', path: project_audit_events_path(@project)
= render 'shared/audit_events/event_sort'
= render 'shared/audit_events/event_table', events: @events = render 'shared/audit_events/event_table', events: @events
- toggle_value = params[:sort].presence || sort_value_recently_created
- toggle_text = audit_logs_sort_order_hash[toggle_value]
.top-area.d-flex.justify-content-end
.nav-controls.prepend-top-8.append-bottom-8
.btn-group.w-100{ role: "group" }
.btn-group.w-100.dropdown
%button.btn.btn-default.dropdown-menu-toggle{ type: 'button', data: { toggle: 'dropdown', display: 'static' } }
= toggle_text
= icon('chevron-down')
%ul.dropdown-menu.dropdown-menu-right.dropdown-menu-selectable
%li.dropdown-header
= _("Sort by")
- audit_logs_sort_order_hash.each do |value, title|
%li
= link_to title, filter_audit_path(sort: value), class: ("is-active" if toggle_text == title)
---
title: Add sort by date to audit logs and events
merge_request: 22887
author:
type: added
...@@ -8,8 +8,10 @@ describe Groups::AuditEventsController do ...@@ -8,8 +8,10 @@ describe Groups::AuditEventsController do
let(:group) { create(:group, :private) } let(:group) { create(:group, :private) }
describe 'GET #index' do describe 'GET #index' do
let(:sort) { nil }
let(:request) do let(:request) do
get :index, params: { group_id: group.to_param } get :index, params: { group_id: group.to_param, sort: sort }
end end
context 'authorized' do context 'authorized' do
...@@ -19,7 +21,7 @@ describe Groups::AuditEventsController do ...@@ -19,7 +21,7 @@ describe Groups::AuditEventsController do
end end
context 'when audit_events feature is available' do context 'when audit_events feature is available' do
let(:audit_logs_params) { ActionController::Parameters.new(entity_type: ::Group.name, entity_id: group.id).permit! } let(:audit_logs_params) { ActionController::Parameters.new(entity_type: ::Group.name, entity_id: group.id, sort: '').permit! }
before do before do
stub_licensed_features(audit_events: true) stub_licensed_features(audit_events: true)
...@@ -35,27 +37,43 @@ describe Groups::AuditEventsController do ...@@ -35,27 +37,43 @@ describe Groups::AuditEventsController do
end end
context 'ordering' do context 'ordering' do
before do shared_examples 'orders by id descending' do
create_list(:group_audit_event, 5, entity_id: group.id)
end
it 'orders by id descending' do it 'orders by id descending' do
request request
expect(assigns(:events)).to eq(group.audit_events.order(id: :desc)) expect(assigns(:events)).to eq(group.audit_events.order(id: :desc))
end end
end end
end
context 'when audit_events feature is not available' do
before do before do
stub_licensed_features(audit_events: false) create_list(:group_audit_event, 5, entity_id: group.id)
end end
it 'renders 404' do context 'when no sort order is specified' do
it_behaves_like 'orders by id descending'
end
context 'when sorting by latest events first' do
let(:sort) { 'created_desc' }
it_behaves_like 'orders by id descending'
end
context 'when sorting by oldest events first' do
let(:sort) { 'created_asc' }
it 'orders by id ascending' do
request request
expect(response).to have_gitlab_http_status(404) expect(assigns(:events)).to eq(group.audit_events.order(id: :asc))
end
end
context 'when sorting by an unsupported sort order' do
let(:sort) { 'FOO' }
it_behaves_like 'orders by id descending'
end
end end
end end
end end
......
...@@ -8,8 +8,10 @@ describe Projects::AuditEventsController do ...@@ -8,8 +8,10 @@ describe Projects::AuditEventsController do
let(:project) { create(:project, :private) } let(:project) { create(:project, :private) }
describe 'GET #index' do describe 'GET #index' do
let(:sort) { nil }
let(:request) do let(:request) do
get :index, params: { project_id: project.to_param, namespace_id: project.namespace.to_param } get :index, params: { project_id: project.to_param, namespace_id: project.namespace.to_param, sort: sort }
end end
context 'authorized' do context 'authorized' do
...@@ -19,7 +21,7 @@ describe Projects::AuditEventsController do ...@@ -19,7 +21,7 @@ describe Projects::AuditEventsController do
end end
context 'when audit_events feature is available' do context 'when audit_events feature is available' do
let(:audit_logs_params) { ActionController::Parameters.new(entity_type: ::Project.name, entity_id: project.id).permit! } let(:audit_logs_params) { ActionController::Parameters.new(entity_type: ::Project.name, entity_id: project.id, sort: '').permit! }
before do before do
stub_licensed_features(audit_events: true) stub_licensed_features(audit_events: true)
...@@ -35,14 +37,42 @@ describe Projects::AuditEventsController do ...@@ -35,14 +37,42 @@ describe Projects::AuditEventsController do
end end
context 'ordering' do context 'ordering' do
shared_examples 'orders by id descending' do
it 'orders by id descending' do
request
expect(assigns(:events)).to eq(project.audit_events.order(id: :desc))
end
end
before do before do
create_list(:project_audit_event, 5, entity_id: project.id) create_list(:project_audit_event, 5, entity_id: project.id)
end end
it 'orders by id descending' do context 'when no sort order is specified' do
it_behaves_like 'orders by id descending'
end
context 'when sorting by latest events first' do
let(:sort) { 'created_desc' }
it_behaves_like 'orders by id descending'
end
context 'when sorting by oldest events first' do
let(:sort) { 'created_asc' }
it 'orders by id ascending' do
request request
expect(assigns(:events)).to eq(project.audit_events.order(id: :desc)) expect(assigns(:events)).to eq(project.audit_events.order(id: :asc))
end
end
context 'when sorting by an unsupported sort order' do
let(:sort) { 'FOO' }
it_behaves_like 'orders by id descending'
end end
end end
end end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment