#7524 add limit on GET with filter param #2802

Merged
jorgep merged 8 commits from 7524-limitSelect into dev 2024-08-16 06:59:48 +00:00
138 changed files with 74 additions and 3510 deletions
Showing only changes of commit 2aaad4316b - Show all commits

View File

@ -24,6 +24,10 @@ module.exports = Self => {
arg: 'lang',
type: 'string',
description: 'The user lang'
}, {
arg: 'twoFactor',
type: 'string',
description: 'The user twoFactor'
}
],
http: {
@ -32,8 +36,8 @@ module.exports = Self => {
}
});
Self.updateUser = async(ctx, id, name, nickname, email, lang) => {
Self.updateUser = async(ctx, id, name, nickname, email, lang, twoFactor) => {
await Self.userSecurity(ctx, id);
await Self.upsertWithWhere({id}, {name, nickname, email, lang});
await Self.upsertWithWhere({id}, {name, nickname, email, lang, twoFactor});
};
};

View File

@ -101,9 +101,10 @@ module.exports = function(Self) {
const headers = httpRequest.headers;
const origin = headers.origin;
const defaultHash = '/reset-password?access_token=$token$';
const defaultHash = '!/reset-password?access_token=$token$';
const recoverHashes = {
hedera: 'verificationToken=$token$'
hedera: '!verificationToken=$token$',
lilium: '/resetPassword?access_token=$token$'
};
const app = info.options?.app;
@ -115,7 +116,7 @@ module.exports = function(Self) {
const params = {
recipient: info.email,
lang: user.lang,
url: origin + '/#!' + recoverHash
url: origin + '/#' + recoverHash
};
const options = Object.assign({}, info.options);

View File

@ -3,4 +3,5 @@ apps:
name: salix-back
instances: 1
max_restarts: 0
autorestart: false
node_args: --tls-min-v1.0 --openssl-legacy-provider

View File

@ -3,7 +3,7 @@ CREATE OR REPLACE DEFINER=`root`@`localhost` TRIGGER `salix`.`ACL_afterDelete`
AFTER DELETE ON `ACL`
FOR EACH ROW
BEGIN
INSERT INTO ACL
INSERT INTO ACLLog
SET `action` = 'delete',
`changedModel` = 'Acl',
`changedModelId` = OLD.id,

View File

@ -21,6 +21,7 @@ BEGIN
AND a.id IS NULL
AND u.active
AND c.created < util.VN_CURDATE() - INTERVAL vMonths MONTH
AND NOT u.role = (SELECT id FROM `role` WHERE name = 'supplier')
AND u.id NOT IN (
SELECT DISTINCT c.id
FROM client c

View File

@ -12,7 +12,7 @@ BEGIN
DECLARE vSaleFk INT;
DECLARE vSectorFk INT;
DECLARE vSales CURSOR FOR
SELECT s.id
SELECT DISTINCT s.id
FROM sectorCollectionSaleGroup sc
JOIN saleGroupDetail sg ON sg.saleGroupFk = sc.saleGroupFk
JOIN sale s ON sg.saleFk = s.id

View File

@ -0,0 +1,8 @@
DELIMITER $$
CREATE OR REPLACE DEFINER=`root`@`localhost` TRIGGER `vn`.`host_beforeInsert`
BEFORE INSERT ON `host`
FOR EACH ROW
BEGIN
SET NEW.editorFk = account.myUser_getId();
END$$
DELIMITER ;

View File

@ -4,5 +4,6 @@ CREATE OR REPLACE DEFINER=`root`@`localhost` TRIGGER `vn`.`host_beforeUpdate`
FOR EACH ROW
BEGIN
SET new.updated = util.VN_NOW();
SET NEW.editorFk = account.myUser_getId();
END$$
DELIMITER ;

View File

@ -0,0 +1 @@
DROP TABLE bi.Greuges_comercial_detail;

View File

@ -0,0 +1 @@
ALTER TABLE vn.host ADD editorFk int(10) unsigned DEFAULT NULL NULL;

View File

@ -0,0 +1,3 @@
ALTER TABLE vn.item
ADD CONSTRAINT item_itemPackingType_FK FOREIGN KEY (itemPackingTypeFk)
REFERENCES vn.itemPackingType(code) ON DELETE RESTRICT ON UPDATE CASCADE;

View File

@ -0,0 +1,9 @@
ALTER TABLE hedera.tpvMerchantEnable
DROP FOREIGN KEY tpvMerchantEnable_ibfk_1,
DROP PRIMARY KEY,
ADD CONSTRAINT tpvMerchantEnable_pk PRIMARY KEY (merchantFk),
ADD CONSTRAINT tpvMerchantEnable_tpvMerchant_FK
FOREIGN KEY (merchantFk)
REFERENCES hedera.tpvMerchant(id)
ON DELETE RESTRICT
ON UPDATE CASCADE;

View File

@ -1,164 +0,0 @@
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer';
describe('Account create and basic data path', () => {
let browser;
let page;
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('itManagement', 'account');
});
afterAll(async() => {
await browser.close();
});
it('should open the new account form by clicking the add button', async() => {
await page.waitToClick(selectors.accountIndex.addAccount);
await page.waitForState('account.create');
});
it('should fill the form and then save it by clicking the create button', async() => {
await page.write(selectors.accountIndex.newName, 'remy');
await page.write(selectors.accountIndex.newNickname, 'Gambit');
await page.write(selectors.accountIndex.newEmail, 'RemyEtienneLeBeau@verdnatura.es');
await page.autocompleteSearch(selectors.accountIndex.newRole, 'Trainee');
await page.write(selectors.accountIndex.newPassword, 'cestlavie');
await page.waitToClick(selectors.accountIndex.createAccountButton);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
it('should redirect the user to the created account basic data section', async() => {
await page.waitForState('account.card.basicData');
});
it('should check the name is as expected', async() => {
const result = await page.waitToGetProperty(selectors.accountBasicData.name, 'value');
expect(result).toEqual('remy');
});
it('should check the nickname is as expected', async() => {
const result = await page.waitToGetProperty(selectors.accountBasicData.nickname, 'value');
expect(result).toEqual('Gambit');
});
it('should check the email is as expected', async() => {
const result = await page.waitToGetProperty(selectors.accountBasicData.email, 'value');
expect(result).toEqual('RemyEtienneLeBeau@verdnatura.es');
});
it('should navigate to the roles section to check the roles are correct', async() => {
await page.accessToSection('account.card.roles');
const rolesCount = await page.countElement(selectors.accountRoles.anyResult);
expect(rolesCount).toEqual(3);
});
describe('Descriptor option', () => {
describe('activate account', () => {
it(`should check the active account icon isn't present in the descriptor`, async() => {
await page.waitForNumberOfElements(selectors.accountDescriptor.activeAccountIcon, 0);
});
it('should activate the account using the descriptor menu', async() => {
await page.waitToClick(selectors.accountDescriptor.menuButton);
await page.waitToClick(selectors.accountDescriptor.activateAccount);
await page.waitToClick(selectors.accountDescriptor.acceptButton);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Account enabled!');
});
it('should check the active account icon is now present in the descriptor', async() => {
await page.waitForSelector(selectors.accountDescriptor.activeAccountIcon, {visible: false});
});
});
describe('deactivate user', () => {
it(`should check the inactive user icon isn't present in the descriptor just yet`, async() => {
await page.waitForNumberOfElements(selectors.accountDescriptor.activeUserIcon, 0);
});
it('should deactivate the user using the descriptor menu', async() => {
await page.waitToClick(selectors.accountDescriptor.menuButton);
await page.waitToClick(selectors.accountDescriptor.deactivateUser);
await page.waitToClick(selectors.accountDescriptor.acceptButton);
const message = await page.waitForSnackbar();
expect(message.text).toContain('User deactivated!');
});
it('should check the inactive user icon is now present', async() => {
await page.waitForNumberOfElements(selectors.accountDescriptor.activeUserIcon, 1);
});
});
describe('activate user', () => {
it('should activate the user using the descriptor menu', async() => {
await page.waitToClick(selectors.accountDescriptor.menuButton);
await page.waitToClick(selectors.accountDescriptor.activateUser);
await page.waitToClick(selectors.accountDescriptor.acceptButton);
const message = await page.waitForSnackbar();
expect(message.text).toContain('User activated!');
});
it('should check the inactive user icon is not present anymore', async() => {
await page.waitForNumberOfElements(selectors.accountDescriptor.activeUserIcon, 0);
});
});
describe('mail forwarding', () => {
it('should activate the mail forwarding and set the recipent email', async() => {
await page.accessToSection('account.card.mailForwarding');
await page.waitToClick(selectors.accountMailForwarding.mailForwardingCheckbox);
await page.write(selectors.accountMailForwarding.email, 'someEmail@someDomain.es');
await page.waitToClick(selectors.accountMailForwarding.save);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
});
describe('Set password', () => {
it('should set the password using the descriptor menu', async() => {
const newPassword = 'quantum.12345';
await page.waitToClick(selectors.accountDescriptor.menuButton);
await page.waitToClick(selectors.accountDescriptor.setPassword);
await page.write(selectors.accountDescriptor.newPassword, newPassword);
await page.write(selectors.accountDescriptor.repeatPassword, newPassword);
await page.waitToClick(selectors.accountDescriptor.acceptButton);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Password changed succesfully!');
});
// cant log into created account for unknown reasons
// it('should login into the created account with the new password', async() => {
// await page.loginAndModule('Remy', 'quantum.crypt0graphy');
// });
});
describe('delete account', () => {
// it('should navigate to the account basic data section', async() => {
// });
it('should delete the account using the descriptor menu', async() => {
await page.waitToClick(selectors.accountDescriptor.menuButton);
await page.waitToClick(selectors.accountDescriptor.deleteAccount);
await page.waitToClick(selectors.accountDescriptor.acceptButton);
const message = await page.waitForSnackbar();
expect(message.text).toContain('User removed');
});
});
});
});

View File

@ -1,66 +0,0 @@
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer';
describe('Account Alias create and basic data path', () => {
let browser;
let page;
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('itManagement', 'account');
await page.accessToSection('account.alias');
});
afterAll(async() => {
await browser.close();
});
it('should open the new account alias form by clicking the add button', async() => {
await page.waitToClick(selectors.accountAliasIndex.addAlias);
await page.waitForState('account.alias.create');
});
it('should fill the form and then save it by clicking the create alias button', async() => {
await page.write(selectors.accountAliasIndex.newName, 'Boring alias');
await page.write(selectors.accountAliasIndex.newDescription, 'Boring description');
await page.waitToClick(selectors.accountAliasIndex.createAliasButton);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
it('should redirect the user to the created account alias basic data section', async() => {
await page.waitForState('account.alias.card.basicData');
});
it('should edit the alias basic data', async() => {
await page.overwrite(selectors.accountAliasBasicData.name, 'Psykers');
await page.overwrite(selectors.accountAliasBasicData.description, 'Email group for psykers');
await page.waitToClick(selectors.accountAliasBasicData.save);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
it('should reload the basicData section and check the name was edited successfully', async() => {
await page.reloadSection('account.alias.card.basicData');
const result = await page.waitToGetProperty(selectors.accountAliasBasicData.name, 'value');
expect(result).toEqual('Psykers');
});
it('should check the alias description was edited successfully', async() => {
const result = await page.waitToGetProperty(selectors.accountAliasBasicData.description, 'value');
expect(result).toContain('psykers');
});
it('should search IT alias then access the user section to check the role listed is the expected one', async() => {
await page.accessToSearchResult('IT');
await page.accessToSection('account.alias.card.users');
const rolesCount = await page.countElement(selectors.accountAliasUsers.anyResult);
expect(rolesCount).toEqual(1);
});
});

View File

@ -1,86 +0,0 @@
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer';
describe('Account Role create and basic data path', () => {
let browser;
let page;
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('it', 'account');
await page.accessToSection('account.role');
});
afterAll(async() => {
await browser.close();
});
it('should open the new account role form by clicking the add button', async() => {
await page.waitToClick(selectors.accountRoleIndex.addRole);
await page.waitForState('account.role.create');
});
it('should fill the form and then save it by clicking the create role button', async() => {
await page.write(selectors.accountRoleIndex.newName, 'boringRole');
await page.write(selectors.accountRoleIndex.newDescription, 'Boring description');
await page.waitToClick(selectors.accountRoleIndex.createRoleButton);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
it('should redirect the user to the created role basic data section', async() => {
await page.waitForState('account.role.card.basicData');
});
it('should edit the role basic data', async() => {
await page.overwrite(selectors.accountRoleBasicData.name, 'psyker');
await page.overwrite(selectors.accountRoleBasicData.description, 'A role just for psykers');
await page.waitToClick(selectors.accountRoleBasicData.save);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
it('should reload the role basicData section and check the name was edited successfully', async() => {
await page.reloadSection('account.role.card.basicData');
const result = await page.waitToGetProperty(selectors.accountRoleBasicData.name, 'value');
expect(result).toEqual('psyker');
});
it('should check the role description was edited successfully', async() => {
const result = await page.waitToGetProperty(selectors.accountRoleBasicData.description, 'value');
expect(result).toContain('psykers');
});
it('should navigate to the subroles section', async() => {
await page.accessToSection('account.role.card.subroles');
});
it('should asign a subrole', async() => {
await page.waitToClick(selectors.accountSubroles.addSubrole);
await page.autocompleteSearch(selectors.accountSubroles.role, 'teamManager');
await page.waitToClick(selectors.accountSubroles.save);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Role added!');
});
it('should reload the subroles section and check a role was added', async() => {
await page.reloadSection('account.role.card.subroles');
const subrolesCount = await page.countElement(selectors.accountSubroles.anyResult);
expect(subrolesCount).toEqual(1);
});
it('should access the employee roles inheritance then check the roles listed are the expected ones', async() => {
await page.accessToSearchResult('employee');
await page.accessToSection('account.role.card.inherited');
const rolesCount = await page.countElement(selectors.accountRoleInheritance.anyResult);
expect(rolesCount).toEqual(7);
});
});

View File

@ -1,60 +0,0 @@
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer';
describe('Account ACL path', () => {
let browser;
let page;
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('developer', 'account');
await page.accessToSection('account.acl');
});
afterAll(async() => {
await browser.close();
});
it('should go to create new acl', async() => {
await page.waitToClick(selectors.accountAcl.addAcl);
await page.waitForState('account.acl.create');
});
it('should create new acl', async() => {
await page.autocompleteSearch(selectors.accountAcl.role, 'sysadmin');
await page.autocompleteSearch(selectors.accountAcl.model, 'Account');
await page.autocompleteSearch(selectors.accountAcl.accessType, '*');
await page.autocompleteSearch(selectors.accountAcl.permission, 'ALLOW');
await page.waitToClick(selectors.accountAcl.save);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
it('should navigate to edit', async() => {
await page.doSearch();
await page.waitToClick(selectors.accountAcl.thirdAcl);
await page.waitForState('account.acl.edit');
});
it('should edit the third acl', async() => {
await page.autocompleteSearch(selectors.accountAcl.model, 'Supplier');
await page.autocompleteSearch(selectors.accountAcl.accessType, 'READ');
await page.waitToClick(selectors.accountAcl.save);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
it('should delete the third result', async() => {
const result = await page.waitToGetProperty(selectors.accountAcl.thirdAcl, 'innerText');
await page.waitToClick(selectors.accountAcl.deleteThirdAcl);
await page.waitToClick(selectors.globalItems.acceptButton);
const message = await page.waitForSnackbar();
const newResult = await page.waitToGetProperty(selectors.accountAcl.thirdAcl, 'innerText');
expect(message.text).toContain('ACL removed');
expect(result).not.toEqual(newResult);
});
});

View File

@ -1,25 +0,0 @@
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer';
describe('Account Connections path', () => {
let browser;
let page;
const account = 'sysadmin';
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule(account, 'account');
await page.accessToSection('account.connections');
});
afterAll(async() => {
await browser.close();
});
it('should check this is the last connection', async() => {
const firstResult = await page.waitToGetProperty(selectors.accountConnections.firstConnection, 'innerText');
expect(firstResult).toContain(account);
});
});

View File

@ -1,37 +0,0 @@
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer';
describe('Account Accounts path', () => {
let browser;
let page;
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('sysadmin', 'account');
await page.accessToSection('account.accounts');
});
afterAll(async() => {
await browser.close();
});
it('should sync roles', async() => {
await page.waitToClick(selectors.accountAccounts.syncRoles);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Roles synchronized!');
});
it('should relogin', async() => {
await page.loginAndModule('sysadmin', 'account');
await page.accessToSection('account.accounts');
});
it('should sync all', async() => {
await page.waitToClick(selectors.accountAccounts.syncAll);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Synchronizing in the background');
});
});

View File

@ -1,41 +0,0 @@
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer';
describe('Account LDAP path', () => {
let browser;
let page;
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('sysadmin', 'account');
await page.accessToSection('account.ldap');
});
afterAll(async() => {
await browser.close();
});
it('should set data and save', async() => {
await page.waitToClick(selectors.accountLdap.checkEnable);
await page.write(selectors.accountLdap.server, '1234');
await page.write(selectors.accountLdap.rdn, '1234');
await page.write(selectors.accountLdap.password, 'nightmare');
await page.write(selectors.accountLdap.userDn, 'sysadmin');
await page.write(selectors.accountLdap.groupDn, '1234');
await page.waitToClick(selectors.accountLdap.save);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
it('should reset data', async() => {
await page.waitToClick(selectors.accountLdap.checkEnable);
await page.waitToClick(selectors.accountLdap.save);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
});

View File

@ -1,42 +0,0 @@
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer';
describe('Account Samba path', () => {
let browser;
let page;
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('sysadmin', 'account');
await page.accessToSection('account.samba');
});
afterAll(async() => {
await browser.close();
});
it('should set data and save', async() => {
await page.waitToClick(selectors.accountSamba.checkEnable);
await page.write(selectors.accountSamba.adDomain, '1234');
await page.write(selectors.accountSamba.adController, '1234');
await page.write(selectors.accountSamba.adUser, 'sysadmin');
await page.write(selectors.accountSamba.adPassword, 'nightmare');
await page.write(selectors.accountSamba.userDn, 'testDn');
await page.waitToClick(selectors.accountSamba.verifyCert);
await page.waitToClick(selectors.accountSamba.save);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
it('should reset data', async() => {
await page.waitToClick(selectors.accountSamba.checkEnable);
await page.waitToClick(selectors.accountSamba.save);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
});

View File

@ -1,112 +0,0 @@
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer';
describe('Account privileges path', () => {
let browser;
let page;
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('developer', 'account');
await page.accessToSearchResult('1101');
await page.accessToSection('account.card.privileges');
});
afterAll(async() => {
await browser.close();
});
describe('as developer', () => {
it('should throw error when give privileges', async() => {
await page.waitToClick(selectors.accountPrivileges.checkHasGrant);
await page.waitToClick(selectors.accountPrivileges.save);
const message = await page.waitForSnackbar();
expect(message.text).toContain(`You don't have grant privilege`);
});
it('should throw error when change role', async() => {
await page.autocompleteSearch(selectors.accountPrivileges.role, 'employee');
await page.waitToClick(selectors.accountPrivileges.save);
const message = await page.waitForSnackbar();
expect(message.text).toContain(`You don't have grant privilege`);
});
});
describe('as sysadmin', () => {
beforeAll(async() => {
await page.loginAndModule('sysadmin', 'account');
await page.accessToSearchResult('9');
await page.accessToSection('account.card.privileges');
});
it('should give privileges', async() => {
await page.waitToClick(selectors.accountPrivileges.checkHasGrant);
await page.waitToClick(selectors.accountPrivileges.save);
const message = await page.waitForSnackbar();
await page.reloadSection('account.card.privileges');
const result = await page.checkboxState(selectors.accountPrivileges.checkHasGrant);
expect(message.text).toContain(`Data saved!`);
expect(result).toBe('checked');
});
it('should throw error when change role and not own role', async() => {
await page.autocompleteSearch(selectors.accountPrivileges.role, 'itBoss');
await page.waitToClick(selectors.accountPrivileges.save);
const message = await page.waitForSnackbar();
expect(message.text).toContain(`You don't own the role and you can't assign it to another user`);
});
it('should change role to employee', async() => {
await page.autocompleteSearch(selectors.accountPrivileges.role, 'employee');
await page.waitToClick(selectors.accountPrivileges.save);
const message = await page.waitForSnackbar();
await page.reloadSection('account.card.privileges');
const result = await page.waitToGetProperty(selectors.accountPrivileges.role, 'value');
expect(message.text).toContain(`Data saved!`);
expect(result).toContain('employee');
});
it('should return role to developer', async() => {
await page.autocompleteSearch(selectors.accountPrivileges.role, 'developer');
await page.waitToClick(selectors.accountPrivileges.save);
const message = await page.waitForSnackbar();
await page.reloadSection('account.card.privileges');
const result = await page.waitToGetProperty(selectors.accountPrivileges.role, 'value');
expect(message.text).toContain(`Data saved!`);
expect(result).toContain('developer');
});
});
describe('as developer again', () => {
it('should remove privileges', async() => {
await page.accessToSearchResult('9');
await page.accessToSection('account.card.privileges');
await page.waitToClick(selectors.accountPrivileges.checkHasGrant);
await page.waitToClick(selectors.accountPrivileges.save);
const message = await page.waitForSnackbar();
expect(message.text).toContain(`Data saved!`);
});
it('should logIn in developer', async() => {
await page.reloadSection('account.card.privileges');
const result = await page.checkboxState(selectors.accountPrivileges.checkHasGrant);
expect(result).toBe('unchecked');
});
});
});

View File

@ -38,7 +38,7 @@
</vn-icon-button>
</div>
<a
ui-sref="worker.card.summary({id: $root.user.id})"
ng-click="$ctrl.redirect($root.user.id)"
class="vn-button colored"
translate>
My account

View File

@ -82,6 +82,9 @@ class Controller {
? {id: $search}
: {bank: {like: '%' + $search + '%'}};
}
async redirect(id) {
window.location.href = await this.vnConfig.vnApp.getUrl(`worker/${id}`);
}
}
Controller.$inject = ['$scope', '$translate', 'vnConfig', 'vnAuth', 'vnToken'];

View File

@ -1,75 +0,0 @@
<vn-watcher
vn-id="watcher"
url="AccountConfigs"
data="$ctrl.config"
id-value="1"
form="form">
</vn-watcher>
<form
name="form"
ng-submit="watcher.submit()"
class="vn-w-md">
<vn-card class="vn-pa-lg" vn-focus>
<vn-vertical>
<vn-textfield
label="Homedir base"
ng-model="$ctrl.config.homedir"
rule="AccountConfig">
</vn-textfield>
<vn-textfield
label="Shell"
ng-model="$ctrl.config.shell"
rule="AccountConfig">
</vn-textfield>
<vn-input-number
label="User and role base id"
ng-model="$ctrl.config.idBase"
rule="AccountConfig">
</vn-input-number>
<vn-horizontal>
<vn-input-number
label="Min"
ng-model="$ctrl.config.min"
rule="AccountConfig">
</vn-input-number>
<vn-input-number
label="Max"
ng-model="$ctrl.config.max"
rule="AccountConfig">
</vn-input-number>
</vn-horizontal>
<vn-horizontal>
<vn-input-number
label="Warn"
ng-model="$ctrl.config.warn"
rule="AccountConfig">
</vn-input-number>
<vn-input-number
label="Inact"
ng-model="$ctrl.config.inact"
rule="AccountConfig">
</vn-input-number>
</vn-horizontal>
</vn-vertical>
</vn-card>
<vn-button-bar>
<vn-submit
disabled="!watcher.dataChanged()"
label="Save">
</vn-submit>
<vn-button
label="Synchronize all"
ng-click="$ctrl.onSynchronizeAll()">
</vn-button>
<vn-button
label="Synchronize roles"
ng-click="$ctrl.onSynchronizeRoles()">
</vn-button>
<vn-button
disabled="!watcher.dataChanged()"
class="cancel"
label="Undo changes"
ng-click="watcher.loadOriginalData()">
</vn-button>
</vn-button-bar>
</form>

View File

@ -1,19 +0,0 @@
import ngModule from '../module';
import Section from 'salix/components/section';
export default class Controller extends Section {
onSynchronizeAll() {
this.vnApp.showSuccess(this.$t('Synchronizing in the background'));
this.$http.patch(`Accounts/syncAll`);
}
onSynchronizeRoles() {
this.$http.patch(`RoleInherits/sync`)
.then(() => this.vnApp.showSuccess(this.$t('Roles synchronized!')));
}
}
ngModule.component('vnAccountAccounts', {
template: require('./index.html'),
controller: Controller
});

View File

@ -1,14 +0,0 @@
Accounts: Cuentas
Homedir base: Directorio base para carpetas de usuario
Shell: Intérprete de línea de comandos
User and role base id: Id base usuarios y roles
Synchronize all: Sincronizar todo
Synchronize roles: Sincronizar roles
If password is not specified, just user attributes are synchronized: >-
Si la contraseña no se especifica solo se sincronizarán lo atributos del usuario
Synchronizing in the background: Sincronizando en segundo plano
Users synchronized!: ¡Usuarios sincronizados!
Username: Nombre de usuario
Synchronize: Sincronizar
Please enter the username: Por favor introduce el nombre de usuario
Roles synchronized!: ¡Roles sincronizados!

View File

@ -1,70 +0,0 @@
<vn-watcher
vn-id="watcher"
url="ACLs"
data="$ctrl.acl"
id-value="$ctrl.$params.id"
insert-mode="!$ctrl.$params.id"
form="form">
</vn-watcher>
<form
name="form"
vn-http-submit="watcher.submitGo('account.acl')"
class="vn-w-md">
<vn-card class="vn-pa-lg">
<vn-horizontal>
<vn-autocomplete
label="Role"
ng-model="$ctrl.acl.principalId"
url="VnRoles"
id-field="name"
value-field="name"
vn-focus>
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete
label="Model"
ng-model="$ctrl.acl.model"
data="$ctrl.models"
id-field="name"
value-field="name">
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-textfield
label="Property"
ng-model="$ctrl.acl.property"
info="Use * to match all properties">
</vn-textfield>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete
label="Access type"
ng-model="$ctrl.acl.accessType"
data="$ctrl.accessTypes"
id-field="name"
value-field="name">
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete
label="Permission"
ng-model="$ctrl.acl.permission"
data="$ctrl.permissions"
id-field="name"
value-field="name">
</vn-autocomplete>
</vn-horizontal>
</vn-card>
<vn-button-bar>
<vn-submit
disabled="!watcher.dataChanged()"
label="Save">
</vn-submit>
<vn-button
class="cancel"
label="Cancel"
ui-sref="account.acl">
</vn-button>
</vn-button-bar>
</form>

View File

@ -1,33 +0,0 @@
import ngModule from '../../module';
import Section from 'salix/components/section';
export default class Controller extends Section {
constructor(...args) {
super(...args);
this.accessTypes = [
{name: '*'},
{name: 'READ'},
{name: 'WRITE'}
];
this.permissions = [
{name: 'ALLOW'},
{name: 'DENY'}
];
this.models = [];
for (let model in window.validations)
this.models.push({name: model});
this.acl = {
property: '*',
principalType: 'ROLE',
accessType: 'READ',
permission: 'ALLOW'
};
}
}
ngModule.component('vnAclCreate', {
template: require('./index.html'),
controller: Controller
});

View File

@ -1,4 +0,0 @@
import './main';
import './index/';
import './create';
import './search-panel';

View File

@ -1,51 +0,0 @@
<vn-auto-search
model="model">
</vn-auto-search>
<vn-data-viewer
model="model"
class="vn-w-sm">
<vn-card>
<vn-list class="separated">
<a
ng-repeat="row in model.data track by row.id"
ui-sref="account.acl.edit(::{id: row.id})"
translate-attr="{title: 'Edit ACL'}"
class="vn-item search-result">
<vn-item-section>
<h6>{{::row.model}}.{{::row.property}}</h6>
<vn-label-value
label="Role"
value="{{::row.principalId}}">
</vn-label-value>
<vn-label-value
label="Access type"
value="{{::row.accessType}}">
</vn-label-value>
<vn-label-value
label="Permission"
value="{{::row.permission}}">
</vn-label-value>
</vn-item-section>
<vn-item-section side>
<vn-icon-button
vn-click-stop="deleteAcl.show(row)"
vn-tooltip="Delete"
icon="delete">
</vn-icon-button>
</vn-item-section>
</a>
</vn-list>
</vn-card>
</vn-data-viewer>
<a ui-sref="account.acl.create"
vn-tooltip="New ACL"
vn-bind="+"
fixed-bottom-right>
<vn-float-button icon="add"></vn-float-button>
</a>
<vn-confirm
vn-id="deleteAcl"
on-accept="$ctrl.onDelete($data)"
question="Are you sure you want to continue?"
message="ACL will be removed">
</vn-confirm>

View File

@ -1,15 +0,0 @@
import ngModule from '../../module';
import Section from 'salix/components/section';
export default class Controller extends Section {
onDelete(row) {
return this.$http.delete(`ACLs/${row.id}`)
.then(() => this.$.model.refresh())
.then(() => this.vnApp.showSuccess(this.$t('ACL removed')));
}
}
ngModule.component('vnAclIndex', {
template: require('./index.html'),
controller: Controller
});

View File

@ -1,4 +0,0 @@
New ACL: Nuevo ACL
Edit ACL: Editar ACL
ACL will be removed: El ACL será eliminado
ACL removed: ACL eliminado

View File

@ -1,4 +0,0 @@
Model: Modelo
Property: Propiedad
Access type: Tipo de acceso
Permission: Permiso

View File

@ -1,20 +0,0 @@
<vn-crud-model
vn-id="model"
url="ACLs"
limit="20"
expr-builder="$ctrl.exprBuilder(param, value)"
auto-save="true">
</vn-crud-model>
<vn-portal slot="topbar">
<vn-searchbar
info="Search ACL by model name"
panel="vn-acl-search-panel"
model="model"
expr-builder="$ctrl.exprBuilder(param, value)"
base-state="account.acl"
entity-state="edit">
</vn-searchbar>
</vn-portal>
<ui-view>
<vn-acl-index></vn-acl-index>
</ui-view>

View File

@ -1,18 +0,0 @@
import ngModule from '../../module';
import ModuleMain from 'salix/components/module-main';
export default class ACL extends ModuleMain {
exprBuilder(param, value) {
switch (param) {
case 'search':
return {model: {like: `%${value}%`}};
default:
return {[param]: value};
}
}
}
ngModule.vnComponent('vnAclComponent', {
controller: ACL,
template: require('./index.html')
});

View File

@ -1,39 +0,0 @@
<div class="search-panel">
<form ng-submit="$ctrl.onSearch()">
<vn-vertical>
<vn-autocomplete
label="Role"
ng-model="filter.principalId"
url="VnRoles"
value-field="name">
</vn-autocomplete>
<vn-autocomplete
label="Model"
ng-model="filter.model"
data="$ctrl.models"
id-field="name"
value-field="name">
</vn-autocomplete>
<vn-textfield
label="Property"
ng-model="filter.property">
</vn-textfield>
<vn-autocomplete
label="Access type"
ng-model="filter.accessType"
data="$ctrl.accessTypes"
value-field="name">
</vn-autocomplete>
<vn-autocomplete
label="Permission"
ng-model="$ctrl.acl.permission"
data="$ctrl.permissions"
value-field="name">
</vn-autocomplete>
<vn-submit
label="Search"
class="vn-mt-lg">
</vn-submit>
</vn-vertical>
</form>
</div>

View File

@ -1,26 +0,0 @@
import ngModule from '../../module';
import SearchPanel from 'core/components/searchbar/search-panel';
export default class Controller extends SearchPanel {
constructor(...args) {
super(...args);
this.accessTypes = [
{name: '*'},
{name: 'READ'},
{name: 'WRITE'}
];
this.permissions = [
{name: 'ALLOW'},
{name: 'DENY'}
];
this.models = [];
for (let model in window.validations)
this.models.push({name: model});
}
}
ngModule.component('vnAclSearchPanel', {
template: require('./index.html'),
controller: Controller
});

View File

@ -1,43 +0,0 @@
<vn-watcher
vn-id="watcher"
url="MailAliases"
data="$ctrl.alias"
form="form">
</vn-watcher>
<form
name="form"
ng-submit="watcher.submit()"
class="vn-w-md">
<vn-card class="vn-pa-lg">
<vn-vertical>
<vn-textfield
label="Name"
ng-model="$ctrl.alias.alias"
rule="MailAlias"
vn-focus>
</vn-textfield>
<vn-textfield
label="Description"
ng-model="$ctrl.alias.description"
rule="MailAlias">
</vn-textfield>
<vn-check
label="Public"
ng-model="$ctrl.alias.isPublic"
rule="MailAlias">
</vn-check>
</vn-vertical>
</vn-card>
<vn-button-bar>
<vn-submit
disabled="!watcher.dataChanged()"
label="Save">
</vn-submit>
<vn-button
class="cancel"
label="Undo changes"
disabled="!watcher.dataChanged()"
ng-click="watcher.loadOriginalData()">
</vn-button>
</vn-button-bar>
</form>

View File

@ -1,12 +0,0 @@
import ngModule from '../../module';
import Section from 'salix/components/section';
export default class Controller extends Section {}
ngModule.component('vnAliasBasicData', {
template: require('./index.html'),
controller: Controller,
bindings: {
alias: '<'
}
});

View File

@ -1,5 +0,0 @@
<vn-portal slot="menu">
<vn-alias-descriptor alias="$ctrl.alias"></vn-alias-descriptor>
<vn-left-menu source="alias"></vn-left-menu>
</vn-portal>
<ui-view></ui-view>

View File

@ -1,14 +0,0 @@
import ngModule from '../../module';
import ModuleCard from 'salix/components/module-card';
class Controller extends ModuleCard {
reload() {
this.$http.get(`MailAliases/${this.$params.id}`)
.then(res => this.alias = res.data);
}
}
ngModule.vnComponent('vnAliasCard', {
template: require('./index.html'),
controller: Controller
});

View File

@ -1,38 +0,0 @@
<vn-watcher
vn-id="watcher"
url="MailAliases"
data="$ctrl.alias"
insert-mode="true"
form="form">
</vn-watcher>
<form
name="form"
vn-http-submit="$ctrl.onSubmit()"
class="vn-w-md">
<vn-card class="vn-pa-lg">
<vn-vertical>
<vn-textfield
label="Name"
ng-model="$ctrl.alias.alias"
rule="MailAlias"
vn-focus>
</vn-textfield>
<vn-textfield
label="Description"
ng-model="$ctrl.alias.description"
rule="MailAlias">
</vn-textfield>
</vn-vertical>
</vn-card>
<vn-button-bar>
<vn-submit
disabled="!watcher.dataChanged()"
label="Create">
</vn-submit>
<vn-button
class="cancel"
label="Cancel"
ui-sref="account.alias">
</vn-button>
</vn-button-bar>
</form>

View File

@ -1,15 +0,0 @@
import ngModule from '../../module';
import Section from 'salix/components/section';
export default class Controller extends Section {
onSubmit() {
return this.$.watcher.submit().then(res =>
this.$state.go('account.alias.card.basicData', {id: res.data.id})
);
}
}
ngModule.component('vnAliasCreate', {
template: require('./index.html'),
controller: Controller
});

View File

@ -1,27 +0,0 @@
<vn-descriptor-content
module="account"
base-state="account.alias"
description="$ctrl.alias.alias">
<slot-menu>
<vn-item
ng-click="deleteAlias.show()"
name="deleteAlias"
translate>
Delete
</vn-item>
</slot-menu>
<slot-body>
<div class="attributes">
<vn-label-value
label="Description"
value="{{$ctrl.alias.description}}">
</vn-label-value>
</div>
</slot-body>
</vn-descriptor-content>
<vn-confirm
vn-id="delete-alias"
on-accept="$ctrl.onDelete()"
question="Are you sure you want to continue?"
message="Alias will be removed">
</vn-confirm>

View File

@ -1,26 +0,0 @@
import ngModule from '../../module';
import Descriptor from 'salix/components/descriptor';
class Controller extends Descriptor {
get alias() {
return this.entity;
}
set alias(value) {
this.entity = value;
}
onDelete() {
return this.$http.delete(`MailAliases/${this.id}`)
.then(() => this.$state.go('account.alias'))
.then(() => this.vnApp.showSuccess(this.$t('Alias removed')));
}
}
ngModule.component('vnAliasDescriptor', {
template: require('./index.html'),
controller: Controller,
bindings: {
alias: '<'
}
});

View File

@ -1,2 +0,0 @@
Alias will be removed: El alias será eliminado
Alias removed: Alias eliminado

View File

@ -1,9 +0,0 @@
import './main';
import './index/';
import './create';
import './summary';
import './card';
import './descriptor';
import './create';
import './basic-data';
import './users';

View File

@ -1,39 +0,0 @@
<vn-auto-search
model="model">
</vn-auto-search>
<vn-data-viewer
model="model"
class="vn-w-sm">
<vn-card>
<vn-list class="separated">
<a
ng-repeat="alias in model.data track by alias.id"
ui-sref="account.alias.card.summary(::{id: alias.id})"
ui-sref-opts="{inherit: false}"
translate-attr="{title: 'View alias'}"
class="vn-item search-result">
<vn-item-section>
<h6>{{::alias.alias}}</h6>
<div>{{::alias.description}}</div>
</vn-item-section>
<vn-item-section side>
<vn-icon-button
vn-click-stop="$ctrl.preview(alias)"
vn-tooltip="Preview"
icon="preview">
</vn-icon-button>
</vn-item-section>
</a>
</vn-list>
</vn-card>
</vn-data-viewer>
<vn-popup vn-id="summary">
<vn-alias-summary alias="$ctrl.selectedAlias"></vn-alias-summary>
</vn-popup>
<a ui-sref="account.alias.create"
ui-sref-opts="{inherit: false}"
vn-tooltip="New alias"
vn-bind="+"
fixed-bottom-right>
<vn-float-button icon="add"></vn-float-button>
</a>

View File

@ -1,14 +0,0 @@
import ngModule from '../../module';
import Section from 'salix/components/section';
export default class Controller extends Section {
preview(alias) {
this.selectedAlias = alias;
this.$.summary.show();
}
}
ngModule.component('vnAliasIndex', {
template: require('./index.html'),
controller: Controller
});

View File

@ -1,2 +0,0 @@
New alias: Nuevo alias
View alias: Ver alias

View File

@ -1 +0,0 @@
Public: Público

View File

@ -1,17 +0,0 @@
<vn-crud-model
vn-id="model"
url="MailAliases"
limit="20"
expr-builder="$ctrl.exprBuilder(param, value)">
</vn-crud-model>
<vn-portal slot="topbar">
<vn-searchbar
info="Search alias by id or name"
model="model"
expr-builder="$ctrl.exprBuilder(param, value)"
base-state="account.alias">
</vn-searchbar>
</vn-portal>
<ui-view>
<vn-alias-index></vn-alias-index>
</ui-view>

View File

@ -1,18 +0,0 @@
import ngModule from '../../module';
import ModuleMain from 'salix/components/module-main';
export default class Alias extends ModuleMain {
exprBuilder(param, value) {
switch (param) {
case 'search':
return /^\d+$/.test(value)
? {id: value}
: {alias: {like: `%${value}%`}};
}
}
}
ngModule.vnComponent('vnAlias', {
controller: Alias,
template: require('./index.html')
});

View File

@ -1,16 +0,0 @@
<vn-card class="summary">
<h5>{{summary.alias}}</h5>
<vn-horizontal class="vn-pa-md">
<vn-one>
<h4 translate>Basic data</h4>
<vn-label-value
label="Id"
value="{{summary.id}}">
</vn-label-value>
<vn-label-value
label="Description"
value="{{summary.description}}">
</vn-label-value>
</vn-one>
</vn-horizontal>
</vn-card>

View File

@ -1,25 +0,0 @@
import ngModule from '../../module';
import Component from 'core/lib/component';
class Controller extends Component {
set alias(value) {
this._alias = value;
this.$.summary = null;
if (!value) return;
this.$http.get(`MailAliases/${value.id}`)
.then(res => this.$.summary = res.data);
}
get alias() {
return this._alias;
}
}
ngModule.component('vnAliasSummary', {
template: require('./index.html'),
controller: Controller,
bindings: {
alias: '<'
}
});

View File

@ -1,26 +0,0 @@
<vn-data-viewer
data="data"
class="vn-w-xs">
<vn-card>
<vn-table>
<vn-tbody>
<vn-tr ng-repeat="row in data | orderBy:'user.name'">
<vn-td>{{::row.user.name}}</vn-td>
<vn-td shrink>
<vn-icon-button
icon="delete"
translate-attr="{title: 'Remove'}"
ng-click="removeConfirm.show(row)">
</vn-icon-button>
</vn-td>
</vn-tr>
</vn-tbody>
</vn-table>
</vn-card>
</vn-data-viewer>
<vn-confirm
vn-id="removeConfirm"
message="User will be removed from alias"
question="Are you sure you want to continue?"
on-accept="$ctrl.onRemove($data)">
</vn-confirm>

View File

@ -1,31 +0,0 @@
import ngModule from '../../module';
import Section from 'salix/components/section';
export default class Controller extends Section {
$onInit() {
let filter = {
include: {
relation: 'user',
scope: {
fields: ['id', 'name']
}
}
};
this.$http.get(`MailAliases/${this.$params.id}/accounts`, {filter})
.then(res => this.$.data = res.data);
}
onRemove(row) {
return this.$http.delete(`MailAliases/${this.$params.id}/accounts/${row.id}`)
.then(() => {
let index = this.$.data.indexOf(row);
if (index !== -1) this.$.data.splice(index, 1);
this.vnApp.showSuccess(this.$t('User removed'));
});
}
}
ngModule.component('vnAliasUsers', {
template: require('./index.html'),
controller: Controller
});

View File

@ -1,42 +0,0 @@
import './index';
describe('component vnAliasUsers', () => {
let controller;
let $httpBackend;
beforeEach(ngModule('account'));
beforeEach(inject(($componentController, _$httpBackend_) => {
$httpBackend = _$httpBackend_;
controller = $componentController('vnAliasUsers', {$element: null});
controller.$params.id = 1;
}));
describe('$onInit()', () => {
it('should delete entity and go to index', () => {
$httpBackend.expectGET('MailAliases/1/accounts').respond('foo');
controller.$onInit();
$httpBackend.flush();
expect(controller.$.data).toBe('foo');
});
});
describe('onRemove()', () => {
it('should call backend method to change role', () => {
jest.spyOn(controller.vnApp, 'showSuccess');
controller.$.data = [
{id: 1, alias: 'foo'},
{id: 2, alias: 'bar'}
];
$httpBackend.expectDELETE('MailAliases/1/accounts/1').respond();
controller.onRemove(controller.$.data[0]);
$httpBackend.flush();
expect(controller.$.data).toEqual([{id: 2, alias: 'bar'}]);
expect(controller.vnApp.showSuccess).toHaveBeenCalled();
});
});
});

View File

@ -1,2 +0,0 @@
User will be removed from alias: El usuario será borrado del alias
User removed: Usuario borrado

View File

@ -1,64 +0,0 @@
<div ng-if="$ctrl.card.hasAccount">
<vn-data-viewer
data="data"
class="vn-w-xs">
<vn-card>
<vn-list class="separated">
<vn-item ng-repeat="row in data | orderBy:'alias.alias'">
<vn-item-section>
<div>
{{::row.alias.alias}}
</div>
<div class="text-caption text-secondary">
{{::row.alias.description}}
</div>
</vn-item-section>
<vn-item-section side>
<vn-icon-button
icon="delete"
translate-attr="{title: 'Unsubscribe'}"
ng-click="removeConfirm.show(row)">
</vn-icon-button>
</vn-item-section>
</vn-item>
</vn-list>
</vn-table>
</vn-card>
</vn-data-viewer>
<vn-float-button
icon="add"
translate-attr="{title: 'Add'}"
vn-bind="+"
ng-click="$ctrl.onAddClick()"
fixed-bottom-right>
</vn-float-button>
<vn-dialog
vn-id="dialog"
on-accept="$ctrl.onAddSave()">
<tpl-body>
<vn-autocomplete
label="Alias"
ng-model="$ctrl.addData.mailAlias"
url="MailAliases"
show-field="alias"
vn-focus>
</vn-autocomplete>
</tpl-body>
<tpl-buttons>
<input type="button" response="cancel" translate-attr="{value: 'Cancel'}"/>
<button response="accept" translate>Save</button>
</tpl-buttons>
</vn-dialog>
<vn-confirm
vn-id="removeConfirm"
message="User will be removed from alias"
question="Are you sure you want to continue?"
on-accept="$ctrl.onRemove($data)">
</vn-confirm>
</div>
<div
ng-if="!$ctrl.card.hasAccount"
class="bg-title"
translate>
Account not enabled
</div>

View File

@ -1,51 +0,0 @@
import ngModule from '../module';
import Section from 'salix/components/section';
export default class Controller extends Section {
$onInit() {
this.refresh();
}
refresh() {
let filter = {
where: {account: this.$params.id},
include: {
relation: 'alias',
scope: {
fields: ['id', 'alias', 'description']
}
}
};
return this.$http.get(`MailAliasAccounts`, {filter})
.then(res => this.$.data = res.data);
}
onAddClick() {
this.addData = {account: this.$params.id};
this.$.dialog.show();
}
onAddSave() {
return this.$http.post(`MailAliasAccounts`, this.addData)
.then(() => this.refresh())
.then(() => this.vnApp.showSuccess(
this.$t('Subscribed to alias!'))
);
}
onRemove(row) {
return this.$http.delete(`MailAliasAccounts/${row.id}`)
.then(() => {
this.$.data.splice(this.$.data.indexOf(row), 1);
this.vnApp.showSuccess(this.$t('Unsubscribed from alias!'));
});
}
}
ngModule.component('vnUserAliases', {
template: require('./index.html'),
controller: Controller,
require: {
card: '^vnUserCard'
}
});

View File

@ -1,53 +0,0 @@
import './index';
describe('component vnUserAliases', () => {
let controller;
let $httpBackend;
beforeEach(ngModule('account'));
beforeEach(inject(($componentController, _$httpBackend_) => {
$httpBackend = _$httpBackend_;
controller = $componentController('vnUserAliases', {$element: null});
jest.spyOn(controller.vnApp, 'showSuccess');
}));
describe('refresh()', () => {
it('should refresh the controller data', () => {
$httpBackend.expectGET('MailAliasAccounts').respond('foo');
controller.refresh();
$httpBackend.flush();
expect(controller.$.data).toBe('foo');
});
});
describe('onAddSave()', () => {
it('should add the new row', () => {
controller.addData = {account: 1};
$httpBackend.expectPOST('MailAliasAccounts').respond();
$httpBackend.expectGET('MailAliasAccounts').respond('foo');
controller.onAddSave();
$httpBackend.flush();
expect(controller.vnApp.showSuccess).toHaveBeenCalled();
});
});
describe('onRemove()', () => {
it('shoud remove the passed row remote and locally', () => {
controller.$.data = [
{id: 1, alias: 'foo'},
{id: 2, alias: 'bar'}
];
$httpBackend.expectDELETE('MailAliasAccounts/1').respond();
controller.onRemove(controller.$.data[0]);
$httpBackend.flush();
expect(controller.$.data).toEqual([{id: 2, alias: 'bar'}]);
expect(controller.vnApp.showSuccess).toHaveBeenCalled();
});
});
});

View File

@ -1,3 +0,0 @@
Unsubscribe: Desuscribir
Subscribed to alias!: ¡Suscrito al alias!
Unsubscribed from alias!: ¡Desuscrito del alias!

View File

@ -1,51 +0,0 @@
<mg-ajax path="VnUsers/{{patch.params.id}}/update-user" options="vnPatch"></mg-ajax>
<vn-watcher
vn-id="watcher"
data="$ctrl.user"
form="form"
save="patch">
</vn-watcher>
<form
name="form"
ng-submit="$ctrl.onSubmit()"
class="vn-w-md">
<vn-card class="vn-pa-lg">
<vn-vertical>
<vn-textfield
label="User"
ng-model="$ctrl.user.name"
rule="VnUser"
vn-focus>
</vn-textfield>
<vn-textfield
label="Nickname"
ng-model="$ctrl.user.nickname"
rule="VnUser">
</vn-textfield>
<vn-textfield
label="Personal email"
ng-model="$ctrl.user.email"
rule="VnUser">
</vn-textfield>
<vn-autocomplete
label="Language"
ng-model="$ctrl.user.lang"
url="Languages"
value-field="code"
rule="VnUser">
</vn-autocomplete>
</vn-vertical>
</vn-card>
<vn-button-bar>
<vn-submit
disabled="!watcher.dataChanged()"
label="Save">
</vn-submit>
<vn-button
class="cancel"
label="Undo changes"
disabled="!watcher.dataChanged()"
ng-click="watcher.loadOriginalData()">
</vn-button>
</vn-button-bar>
</form>

View File

@ -1,25 +0,0 @@
import ngModule from '../module';
import Section from 'salix/components/section';
export default class Controller extends Section {
$onInit() {
if (this.$params.emailConfirmed)
this.vnApp.showSuccess(this.$t('Email verified successfully!'));
}
onSubmit() {
this.$.watcher.submit()
.then(() => this.card.reload());
}
}
ngModule.component('vnUserBasicData', {
template: require('./index.html'),
controller: Controller,
require: {
card: '^vnUserCard'
},
bindings: {
user: '<'
}
});

View File

@ -1 +0,0 @@
Email verified successfully!: Correo verificado correctamente!

View File

@ -1,8 +0,0 @@
<vn-portal slot="menu">
<vn-user-descriptor
user="$ctrl.user"
on-change="$ctrl.reload()">
</vn-user-descriptor>
<vn-left-menu source="card"></vn-left-menu>
</vn-portal>
<ui-view></ui-view>

View File

@ -1,32 +0,0 @@
import ngModule from '../module';
import ModuleCard from 'salix/components/module-card';
import './style.scss';
class Controller extends ModuleCard {
reload() {
const filter = {
where: {id: this.$params.id},
include: {
relation: 'role',
scope: {
fields: ['id', 'name']
}
}
};
return Promise.all([
this.$http.get(`VnUsers/preview`, {filter})
.then(res => {
const [user] = res.data;
this.user = user;
}),
this.$http.get(`Accounts/${this.$params.id}/exists`)
.then(res => this.hasAccount = res.data.exists)
]);
}
}
ngModule.vnComponent('vnUserCard', {
template: require('./index.html'),
controller: Controller
});

View File

@ -1,27 +0,0 @@
import './index';
describe('component vnUserCard', () => {
let controller;
let $httpBackend;
beforeEach(ngModule('account'));
beforeEach(inject(($componentController, _$httpBackend_) => {
$httpBackend = _$httpBackend_;
controller = $componentController('vnUserCard', {$element: null});
}));
describe('reload()', () => {
it('should reload the controller data', () => {
controller.$params.id = 1;
$httpBackend.expectGET('VnUsers/preview').respond('foo');
$httpBackend.expectGET('Accounts/1/exists').respond({exists: true});
controller.reload();
$httpBackend.flush();
expect(controller.user).toBe('f');
expect(controller.hasAccount).toBeTruthy();
});
});
});

View File

@ -1,10 +0,0 @@
@import "variables";
.bg-title {
display: block;
text-align: center;
padding: 24px;
box-sizing: border-box;
color: $color-font-secondary;
font-size: 1.375rem;
}

View File

@ -1,45 +0,0 @@
<vn-crud-model
vn-id="model"
url="AccessTokens"
filter="::$ctrl.filter"
limit="20"
auto-load="true">
</vn-crud-model>
<vn-data-viewer
model="model"
class="vn-w-sm">
<vn-card>
<vn-list class="separated">
<a
ng-repeat="row in model.data"
ui-sref="account.card.summary(::{id: row.user.id})"
translate-attr="{title: 'Go to user'}"
class="vn-item search-result">
<vn-item-section>
<h6>{{::row.user.username}}</h6>
<div>{{::row.created | date:'dd/MM HH:mm'}}</div>
</vn-item-section>
<vn-item-section side>
<vn-icon-button
vn-click-stop="disconnect.show(row)"
vn-tooltip="Kill session"
icon="exit_to_app">
</vn-icon-button>
</vn-item-section>
</a>
</vn-list>
</vn-card>
</vn-data-viewer>
<vn-confirm
vn-id="disconnect"
on-accept="$ctrl.onDisconnect($data)"
question="Are you sure you want to continue?"
message="Session will be killed">
</vn-confirm>
<vn-float-button
vn-tooltip="Refresh"
icon="refresh"
ng-click="model.refresh()"
vn-bind="r"
fixed-bottom-right>
</vn-float-button>

View File

@ -1,29 +0,0 @@
import ngModule from '../module';
import Section from 'salix/components/section';
export default class Controller extends Section {
constructor(...args) {
super(...args);
this.filter = {
fields: ['id', 'created', 'userId'],
include: {
relation: 'user',
scope: {
fields: ['username']
}
},
order: 'created DESC'
};
}
onDisconnect(row) {
return this.$http.delete(`AccessTokens/${row.id}`)
.then(() => this.$.model.refresh())
.then(() => this.vnApp.showSuccess(this.$t('Session killed')));
}
}
ngModule.component('vnConnections', {
template: require('./index.html'),
controller: Controller
});

View File

@ -1,5 +0,0 @@
Go to user: Ir al usuario
Refresh: Actualizar
Session will be killed: Se va a matar la sesión
Kill session: Matar sesión
Session killed: Sesión matada

View File

@ -1,57 +0,0 @@
<vn-watcher
vn-id="watcher"
url="VnUsers"
data="$ctrl.user"
insert-mode="true"
form="form">
</vn-watcher>
<form
name="form"
vn-http-submit="$ctrl.onSubmit()"
class="vn-w-md">
<vn-card class="vn-pa-lg">
<vn-vertical>
<vn-textfield
label="Name"
ng-model="$ctrl.user.name"
rule="VnUser"
vn-focus>
</vn-textfield>
<vn-textfield
label="Nickname"
ng-model="$ctrl.user.nickname"
rule="VnUser">
</vn-textfield>
<vn-textfield
label="Email"
ng-model="$ctrl.user.email"
rule="VnUser">
</vn-textfield>
<vn-autocomplete
label="Role"
ng-model="$ctrl.user.roleFk"
url="VnRoles"
rule="VnUser">
</vn-autocomplete>
<vn-textfield
label="Password"
ng-model="$ctrl.user.password"
type="password">
</vn-textfield>
<vn-check
label="Active"
ng-model="$ctrl.user.active">
</vn-check>
</vn-vertical>
</vn-card>
<vn-button-bar>
<vn-submit
label="Create">
</vn-submit>
<vn-button
class="cancel"
label="Cancel"
ui-sref="account.index">
</vn-button>
</vn-button-bar>
</form>

View File

@ -1,20 +0,0 @@
import ngModule from '../module';
import Section from 'salix/components/section';
export default class Controller extends Section {
constructor($element, $) {
super($element, $);
this.user = {active: true};
}
onSubmit() {
return this.$.watcher.submit().then(res => {
this.$state.go('account.card.basicData', {id: res.data.id});
});
}
}
ngModule.component('vnUserCreate', {
template: require('./index.html'),
controller: Controller
});

View File

@ -1,5 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`component vnUserDescriptor onPassChange() should throw an error when password is empty 1`] = `"You must enter a new password"`;
exports[`component vnUserDescriptor onPassChange() should throw an error when repeat password not matches new password 1`] = `"Passwords don't match"`;

View File

@ -1,97 +0,0 @@
import './index';
describe('component vnUserDescriptor', () => {
let controller;
let $httpBackend;
let user = {id: 1, name: 'foo'};
beforeEach(ngModule('account'));
beforeEach(inject(($componentController, _$httpBackend_) => {
$httpBackend = _$httpBackend_;
$httpBackend.whenGET('Accounts/1/exists').respond({exists: true});
controller = $componentController('vnUserDescriptor', {$element: null}, {user});
jest.spyOn(controller, 'emit');
jest.spyOn(controller.vnApp, 'showSuccess');
}));
describe('onDelete()', () => {
it('should delete entity and go to index', () => {
controller.$state.go = jest.fn();
$httpBackend.expectDELETE('VnUsers/1').respond();
controller.onDelete();
$httpBackend.flush();
expect(controller.$state.go).toHaveBeenCalledWith('account.index');
expect(controller.vnApp.showSuccess).toHaveBeenCalled();
});
});
describe('onPassChange()', () => {
it('should throw an error when password is empty', () => {
expect(() => {
controller.onPassChange();
}).toThrowErrorMatchingSnapshot();
});
it('should throw an error when repeat password not matches new password', () => {
controller.newPassword = 'foo';
controller.repeatPassword = 'bar';
expect(() => {
controller.onPassChange();
}).toThrowErrorMatchingSnapshot();
});
it('should make a request when password checks passes', () => {
controller.newPassword = 'foo';
controller.repeatPassword = 'foo';
$httpBackend.expectPATCH('Accounts/1/setPassword').respond();
controller.onPassChange();
$httpBackend.flush();
expect(controller.vnApp.showSuccess).toHaveBeenCalled();
expect(controller.emit).toHaveBeenCalledWith('change');
});
});
describe('onEnableAccount()', () => {
it('should make request to enable account', () => {
$httpBackend.expectPOST('Accounts', {id: 1}).respond();
controller.onEnableAccount();
$httpBackend.flush();
expect(controller.hasAccount).toBeTruthy();
expect(controller.vnApp.showSuccess).toHaveBeenCalled();
expect(controller.emit).toHaveBeenCalledWith('change');
});
});
describe('onDisableAccount()', () => {
it('should make request to disable account', () => {
$httpBackend.expectDELETE('Accounts/1').respond();
controller.onDisableAccount();
$httpBackend.flush();
expect(controller.hasAccount).toBeFalsy();
expect(controller.vnApp.showSuccess).toHaveBeenCalled();
expect(controller.emit).toHaveBeenCalledWith('change');
});
});
describe('onSetActive()', () => {
it('should make request to activate/deactivate the user', () => {
$httpBackend.expectPATCH('VnUsers/1', {active: true}).respond();
controller.onSetActive(true);
$httpBackend.flush();
expect(controller.user.active).toBeTruthy();
expect(controller.vnApp.showSuccess).toHaveBeenCalled();
expect(controller.emit).toHaveBeenCalledWith('change');
});
});
});

View File

@ -1,24 +1,6 @@
export * from './module';
import './main';
import './index/';
import './role';
import './alias';
import './connections';
import './acl';
import './summary';
import './card';
import './descriptor';
import './descriptor-popover';
import './search-panel';
import './create';
import './basic-data';
import './mail-forwarding';
import './aliases';
import './roles';
import './ldap';
import './samba';
import './accounts';
import './privileges';
import './user-log';
import './role-log';
import './summary';

View File

@ -1,47 +0,0 @@
<vn-auto-search
model="model">
</vn-auto-search>
<vn-data-viewer
model="model"
class="vn-w-sm">
<vn-card>
<div class="vn-list separated">
<a
ng-repeat="user in model.data track by user.id"
ui-sref="account.card.summary(::{id: user.id})"
translate-attr="{title: 'View user'}"
class="vn-item search-result">
<vn-item-section>
<h6>{{::user.nickname}}</h6>
<vn-label-value
label="Id"
value="{{::user.id}}">
</vn-label-value>
<vn-label-value
label="User"
value="{{::user.name}}">
</vn-label-value>
</vn-item-section>
<vn-item-section side>
<vn-icon-button
vn-click-stop="$ctrl.preview(user)"
vn-tooltip="Preview"
icon="preview">
</vn-icon-button>
</vn-item-section>
</a>
</div>
</vn-card>
</vn-data-viewer>
<vn-popup vn-id="summary">
<vn-user-summary user="$ctrl.selectedUser"></vn-user-summary>
</vn-popup>
<a
fixed-bottom-right
ui-sref="account.create"
vn-tooltip="New user"
vn-bind="+"
vn-acl="itManagement"
vn-acl-action="remove">
<vn-float-button icon="add"></vn-float-button>
</a>

View File

@ -1,14 +0,0 @@
import ngModule from '../module';
import Section from 'salix/components/section';
export default class Controller extends Section {
preview(user) {
this.selectedUser = user;
this.$.summary.show();
}
}
ngModule.component('vnUserIndex', {
template: require('./index.html'),
controller: Controller
});

View File

@ -1,2 +0,0 @@
New user: Nuevo usuario
View user: Ver usuario

View File

@ -1,66 +0,0 @@
<vn-watcher
vn-id="watcher"
url="LdapConfigs"
data="$ctrl.config"
id-value="1"
form="form">
</vn-watcher>
<form
name="form"
ng-submit="watcher.submit()"
class="vn-w-md">
<vn-card class="vn-pa-lg" vn-focus>
<vn-vertical>
<vn-check
label="Enable synchronization"
ng-model="watcher.hasData">
</vn-check>
</vn-vertical>
<vn-vertical
ng-if="watcher.hasData"
class="vn-mt-md">
<vn-textfield
label="Server"
ng-model="$ctrl.config.server"
rule="LdapConfig">
</vn-textfield>
<vn-textfield
label="RDN"
ng-model="$ctrl.config.rdn"
rule="LdapConfig">
</vn-textfield>
<vn-textfield
label="Password"
ng-model="$ctrl.config.password"
type="password"
rule="LdapConfig">
</vn-textfield>
<vn-textfield
label="User DN"
ng-model="$ctrl.config.userDn"
rule="LdapConfig">
</vn-textfield>
<vn-textfield
label="Group DN"
ng-model="$ctrl.config.groupDn"
rule="LdapConfig">
</vn-textfield>
</vn-vertical>
</vn-card>
<vn-button-bar>
<vn-submit
disabled="!watcher.dataChanged()"
label="Save">
</vn-submit>
<vn-button
label="Test connection"
ng-click="$ctrl.onTestConection()">
</vn-button>
<vn-button
class="cancel"
label="Undo changes"
disabled="!watcher.dataChanged()"
ng-click="watcher.loadOriginalData()">
</vn-button>
</vn-button-bar>
</form>

View File

@ -1,14 +0,0 @@
import ngModule from '../module';
import Section from 'salix/components/section';
export default class Controller extends Section {
onTestConection() {
this.$http.get(`LdapConfigs/test`)
.then(() => this.vnApp.showSuccess(this.$t('LDAP connection established!')));
}
}
ngModule.component('vnAccountLdap', {
template: require('./index.html'),
controller: Controller
});

View File

@ -1,8 +0,0 @@
Enable synchronization: Habilitar sincronización
Server: Servidor
RDN: RDN
User DN: DN usuarios
Filter: Filtro
Group DN: DN grupos
Test connection: Probar conexión
LDAP connection established!: ¡Conexión con LDAP establecida!

View File

@ -1,49 +0,0 @@
<div ng-if="$ctrl.card.hasAccount">
<vn-watcher
vn-id="watcher"
url="MailForwards"
id-field="account"
id-value="$ctrl.$params.id"
data="data"
form="form">
</vn-watcher>
<form
name="form"
ng-submit="watcher.submit()"
class="vn-w-md">
<vn-card class="vn-pa-lg">
<vn-vertical>
<vn-check
label="Enable mail forwarding"
ng-model="watcher.hasData">
</vn-check>
<vn-textfield
ng-if="watcher.hasData"
label="Forward email"
ng-model="data.forwardTo"
info="All emails will be forwarded to the specified address."
rule="MailForward"
vn-focus>
</vn-textfield>
</vn-vertical>
</vn-card>
<vn-button-bar>
<vn-submit
disabled="!watcher.dataChanged()"
label="Save">
</vn-submit>
<vn-button
class="cancel"
label="Undo changes"
disabled="!watcher.dataChanged()"
ng-click="watcher.loadOriginalData()">
</vn-button>
</vn-button-bar>
</form>
</div>
<div
ng-if="!$ctrl.card.hasAccount"
class="bg-title"
translate>
Account not enabled
</div>

View File

@ -1,12 +0,0 @@
import ngModule from '../module';
import Section from 'salix/components/section';
export default class Controller extends Section {}
ngModule.component('vnUserMailForwarding', {
template: require('./index.html'),
controller: Controller,
require: {
card: '^vnUserCard'
},
});

View File

@ -1,7 +0,0 @@
Mail forwarding: Reenvío de correo
Forward email: Dirección de reenvío
Enable mail forwarding: Habilitar redirección de correo
All emails will be forwarded to the specified address.: >
Todos los correos serán reenviados a la dirección especificada, no se
mantendrá copia de los mismos en el buzón del usuario.
You don't have enough privileges: No tienes suficientes permisos

View File

@ -1,19 +0,0 @@
<vn-crud-model
vn-id="model"
url="VnUsers/preview"
filter="::$ctrl.filter"
limit="20">
</vn-crud-model>
<vn-portal slot="topbar">
<vn-searchbar
vn-focus
panel="vn-user-search-panel"
info="Search user by id, name or nickname"
model="model"
expr-builder="$ctrl.exprBuilder(param, value)">
</vn-searchbar>
</vn-portal>
<vn-portal slot="menu">
<vn-left-menu></vn-left-menu>
</vn-portal>
<ui-view></ui-view>

View File

@ -4,32 +4,10 @@ import ModuleMain from 'salix/components/module-main';
export default class User extends ModuleMain {
constructor($element, $) {
super($element, $);
this.filter = {
fields: ['id', 'nickname', 'name', 'role'],
include: {
relation: 'role',
scope: {
fields: ['id', 'name']
}
}
};
}
exprBuilder(param, value) {
switch (param) {
case 'search':
return /^\d+$/.test(value)
? {id: value}
: {or: [
{name: {like: `%${value}%`}},
{nickname: {like: `%${value}%`}}
]};
case 'name':
case 'nickname':
return {[param]: {like: `%${value}%`}};
case 'roleFk':
return {[param]: value};
}
async $onInit() {
this.$state.go('home');
window.location.href = await this.vnApp.getUrl(`account/`);
}
}

View File

@ -1,28 +0,0 @@
import './index';
describe('component vnUser', () => {
let controller;
beforeEach(ngModule('account'));
beforeEach(inject($componentController => {
controller = $componentController('vnUser', {$element: null});
}));
describe('exprBuilder()', () => {
it('should search by id when only digits string is passed', () => {
let expr = controller.exprBuilder('search', '1');
expect(expr).toEqual({id: '1'});
});
it('should search by name when non-only digits string is passed', () => {
let expr = controller.exprBuilder('search', '1foo');
expect(expr).toEqual({or: [
{name: {like: '%1foo%'}},
{nickname: {like: '%1foo%'}}
]});
});
});
});

View File

@ -1,41 +0,0 @@
<mg-ajax path="VnUsers/{{post.params.id}}/privileges" options="vnPost"></mg-ajax>
<vn-watcher
vn-id="watcher"
data="$ctrl.user"
form="form"
save="post">
</vn-watcher>
<form
name="form"
ng-submit="watcher.submit()"
class="vn-w-md">
<vn-card class="vn-pa-lg">
<vn-vertical>
<vn-check
label="Has grant"
ng-model="$ctrl.user.hasGrant">
</vn-check>
</vn-vertical>
</vn-card>
<vn-card class="vn-pa-lg vn-mt-md">
<vn-vertical>
<vn-autocomplete
label="Role"
ng-model="$ctrl.user.roleFk"
url="VnRoles">
</vn-autocomplete>
</vn-vertical>
</vn-card>
<vn-button-bar>
<vn-submit
disabled="!watcher.dataChanged()"
label="Save">
</vn-submit>
<vn-button
class="cancel"
label="Undo changes"
disabled="!watcher.dataChanged()"
ng-click="watcher.loadOriginalData()">
</vn-button>
</vn-button-bar>
</form>

View File

@ -1,21 +0,0 @@
import ngModule from '../module';
import Section from 'salix/components/section';
export default class Controller extends Section {
get user() {
return this._user;
}
set user(value) {
this._user = value;
if (!value) return;
}
}
ngModule.component('vnUserPrivileges', {
template: require('./index.html'),
controller: Controller,
bindings: {
user: '<'
}
});

View File

@ -1,2 +0,0 @@
Privileges: Privilegios
Has grant: Puede delegar privilegios

View File

@ -1 +0,0 @@
<vn-log url="RoleLogs" origin-id="$ctrl.$params.id"></vn-log>

View File

@ -1,7 +0,0 @@
import ngModule from '../module';
import Section from 'salix/components/section';
ngModule.vnComponent('vnRoleLog', {
template: require('./index.html'),
controller: Section,
});

View File

@ -1,40 +0,0 @@
<vn-watcher
vn-id="watcher"
url="VnRoles"
data="$ctrl.role"
form="form">
</vn-watcher>
<form
name="form"
ng-submit="watcher.submit()"
class="vn-w-md">
<vn-card class="vn-pa-lg">
<vn-vertical>
<vn-textfield
label="Name"
ng-model="$ctrl.role.name"
rule="VnRole.name"
vn-focus>
</vn-textfield>
<vn-textfield
label="Description"
ng-model="$ctrl.role.description"
rule="VnRole.description"
>
</vn-textfield>
</vn-vertical>
</vn-card>
<vn-button-bar>
<vn-submit
disabled="!watcher.dataChanged()"
label="Save">
</vn-submit>
<vn-button
class="cancel"
label="Undo changes"
disabled="!watcher.dataChanged()"
ng-click="watcher.loadOriginalData()">
</vn-button>
</vn-button-bar>
</form>

View File

@ -1,12 +0,0 @@
import ngModule from '../../module';
import Section from 'salix/components/section';
export default class Controller extends Section {}
ngModule.component('vnRoleBasicData', {
template: require('./index.html'),
controller: Controller,
bindings: {
role: '<'
}
});

View File

@ -1,5 +0,0 @@
<vn-portal slot="menu">
<vn-role-descriptor role="$ctrl.role"></vn-role-descriptor>
<vn-left-menu source="role"></vn-left-menu>
</vn-portal>
<ui-view></ui-view>

View File

@ -1,14 +0,0 @@
import ngModule from '../../module';
import ModuleCard from 'salix/components/module-card';
class Controller extends ModuleCard {
reload() {
this.$http.get(`VnRoles/${this.$params.id}`)
.then(res => this.role = res.data);
}
}
ngModule.vnComponent('vnRoleCard', {
template: require('./index.html'),
controller: Controller
});

View File

@ -1,25 +0,0 @@
import './index';
describe('component vnRoleCard', () => {
let controller;
let $httpBackend;
beforeEach(ngModule('account'));
beforeEach(inject(($componentController, _$httpBackend_) => {
$httpBackend = _$httpBackend_;
controller = $componentController('vnRoleCard', {$element: null});
}));
describe('reload()', () => {
it('should reload the controller data', () => {
controller.$params.id = 1;
$httpBackend.expectGET('VnRoles/1').respond('foo');
controller.reload();
$httpBackend.flush();
expect(controller.role).toBe('foo');
});
});
});

Some files were not shown because too many files have changed in this diff Show More