fix problems panel tests
This commit is contained in:
3
.babelrc
3
.babelrc
@@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"presets": [
|
"presets": [
|
||||||
"env"
|
"env"
|
||||||
]
|
],
|
||||||
|
"retainLines": true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,12 +7,20 @@ module.exports = {
|
|||||||
// "<rootDir>/dist/test/test-setup/jest-setup.js"
|
// "<rootDir>/dist/test/test-setup/jest-setup.js"
|
||||||
"<rootDir>/src/test-setup/jest-setup.js"
|
"<rootDir>/src/test-setup/jest-setup.js"
|
||||||
],
|
],
|
||||||
|
"moduleFileExtensions": [
|
||||||
|
"ts",
|
||||||
|
"tsx",
|
||||||
|
"js",
|
||||||
|
"jsx",
|
||||||
|
"json"
|
||||||
|
],
|
||||||
"moduleNameMapper": {
|
"moduleNameMapper": {
|
||||||
"^[./a-zA-Z0-9$_-]+\.css\!?$": "<rootDir>/src/test-setup/cssStub.js",
|
"^[./a-zA-Z0-9$_-]+\.css\!?$": "<rootDir>/src/test-setup/cssStub.js",
|
||||||
},
|
},
|
||||||
"testRegex": "(\\.|/)(test|spec)\\.(jsx?|tsx?)$",
|
"testRegex": "(\\.|/)(test|spec)\\.(jsx?|tsx?)$",
|
||||||
"transform": {
|
"transform": {
|
||||||
"^.+\\.js$": "babel-jest"
|
"^.+\\.js$": "babel-jest",
|
||||||
|
"^.+\\.(ts|tsx)$": "ts-jest"
|
||||||
},
|
},
|
||||||
"coverageDirectory": "<rootDir>/tmp/coverage/",
|
"coverageDirectory": "<rootDir>/tmp/coverage/",
|
||||||
"collectCoverage": false
|
"collectCoverage": false
|
||||||
|
|||||||
@@ -63,13 +63,15 @@
|
|||||||
"ng-annotate-webpack-plugin": "^0.3.0",
|
"ng-annotate-webpack-plugin": "^0.3.0",
|
||||||
"node-sass": "^4.9.4",
|
"node-sass": "^4.9.4",
|
||||||
"prop-types": "^15.6.2",
|
"prop-types": "^15.6.2",
|
||||||
|
"react": "^16.7.0",
|
||||||
|
"react-dom": "^16.7.0",
|
||||||
"react-popper": "^1.3.2",
|
"react-popper": "^1.3.2",
|
||||||
"react-table": "^6.8.6",
|
"react-table": "^6.8.6",
|
||||||
"react-transition-group": "^2.5.2",
|
"react-transition-group": "^2.5.2",
|
||||||
"sass-loader": "^7.1.0",
|
"sass-loader": "^7.1.0",
|
||||||
"style-loader": "^0.23.1",
|
"style-loader": "^0.23.1",
|
||||||
"tether-drop": "^1.4.2",
|
"tether-drop": "^1.4.2",
|
||||||
"ts-jest": "^22.4.6",
|
"ts-jest": "^23.10.5",
|
||||||
"ts-loader": "^4.4.1",
|
"ts-loader": "^4.4.1",
|
||||||
"tslint": "^5.11.0",
|
"tslint": "^5.11.0",
|
||||||
"typescript": "^2.9.2",
|
"typescript": "^2.9.2",
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import _ from 'lodash';
|
|||||||
import {DEFAULT_TARGET} from './triggers_panel_ctrl';
|
import {DEFAULT_TARGET} from './triggers_panel_ctrl';
|
||||||
|
|
||||||
// Actual schema version
|
// Actual schema version
|
||||||
export const CURRENT_SCHEMA_VERSION = 5;
|
export const CURRENT_SCHEMA_VERSION = 6;
|
||||||
|
|
||||||
export function migratePanelSchema(panel) {
|
export function migratePanelSchema(panel) {
|
||||||
if (isEmptyPanel(panel)) {
|
if (isEmptyPanel(panel)) {
|
||||||
@@ -26,7 +26,6 @@ export function migratePanelSchema(panel) {
|
|||||||
if (schemaVersion < 3) {
|
if (schemaVersion < 3) {
|
||||||
// delete old props
|
// delete old props
|
||||||
delete panel.lastChangeField;
|
delete panel.lastChangeField;
|
||||||
delete panel.ageField;
|
|
||||||
delete panel.infoField;
|
delete panel.infoField;
|
||||||
delete panel.scroll;
|
delete panel.scroll;
|
||||||
delete panel.hideHostsInMaintenance;
|
delete panel.hideHostsInMaintenance;
|
||||||
@@ -40,6 +39,12 @@ export function migratePanelSchema(panel) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (schemaVersion < 6) {
|
||||||
|
if (panel.showEvents && panel.showEvents.value === '1') {
|
||||||
|
panel.showEvents.value = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return panel;
|
return panel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
import mocks from '../../test-setup/mocks';
|
||||||
import {TriggerPanelCtrl} from '../triggers_panel_ctrl';
|
import {TriggerPanelCtrl} from '../triggers_panel_ctrl';
|
||||||
import {DEFAULT_TARGET, DEFAULT_SEVERITY, PANEL_DEFAULTS} from '../triggers_panel_ctrl';
|
import {DEFAULT_TARGET, DEFAULT_SEVERITY, PANEL_DEFAULTS} from '../triggers_panel_ctrl';
|
||||||
import {CURRENT_SCHEMA_VERSION} from '../migrations';
|
import {CURRENT_SCHEMA_VERSION} from '../migrations';
|
||||||
|
|
||||||
describe('Triggers Panel schema migration', () => {
|
describe('Triggers Panel schema migration', () => {
|
||||||
let ctx = {};
|
let ctx = {};
|
||||||
|
let updatePanelCtrl;
|
||||||
let datasourceSrvMock = {
|
let datasourceSrvMock = {
|
||||||
getMetricSources: () => {
|
getMetricSources: () => {
|
||||||
return [{ meta: {id: 'alexanderzobnin-zabbix-datasource'}, value: {}, name: 'zabbix_default' }];
|
return [{ meta: {id: 'alexanderzobnin-zabbix-datasource'}, value: {}, name: 'zabbix_default' }];
|
||||||
@@ -40,10 +42,12 @@ describe('Triggers Panel schema migration', () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
updatePanelCtrl = (scope) => new TriggerPanelCtrl(scope, {}, timeoutMock, datasourceSrvMock, {}, {}, {}, mocks.timeSrvMock);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should update old panel schema', () => {
|
it('should update old panel schema', () => {
|
||||||
let updatedPanelCtrl = new TriggerPanelCtrl(ctx.scope, {}, timeoutMock, datasourceSrvMock, {}, {}, {});
|
let updatedPanelCtrl = updatePanelCtrl(ctx.scope);
|
||||||
|
|
||||||
let expected = _.defaultsDeep({
|
let expected = _.defaultsDeep({
|
||||||
schemaVersion: CURRENT_SCHEMA_VERSION,
|
schemaVersion: CURRENT_SCHEMA_VERSION,
|
||||||
@@ -51,6 +55,7 @@ describe('Triggers Panel schema migration', () => {
|
|||||||
targets: {
|
targets: {
|
||||||
'zabbix': DEFAULT_TARGET
|
'zabbix': DEFAULT_TARGET
|
||||||
},
|
},
|
||||||
|
ageField: true,
|
||||||
statusField: false,
|
statusField: false,
|
||||||
severityField: false,
|
severityField: false,
|
||||||
limit: 10,
|
limit: 10,
|
||||||
@@ -63,7 +68,7 @@ describe('Triggers Panel schema migration', () => {
|
|||||||
|
|
||||||
it('should create new panel with default schema', () => {
|
it('should create new panel with default schema', () => {
|
||||||
ctx.scope.panel = {};
|
ctx.scope.panel = {};
|
||||||
let updatedPanelCtrl = new TriggerPanelCtrl(ctx.scope, {}, {}, datasourceSrvMock, {}, {}, {});
|
let updatedPanelCtrl = updatePanelCtrl(ctx.scope);
|
||||||
|
|
||||||
let expected = _.defaultsDeep({
|
let expected = _.defaultsDeep({
|
||||||
schemaVersion: CURRENT_SCHEMA_VERSION,
|
schemaVersion: CURRENT_SCHEMA_VERSION,
|
||||||
@@ -79,7 +84,7 @@ describe('Triggers Panel schema migration', () => {
|
|||||||
ctx.scope.panel = {
|
ctx.scope.panel = {
|
||||||
targets: [{}]
|
targets: [{}]
|
||||||
};
|
};
|
||||||
let updatedPanelCtrl = new TriggerPanelCtrl(ctx.scope, {}, timeoutMock, datasourceSrvMock, {}, {}, {});
|
let updatedPanelCtrl = updatePanelCtrl(ctx.scope);
|
||||||
|
|
||||||
let expected = _.defaultsDeep({
|
let expected = _.defaultsDeep({
|
||||||
datasources: ['zabbix_default'],
|
datasources: ['zabbix_default'],
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
import mocks from '../../test-setup/mocks';
|
||||||
import {TriggerPanelCtrl} from '../triggers_panel_ctrl';
|
import {TriggerPanelCtrl} from '../triggers_panel_ctrl';
|
||||||
import {PANEL_DEFAULTS, DEFAULT_TARGET} from '../triggers_panel_ctrl';
|
import {PANEL_DEFAULTS, DEFAULT_TARGET} from '../triggers_panel_ctrl';
|
||||||
// import { create } from 'domain';
|
// import { create } from 'domain';
|
||||||
@@ -15,7 +16,8 @@ describe('TriggerPanelCtrl', () => {
|
|||||||
replaceTemplateVars: () => {},
|
replaceTemplateVars: () => {},
|
||||||
zabbix: {
|
zabbix: {
|
||||||
getTriggers: jest.fn().mockReturnValue([generateTrigger("1"), generateTrigger("1")]),
|
getTriggers: jest.fn().mockReturnValue([generateTrigger("1"), generateTrigger("1")]),
|
||||||
getAcknowledges: jest.fn().mockReturnValue(Promise.resolve([]))
|
getAcknowledges: jest.fn().mockReturnValue(Promise.resolve([])),
|
||||||
|
getEventAlerts: jest.fn().mockResolvedValue([]),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -29,7 +31,7 @@ describe('TriggerPanelCtrl', () => {
|
|||||||
},
|
},
|
||||||
get: () => Promise.resolve(zabbixDSMock)
|
get: () => Promise.resolve(zabbixDSMock)
|
||||||
};
|
};
|
||||||
createPanelCtrl = () => new TriggerPanelCtrl(ctx.scope, {}, timeoutMock, datasourceSrvMock, {}, {}, {});
|
createPanelCtrl = () => new TriggerPanelCtrl(ctx.scope, {}, timeoutMock, datasourceSrvMock, {}, {}, {}, mocks.timeSrvMock);
|
||||||
|
|
||||||
const getTriggersResp = [
|
const getTriggersResp = [
|
||||||
[
|
[
|
||||||
@@ -72,6 +74,19 @@ describe('TriggerPanelCtrl', () => {
|
|||||||
'zabbix_default'
|
'zabbix_default'
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should rewrite default empty target', () => {
|
||||||
|
ctx.scope.panel = {
|
||||||
|
targets: [{
|
||||||
|
"target": "",
|
||||||
|
"refId": "A"
|
||||||
|
}],
|
||||||
|
};
|
||||||
|
let panelCtrl = createPanelCtrl();
|
||||||
|
expect(panelCtrl.available_datasources).toEqual([
|
||||||
|
'zabbix_default', 'zabbix'
|
||||||
|
]);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('When refreshing panel', () => {
|
describe('When refreshing panel', () => {
|
||||||
@@ -90,9 +105,7 @@ describe('TriggerPanelCtrl', () => {
|
|||||||
expect(formattedTrigger.host).toBe('backend01');
|
expect(formattedTrigger.host).toBe('backend01');
|
||||||
expect(formattedTrigger.hostTechName).toBe('backend01_tech');
|
expect(formattedTrigger.hostTechName).toBe('backend01_tech');
|
||||||
expect(formattedTrigger.datasource).toBe('zabbix_default');
|
expect(formattedTrigger.datasource).toBe('zabbix_default');
|
||||||
expect(formattedTrigger.severity).toBe('Disaster');
|
|
||||||
expect(formattedTrigger.maintenance).toBe(true);
|
expect(formattedTrigger.maintenance).toBe(true);
|
||||||
expect(formattedTrigger.age).toBeTruthy();
|
|
||||||
expect(formattedTrigger.lastchange).toBeTruthy();
|
expect(formattedTrigger.lastchange).toBeTruthy();
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -100,14 +100,17 @@ export class TriggerPanelCtrl extends PanelCtrl {
|
|||||||
this.datasources = {};
|
this.datasources = {};
|
||||||
this.range = {};
|
this.range = {};
|
||||||
|
|
||||||
|
// this.panel.schemaVersion = CURRENT_SCHEMA_VERSION;
|
||||||
this.panel = migratePanelSchema(this.panel);
|
this.panel = migratePanelSchema(this.panel);
|
||||||
_.defaultsDeep(this.panel, _.cloneDeep(PANEL_DEFAULTS));
|
_.defaultsDeep(this.panel, _.cloneDeep(PANEL_DEFAULTS));
|
||||||
|
|
||||||
this.available_datasources = _.map(this.getZabbixDataSources(), 'name');
|
this.available_datasources = _.map(this.getZabbixDataSources(), 'name');
|
||||||
|
// console.log(this.available_datasources);
|
||||||
if (this.panel.datasources.length === 0) {
|
if (this.panel.datasources.length === 0) {
|
||||||
this.panel.datasources.push(this.available_datasources[0]);
|
this.panel.datasources.push(this.available_datasources[0]);
|
||||||
}
|
}
|
||||||
if (_.isEmpty(this.panel.targets)) {
|
if (this.isEmptyTargets()) {
|
||||||
|
console.log("isEmptyTargets");
|
||||||
this.panel.targets[this.panel.datasources[0]] = DEFAULT_TARGET;
|
this.panel.targets[this.panel.datasources[0]] = DEFAULT_TARGET;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,6 +154,15 @@ export class TriggerPanelCtrl extends PanelCtrl {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isEmptyTargets() {
|
||||||
|
const emptyTargets = _.isEmpty(this.panel.targets);
|
||||||
|
const emptyTarget = (this.panel.targets.length === 1 && (
|
||||||
|
_.isEmpty(this.panel.targets[0]) ||
|
||||||
|
this.panel.targets[0].target === ""
|
||||||
|
));
|
||||||
|
return emptyTargets || emptyTarget;
|
||||||
|
}
|
||||||
|
|
||||||
onInitEditMode() {
|
onInitEditMode() {
|
||||||
this.addEditorTab('Triggers', triggerPanelTriggersTab, 1);
|
this.addEditorTab('Triggers', triggerPanelTriggersTab, 1);
|
||||||
this.addEditorTab('Options', triggerPanelOptionsTab, 2);
|
this.addEditorTab('Options', triggerPanelOptionsTab, 2);
|
||||||
|
|||||||
@@ -11,6 +11,10 @@ export let datasourceSrvMock = {
|
|||||||
getAll: jest.fn()
|
getAll: jest.fn()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export let timeSrvMock = {
|
||||||
|
timeRange: jest.fn().mockReturnValue({ from: '', to: '' })
|
||||||
|
}
|
||||||
|
|
||||||
export let zabbixAlertingSrvMock = {
|
export let zabbixAlertingSrvMock = {
|
||||||
setPanelAlertState: jest.fn(),
|
setPanelAlertState: jest.fn(),
|
||||||
removeZabbixThreshold: jest.fn(),
|
removeZabbixThreshold: jest.fn(),
|
||||||
@@ -20,6 +24,7 @@ const defaultExports = {
|
|||||||
templateSrvMock,
|
templateSrvMock,
|
||||||
backendSrvMock,
|
backendSrvMock,
|
||||||
datasourceSrvMock,
|
datasourceSrvMock,
|
||||||
|
timeSrvMock,
|
||||||
zabbixAlertingSrvMock
|
zabbixAlertingSrvMock
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
30
yarn.lock
30
yarn.lock
@@ -5129,7 +5129,7 @@ longest@^1.0.1:
|
|||||||
resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097"
|
resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097"
|
||||||
integrity sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=
|
integrity sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=
|
||||||
|
|
||||||
loose-envify@^1.0.0, loose-envify@^1.3.1, loose-envify@^1.4.0:
|
loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.3.1, loose-envify@^1.4.0:
|
||||||
version "1.4.0"
|
version "1.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
|
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
|
||||||
integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
|
integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
|
||||||
@@ -6474,6 +6474,16 @@ rc@^1.2.7:
|
|||||||
minimist "^1.2.0"
|
minimist "^1.2.0"
|
||||||
strip-json-comments "~2.0.1"
|
strip-json-comments "~2.0.1"
|
||||||
|
|
||||||
|
react-dom@^16.7.0:
|
||||||
|
version "16.7.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.7.0.tgz#a17b2a7ca89ee7390bc1ed5eb81783c7461748b8"
|
||||||
|
integrity sha512-D0Ufv1ExCAmF38P2Uh1lwpminZFRXEINJe53zRAbm4KPwSyd6DY/uDoS0Blj9jvPpn1+wivKpZYc8aAAN/nAkg==
|
||||||
|
dependencies:
|
||||||
|
loose-envify "^1.1.0"
|
||||||
|
object-assign "^4.1.1"
|
||||||
|
prop-types "^15.6.2"
|
||||||
|
scheduler "^0.12.0"
|
||||||
|
|
||||||
react-lifecycles-compat@^3.0.4:
|
react-lifecycles-compat@^3.0.4:
|
||||||
version "3.0.4"
|
version "3.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"
|
resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"
|
||||||
@@ -6508,6 +6518,16 @@ react-transition-group@^2.5.2:
|
|||||||
prop-types "^15.6.2"
|
prop-types "^15.6.2"
|
||||||
react-lifecycles-compat "^3.0.4"
|
react-lifecycles-compat "^3.0.4"
|
||||||
|
|
||||||
|
react@^16.7.0:
|
||||||
|
version "16.7.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/react/-/react-16.7.0.tgz#b674ec396b0a5715873b350446f7ea0802ab6381"
|
||||||
|
integrity sha512-StCz3QY8lxTb5cl2HJxjwLFOXPIFQp+p+hxQfc8WE0QiLfCtIlKj8/+5tjjKm8uSTlAW+fCPaavGFS06V9Ar3A==
|
||||||
|
dependencies:
|
||||||
|
loose-envify "^1.1.0"
|
||||||
|
object-assign "^4.1.1"
|
||||||
|
prop-types "^15.6.2"
|
||||||
|
scheduler "^0.12.0"
|
||||||
|
|
||||||
read-pkg-up@^1.0.1:
|
read-pkg-up@^1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02"
|
resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02"
|
||||||
@@ -6897,6 +6917,14 @@ sax@^1.2.1, sax@^1.2.4:
|
|||||||
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
|
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
|
||||||
integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==
|
integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==
|
||||||
|
|
||||||
|
scheduler@^0.12.0:
|
||||||
|
version "0.12.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.12.0.tgz#8ab17699939c0aedc5a196a657743c496538647b"
|
||||||
|
integrity sha512-t7MBR28Akcp4Jm+QoR63XgAi9YgCUmgvDHqf5otgAj4QvdoBE4ImCX0ffehefePPG+aitiYHp0g/mW6s4Tp+dw==
|
||||||
|
dependencies:
|
||||||
|
loose-envify "^1.1.0"
|
||||||
|
object-assign "^4.1.1"
|
||||||
|
|
||||||
schema-utils@^0.4.4, schema-utils@^0.4.5:
|
schema-utils@^0.4.4, schema-utils@^0.4.5:
|
||||||
version "0.4.7"
|
version "0.4.7"
|
||||||
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-0.4.7.tgz#ba74f597d2be2ea880131746ee17d0a093c68187"
|
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-0.4.7.tgz#ba74f597d2be2ea880131746ee17d0a093c68187"
|
||||||
|
|||||||
Reference in New Issue
Block a user