/*! meta-admin/modules/viewmodel */
/*jslint
browser, long
*/
/*global
*/
import Logger from "js-logger";
import ko from "knockout";
import clientViewmodel from "meta-client/modules/viewmodel";
import metaModel from "meta-core/modules/model";
import _ from "underscore";
import ext from "util-web/modules/ext";
import nav from "util-web/modules/nav";
const LOG = Logger.get("meta-admin/viewmodel");
const CLIENT_LIST_ENTRY_KO_MAPPING = {
copy: ["clientId"]
};
const CONFIG_KO_MAPPING = {
config: clientViewmodel.newObjectMapping("config"),
group: clientViewmodel.newObjectMapping("group"),
user: clientViewmodel.newObjectMapping("user")
};
const GROUP_KO_MAPPING = {
grants: clientViewmodel.newObjectMapping("grants")
};
const LOGGING_ENTRY_KO_MAPPING = {
copy: ["properties"]
};
const META_KO_MAPPING = {
copy: [metaModel.META_TYPE_PROPERTY, metaModel.META_ID],
data: clientViewmodel.newObjectMapping("data"),
paramSchema: clientViewmodel.newObjectMapping("paramSchema"),
position: clientViewmodel.newObjectMapping("position"),
resultSchema: clientViewmodel.newObjectMapping("resultSchema"),
schema: clientViewmodel.newObjectMapping("schema"),
value: clientViewmodel.newObjectMapping("value")
};
const USER_KO_MAPPING = {
grants: clientViewmodel.newObjectMapping("grants"),
otp: clientViewmodel.newObjectMapping("otp"),
webAuthn: clientViewmodel.newObjectMapping("webAuthn")
};
const WATCHDOG_STATUS_KO_MAPPING = {
updateStatus: clientViewmodel.newObjectMapping("updateStatus")
};
const formatJsonField = (obj) => JSON.stringify(obj, undefined, 4);
const newComputedJsonField = function (observable, defaultValue, validator) {
let isValid = false;
let json;
const computed = ko.computed({
read: function () {
observable(); // Register dependency
return json || formatJsonField(observable());
},
write: function (value) {
json = value;
if (_.isEmpty(value)) {
observable(defaultValue);
isValid = true;
} else {
try {
observable(JSON.parse(value));
isValid = true;
} catch (ignore) {
isValid = false;
}
}
if (ko.isWritableObservable(validator)) {
validator(isValid);
}
}
});
computed.format = function () {
if (isValid) {
computed(formatJsonField(observable()));
}
};
return computed;
};
/**
* viewmodel module.
*
* @module meta-admin/modules/viewmodel
*/
export default Object.freeze(function ({client, config}) {
const viewmodel = {
isAdmin: ko.observable(false)
};
viewmodel.version = __version;
viewmodel.client = client;
viewmodel.config = config;
viewmodel.newComputedJsonField = newComputedJsonField;
viewmodel.sortByName = ext.simpleSort.bind(undefined, metaModel.META_NAME);
viewmodel.activeComponent = ko.observable();
viewmodel.COMPONENTS = Object.freeze({
CLIENTS: "clients",
EDITOR: "editor",
FILES: "files",
INDEX: "index",
LOGGING: "logging",
NOTIFICATIONS: "notifications",
SECURITY: "security",
USERS: "users",
WATCHDOG: "watchdog"
});
viewmodel.EDITOR_TABS = Object.freeze({
ACTIONS: "actions",
LOGS: "logs",
OBJECTS: "objects",
TYPES: "types"
});
viewmodel.LOGGING_TABS = Object.freeze({
ENTRIES: "entries",
METRICS: "metrics"
});
viewmodel.NOTIFICATIONS_TABS = Object.freeze({
CLIENT: "client",
META: "meta"
});
viewmodel.USERS_TABS = Object.freeze({
CONFIGS: "configs",
GROUPS: "groups",
USERS: "users"
});
viewmodel.navigateToComponent = function (component, param = "") {
if (viewmodel.activeComponent() === component) {
return Promise.resolve(undefined);
}
LOG.debug(`navigateToComponent, component=${component}`);
const params = nav.newParams();
params.set(viewmodel.COMPONENTS.INDEX, component);
params.set(component, param);
return nav.pushState(params);
};
viewmodel.navigateToIndex = () => viewmodel.navigateToComponent(viewmodel.COMPONENTS.INDEX);
viewmodel.navigateToClients = () => viewmodel.navigateToComponent(viewmodel.COMPONENTS.CLIENTS);
viewmodel.navigateToEditor = () => viewmodel.navigateToComponent(viewmodel.COMPONENTS.EDITOR);
viewmodel.navigateToFiles = () => viewmodel.navigateToComponent(viewmodel.COMPONENTS.FILES);
viewmodel.navigateToLogging = () => viewmodel.navigateToComponent(viewmodel.COMPONENTS.LOGGING);
viewmodel.navigateToNotifications = () => viewmodel.navigateToComponent(viewmodel.COMPONENTS.NOTIFICATIONS);
viewmodel.navigateToSecurity = () => viewmodel.navigateToComponent(viewmodel.COMPONENTS.SECURITY);
viewmodel.navigateToUsers = () => viewmodel.navigateToComponent(viewmodel.COMPONENTS.USERS);
viewmodel.navigateToWatchdog = () => viewmodel.navigateToComponent(viewmodel.COMPONENTS.WATCHDOG);
viewmodel.navigateToTab = function (component, tab) {
const params = nav.currentParams();
params.set(component, tab);
return nav.pushState(params);
};
viewmodel.metaToVm = function (meta) {
const metaType = meta[metaModel.META_TYPE_PROPERTY];
const metaForVm = Object.assign({}, meta);
switch (metaType) {
case metaModel.META_TYPE_ACTION:
metaForVm.name = ko.observable(meta.name);
metaForVm.paramSchema = ko.observable(meta.paramSchema);
metaForVm.resultSchema = ko.observable(meta.resultSchema);
break;
case metaModel.META_TYPE_LOG:
metaForVm.data = ko.observable(meta.data);
metaForVm.message = ko.observable(meta.message);
metaForVm.metaName = ko.observable(meta.metaName);
break;
case metaModel.META_TYPE_OBJECT:
metaForVm.name = ko.observable(meta.name);
metaForVm.position = ko.observable(meta.position);
metaForVm.value = ko.observable(meta.value);
break;
case metaModel.META_TYPE_TYPE:
metaForVm.name = ko.observable(meta.name);
metaForVm.schema = ko.observable(meta.schema);
break;
}
const vm = ko.mapping.fromJS(metaForVm, META_KO_MAPPING);
switch (metaType) {
case metaModel.META_TYPE_ACTION:
vm.isParamSchemaValid = ko.observable(true);
vm.paramSchemaString = newComputedJsonField(vm.paramSchema, undefined, vm.isParamSchemaValid);
vm.isResultSchemaValid = ko.observable(true);
vm.resultSchemaString = newComputedJsonField(vm.resultSchema, undefined, vm.isResultSchemaValid);
break;
case metaModel.META_TYPE_LOG:
vm.isDataValid = ko.observable(true);
vm.dataString = newComputedJsonField(vm.data, {}, vm.isDataValid);
break;
case metaModel.META_TYPE_OBJECT:
vm.isValueValid = ko.observable(true);
vm.valueString = newComputedJsonField(vm.value, {}, vm.isValueValid);
break;
case metaModel.META_TYPE_TYPE:
vm.isSchemaValid = ko.observable(true);
vm.schemaString = newComputedJsonField(vm.schema, {}, vm.isSchemaValid);
break;
}
vm.deleted = ko.observable(false);
return vm;
};
viewmodel.vmToMeta = (vm) => ko.mapping.toJS(vm);
viewmodel.userToVm = function (user) {
const vm = ko.mapping.fromJS(Object.assign({}, user, {
grants: ko.observable(user.grants),
otp: ko.observable(user.otp),
webAuthn: ko.observable(user.webAuthn)
}), USER_KO_MAPPING);
vm.isGrantsValid = ko.observable(true);
vm.grantsString = newComputedJsonField(vm.grants, {}, vm.isGrantsValid);
vm.isDisabled = ko.pureComputed(function () {
const grants = (
vm.grantsString()
? JSON.parse(vm.grantsString())
: undefined
);
if (_.isUndefined(grants) || vm.isGrantsValid() === false) {
return true;
}
const loginGrant = _.get(grants, metaModel.metaGrantKeys.LOGIN, 0);
return loginGrant < metaModel.metaGrantPermission.ALLOW;
});
vm.isNew = ko.observable(false);
vm.deleted = ko.observable(false);
return vm;
};
viewmodel.vmToUser = (vm) => ko.mapping.toJS(vm);
viewmodel.groupToVm = function (group) {
const vm = ko.mapping.fromJS(Object.assign({}, group, {
grants: ko.observable(group.grants)
}), GROUP_KO_MAPPING);
vm.isUsersValid = ko.observable(true);
vm.usersString = newComputedJsonField(vm.users, [], vm.isUsersValid);
vm.isGrantsValid = ko.observable(true);
vm.grantsString = newComputedJsonField(vm.grants, {}, vm.isGrantsValid);
vm.isNew = ko.observable(false);
vm.deleted = ko.observable(false);
return vm;
};
viewmodel.vmToGroup = (vm) => ko.mapping.toJS(vm);
viewmodel.userConfigToVm = function (userConfig) {
const vm = ko.mapping.fromJS(Object.assign({}, userConfig, {
config: ko.observable(userConfig.config),
group: ko.observable(userConfig.group),
user: ko.observable(userConfig.user)
}), CONFIG_KO_MAPPING);
vm.id = metaModel.forgeConfigId(userConfig);
vm.isConfigValid = ko.observable(true);
vm.configString = newComputedJsonField(vm.config, {}, vm.isConfigValid);
vm.isNew = ko.observable(false);
vm.deleted = ko.observable(false);
return vm;
};
viewmodel.vmToUserConfig = (vm) => ko.mapping.toJS(vm);
viewmodel.clientListEntryVm = function (clientListEntry) {
return ko.mapping.fromJS(clientListEntry, CLIENT_LIST_ENTRY_KO_MAPPING);
};
viewmodel.loggingEntryVm = function (entry, colorProvider) {
const vm = ko.mapping.fromJS(entry, LOGGING_ENTRY_KO_MAPPING);
const clientId = _.get(entry, ["properties", "clientId"]);
const properties = _.get(entry, "properties");
vm.clientId = (
"null" !== clientId
? clientId
: undefined
);
vm.clientColor = colorProvider(vm.clientId);
vm.isEngine = 0 < entry.loggerName.indexOf("engine");
vm.propertyList = Object.keys(
_.isObject(properties)
? properties
: {}
).map(function (key) {
return {key, value: entry.properties[key]};
});
vm.showDetails = ko.observable(false);
vm.hide = ko.observable(false);
return vm;
};
viewmodel.toLoggingEntryVm = function (entry, colorProvider) {
return viewmodel.loggingEntryVm(entry, colorProvider);
};
viewmodel.fileInfoVm = function (fileInfo) {
const vm = ko.mapping.fromJS(fileInfo);
vm.sizeFormatted = ext.formatBytes(fileInfo.size);
vm.isBackup = (
_.isArray(fileInfo.tags)
? fileInfo.tags
: []
).includes(metaModel.BACKUP_TAG);
return vm;
};
viewmodel.vmToFileInfo = (vm) => ko.mapping.toJS(vm);
viewmodel.watchdogStatusVm = function (id, status, position) {
const vm = ko.mapping.fromJS(Object.assign({}, status, {
updateStatus: ko.observable(status.updateStatus)
}), WATCHDOG_STATUS_KO_MAPPING);
vm.id = id;
vm.position = ko.observable(position);
const updateStatus = vm.updateStatus();
if (updateStatus && _.has(updateStatus, "log") === false) {
updateStatus.log = ["-"];
}
vm.hasAlerts = ko.pureComputed(() => _.isEmpty(vm.alerts()) === false);
vm.icon = ko.computed(function () {
if (vm.healthy() === false || vm.hasAlerts() === false) {
return "WARN";
}
return "BLUE";
});
vm.rowspan = function () {
let rowspan = 1;
if (vm.hasAlerts()) {
rowspan = rowspan + 1;
}
if (vm.updateStatus()) {
rowspan = rowspan + 1;
}
return rowspan;
};
return vm;
};
viewmodel.toWatchdogStatusVm = function (meta) {
const status = metaModel.watchdogStatus(meta.value);
return viewmodel.watchdogStatusVm(meta.id, status, meta.position);
};
viewmodel.types = ko.observableArray();
viewmodel.actions = ko.observableArray();
viewmodel.pushTypesAndActions = function (types, actions) {
viewmodel.types(types.sort(viewmodel.sortByName).map(viewmodel.metaToVm));
LOG.debug(`pushTypesAndActions, types=${viewmodel.types().length}`);
viewmodel.actions(actions.sort(viewmodel.sortByName).map(viewmodel.metaToVm));
LOG.debug(`pushTypesAndActions, actions=${viewmodel.actions().length}`);
};
viewmodel.mapApiProvider = ko.observable();
viewmodel.registerMapApiProvider = function (mapApi) {
const mapPickPosition = mapApi.pickPosition;
const pickPosition = function () {
const activeComponent = viewmodel.activeComponent();
let position;
LOG.debug(`pickPosition, activeComponent=${activeComponent}`);
viewmodel.navigateToIndex();
return mapPickPosition().then(function (pickedPosition) {
position = pickedPosition;
}, function (exc) {
LOG.error("pickPosition failed", exc);
}).then(function () {
return viewmodel.navigateToComponent(activeComponent);
}).then(function () {
return position;
});
};
mapApi.editedObjectMarker.subscribe(function (metaId) {
LOG.debug(`registerMapApiProvider, editedObjectMarker=${metaId}`);
if (metaId) {
viewmodel.navigateToEditor();
}
});
viewmodel.mapApiProvider(Object.assign({pickPosition}, mapApi));
};
viewmodel.edited = ko.observable();
viewmodel.metaCreateHook = ko.observable();
viewmodel.newLog = () => viewmodel.metaCreateHook(metaModel.META_TYPE_LOG);
viewmodel.newObject = (type) => viewmodel.metaCreateHook(metaModel.META_TYPE_OBJECT + ";" + type.id);
viewmodel.userCreateHook = ko.observable();
viewmodel.newUser = () => viewmodel.userCreateHook("user");
viewmodel.newGroup = () => viewmodel.userCreateHook("group");
viewmodel.newConfig = () => viewmodel.userCreateHook("userConfig");
return Object.freeze(viewmodel);
});