summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/assets/javascripts/clusters/clusters_index.js62
-rw-r--r--app/assets/javascripts/clusters/services/clusters_service.js4
-rw-r--r--app/assets/javascripts/dispatcher.js10
-rw-r--r--app/controllers/projects/clusters_controller.rb18
-rw-r--r--app/views/projects/clusters/_empty_state.html.haml2
-rw-r--r--app/views/projects/clusters/index.html.haml53
-rw-r--r--spec/features/projects/clusters_spec.rb29
-rw-r--r--spec/javascripts/clusters/clusters_index_spec.js31
8 files changed, 178 insertions, 31 deletions
diff --git a/app/assets/javascripts/clusters/clusters_index.js b/app/assets/javascripts/clusters/clusters_index.js
new file mode 100644
index 00000000000..52dfed5668a
--- /dev/null
+++ b/app/assets/javascripts/clusters/clusters_index.js
@@ -0,0 +1,62 @@
+import Flash from '../flash';
+import { s__ } from '../locale';
+import ClustersService from './services/clusters_service';
+
+/**
+ * Handles toggle buttons in the cluster's table.
+ *
+ * When the user clicks the toggle button for each cluster, it:
+ * - toggles the button
+ * - shows a loding and disabled state
+ * - Makes a put request to the given endpoint
+ * Once we receive the response, either:
+ * 1) Show updated status in case of successfull response
+ * 2) Show initial status in case of failed response
+ */
+export default class ClusterTable {
+ constructor() {
+ this.container = '.js-clusters-list';
+ document.querySelectorAll(`${this.container} .js-toggle-cluster-list`).forEach(button => button.addEventListener('click', e => ClusterTable.updateCluster(e)));
+ }
+
+ removeListeners() {
+ document.querySelectorAll(`${this.container} .js-toggle-cluster-list`).forEach(button => button.removeEventListener('click'));
+ }
+
+ static updateCluster(e) {
+ const toggleButton = e.currentTarget;
+ const value = toggleButton.classList.contains('checked').toString();
+ const endpoint = toggleButton.getAttribute('data-endpoint');
+
+ ClusterTable.toggleValue(toggleButton);
+ ClusterTable.toggleLoadingButton(toggleButton);
+
+ ClustersService.updateCluster(endpoint, { cluster: { enabled: value } })
+ .then(() => {
+ ClusterTable.toggleLoadingButton(toggleButton);
+ })
+ .catch(() => {
+ ClusterTable.toggleLoadingButton(toggleButton);
+ ClusterTable.toggleValue(toggleButton);
+ Flash(s__('ClusterIntegration|Something went wrong on our end.'));
+ });
+ }
+
+ /**
+ * Toggles loading and disabled classes.
+ * @param {HTMLElement} button
+ */
+ static toggleLoadingButton(button) {
+ button.setAttribute('disabled', button.getAttribute('disabled'));
+ button.classList.toggle('disabled');
+ button.classList.toggle('loading');
+ }
+
+ /**
+ * Toggles checked class for the given button
+ * @param {HTMLElement} button
+ */
+ static toggleValue(button) {
+ button.classList.toggle('checked');
+ }
+}
diff --git a/app/assets/javascripts/clusters/services/clusters_service.js b/app/assets/javascripts/clusters/services/clusters_service.js
index ce14c9a9945..755c2981c2e 100644
--- a/app/assets/javascripts/clusters/services/clusters_service.js
+++ b/app/assets/javascripts/clusters/services/clusters_service.js
@@ -17,4 +17,8 @@ export default class ClusterService {
installApplication(appId) {
return axios.post(this.appInstallEndpointMap[appId]);
}
+
+ static updateCluster(endpoint, data) {
+ return axios.put(endpoint, data);
+ }
}
diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js
index 34708977d20..49459f01bea 100644
--- a/app/assets/javascripts/dispatcher.js
+++ b/app/assets/javascripts/dispatcher.js
@@ -550,7 +550,15 @@ import ProjectVariables from './project_variables';
import(/* webpackChunkName: "clusters" */ './clusters/clusters_bundle')
.then(cluster => new cluster.default()) // eslint-disable-line new-cap
.catch((err) => {
- Flash(s__('ClusterIntegration|Problem setting up the cluster JavaScript'));
+ Flash(s__('ClusterIntegration|Problem setting up the cluster'));
+ throw err;
+ });
+ break;
+ case 'projects:clusters:index':
+ import(/* webpackChunkName: "clusters_index" */ './clusters/clusters_index')
+ .then(clusterIndex => new clusterIndex.default()) // eslint-disable-line new-cap
+ .catch((err) => {
+ Flash(s__('ClusterIntegration|Problem setting up the clusters list'));
throw err;
});
break;
diff --git a/app/controllers/projects/clusters_controller.rb b/app/controllers/projects/clusters_controller.rb
index 19ae3192044..37271c4708d 100644
--- a/app/controllers/projects/clusters_controller.rb
+++ b/app/controllers/projects/clusters_controller.rb
@@ -7,7 +7,7 @@ class Projects::ClustersController < Projects::ApplicationController
before_action :authorize_admin_cluster!, only: [:destroy]
def index
- @clusters ||= project.clusters.map { |cluster| cluster.present(current_user: current_user) }
+ @clusters ||= project.clusters.page(params[:page]).per(20).map { |cluster| cluster.present(current_user: current_user) }
end
def login
@@ -64,10 +64,20 @@ class Projects::ClustersController < Projects::ApplicationController
.execute(cluster)
if cluster.valid?
- flash[:notice] = "Cluster was successfully updated."
- redirect_to project_cluster_path(project, project.cluster)
+ respond_to do |format|
+ format.json do
+ head :no_content
+ end
+ format.html do
+ flash[:notice] = "Cluster was successfully updated."
+ redirect_to project_cluster_path(project, project.cluster)
+ end
+ end
else
- render :show
+ respond_to do |format|
+ format.json { head :bad_request }
+ format.html { render :show }
+ end
end
end
diff --git a/app/views/projects/clusters/_empty_state.html.haml b/app/views/projects/clusters/_empty_state.html.haml
index 44f13a009b9..4554f5c624d 100644
--- a/app/views/projects/clusters/_empty_state.html.haml
+++ b/app/views/projects/clusters/_empty_state.html.haml
@@ -5,6 +5,6 @@
%p= s_('ClusterIntegration|Clusters allow you to use review apps, deploy your applications, run your pipelines, and much more in an easy way. %{link_to_help_page}').html_safe % { link_to_help_page: link_to_help_page}
%p
- = link_to s_('ClusterIntegration|Add cluster'), '', class: 'btn btn-success', title: 'Add cluster'
+ = link_to s_('ClusterIntegration|Add cluster'), new_project_cluster_path(@project), class: 'btn btn-success', title: 'Add cluster'
.svg-content
= image_tag 'illustrations/labels.svg'
diff --git a/app/views/projects/clusters/index.html.haml b/app/views/projects/clusters/index.html.haml
index 6b7fbb44c4a..d7e0940cb65 100644
--- a/app/views/projects/clusters/index.html.haml
+++ b/app/views/projects/clusters/index.html.haml
@@ -21,8 +21,8 @@
%span.badge
0
.nav-controls
- = link_to s_('ClusterIntegration|Add cluster'), '', class: 'btn btn-success', title: 'Add cluster'
- .ci-table
+ = link_to s_('ClusterIntegration|Add cluster'), new_project_cluster_path(@project), class: 'btn btn-success', title: 'Add cluster'
+ .ci-table.js-clusters-list
.gl-responsive-table-row.table-row-header{ role: 'row' }
.table-section.section-30{ role: 'rowheader' }
= s_('ClusterIntegration|Cluster')
@@ -31,27 +31,30 @@
.table-section.section-30{ role: 'rowheader' }
= s_('ClusterIntegration|Project namespace')
.table-section.section-10{ role: 'rowheader' }
- .gl-responsive-table-row
- .table-section.section-30
- .table-mobile-header{ role: 'rowheader' }
- = s_('ClusterIntegration|Cluster')
- .table-mobile-content
- Content goes here
- .table-section.section-30
- .table-mobile-header{ role: 'rowheader' }
- = s_('ClusterIntegration|Environment pattern')
- .table-mobile-content
- Content goes here
- .table-section.section-30
- .table-mobile-header{ role: 'rowheader' }
- = s_('ClusterIntegration|Project namespace')
- .table-mobile-content
- Content goes here
- .table-section.section-10
- .table-mobile-header{ role: 'rowheader' }
- .table-mobile-content
- %button{ type: 'button',
- class: "js-toggle-cluster project-feature-toggle",
- 'aria-label': s_('ClusterIntegration|Toggle Cluster'),
- data: { 'enabled-text': 'Enabled', 'disabled-text': 'Disabled' } }
+ - @clusters.each do |cluster|
+ .gl-responsive-table-row
+ .table-section.section-30
+ .table-mobile-header{ role: 'rowheader' }= s_('ClusterIntegration|Cluster')
+ .table-mobile-content= cluster.name
+ .table-section.section-30
+ .table-mobile-header{ role: 'rowheader' }
+ = s_('ClusterIntegration|Environment pattern')
+ .table-mobile-content
+ Content goes here
+ .table-section.section-30
+ .table-mobile-header{ role: 'rowheader' }
+ = s_('ClusterIntegration|Project namespace')
+ .table-mobile-content
+ Content goes here
+ .table-section.section-10
+ .table-mobile-header{ role: 'rowheader' }
+ .table-mobile-content
+ %button{ type: 'button',
+ class: "js-toggle-cluster-list project-feature-toggle #{'checked' unless !cluster.enabled?} #{'disabled' unless can?(current_user, :update_cluster, cluster)}",
+ 'aria-label': s_('ClusterIntegration|Toggle Cluster'),
+ disabled: !can?(current_user, :update_cluster, cluster),
+ data: { 'enabled-text': 'Enabled',
+ 'disabled-text': 'Disabled',
+ endpoint: namespace_project_cluster_path(@project.namespace, @project, cluster, format: :json) } }
+ = icon('loading', class: 'hidden')
diff --git a/spec/features/projects/clusters_spec.rb b/spec/features/projects/clusters_spec.rb
index 21f00b4e266..dc78ac48cc9 100644
--- a/spec/features/projects/clusters_spec.rb
+++ b/spec/features/projects/clusters_spec.rb
@@ -94,6 +94,33 @@ feature 'Clusters', :js do
visit project_clusters_path(project)
end
+ it 'user sees a table with one cluster' do
+
+ end
+
+ it 'user sees a disabled add cluster button ' do
+
+ end
+
+ it 'user sees navigation tabs' do
+
+ end
+
+ context 'update cluster' do
+ it 'user can update cluster' do
+ end
+
+ context 'with sucessfull request' do
+ it 'user sees updated cluster' do
+ end
+ end
+
+ context 'with failed request' do
+ it 'user sees not update cluster and error message' do
+ end
+ end
+ end
+
context 'when user clicks on a cluster' do
before do
# TODO: Replace with Click on cluster after frontend implements list
@@ -216,4 +243,6 @@ feature 'Clusters', :js do
expect(page).to have_css('.signin-with-google')
end
end
+
+ context
end
diff --git a/spec/javascripts/clusters/clusters_index_spec.js b/spec/javascripts/clusters/clusters_index_spec.js
new file mode 100644
index 00000000000..8798f5c37f0
--- /dev/null
+++ b/spec/javascripts/clusters/clusters_index_spec.js
@@ -0,0 +1,31 @@
+import ClusterTable from '~/clusters/clusters_index';
+
+describe('Clusters table', () => {
+ let ClustersClass;
+
+ beforeEach(() => {
+ ClustersClass = new ClusterTable();
+ });
+
+ afterEach(() => {
+ ClustersClass.removeListeners();
+ });
+
+ describe('update cluster', () => {
+ it('renders a toggle button', () => {
+
+ });
+
+ it('renders loading state while request is made', () => {
+
+ });
+
+ it('shows updated state after sucessfull request', () => {
+
+ });
+
+ it('shows inital state after failed request', () => {
+
+ });
+ });
+});