Skip to content

Commit

Permalink
Feature flags GUI (#513)
Browse files Browse the repository at this point in the history
Feature flags

---------

Co-authored-by: Jano Suchal <[email protected]>
  • Loading branch information
richardlences and jsuchal authored Dec 5, 2024
1 parent 0839fd7 commit 42dde8d
Show file tree
Hide file tree
Showing 11 changed files with 154 additions and 5 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<div class="self-stretch p-4 border-b border-gray-200 items-center gap-4 inline-flex">
<div class="grow shrink basis-0 gap-1">
<div class="text-gray-900 text-lg font-medium leading-loose w-fit">
<div class="grow shrink basis-0 flex-col justify-start items-start gap-1 inline-flex">
<div class="text-center text-gray-900 text-lg font-medium leading-loose">
<%= t "feature_flags.#{@feature_flag}.name"%>
</div>
<div class="text-center text-gray-500 text-base font-normal leading-normal">
<%= t "feature_flags.#{@feature_flag}.description"%>
</div>
</div>
</div>
</div>
<%= form_with model: Current.tenant, url: admin_tenant_feature_flag_path(Current.tenant, @feature_flag), title: "Toggle feature state", method: :patch do |form| %>
<%= form.hidden_field :enabled, value: !@enabled %>
<%= form.button name: @feature_flag, class: "#{@enabled ? "bg-indigo-600" : "bg-gray-200"} relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-indigo-600 focus:ring-offset-2", role: :switch, aria: { checked: @enabled } do %>
<span class="sr-only">Use setting</span>
<span aria-hidden="true" class="<%= @enabled ? "translate-x-5" : "translate-x-0" %> pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out"></span>
<% end %>
<% end %>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
class Admin::FeatureFlags::FeatureFlagsListRowComponent < ViewComponent::Base
def initialize(flag, enabled_features)
@feature_flag = flag
@enabled = enabled_features.include?(flag)
end
end
3 changes: 3 additions & 0 deletions app/components/common/icon_component.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ class IconComponent < ViewComponent::Base
"funnel-slash" => "M12 3c2.755 0 5.455.232 8.083.678.533.09.917.556.917 1.096v1.044a2.25 2.25 0 0 1-.659 1.591l-5.432 5.432a2.25 2.25 0 0 0-.659 1.591v2.927a2.25 2.25 0 0 1-1.244 2.013L9.75 21v-6.568a2.25 2.25 0 0 0-.659-1.591L3.659 7.409A2.25 2.25 0 0 1 3 5.818V4.774c0-.54.384-1.006.917-1.096A48.32 48.32 0 0 1 12 3ZM2 2l20 20",
"tag-slash" => "M9.568 3H5.25A2.25 2.25 0 0 0 3 5.25v4.318c0 .597.237 1.17.659 1.591l9.581 9.581c.699.699 1.78.872 2.607.33a18.095 18.095 0 0 0 5.223-5.223c.542-.827.369-1.908-.33-2.607L11.16 3.66A2.25 2.25 0 0 0 9.568 3Z M6 6h.008v.008H6V6ZM3 21l18-18",
"inbox-stack" => "m7.875 14.25 1.214 1.942a2.25 2.25 0 0 0 1.908 1.058h2.006c.776 0 1.497-.4 1.908-1.058l1.214-1.942M2.41 9h4.636a2.25 2.25 0 0 1 1.872 1.002l.164.246a2.25 2.25 0 0 0 1.872 1.002h2.092a2.25 2.25 0 0 0 1.872-1.002l.164-.246A2.25 2.25 0 0 1 16.954 9h4.636M2.41 9a2.25 2.25 0 0 0-.16.832V12a2.25 2.25 0 0 0 2.25 2.25h15A2.25 2.25 0 0 0 21.75 12V9.832c0-.287-.055-.57-.16-.832M2.41 9a2.25 2.25 0 0 1 .382-.632l3.285-3.832a2.25 2.25 0 0 1 1.708-.786h8.43c.657 0 1.281.287 1.709.786l3.284 3.832c.163.19.291.404.382.632M4.5 20.25h15A2.25 2.25 0 0 0 21.75 18v-2.625c0-.621-.504-1.125-1.125-1.125H3.375c-.621 0-1.125.504-1.125 1.125V18a2.25 2.25 0 0 0 2.25 2.25Z",
"flag" => "M3 3v1.5M3 21v-6m0 0 2.77-.693a9 9 0 0 1 6.208.682l.108.054a9 9 0 0 0 6.086.71l3.114-.732a48.524 48.524 0 0 1-.005-10.499l-3.11.732a9 9 0 0 1-6.085-.711l-.108-.054a9 9 0 0 0-6.208-.682L3 4.5M3 15V4.5",
"puzzle-piece" => "M14.25 6.087c0-.355.186-.676.401-.959.221-.29.349-.634.349-1.003 0-1.036-1.007-1.875-2.25-1.875s-2.25.84-2.25 1.875c0 .369.128.713.349 1.003.215.283.401.604.401.959v0a.64.64 0 0 1-.657.643 48.39 48.39 0 0 1-4.163-.3c.186 1.613.293 3.25.315 4.907a.656.656 0 0 1-.658.663v0c-.355 0-.676-.186-.959-.401a1.647 1.647 0 0 0-1.003-.349c-1.036 0-1.875 1.007-1.875 2.25s.84 2.25 1.875 2.25c.369 0 .713-.128 1.003-.349.283-.215.604-.401.959-.401v0c.31 0 .555.26.532.57a48.039 48.039 0 0 1-.642 5.056c1.518.19 3.058.309 4.616.354a.64.64 0 0 0 .657-.643v0c0-.355-.186-.676-.401-.959a1.647 1.647 0 0 1-.349-1.003c0-1.035 1.008-1.875 2.25-1.875 1.243 0 2.25.84 2.25 1.875 0 .369-.128.713-.349 1.003-.215.283-.4.604-.4.959v0c0 .333.277.599.61.58a48.1 48.1 0 0 0 5.427-.63 48.05 48.05 0 0 0 .582-4.717.532.532 0 0 0-.533-.57v0c-.355 0-.676.186-.959.401-.29.221-.634.349-1.003.349-1.035 0-1.875-1.007-1.875-2.25s.84-2.25 1.875-2.25c.37 0 .713.128 1.003.349.283.215.604.401.96.401v0a.656.656 0 0 0 .658-.663 48.422 48.422 0 0 0-.37-5.36c-1.886.342-3.81.574-5.766.689a.578.578 0 0 1-.61-.58v0Z"

}.freeze

def initialize(icon, classes: "", stroke_width: 1.5)
Expand Down
34 changes: 34 additions & 0 deletions app/controllers/admin/feature_flags_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
class Admin::FeatureFlagsController < ApplicationController
before_action :set_tenant

def index
authorize([:admin, :feature_flag])
if params.include?(:labs)
@feature_flags = @tenant.list_all_features
else
@feature_flags = @tenant.list_available_features
end
@enabled_features = @tenant.feature_flags
end

def update
authorize([:admin, :feature_flag])
if feature_flags_params[:enabled] == "true"
@tenant.feature_flags << params[:id]
else
@tenant.feature_flags.delete(params[:id])
end
@tenant.save!
redirect_to admin_tenant_feature_flags_path
end

private

def set_tenant
@tenant = policy_scope([:admin, :feature_flag]).find(params[:tenant_id])
end

def feature_flags_params
params.require(:tenant).permit(:enabled)
end
end
5 changes: 3 additions & 2 deletions app/lib/sidebar_menu.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ def initialize(controller, action, parameters = nil)
private

def initial_structure(controller, _action)
return admin_menu + site_admin_menu if controller.in? %w[groups users tags tag_groups automation_rules boxes api_connections filters automation_webhooks]
return admin_menu + site_admin_menu if controller.in? %w[groups users tags tag_groups automation_rules boxes api_connections filters automation_webhooks feature_flags]

default_main_menu
end
Expand Down Expand Up @@ -40,7 +40,8 @@ def admin_menu
TW::SidebarMenuItemComponent.new(name: 'API Prepojenia', url: admin_tenant_api_connections_path(Current.tenant), icon: Icons::RectangleStackComponent.new),
TW::SidebarMenuItemComponent.new(name: 'Skupiny', url: admin_tenant_groups_path(Current.tenant), icon: Icons::UserGroupsComponent.new),
TW::SidebarMenuItemComponent.new(name: 'Štítky', url: admin_tenant_tags_path(Current.tenant), icon: Icons::TagComponent.new),
TW::SidebarMenuItemComponent.new(name: 'Integrácie', url: admin_tenant_automation_webhooks_path(Current.tenant), icon: Common::IconComponent.new("code-bracket"))
TW::SidebarMenuItemComponent.new(name: 'Integrácie', url: admin_tenant_automation_webhooks_path(Current.tenant), icon: Common::IconComponent.new("code-bracket")),
TW::SidebarMenuItemComponent.new(name: 'Funkcie', url: admin_tenant_feature_flags_path(Current.tenant), icon: Common::IconComponent.new("puzzle-piece"))
]
end

Expand Down
15 changes: 12 additions & 3 deletions app/models/tenant.rb
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,15 @@ class Tenant < ApplicationRecord

validates_presence_of :name

AVAILABLE_FEATURE_FLAGS = [:audit_log, :archive, :api, :message_draft_import, :fs_api, :fs_sync]
AVAILABLE_FEATURE_FLAGS = [:audit_log, :archive, :api, :fs_sync]
ALL_FEATURE_FLAGS = [:audit_log, :archive, :api, :message_draft_import, :fs_api, :fs_sync]

def draft_tag!
draft_tag || raise(ActiveRecord::RecordNotFound, "`DraftTag` not found in tenant: #{id}")
end

def signed_externally_tag!
signed_externally_tag || raise(ActiveRecord::RecordNotFound, "`SignedExternallyTag` not found in tenant: #{self.id}")
signed_externally_tag || raise(ActiveRecord::RecordNotFound, "`SignedExternallyTag` not found in tenant: #{id}")
end

def signature_requested_tag!
Expand All @@ -67,7 +68,7 @@ def user_signature_tags
end

def feature_enabled?(feature)
raise "Unknown feature #{feature}" unless feature.in? AVAILABLE_FEATURE_FLAGS
raise "Unknown feature #{feature}" unless feature.in? ALL_FEATURE_FLAGS

feature.to_s.in? feature_flags
end
Expand All @@ -88,6 +89,14 @@ def disable_feature(feature)
save!
end

def list_available_features
AVAILABLE_FEATURE_FLAGS
end

def list_all_features
ALL_FEATURE_FLAGS
end

def make_admins_see_everything!
everything_tag.groups << admin_group
end
Expand Down
24 changes: 24 additions & 0 deletions app/policies/admin/feature_flag_policy.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# frozen_string_literal: true

class Admin::FeatureFlagPolicy < ApplicationPolicy
attr_reader :user, :tenant

def initialize(user, tenant)
@user = user
@tenant = tenant
end

class Scope < Scope
def resolve
Tenant.where(id: @user.tenant)
end
end

def index?
@user.admin?
end

def update?
@user.admin?
end
end
12 changes: 12 additions & 0 deletions app/views/admin/feature_flags/index.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<div class="w-full p-4 flex-col justify-start items-start gap-4 inline-flex">
<div class="self-stretch bg-white rounded-md border border-gray-200 flex-col justify-start items-start flex">
<div class="self-stretch p-6 border-b border-gray-200 justify-start items-center gap-4 inline-flex">
<div class="grow shrink basis-0 text-gray-900 text-xl font-semibold leading-[35px]">Aktivácia rozšírení</div>
</div>
<% @feature_flags.each do |flag| %>
<div class="self-stretch flex-col justify-start items-start flex">
<%= render Admin::FeatureFlags::FeatureFlagsListRowComponent.new(flag.to_s, @enabled_features) %>
</div>
<% end %>
</div>
</div>
19 changes: 19 additions & 0 deletions config/locales/sk.yml
Original file line number Diff line number Diff line change
Expand Up @@ -341,3 +341,22 @@ sk:
notifications:
header: "Žiadne notifikácie"
description: "Nemáte žiadne notifikácie."
feature_flags:
api:
name: "API"
description: "Prístup k vybraným funkciám systému a dátam cez API"
archive:
name: "Archive"
description: "Archivácia správ"
audit_log:
name: "Audit log"
description: "Zaznamenávanie a prezeranie auditných záznamov o činnosti používateľov"
fs_api:
name: "API finančnej správy"
description: "Funkčnosť prepojenia s finančnou správou"
fs_sync:
name: "Synchronizácia schránky z finančnej správy"
description: "Synchronizácia schránky z finančnej správy"
message_draft_import:
name: "Import správ"
description: "Funkcionalita pre hromadné zasielanie správ"
1 change: 1 addition & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
end

resources :users
resources :feature_flags, only: [:index, :update]

resources :boxes, only: :index
namespace :boxes do
Expand Down
19 changes: 19 additions & 0 deletions test/system/admin/feature_flags_management_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
require "application_system_test_case"

class FeatureFlagsManagementTest < ApplicationSystemTestCase
setup do
sign_in_as(:admin)
visit root_path
click_link "Nastavenia"
click_link "Funkcie"
end

test "admin can enable and disable a feature" do
available_features = users(:admin).tenant.list_available_features
enabled = users(:admin).tenant.feature_enabled?(available_features[0])
click_button available_features[0]
assert_button available_features[0]
users(:admin).tenant.reload
assert_not_equal enabled, users(:admin).tenant.feature_enabled?(available_features[0])
end
end

0 comments on commit 42dde8d

Please sign in to comment.