Commit 9621501f authored by Andrey Bezugliy's avatar Andrey Bezugliy
Browse files

Initial commit.

parents
/.idea/watcherTasks.xml
/.idea/workspace.xml
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectCodeStyleSettingsManager">
<option name="PER_PROJECT_SETTINGS">
<value>
<option name="OTHER_INDENT_OPTIONS">
<value>
<option name="INDENT_SIZE" value="4" />
<option name="CONTINUATION_INDENT_SIZE" value="8" />
<option name="TAB_SIZE" value="4" />
<option name="USE_TAB_CHARACTER" value="true" />
<option name="SMART_TABS" value="true" />
<option name="LABEL_INDENT_SIZE" value="0" />
<option name="LABEL_INDENT_ABSOLUTE" value="false" />
<option name="USE_RELATIVE_INDENTS" value="false" />
</value>
</option>
<XML>
<option name="XML_LEGACY_SETTINGS_IMPORTED" value="true" />
</XML>
<codeStyleSettings language="JSON">
<option name="KEEP_BLANK_LINES_IN_CODE" value="1" />
<indentOptions>
<option name="INDENT_SIZE" value="4" />
<option name="USE_TAB_CHARACTER" value="true" />
<option name="SMART_TABS" value="true" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="JavaScript">
<indentOptions>
<option name="USE_TAB_CHARACTER" value="true" />
<option name="KEEP_INDENTS_ON_EMPTY_LINES" value="true" />
</indentOptions>
</codeStyleSettings>
</value>
</option>
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
<excludeFolder url="file://$MODULE_DIR$/temp" />
<excludeFolder url="file://$MODULE_DIR$/tmp" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="UB_modules" level="project" />
</component>
</module>
\ No newline at end of file
aspx
delims
findstr
goto
errorlevel
noinit
setlocal
sqlcmd
usebackq
windowtitle
\ No newline at end of file
__dirname
dirname
lodash
yaml
falsy
<component name="ProjectDictionaryState">
<dictionary name="rumata" />
</component>
\ No newline at end of file
unitybase
adminui
mssql
postgre
postgresql
inet
localdb
postprocessors
sqlite
addnew
attrs
mixins
querystring
ubql
xlsx
addqueue
orgunit
navshortcut
userrole
usergroup
grouprole
admins
subquery
pagingtb
ubcodemirror
ubcombobox
ubdetailgrid
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding">
<file url="PROJECT" charset="UTF-8" />
</component>
</project>
\ No newline at end of file
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="TaskInspection" enabled="false" level="INFO" enabled_by_default="false" />
</profile>
</component>
\ No newline at end of file
<component name="InspectionProjectProfileManager">
<settings>
<option name="PROJECT_PROFILE" value="Project Default" />
<option name="USE_PROJECT_PROFILE" value="true" />
<version value="1.0" />
</settings>
</component>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaScriptLibraryMappings">
<file url="PROJECT" libraries="{UB_modules}" />
<file url="file://$PROJECT_DIR$" libraries="{ECMAScript 6, Node.js Core, UB_modules}" />
<includedPredefinedLibrary name="ECMAScript 6" />
<includedPredefinedLibrary name="Node.js Core" />
</component>
</project>
\ No newline at end of file
<component name="libraryTable">
<library name="UB_modules" type="javaScript">
<properties>
<option name="frameworkName" value="node_modules" />
<sourceFilesUrls>
<item url="file://$UB_HOME$/node_modules" />
</sourceFilesUrls>
</properties>
<CLASSES>
<root url="file://$UB_HOME$/node_modules" />
</CLASSES>
<SOURCES />
</library>
</component>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaScriptSettings">
<option name="languageLevel" value="ES6" />
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/csv-loader-utils.iml" filepath="$PROJECT_DIR$/.idea/csv-loader-utils.iml" />
</modules>
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>
\ No newline at end of file
# CSV Loader Utils
This package helps initialize UnityBase models with less code written.
# Entity data initialization
There `loadCsv` function loads data from CSV file using minial possible parameters passed, for example:
```javascript
const csvLoaderUtils = require('@unitybase/csv-loader-utils');
csvLoaderUtils.loadCsv(conn, __dirname, 'uba_role', ['name', 'description']);
csvLoaderUtils.loadCsv(conn, __dirname, 'uba_els',
['ruleRole', 'entityMask', 'methodMask', 'code', 'description'],
[dataLoader.lookup(conn, 'uba_role', 'name', 0), 1, 2, 3, 4]
);
```
The method assumes the CSV files will following the pattern: `<entity>-<model>.csv`
The `loadAdm` method helps with the typical task of loading data into administration entities, like `ubm_desktop_adm`:
```javascript
const csvLoaderUtils = require('@unitybase/csv-loader-utils');
csvLoaderUtils.loadAdm(conn, __dirname, 'ubm_desktop');
```
The method assumes that CSV file names with the data follows the pattern: `<entity_adm>-<model>.csv`,
for example `ubm_desktop_adm-bpm.csv`
# Localization
## Configuring Localization
A file `_localization.json` must be created.
The file shall contain array of arrays. Each inner array defines localization for an entity.
Example of file content:
```json
[
["ubm_enum", ["eGroup", "code", "name"], 2],
["ubm_desktop", ["code", "caption"]],
["ubm_navshortcut", ["code", "caption"]]
]
```
Inner array elements meaning:
* First item is the entity name
* Second item is list of attributes in CSV files
* Last item is optional and specifies how many items are the row key
## Localization CSV files
The CSV files for localization are pretty much the same as the regular CSV files for entity initialization.
Their names must follow the patter: `<language>^<entity>-<model>.csv`, for example: `uk^ubm_enum-bpm.csv`.
Columns inside the CSV files must match columns defined for appropriate entity in the `_localization.json` file.
## Localization scripts
To run localization from CSV files, it is enough to include a file per supported language with the following content:
```javascript
module.exports = function(session) {
require('@unitybase/csv-loader-utils').localize(session, __dirname, __filename);
};
```
File name must follow the pattern: `<lang>^localization.js`, for example, `uk^localization.js`.
The file content would be the same for each language and each model, all the information needed to run
localization scripts is contained in `_localization.json` file.
const
path = require('path'),
_ = require('lodash'),
dataLoader = require('dataLoader'),
COMMON_UB_MODEL_PREFIX = 'ub_model_';
/**
* @param {number} entityNameColIndex Index of column with entity name.
* @param {number} fieldListColIndex Index of column with array of fields to show.
* @param {number} orderByColIndex Index of column with order by fields.
* @param {Array<[]>} row
* @returns {string} String with JSON.
* @private
*/
function _doBuildShowListCommand(entityNameColIndex, fieldListColIndex, orderByColIndex, row) {
function buildFieldList(l) {
return typeof l !== 'string' ? l : l ? JSON.parse(l) : null;
}
const cmd = {
entity: row[entityNameColIndex], method: 'select',
fieldList: buildFieldList(row[fieldListColIndex])
};
if (orderByColIndex) {
const orderByFieldList = buildFieldList(row[orderByColIndex]);
if (orderByFieldList) {
cmd.orderList = orderByFieldList.map(f => ({expression: f}));
}
}
return JSON.stringify({
cmdType: 'showList',
cmdData: {params: [cmd]}
});
}
/**
* @param {number} formCodeColIndex Index of column with form code.
* @param {Array<[]>} row
* @returns {string} String with JSON.
* @private
*/
function _doBuildShowFormCommand(formCodeColIndex, row) {
const
formCode = row[formCodeColIndex],
entityName = formCode.split('-')[0];
return JSON.stringify({cmdType: 'showForm', entity: entityName, formCode: formCode});
}
function _doBuildDelegateCommand(functionNameColIndex, row) {
return `${row[functionNameColIndex]}();`;
}
/**
* A helper function which finds a model name from "__dirname" value of a module.
* @param dirName
* @returns {*}
*/
function _getModelName(dirName) {
let modelName = path.basename(dirName);
if (modelName === 'locale') {
dirName = path.dirname(dirName);
modelName = path.basename(dirName);
}
if (modelName[0] === '_') {
dirName = path.dirname(dirName);
modelName = path.basename(dirName);
}
if (modelName.startsWith(COMMON_UB_MODEL_PREFIX)) {
modelName = modelName.slice(COMMON_UB_MODEL_PREFIX.length);
}
return modelName;
}
/**
* A shortcut for loading CSV file, which is named by convention: {entityName}-{modelName}.csv.
* Must be called from scripts in "_initialData" folder.
* @param {UBConnection} conn
* @param {string} dirName Pass "__dirname" of module. Is used to automatically determine model name.
* It prevents from copy-paste mistakes.
* @param {string} entityName
* @param {Array<string>} attrs
* @param {Array} [mapping]
* @param {number} [tranLen]
*/
function loadCsv(conn, dirName, entityName, attrs, mapping, tranLen) {
console.log(`\t\tCreating "${entityName}" instances...`);
const fileName = `${dirName}/${entityName}-${_getModelName(dirName)}.csv`;
dataLoader.loadSimpleCSVData(conn, fileName, entityName, attrs, mapping, 1, null, tranLen);
}
/**
* @param {Array} mapping
* @param {Array} row
* @param {number} index
* @return {*}
*/
function _mapAttributeValue(mapping, row, index) {
const curMapObj = mapping[index];
if (_.isNumber(curMapObj)) {
return row[curMapObj];
} else if (_.isFunction(curMapObj)) {
return curMapObj(row);
} else {
throw new Error('Invalid mapping definition in element #' + a);
}
}
function localize(session, dirname, filename) {
const
localizationConfigStr = fs.readFileSync(path.join(dirname, '_localization.json')),
localizationConfig = JSON.parse(localizationConfigStr);
localizationConfig.forEach(
l => loadLocaleFromCsv(session, dirname, filename, l[0], l[1], l[2])
);
}
/**
* A shortcut for loading CSV file with localization data, which is named by convention: {locale}^{entityName}-{modelName}.csv.
* Must be called from scripts in "_initialData/locale" folder.
* @param {ServerSession} session
* @param {string} dirName Pass "__dirname" of module. Is used to automatically determine model name.
* It prevents from copy-paste mistakes.
* @param {string} locale
* @param {string} entityName
* @param {Array<string>} attrs
* @param {number} [keyAttrsCount] = 1
*/
function loadLocaleFromCsv(session, dirName, locale, entityName, attrs, keyAttrsCount = 1) {
console.log(`\t\tLocalizing "${entityName}" instances...`);
const
mapping = attrs.map((a, i) => i),
lang = path.basename(locale).split('^')[0],
fileName = `${dirName}/${lang}^${entityName}-${_getModelName(dirName)}.csv`,
csvData = /** @type Array<Array> */ loadCsvAsArray(fileName);
const
keyAttrs = [...new Array(attrs.length).keys()],
valueAttrs = keyAttrs.splice(keyAttrsCount);
function rowToLocalizationConfig(r) {
const
keyValue = keyAttrs.map(i => _mapAttributeValue(mapping, r, i)).join(';'),
execParams = {};
for (let i of valueAttrs) {
const attrValue = _mapAttributeValue(mapping, r, i);
if (attrValue) execParams[attrs[i]] = attrValue;
}
return {keyValue, execParams};
}
const localizationConfig = {
entity: entityName,
keyAttribute: keyAttrs.map(i => attrs[i]).join(';'),
localization: csvData.map(rowToLocalizationConfig)
};
dataLoader.localizeEntity(session, localizationConfig, locale);
}
// TODO: remove as soon as Pasha add this to dataLoader module
const csv = require('csv1'), fs = require('fs');
/**
* Load data from CSV with delimiter (";" by default)
* @param {string} fileName Full path to file
* @param {number} [startRow=0] Start from this CSV file row
* @param {string} [delimiter=';'] CSV file delimiter
*/
function loadCsvAsArray(fileName, startRow = 1, delimiter = ';') {
let fContent = fs.readFileSync(fileName);
if (!fContent) throw new Error('File ' + fileName + ' is empty or not exist');
fContent = fContent.trim();
const csvData = csv.parse(fContent, delimiter);
if (!Array.isArray(csvData)) throw new Error(`Invalid CSV format or file ${fileName} not found`);
if (csvData.length < startRow) {
throw new Error(`Length of CSVData (${csvData.length}) is smaller then startRow (${startRow})`);
}
if (startRow > 0) csvData.splice(0, startRow);
return csvData;
}
/**
* A shortcut for loading adm setting from CSV file. Assumed "_adm" suffix for entity and
* structure like in "ubm_desktop_adm" entity.
* Must be called from scripts in "_initialData" folder.
* @param {UBConnection} conn
* @param {string} dirName Pass "__dirname" of module. Is used to automatically determine model name.
* It prevents from copy-paste mistakes.
* @param {string} entityName
*/
function loadAdm(conn, dirName, entityName) {
loadCsv(conn, dirName, entityName + '_adm', ['instanceID', 'admSubjID'],
[
dataLoader.lookup(conn, entityName, 'code', 0),
dataLoader.lookup(conn, 'uba_role', 'name', 1)
]
);
}
/**
* A helper for dataLoader. Builds JSON with a standard showList command.
* @param {number} entityNameColIndex Index of a column with entity name.
* @param {number} fieldListColIndex Index of a column with array of fields to show.
* @param {number} [orderByColIndex] Index of a column with order by fields.
* @returns {Function}
*
* @example
* Example of usage:
* dataLoader.loadArrayData(
* conn, [
* [
* 'bpm_desktop', false, 'bpm_Definitions', 'bpm_ProcessDefinition', 'Process Definitions', null, 10,
* ['name', 'engineType', 'externalID']
* ],
* ],
* 'ubm_navshortcut',
* ['desktopID', 'isFolder', 'parentID', 'code', 'caption', 'iconCls', 'displayOrder', 'cmdCode'],
* [
* lookup(conn, 'ubm_desktop', 'code', 0),
* 1,
* lookup(conn, 'ubm_navshortcut', 'code', 2),
* 3, 4, 5, 6,
* buildShowListCommand(3, 7, 8)
* ],
* 1000
* );
*
* This will generate JSON for show-list command with specified list of columns. Entity name is taken from 3 column.
*/
function buildShowListCommand(entityNameColIndex, fieldListColIndex, orderByColIndex) {
return _doBuildShowListCommand.bind(null, entityNameColIndex, fieldListColIndex, orderByColIndex);
}
/**
* A helper for dataLoader. Builds JSON with a standard showForm command.
* @param {number} functionNameColIndex Index of a column with name of function to call to get command
* @returns {Function}
*
* @example
* Example of usage:
* dataLoader.loadArrayData(
* conn,
* [['bpm_desktop', false, 'camunda', 'camunda_Database-sync', 'Synchronization', null, 10]],
* 'ubm_navshortcut',
* ['desktopID', 'isFolder', 'parentID', 'code', 'caption', 'iconCls', 'displayOrder'],
* [
* dataLoader.lookup(conn, 'ubm_desktop', 'code', 0), 1,
* dataLoader.lookup(conn, 'ubm_navshortcut', 'code', 2), 3, 4, 5, 6,
* loaderUtils.buildDelegateCommand(7)
* ],
* 1000
* );
*
* This will generate JSON for show-form command with specified entity and form code.
*/
function buildDelegateCommand(functionNameColIndex) {
return _doBuildDelegateCommand.bind(null, functionNameColIndex);
}
/**
* A helper for dataLoader. Builds JSON with a standard showForm command.
* @param {number} formCodeColIndex Index of a column with array of fields to show.
* @returns {Function}
*
* @example
* Example of usage:
* dataLoader.loadArrayData(
* conn,
* [['bpm_desktop', false, 'camunda', 'camunda_Database-sync', 'Synchronization', null, 10]],
* 'ubm_navshortcut',
* ['desktopID', 'isFolder', 'parentID', 'code', 'caption', 'iconCls', 'displayOrder'],
* [
* dataLoader.lookup(conn, 'ubm_desktop', 'code', 0), 1,
* dataLoader.lookup(conn, 'ubm_navshortcut', 'code', 2), 3, 4, 5, 6,
* loaderUtils.buildShowFormCommand(3)
* ],
* 1000
* );
*
* This will generate JSON for show-form command with specified entity and form code.
*/
function buildShowFormCommand(formCodeColIndex) {
return _doBuildShowFormCommand.bind(null, formCodeColIndex);
}
/**
* Build a command for ubm desktop.
* Supports 'showList' and 'showForm' command types.
* @param {number} cmdTypeColIndex Index of a column with type of column ('showList' or 'showForm').
* @param {number} entityNameColIndex Index of a column with entity name.
* @param {number} fieldListColIndex Index of a column with array of fields to show.
* @param {number} [orderByColIndex] Index of a column with order by fields.
* @param {number} formCodeColIndex Index of column with form code.
* @returns {function(this:null)}
*/
function buildCommand(cmdTypeColIndex, entityNameColIndex, fieldListColIndex, orderByColIndex, formCodeColIndex) {
function doBuildCommand(cmdTypeColIndex, entityNameColIndex, fieldListColIndex, orderByColIndex, formCodeColIndex, row) {
const cmdType = row[cmdTypeColIndex];
if (!cmdType) return null;
switch (cmdType) {
case 'showList':
return _doBuildShowListCommand(entityNameColIndex, fieldListColIndex, orderByColIndex, row);
case 'showForm':
return _doBuildShowFormCommand(formCodeColIndex, row);
case 'delegate':
return _doBuildDelegateCommand(row);
default:
throw new Error(`Invalid command type: "${cmdType}"`);
}
}
return doBuildCommand.bind(null, cmdTypeColIndex,
entityNameColIndex, fieldListColIndex, orderByColIndex,
formCodeColIndex);
}
module.exports = {loadCsv, localize, loadAdm, buildCommand, buildShowListCommand, buildShowFormCommand, buildDelegateCommand};
{
"name": "@unitybase/csv-loader-utils",
"version": "1.0.0",
"description": "Extension of '@unitybase/dataLoader' package, which contains helper methods to initialize UnityBase models from csv files.",
"dependencies": {
},
"devDependencies": {},
"engines": {
"UnityBase": ">=4.0"
},
"publishConfig": {"registry": "http://registry.unitybase.info"},
"scripts": {},
"repository": "https://dev.softengi.com/scm/git/UB/csv-loader-utils",
"main": "./index.js",
"author": "Andrey Bezugliy",
"license": "ISC"
}
Markdown is supported