Skip to content

Commit

Permalink
Merge pull request #3895 from 3scale/THREESCALE-11178_new_edit_token
Browse files Browse the repository at this point in the history
🦋 Update New/Edit Access Token page
  • Loading branch information
josemigallas authored Oct 11, 2024
2 parents 4dbb1d6 + 7c09f89 commit 09e62e3
Show file tree
Hide file tree
Showing 14 changed files with 265 additions and 35 deletions.
56 changes: 56 additions & 0 deletions app/inputs/patternfly_check_boxes_input.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# frozen_string_literal: true

class PatternflyCheckBoxesInput < Formtastic::Inputs::CheckBoxesInput
delegate :tag, to: :template

def to_html
tag.div(class: 'pf-c-form__group') do
label + control
end
end

def label
tag.div(class: 'pf-c-form__group-label') do
tag.label(class: 'pf-c-form__label', for: input_html_options[:id]) do
tag.span(label_text, class: 'pf-c-form__label-text')
end
end
end

def control
tag.div(class: 'pf-c-form__group-control') do
collection.map { |item| choice_html(item) }.reduce(&:concat) <<
helper_text_invalid
end
end

def choice_html(choice)
tag.div(class: 'pf-c-check') do
checkbox_input(choice) + choice_label(choice)
end
end

def checkbox_input(choice)
value = choice_value(choice)
template.check_box_tag(
input_name,
value,
checked?(value),
extra_html_options(choice).merge(id: choice_input_dom_id(choice),
class: 'pf-c-check__input',
required: false)
)
end

def choice_label(choice)
label_text = choice[0]
tag.label(label_text, class: 'pf-c-check__label',
for: choice_input_dom_id(choice))
end

def helper_text_invalid
return if errors.empty?

template.render partial: 'shared/pf_error_helper_text', locals: { error: errors.first }
end
end
5 changes: 5 additions & 0 deletions app/lib/fields/patternfly_form_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ def commit_button(title, opts = {})
tag.button(title, type: :submit, class: 'pf-c-button pf-m-primary', **opts)
end

def delete_button(title, href, opts = {})
opts.reverse_merge!(method: :delete, class: 'pf-c-button pf-m-danger')
template.link_to(title, href, **opts)
end

def collection_select(*opts)
super(*opts.first(4), {}, { class: 'pf-c-form-control' })
end
Expand Down
2 changes: 1 addition & 1 deletion app/models/access_token.rb
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ def values
scopes.map(&:value)
end

def to_a
def to_collection_for_check_boxes
map { |scope| [scope.key, scope.value] }
end

Expand Down
22 changes: 7 additions & 15 deletions app/views/provider/admin/user/access_tokens/_form.html.slim
Original file line number Diff line number Diff line change
@@ -1,17 +1,9 @@
= semantic_form_for access_token, url: [:provider, :admin, :user, access_token] do |f|
= form.input :name, as: :patternfly_input,
input_html: { autofocus: true }

= f.inputs do
= f.input :name, input_html: { autofocus: true }
= f.input :scopes,
as: :check_boxes,
collection: access_token.available_scopes.to_a
= form.input :scopes, as: :patternfly_check_boxes,
collection: @access_token.available_scopes.to_collection_for_check_boxes

= f.input :permission, as: :select, collection: access_token.available_permissions, include_blank: false


= f.actions
= f.commit_button
- unless access_token.new_record?
= link_to 'Delete', provider_admin_user_access_token_path(@access_token),
data: {confirm: 'Are you sure?'}, method: :delete,
title: 'Delete Access Token', class: 'action delete'
= form.input :permission, as: :patternfly_select,
collection: @access_token.available_permissions,
include_blank: false
19 changes: 17 additions & 2 deletions app/views/provider/admin/user/access_tokens/edit.html.slim
Original file line number Diff line number Diff line change
@@ -1,3 +1,18 @@
- content_for :page_header_title, 'Edit Access Token'
- content_for :page_header_title, t('.page_header_title')

- content_for :javascripts do
= javascript_packs_with_chunks_tag 'pf_form'

div class="pf-c-card"
div class="pf-c-card__body"
= semantic_form_for @access_token, builder: Fields::PatternflyFormBuilder,
url: [:provider, :admin, :user, @access_token],
html: { class: 'pf-c-form pf-m-limit-width' } do |f|
= render 'form', form: f

= f.actions do
= f.commit_button t('.submit_button_label')
= f.delete_button 'Delete', provider_admin_user_access_token_path(@access_token),
data: { confirm: 'Are you sure?' },
title: 'Delete Access Token'

= render 'form', access_token: @access_token
23 changes: 21 additions & 2 deletions app/views/provider/admin/user/access_tokens/new.html.slim
Original file line number Diff line number Diff line change
@@ -1,3 +1,22 @@
- content_for :page_header_title, 'New Access Token'
- content_for :page_header_title, t('.page_header_title')

= render 'form', access_token: @access_token
- content_for :javascripts do
= javascript_packs_with_chunks_tag 'pf_form'

div class="pf-c-card"
div class="pf-c-card__body"
= semantic_form_for @access_token, builder: Fields::PatternflyFormBuilder,
url: [:provider, :admin, :user, @access_token],
html: { class: 'pf-c-form pf-m-limit-width' } do |f|
= f.input :name, as: :patternfly_input,
input_html: { autofocus: true }

= f.input :scopes, as: :patternfly_check_boxes,
collection: @access_token.available_scopes.to_collection_for_check_boxes

= f.input :permission, as: :patternfly_select,
collection: @access_token.available_permissions,
include_blank: false

= f.actions do
= f.commit_button t('.submit_button_label')
13 changes: 12 additions & 1 deletion config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,13 @@ en:
these settings will no longer have any effect.
</p>
user:
access_tokens:
edit:
page_header_title: Edit Access Token
submit_button_label: Update Access Token
new:
page_header_title: New Access Token
submit_button_label: Create Access Token
notification_preferences:
show:
no_notification_preferences_html: |
Expand Down Expand Up @@ -1087,7 +1094,11 @@ en:
invalid_transition: 'cannot transition via "%{event}"'
not_path_format: 'must be a path separated by "/". E.g. "" or "my/path"'
models:
authentication/by_access_token/access_token:
access_token:
attributes:
scopes:
too_short: select at least one scope
authentication/by_access_token:
attributes:
scopes:
too_short: "Select at least %{count} scope"
Expand Down
13 changes: 0 additions & 13 deletions features/access_tokens/show.feature

This file was deleted.

100 changes: 100 additions & 0 deletions features/provider/admin/user/access_tokens.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
@javascript
Feature: Provider Admin Access tokens

As an admin I want to be able to read, create and edit access tokens

Background:
Given a provider is logged in
And they go to the provider personal page

Rule: Index page
Background:
Given the provider has the following access tokens:
| Name | Scopes | Permission |
| Potato | Analytics API | Read Only |
| Banana | Billing API | Read & Write |
And they go to the personal tokens page

Scenario: Navigation to index page
Given they go to the provider dashboard
When they select "Account Settings" from the context selector
And press "Personal" within the main menu
And follow "Tokens" within the main menu's section Personal
Then the current page is the personal tokens page

Scenario: Tokens are listed in a table
Then the table should contain the following:
| Name | Scopes | Permission |
| Potato | Analytics API | Read Only |
| Banana | Billing API | Read & Write |

Rule: New page
Background:
Given they go to the new access token page

Scenario: Navigation to the new page
Given they go to the personal tokens page
When they follow "Add Access Token"
Then the current page is the new access token page

Scenario: New access token required fields
When the current page is the new access token page
Then there is a required field "Name"
And there is a required field "Scopes"
And there is a required field "Permission"
And the submit button is enabled

Scenario: Create access tokens without required fields
When they press "Create Access Token"
Then field "Name" has inline error "can't be blank"
And field "Scopes" has inline error "select at least one scope"
And field "Permission" has no inline error

Scenario: Create access token
When they press "Create Access Token"
And the form is submitted with:
| Name | LeToken |
| Analytics API | Yes |
| Permission | Read & Write |
Then the current page is the personal tokens page
And they should see the flash message "Access token was successfully created"
And should see the following details:
| Name | LeToken |
| Scopes | Analytics API |
| Permission | Read & Write |
And there should be a link to "I have copied the token"

Rule: Edit page
Background:
Given the provider has the following access tokens:
| Name | Scopes | Permission |
| LeToken | Billing API, Analytics API | Read Only |
And they go to the access token's edit page

Scenario: Navigation to edit page
Given they go to the personal tokens page
When they follow "Edit" in the 1st row within the access tokens table
Then the current page is the access token's edit page

Scenario: Edit access token
When the form is submitted with:
| Name | New Token Name |
| Billing API | No |
| Permission | Read & Write |
Then they should see the flash message "Access Token was successfully updated."
Then the table should contain the following:
| Name | Scopes | Permission |
| New Token Name | Analytics API | Read & Write |

Scenario: Edit access tokens without required fields
When the form is submitted with:
| Name | |
Then field "Name" has inline error "can't be blank"

Scenario: Delete access token
Given the current page is access token "LeToken" edit page
When they follow "Delete"
And confirm the dialog
Then the current page is the personal tokens page
And they should see the flash message "Access token was successfully deleted"
But should not see "LeToken"
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# frozen_string_literal: true

# Example:
#
# Given a provider
# And the provider has the following access tokens:
# | Name | Scopes | Permission |
# | LeToken | Billing API, Analytics API | Read Only |
#
Given "{provider} has the following access tokens:" do |provider, table|
transform_access_tokens_table(table)

table.hashes.each do |options|
@access_token = FactoryBot.create(:access_token, owner: @provider.admin_users.first!, **options)
end
end
7 changes: 6 additions & 1 deletion features/support/capybara_extensions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,16 @@ def has_select?(locator = nil, **options, &optional_filter_block)

# Capybara::Node::Matchers#has_field?
def has_field?(locator = nil, **options, &optional_filter_block)
if (form_group = find_all('.pf-c-form__group', text: locator, wait: 0).first) && form_group.has_css?('.CodeMirror', wait: 0)
form_group = find_all('.pf-c-form__group', text: locator, wait: 0).first

if form_group&.has_css?('.CodeMirror', wait: 0)
# If the field is enhanced by CodeMirror, the textarea will be hidden.
options[:visible] = :hidden
end

# Matches patternfly_check_boxes_input
return true if form_group&.has_css?('.pf-c-form__group-control .pf-c-check', wait: 0)

super
end
end
Expand Down
11 changes: 11 additions & 0 deletions features/support/data_table_transforms.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,17 @@
# Original transformers: https://github.com/3scale/porta/blob/a5d6622d5a56bbda401f7d95e09b0ab19d05adba/features/support/transforms.rb#L185-L202

module DataTableTransforms
def transform_access_tokens_table(table)
parameterize_headers(table)

codes = I18n.t('.access_token_options')

table.map_column!(:scopes) do |scopes|
scopes.split(',').map(&:strip).map { |scope| codes.key(scope).to_s }
end
table.map_column!(:permission) { |permission| codes.key(permission) }
end

def transform_invoices_table(table)
parameterize_headers(table, 'Buyer' => 'buyer_account',
'Month' => 'period')
Expand Down
10 changes: 10 additions & 0 deletions features/support/paths.rb
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,16 @@ def path_to(page_name, *args) # rubocop:disable Metrics/AbcSize, Metrics/Cycloma
when 'the new email configurations page'
new_provider_admin_account_email_configurations_path

when 'the personal tokens page'
provider_admin_user_access_tokens_path

when 'the new access token page'
new_provider_admin_user_access_token_path

when /^(access token "(.*)"|the access token's) edit page$/
access_token = AccessToken.find_by(name: $2) || @access_token
edit_provider_admin_user_access_token_path(access_token)

#
# SSO Integrations (Admin portal)
#
Expand Down
3 changes: 3 additions & 0 deletions features/support/selectors.rb
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ def selector_for(scope) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticCom
when /the bulk operations/ # Legacy bulk operations card, not the toolbar dropdown
'.pf-c-card#bulk-operations'

when /the access tokens table/
'.pf-c-table[aria-label="Access tokens table"]'

#
# Product
#
Expand Down

0 comments on commit 09e62e3

Please sign in to comment.