mirror of https://github.com/portainer/portainer
fix(app): wrapper for UI refresh trigger with async/await (#2945)
* fix(app): wrapper for UI refresh trigger with async/await * fix(async): $async wrapper now accepts functions with params * fix(async): $async should return a promise to be chained with ES5 .then() style * fix(async): $async with multiple params was not working * refactor(app): wrap all async functions with $async * docs(async): add link to async wrapper documentationpull/2942/head
parent
09cf55a7dc
commit
71b1da8d32
|
@ -3,12 +3,14 @@ import angular from 'angular';
|
||||||
class ConfigsController {
|
class ConfigsController {
|
||||||
|
|
||||||
/* @ngInject */
|
/* @ngInject */
|
||||||
constructor($state, ConfigService, Notifications) {
|
constructor($state, ConfigService, Notifications, $async) {
|
||||||
this.$state = $state;
|
this.$state = $state;
|
||||||
this.ConfigService = ConfigService;
|
this.ConfigService = ConfigService;
|
||||||
this.Notifications = Notifications;
|
this.Notifications = Notifications;
|
||||||
|
this.$async = $async;
|
||||||
|
|
||||||
this.removeAction = this.removeAction.bind(this);
|
this.removeAction = this.removeAction.bind(this);
|
||||||
|
this.removeActionAsync = this.removeActionAsync.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
async $onInit() {
|
async $onInit() {
|
||||||
|
@ -20,7 +22,11 @@ class ConfigsController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async removeAction(selectedItems) {
|
removeAction(selectedItems) {
|
||||||
|
return this.$async(this.removeActionAsync, selectedItems);
|
||||||
|
}
|
||||||
|
|
||||||
|
async removeActionAsync(selectedItems) {
|
||||||
let actionCount = selectedItems.length;
|
let actionCount = selectedItems.length;
|
||||||
for (const config of selectedItems) {
|
for (const config of selectedItems) {
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -5,7 +5,7 @@ import angular from "angular";
|
||||||
|
|
||||||
class CreateConfigController {
|
class CreateConfigController {
|
||||||
/* @ngInject */
|
/* @ngInject */
|
||||||
constructor($state, $transition$, Notifications, ConfigService, Authentication, FormValidator, ResourceControlService) {
|
constructor($async, $state, $transition$, Notifications, ConfigService, Authentication, FormValidator, ResourceControlService) {
|
||||||
this.$state = $state;
|
this.$state = $state;
|
||||||
this.$transition$ = $transition$;
|
this.$transition$ = $transition$;
|
||||||
this.Notifications = Notifications;
|
this.Notifications = Notifications;
|
||||||
|
@ -13,6 +13,7 @@ class CreateConfigController {
|
||||||
this.Authentication = Authentication;
|
this.Authentication = Authentication;
|
||||||
this.FormValidator = FormValidator;
|
this.FormValidator = FormValidator;
|
||||||
this.ResourceControlService = ResourceControlService;
|
this.ResourceControlService = ResourceControlService;
|
||||||
|
this.$async = $async;
|
||||||
|
|
||||||
this.formValues = {
|
this.formValues = {
|
||||||
Name: "",
|
Name: "",
|
||||||
|
@ -26,6 +27,30 @@ class CreateConfigController {
|
||||||
};
|
};
|
||||||
|
|
||||||
this.editorUpdate = this.editorUpdate.bind(this);
|
this.editorUpdate = this.editorUpdate.bind(this);
|
||||||
|
this.createAsync = this.createAsync.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
async $onInit() {
|
||||||
|
if (!this.$transition$.params().id) {
|
||||||
|
this.formValues.displayCodeEditor = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
let data = await this.ConfigService.config(this.$transition$.params().id);
|
||||||
|
this.formValues.Name = data.Name + "_copy";
|
||||||
|
this.formValues.Data = data.Data;
|
||||||
|
let labels = _.keys(data.Labels);
|
||||||
|
for (let i = 0; i < labels.length; i++) {
|
||||||
|
let labelName = labels[i];
|
||||||
|
let labelValue = data.Labels[labelName];
|
||||||
|
this.formValues.Labels.push({ name: labelName, value: labelValue });
|
||||||
|
}
|
||||||
|
this.formValues.displayCodeEditor = true;
|
||||||
|
} catch (err) {
|
||||||
|
this.formValues.displayCodeEditor = true;
|
||||||
|
this.Notifications.error("Failure", err, "Unable to clone config");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
addLabel() {
|
addLabel() {
|
||||||
|
@ -74,7 +99,11 @@ class CreateConfigController {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
async create() {
|
create() {
|
||||||
|
return this.$async(this.createAsync);
|
||||||
|
}
|
||||||
|
|
||||||
|
async createAsync() {
|
||||||
let accessControlData = this.formValues.AccessControlData;
|
let accessControlData = this.formValues.AccessControlData;
|
||||||
let userDetails = this.Authentication.getUserDetails();
|
let userDetails = this.Authentication.getUserDetails();
|
||||||
let isAdmin = this.Authentication.isAdmin();
|
let isAdmin = this.Authentication.isAdmin();
|
||||||
|
@ -111,29 +140,6 @@ class CreateConfigController {
|
||||||
editorUpdate(cm) {
|
editorUpdate(cm) {
|
||||||
this.formValues.ConfigContent = cm.getValue();
|
this.formValues.ConfigContent = cm.getValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
async $onInit() {
|
|
||||||
if (!this.$transition$.params().id) {
|
|
||||||
this.formValues.displayCodeEditor = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
let data = await this.ConfigService.config(this.$transition$.params().id);
|
|
||||||
this.formValues.Name = data.Name + "_copy";
|
|
||||||
this.formValues.Data = data.Data;
|
|
||||||
let labels = _.keys(data.Labels);
|
|
||||||
for (let i = 0; i < labels.length; i++) {
|
|
||||||
let labelName = labels[i];
|
|
||||||
let labelValue = data.Labels[labelName];
|
|
||||||
this.formValues.Labels.push({ name: labelName, value: labelValue });
|
|
||||||
}
|
|
||||||
this.formValues.displayCodeEditor = true;
|
|
||||||
} catch (err) {
|
|
||||||
this.formValues.displayCodeEditor = true;
|
|
||||||
this.Notifications.error("Failure", err, "Unable to clone config");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default CreateConfigController;
|
export default CreateConfigController;
|
||||||
|
|
|
@ -1,33 +1,37 @@
|
||||||
angular.module('portainer.extensions.rbac').directive('authorization', ['Authentication', 'ExtensionService',
|
angular.module('portainer.extensions.rbac').directive('authorization', ['Authentication', 'ExtensionService', '$async',
|
||||||
function(Authentication, ExtensionService) {
|
function(Authentication, ExtensionService, $async) {
|
||||||
return {
|
|
||||||
restrict: 'A',
|
async function linkAsync(scope, elem, attrs) {
|
||||||
link: async function(scope, elem, attrs) {
|
elem.hide();
|
||||||
elem.hide();
|
try {
|
||||||
try {
|
const rbacEnabled = await ExtensionService.extensionEnabled(ExtensionService.EXTENSIONS.RBAC);
|
||||||
const rbacEnabled = await ExtensionService.extensionEnabled(ExtensionService.EXTENSIONS.RBAC);
|
if (!rbacEnabled) {
|
||||||
if (!rbacEnabled) {
|
|
||||||
elem.show();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
elem.show();
|
elem.show();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
} catch (err) {
|
||||||
|
elem.show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var authorizations = attrs.authorization.split(",");
|
||||||
|
for (var i = 0; i < authorizations.length; i++) {
|
||||||
|
authorizations[i] = authorizations[i].trim();
|
||||||
|
}
|
||||||
|
|
||||||
var authorizations = attrs.authorization.split(",");
|
var hasAuthorizations = Authentication.hasAuthorizations(authorizations);
|
||||||
for (var i = 0; i < authorizations.length; i++) {
|
|
||||||
authorizations[i] = authorizations[i].trim();
|
|
||||||
}
|
|
||||||
|
|
||||||
var hasAuthorizations = Authentication.hasAuthorizations(authorizations);
|
if (hasAuthorizations) {
|
||||||
|
elem.show();
|
||||||
|
} else if (!hasAuthorizations && elem[0].tagName === 'A') {
|
||||||
|
elem.show();
|
||||||
|
elem.addClass('portainer-disabled-link');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (hasAuthorizations) {
|
return {
|
||||||
elem.show();
|
restrict: 'A',
|
||||||
} else if (!hasAuthorizations && elem[0].tagName === 'A') {
|
link: function(scope, elem, attrs) {
|
||||||
elem.show();
|
return $async(linkAsync, scope, elem, attrs);
|
||||||
elem.addClass('portainer-disabled-link');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}]);
|
}]);
|
||||||
|
|
|
@ -1,27 +1,32 @@
|
||||||
angular.module('portainer.extensions.rbac')
|
angular.module('portainer.extensions.rbac')
|
||||||
.directive('disableAuthorization', ['Authentication', 'ExtensionService', function(Authentication, ExtensionService) {
|
.directive('disableAuthorization', ['Authentication', 'ExtensionService', '$async', function(Authentication, ExtensionService, $async) {
|
||||||
return {
|
|
||||||
restrict: 'A',
|
async function linkAsync(scope, elem, attrs) {
|
||||||
link: async function (scope, elem, attrs) {
|
try {
|
||||||
try {
|
const rbacEnabled = await ExtensionService.extensionEnabled(ExtensionService.EXTENSIONS.RBAC);
|
||||||
const rbacEnabled = await ExtensionService.extensionEnabled(ExtensionService.EXTENSIONS.RBAC);
|
if (!rbacEnabled) {
|
||||||
if (!rbacEnabled) {
|
|
||||||
elem.show();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
elem.show();
|
elem.show();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
} catch (err) {
|
||||||
|
elem.show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var authorizations = attrs.disableAuthorization.split(",");
|
var authorizations = attrs.disableAuthorization.split(",");
|
||||||
for (var i = 0; i < authorizations.length; i++) {
|
for (var i = 0; i < authorizations.length; i++) {
|
||||||
authorizations[i] = authorizations[i].trim();
|
authorizations[i] = authorizations[i].trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Authentication.hasAuthorizations(authorizations)) {
|
if (!Authentication.hasAuthorizations(authorizations)) {
|
||||||
elem.attr('disabled', true);
|
elem.attr('disabled', true);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
restrict: 'A',
|
||||||
|
link: function (scope, elem, attrs) {
|
||||||
|
return $async(linkAsync, scope, elem, attrs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}]);
|
}]);
|
||||||
|
|
|
@ -2,7 +2,7 @@ import _ from 'lodash-es';
|
||||||
import { ExtensionViewModel } from '../../models/extension';
|
import { ExtensionViewModel } from '../../models/extension';
|
||||||
|
|
||||||
angular.module('portainer.app')
|
angular.module('portainer.app')
|
||||||
.factory('ExtensionService', ['$q', 'Extension', 'StateManager', function ExtensionServiceFactory($q, Extension, StateManager) {
|
.factory('ExtensionService', ['$q', 'Extension', 'StateManager', '$async', function ExtensionServiceFactory($q, Extension, StateManager, $async) {
|
||||||
'use strict';
|
'use strict';
|
||||||
var service = {};
|
var service = {};
|
||||||
|
|
||||||
|
@ -12,19 +12,27 @@ angular.module('portainer.app')
|
||||||
RBAC: 3
|
RBAC: 3
|
||||||
});
|
});
|
||||||
|
|
||||||
service.enable = function(license) {
|
service.enable = enable;
|
||||||
|
service.update = update;
|
||||||
|
service.delete = _delete;
|
||||||
|
service.extensions = extensions;
|
||||||
|
service.extension = extension;
|
||||||
|
service.extensionEnabled = extensionEnabled;
|
||||||
|
service.retrieveAndSaveEnabledExtensions = retrieveAndSaveEnabledExtensions;
|
||||||
|
|
||||||
|
function enable(license) {
|
||||||
return Extension.create({ license: license }).$promise;
|
return Extension.create({ license: license }).$promise;
|
||||||
};
|
}
|
||||||
|
|
||||||
service.update = function(id, version) {
|
function update(id, version) {
|
||||||
return Extension.update({ id: id, version: version }).$promise;
|
return Extension.update({ id: id, version: version }).$promise;
|
||||||
};
|
}
|
||||||
|
|
||||||
service.delete = function(id) {
|
function _delete(id) {
|
||||||
return Extension.delete({ id: id }).$promise;
|
return Extension.delete({ id: id }).$promise;
|
||||||
};
|
}
|
||||||
|
|
||||||
service.extensions = function(store) {
|
function extensions(store) {
|
||||||
var deferred = $q.defer();
|
var deferred = $q.defer();
|
||||||
|
|
||||||
Extension.query({ store: store }).$promise
|
Extension.query({ store: store }).$promise
|
||||||
|
@ -39,9 +47,9 @@ angular.module('portainer.app')
|
||||||
});
|
});
|
||||||
|
|
||||||
return deferred.promise;
|
return deferred.promise;
|
||||||
};
|
}
|
||||||
|
|
||||||
service.extension = function(id) {
|
function extension(id) {
|
||||||
var deferred = $q.defer();
|
var deferred = $q.defer();
|
||||||
|
|
||||||
Extension.get({ id: id }).$promise
|
Extension.get({ id: id }).$promise
|
||||||
|
@ -54,9 +62,13 @@ angular.module('portainer.app')
|
||||||
});
|
});
|
||||||
|
|
||||||
return deferred.promise;
|
return deferred.promise;
|
||||||
};
|
}
|
||||||
|
|
||||||
service.extensionEnabled = async function(extensionId) {
|
function extensionEnabled(extensionId) {
|
||||||
|
return $async(extensionsEnabledAsync, extensionId)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function extensionsEnabledAsync(extensionId) {
|
||||||
if (extensionId === service.EXTENSIONS.RBAC) {
|
if (extensionId === service.EXTENSIONS.RBAC) {
|
||||||
return StateManager.getExtension(extensionId) ? true : false;
|
return StateManager.getExtension(extensionId) ? true : false;
|
||||||
} else {
|
} else {
|
||||||
|
@ -64,13 +76,17 @@ angular.module('portainer.app')
|
||||||
const extension = _.find(extensions, (ext) => ext.Id === extensionId);
|
const extension = _.find(extensions, (ext) => ext.Id === extensionId);
|
||||||
return extension ? extension.Enabled : false;
|
return extension ? extension.Enabled : false;
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
service.retrieveAndSaveEnabledExtensions = async function() {
|
function retrieveAndSaveEnabledExtensions() {
|
||||||
|
return $async(retrieveAndSaveEnabledExtensionsAsync)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function retrieveAndSaveEnabledExtensionsAsync() {
|
||||||
const extensions = await service.extensions(false);
|
const extensions = await service.extensions(false);
|
||||||
_.forEach(extensions, (ext) => delete ext.License);
|
_.forEach(extensions, (ext) => delete ext.License);
|
||||||
StateManager.saveExtensions(extensions);
|
StateManager.saveExtensions(extensions);
|
||||||
};
|
}
|
||||||
|
|
||||||
return service;
|
return service;
|
||||||
}]);
|
}]);
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
/**
|
||||||
|
* Look a the following PR for how to use the wrapper
|
||||||
|
* and documentation about it
|
||||||
|
* https://github.com/portainer/portainer/pull/2945
|
||||||
|
*/
|
||||||
|
|
||||||
|
angular.module('portainer').factory('$async', ['$q',
|
||||||
|
function($q) {
|
||||||
|
return function(asyncFunc, ...args) {
|
||||||
|
const def = $q.defer();
|
||||||
|
const wrapper = function(params) {
|
||||||
|
const deferred = $q.defer();
|
||||||
|
asyncFunc(...params)
|
||||||
|
.then(deferred.resolve)
|
||||||
|
.catch(deferred.reject);
|
||||||
|
return deferred.promise;
|
||||||
|
};
|
||||||
|
|
||||||
|
wrapper(args).then(def.resolve).catch(def.reject)
|
||||||
|
return def.promise;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
]);
|
|
@ -1,6 +1,6 @@
|
||||||
angular.module('portainer.app')
|
angular.module('portainer.app')
|
||||||
.controller('AuthenticationController', ['$q', '$scope', '$state', '$stateParams', '$sanitize', 'Authentication', 'UserService', 'EndpointService', 'ExtensionService', 'StateManager', 'Notifications', 'SettingsService', 'URLHelper',
|
.controller('AuthenticationController', ['$async', '$q', '$scope', '$state', '$stateParams', '$sanitize', 'Authentication', 'UserService', 'EndpointService', 'ExtensionService', 'StateManager', 'Notifications', 'SettingsService', 'URLHelper',
|
||||||
function($q, $scope, $state, $stateParams, $sanitize, Authentication, UserService, EndpointService, ExtensionService, StateManager, Notifications, SettingsService, URLHelper) {
|
function($async, $q, $scope, $state, $stateParams, $sanitize, Authentication, UserService, EndpointService, ExtensionService, StateManager, Notifications, SettingsService, URLHelper) {
|
||||||
$scope.logo = StateManager.getState().application.logo;
|
$scope.logo = StateManager.getState().application.logo;
|
||||||
|
|
||||||
$scope.formValues = {
|
$scope.formValues = {
|
||||||
|
@ -14,7 +14,11 @@ function($q, $scope, $state, $stateParams, $sanitize, Authentication, UserServic
|
||||||
OAuthProvider: ''
|
OAuthProvider: ''
|
||||||
};
|
};
|
||||||
|
|
||||||
async function retrieveAndSaveEnabledExtensions() {
|
function retrieveAndSaveEnabledExtensions() {
|
||||||
|
return $async(retrieveAndSaveEnabledExtensionsAsync);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function retrieveAndSaveEnabledExtensionsAsync() {
|
||||||
try {
|
try {
|
||||||
await ExtensionService.retrieveAndSaveEnabledExtensions();
|
await ExtensionService.retrieveAndSaveEnabledExtensions();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|
|
@ -2,13 +2,16 @@ import angular from "angular";
|
||||||
|
|
||||||
class EndpointAccessController {
|
class EndpointAccessController {
|
||||||
/* @ngInject */
|
/* @ngInject */
|
||||||
constructor($state, $transition$, Notifications, EndpointService, GroupService) {
|
constructor($state, $transition$, Notifications, EndpointService, GroupService, $async) {
|
||||||
this.$state = $state;
|
this.$state = $state;
|
||||||
this.$transition$ = $transition$;
|
this.$transition$ = $transition$;
|
||||||
this.Notifications = Notifications;
|
this.Notifications = Notifications;
|
||||||
this.EndpointService = EndpointService;
|
this.EndpointService = EndpointService;
|
||||||
this.GroupService = GroupService;
|
this.GroupService = GroupService;
|
||||||
|
this.$async = $async;
|
||||||
|
|
||||||
this.updateAccess = this.updateAccess.bind(this);
|
this.updateAccess = this.updateAccess.bind(this);
|
||||||
|
this.updateAccessAsync = this.updateAccessAsync.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
async $onInit() {
|
async $onInit() {
|
||||||
|
@ -23,7 +26,11 @@ class EndpointAccessController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateAccess() {
|
updateAccess() {
|
||||||
|
return this.$async(this.updateAccessAsync);
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateAccessAsync() {
|
||||||
try {
|
try {
|
||||||
this.state.actionInProgress = true;
|
this.state.actionInProgress = true;
|
||||||
await this.EndpointService.updateEndpoint(this.$transition$.params().id, this.endpoint);
|
await this.EndpointService.updateEndpoint(this.$transition$.params().id, this.endpoint);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
angular.module('portainer.app')
|
angular.module('portainer.app')
|
||||||
.controller('InitAdminController', ['$scope', '$state', 'Notifications', 'Authentication', 'StateManager', 'UserService', 'EndpointService', 'ExtensionService',
|
.controller('InitAdminController', ['$async', '$scope', '$state', 'Notifications', 'Authentication', 'StateManager', 'UserService', 'EndpointService', 'ExtensionService',
|
||||||
function ($scope, $state, Notifications, Authentication, StateManager, UserService, EndpointService, ExtensionService) {
|
function ($async, $scope, $state, Notifications, Authentication, StateManager, UserService, EndpointService, ExtensionService) {
|
||||||
|
|
||||||
$scope.logo = StateManager.getState().application.logo;
|
$scope.logo = StateManager.getState().application.logo;
|
||||||
|
|
||||||
|
@ -14,7 +14,11 @@ function ($scope, $state, Notifications, Authentication, StateManager, UserServi
|
||||||
actionInProgress: false
|
actionInProgress: false
|
||||||
};
|
};
|
||||||
|
|
||||||
async function retrieveAndSaveEnabledExtensions() {
|
function retrieveAndSaveEnabledExtensions() {
|
||||||
|
return $async(retrieveAndSaveEnabledExtensionsAsync)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function retrieveAndSaveEnabledExtensionsAsync() {
|
||||||
try {
|
try {
|
||||||
await ExtensionService.retrieveAndSaveEnabledExtensions();
|
await ExtensionService.retrieveAndSaveEnabledExtensions();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|
Loading…
Reference in New Issue