From 70a2aa796cd156b8d5da1561fdffda5cdadb6881 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Robles?= Date: Fri, 12 Apr 2024 15:24:11 -0500 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Check=20if=20the=20authorities=20ar?= =?UTF-8?q?e=20online=20(#416)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Parent issue: https://github.com/sequentech/meta/issues/704 --- avAdmin/admin-directives/create/create.js | 43 +++++++++ .../admin-directives/dashboard/dashboard.html | 10 ++- .../admin-directives/dashboard/dashboard.js | 90 +++++++++++++++++-- .../admin-directives/dashboard/dashboard.less | 37 ++++++++ avAdmin/elections-api-service.js | 19 ++++ locales/en.json | 4 + locales/es.json | 4 + 7 files changed, 197 insertions(+), 10 deletions(-) diff --git a/avAdmin/admin-directives/create/create.js b/avAdmin/admin-directives/create/create.js index 83a133f4..7121baeb 100644 --- a/avAdmin/admin-directives/create/create.js +++ b/avAdmin/admin-directives/create/create.js @@ -1044,6 +1044,48 @@ angular.module('avAdmin') } }); + function checkTrustees(el) { + logInfo($i18next.t('avAdmin.create.checkingTrustees', {title: el.title})); + var deferred = $q.defer(); + var auths = el.authorities && Array.from(el.authorities) || []; + if (el.director) { + auths.push(el.director); + } + + if (0 === auths.length) { + logError($i18next.t('avAdmin.create.errors.election-auths-missing', {eltitle: el.title})); + deferred.reject(); + } else { + ElectionsApi + .authoritiesStatus() + .then(function (trustees) { + var hasError = false; + for (var i = 0; i < auths.length; i++) { + var auth = auths[i]; + if (!trustees[auth]) { + logError($i18next.t('avAdmin.create.errors.election-auth-not-found', {eltitle: el.title, auth: auth})); + hasError = true; + continue; + } + if ('ok' !== trustees[auth].state) { + logError($i18next.t('avAdmin.create.errors.election-auth-error', {eltitle: el.title, auth: auth, message: trustees[auth].message})); + hasError = true; + continue; + } + } + if (hasError) { + deferred.reject(); + } else { + deferred.resolve(el); + } + }) + .catch(deferred.reject); + } + + + return deferred.promise; + } + function createAuthEvent(el) { console.log("creating auth event for election " + el.title); var deferred = $q.defer(); @@ -1264,6 +1306,7 @@ angular.module('avAdmin') var promise = deferred.promise; promise = promise + .then(checkTrustees) .then(createAuthEvent) .then(registerElection) .then(function(election) { diff --git a/avAdmin/admin-directives/dashboard/dashboard.html b/avAdmin/admin-directives/dashboard/dashboard.html index 2222fbac..a8a82e90 100644 --- a/avAdmin/admin-directives/dashboard/dashboard.html +++ b/avAdmin/admin-directives/dashboard/dashboard.html @@ -212,8 +212,14 @@

diff --git a/avAdmin/admin-directives/dashboard/dashboard.js b/avAdmin/admin-directives/dashboard/dashboard.js index aa4ac26e..3cbe9074 100644 --- a/avAdmin/admin-directives/dashboard/dashboard.js +++ b/avAdmin/admin-directives/dashboard/dashboard.js @@ -22,6 +22,7 @@ angular.module('avAdmin') $q, $window, $state, + $i18next, Authmethod, Plugins, ElectionsApi, @@ -37,6 +38,7 @@ angular.module('avAdmin') function link(scope, element, attrs) { scope.reloadTimeout = null; + scope.trusteesState = {}; scope.isWriteInResult = function(answer) { @@ -81,8 +83,25 @@ angular.module('avAdmin') } } + function setTrusteesState() { + ElectionsApi + .authoritiesStatus() + .then(function (trustees) { + scope.trusteesState = trustees; + }); + } + + function isTrusteeOk(name) { + return scope.trusteesState && scope.trusteesState[name] && 'ok' === scope.trusteesState[name].state; + } + + function getTrusteeMsg(name) { + return scope.trusteesState && scope.trusteesState[name] && scope.trusteesState && scope.trusteesState[name].message || ''; + } + function waitElectionChange() { + setTrusteesState(); ElectionsApi .getElection(scope.id, /*ignorecache = */ true) .then(function(el) @@ -743,6 +762,46 @@ angular.module('avAdmin') ); } + function checkTrustees() { + var deferred = $q.defer(); + + var errors = []; + + var auths = scope.election.authorities && Array.from(scope.election.authorities) || []; + if (scope.election.director) { + auths.push(scope.election.director); + } + + if (0 === auths.length) { + errors.push($i18next.t('avAdmin.create.errors.election-auths-missing', {eltitle: scope.election.title})); + deferred.reject(errors.join("\n")); + } else { + ElectionsApi + .authoritiesStatus() + .then(function (trustees) { + for (var i = 0; i < auths.length; i++) { + var auth = auths[i]; + if (!trustees[auth]) { + errors.push($i18next.t('avAdmin.create.errors.election-auth-not-found', {eltitle: scope.election.title, auth: auth})); + continue; + } + if ('ok' !== trustees[auth].state) { + errors.push($i18next.t('avAdmin.create.errors.election-auth-error', {eltitle: scope.election.title, auth: auth, message: trustees[auth].message})); + continue; + } + } + if (errors.length > 0) { + deferred.reject(errors); + } else { + deferred.resolve(); + } + }) + .catch(deferred.reject); + } + + return deferred.promise; + } + // performs all the initialization function init() { @@ -895,13 +954,17 @@ angular.module('avAdmin') scope.index = scope.getStatusIndex('stopped') + 1; } scope.nextaction = false; - Authmethod - .launchTally( - scope.election.id, - data.tallyElectionIds, - 'force-all', - data.mode - ) + + checkTrustees() + .then(function () { + return Authmethod + .launchTally( + scope.election.id, + data.tallyElectionIds, + 'force-all', + data.mode + ); + }) .then( function onSuccess() { @@ -915,7 +978,15 @@ angular.module('avAdmin') scope.loading = false; scope.error = error; } - ); + ) + .catch(function(error) + { + if (scope.launchedTally) { + scope.launchedTally = false; + } + scope.loading = false; + scope.error = error; + }); }, enableFunc: function () { return ( @@ -1460,6 +1531,7 @@ angular.module('avAdmin') scope.prevStatus = null; scope.percentVotes = PercentVotesService; + setTrusteesState(); // get the election at the begining ElectionsApi .getElection(scope.id) @@ -1532,6 +1604,8 @@ angular.module('avAdmin') launchKeyDistributionCeremony: launchKeyDistributionCeremony, launchOpeningCeremony: launchOpeningCeremony, configureScheduledEvents: configureScheduledEvents, + isTrusteeOk: isTrusteeOk, + getTrusteeMsg: getTrusteeMsg, downloadTurnout: downloadTurnout }); diff --git a/avAdmin/admin-directives/dashboard/dashboard.less b/avAdmin/admin-directives/dashboard/dashboard.less index 86697b69..ad90c00b 100644 --- a/avAdmin/admin-directives/dashboard/dashboard.less +++ b/avAdmin/admin-directives/dashboard/dashboard.less @@ -21,6 +21,43 @@ } } +.authority { + display: flex; + flex-direction: row; + justify-content: space-between; +} + +.auth-badge { + width: 25px; + height: 25px; + border-radius: 25px; + background-color: @error-primary-color; + margin: auto 0; + position: relative; + + &.auth-badge-green { + background-color: @av-secondary-contrast; + } + + .auth-tooltip { + visibility: hidden; + width: 240px; + background-color: @av-bg; + text-align: center; + padding: 5px 0; + border-radius: 6px; + position: absolute; + z-index: 1; + font-size: 1rem; + top: 25px; + left: 16px; + } +} + +.auth-badge:hover .auth-tooltip { + visibility: visible; +} + /** descriptive radio buttons */ .descriptive-radio { border: 2px solid transparent; diff --git a/avAdmin/elections-api-service.js b/avAdmin/elections-api-service.js index 513ef653..6e772289 100644 --- a/avAdmin/elections-api-service.js +++ b/avAdmin/elections-api-service.js @@ -275,6 +275,25 @@ angular.module('avAdmin') return deferred.promise; }; + electionsapi.authoritiesStatus = function () + { + var deferred = $q.defer();$http + .get( + backendUrl + 'authorities-state' + ) + .then( + function (response) { + if (response && response.data && response.data.payload) { + deferred.resolve(response.data.payload); + } else { + deferred.reject(response); + } + }, + deferred.reject + ); + return deferred.promise; + }; + electionsapi.parseElection = function(d) { var election = d.payload; var conf = electionsapi.templateEl(); diff --git a/locales/en.json b/locales/en.json index 909f3a15..90a23f28 100644 --- a/locales/en.json +++ b/locales/en.json @@ -934,6 +934,7 @@ "create": { "summary": "Summary: creating __num__ elections", "summary__plural": "Summary: creating __num__ elections", + "checkingTrustees": "Checking trustees status for election __title__", "create": "Create the elections", "creating": "Creating the authentication for election __title__", "setChildrenElectionInfo": "Setting the children election info for election (id: __id__) __title__", @@ -954,6 +955,9 @@ "confirmEdit": "Finish edit" }, "errors": { + "election-auths-missing": "Election '__eltitle__': Missing trustees", + "election-auth-not-found": "Election '__eltitle__': Trustee not found '__auth__'", + "election-auth-error": "Election '__eltitle__': Trustee '__auth__' has invalid status '__message__'", "lambda-live-preview-parent-children": "Missing election IDs for a parent-children election", "election-is-array-questions": "Election '__eltitle__': list of questions is not an array", "election-lambda-success-action-url-mode": "Election '__eltitle__': invalid success action url", diff --git a/locales/es.json b/locales/es.json index fca96d12..0e251fc5 100644 --- a/locales/es.json +++ b/locales/es.json @@ -821,6 +821,7 @@ "create": { "summary": "Resumen: creando __num__ votaciones", "summary__plural": "Resumen: creando __num__ votaciones", + "checkingTrustees": "Checking trustees status for election __title__", "create": "Crear las votaciones", "create__plural": "Crear las votaciones", "creating": "Creando la autenticación de la votación __title__", @@ -834,6 +835,9 @@ "livePreview": "Previsualización en vivo" }, "errors": { + "election-auths-missing": "Election '__eltitle__': Faltan autoridades", + "election-auth-not-found": "Election '__eltitle__': Autoridad no encontrada '__auth__'", + "election-auth-error": "Election '__eltitle__': Autoridad '__auth__' tiene un estado inválido '__message__'", "lambda-live-preview-parent-children": "Faltan ID de la votación para una votación padre-hijos", "election-is-array-questions": "Votación '__eltitle__': la lista de pregunta no es un array", "election-lambda-success-action-url-mode": "Votación '__eltitle__': url de redirección en 'Acción de éxito' inválida",