Skip to content
Snippets Groups Projects
Commit eb9e4362 authored by Malte Kremer's avatar Malte Kremer Committed by Sebastian Listl
Browse files

1081754/1079943 Timeshift in Basic übernehmen

parent 55a8068e
No related branches found
No related tags found
No related merge requests found
<?xml version="1.0" encoding="UTF-8"?>
<process xmlns="http://www.adito.de/2018/ao/Model" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" VERSION="1.2.2" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/process/1.2.2">
<name>DataTimeshift</name>
<majorModelMode>DISTRIBUTED</majorModelMode>
<process>%aditoprj%/process/DataTimeshift/process.js</process>
<variants>
<element>EXECUTABLE</element>
</variants>
</process>
import("system.logging");
import("system.text");
import("system.vars");
import("system.eMath");
import("Employee_lib");
import("PermissionCalendar_lib");
import("system.tools");
import("system.calendars");
import("Sql_lib");
import("system.datetime");
import("system.db");
import("system.project");
import("DataTimeshift_lib");
var userTitle = vars.get("$sys.user");
//only allow the timeshifting-service user to perform this because the process_audit checks the origin of a change
//=> a no-audit alias is not required
if (userTitle != TimeShiftUtils.getTimeShiftingServiceUserTitle())
throw new Error("[dataTimeshifting_serverProcess]Wrong user started the process. Given user: \"" + userTitle + "\""
+ " but required user is \"" + TimeShiftUtils.getTimeShiftingServiceUserTitle() + "\"");
//the reference timestamp (=the time when the last time shifting has been performed) is stored in the service users params
var user = tools.getUser(userTitle, tools.PROFILE_FULL);
var referenceTimestamp = user[tools.PARAMS]["timeshiftReferenceTimestamp"];
if (!referenceTimestamp)
throw new Error("[dataTimeshifting_serverProcess]The given timeshiftReferenceTimestamp paran is empty."
+ "Tried to receive the param from user: \"" + userTitle + "\"");
referenceTimestamp = datetime.toLong(referenceTimestamp, "yyyy-MM-dd'T'HH:mm:ssZ", "UTC");
var now = datetime.date();
var timeshiftingTime = eMath.subInt(now, referenceTimestamp);
_getCommonTableShifter().timeshift(timeshiftingTime);
_getBinariesShifter().timeshift(timeshiftingTime);
//calendars are different and need a custom treatment, since the calendars.***-methods are used
var calendarShifter = _getCalendarShifter();
if (calendarShifter) //is null when calendarbackend != DB
{
calendarShifter.timeshift(timeshiftingTime);
}
//Initial reference point is: "2020-09-16T00:00:00+0000";
user[tools.PARAMS]["timeshiftReferenceTimestamp"] = datetime.toDate(now, "yyyy-MM-dd'T'HH:mm:ssZ", "UTC");
tools.updateUser(user);
/**
* Creates a TimeShifter object for the "Data_alias"-alias and returns the object.
* ASYS_*** tables are skipped as well as the liquibase-tables "DATABASECHANGELOG" and "DATABASECHANGELOGLOCK"
*/
function _getCommonTableShifter()
{
var alias = "Data_alias";
var timeshifter = new TimeShifter(alias);
var excludedTables = ["DATABASECHANGELOG", "DATABASECHANGELOGLOCK"];
var tables = db.getTables(alias);
var tablesToShift = timeshifter.getTablesToShift();
tables.forEach(function (tableName){
if (!tableName.startsWith("ASYS_") && !excludedTables.includes(tableName))
tablesToShift.add(tableName);
});
timeshifter.setTablesToShift( tablesToShift);
timeshifter.getTablesToShift().forEach(function(table){ str_log+= "\n"+table; });
return timeshifter;
}
/**
* Creates a TimeShifter object for the ASYS_BINARIES and returns that object.
*/
function _getBinariesShifter()
{
var alias = SqlUtils.getBinariesAlias();
var timeshifter = new TimeShifter(alias);
timeshifter.getTablesToShift().add("ASYS_BINARIES");
return timeshifter;
}
/**
* Creates a TimeShifter object for calendar events and returns it.
* This will only work if the calendar-event backend is database and appopriate grants are set.
*/
function _getCalendarShifter()
{
var backendType = calendars.getBackendType();
if (backendType == calendars.BACKEND_DB)
{
var alias = project.getInstanceConfigValue("calendarAlias", null);
//calendars set grant to everyone here
var timeshifter = new TimeShifter(alias);
timeshifter.getTablesToShift().add("ASYS_CALENDARBACKEND");
timeshifter.getCustomTreatments().set("ASYS_CALENDARBACKEND", function(pTimeDiff){
var entryQuery = newSelect("ASYS_CALENDARBACKEND.ELEMENTUID, min(ASYS_CALENDARBACKEND.OWNER)", alias)
.from("ASYS_CALENDARBACKEND")
.where("ASYS_CALENDARBACKEND.ENTRYTYPE", calendars.VEVENT)
.and("ASYS_CALENDARBACKEND.CLASSIFICATION", 0)
.and("VCOMPONENT not like '%CATEGORIES:Feiertag%'")
.groupBy("ASYS_CALENDARBACKEND.ELEMENTUID");
entryQuery.pageSize(500);
entryQuery.forEachPage(function(pEntries){
for (var i = 0, l = pEntries.length; i < l; i++)
{
var entry = calendars.getEntry(pEntries[i][0], null, _getTitleCalenderUser(pEntries[i][1]), calendars.VEVENT);
entry[calendars.DTSTART] = String(eMath.addInt(entry[calendars.DTSTART], pTimeDiff));
entry[calendars.DTEND] = String(eMath.addInt(entry[calendars.DTEND], pTimeDiff));
calendars.update([entry]);
}
});
});
return timeshifter;
}
return null;
}
/**
* Parses a calendarUser in calendaruser-form (e.g.: "; mailo: hans.mueller@mailing.de;" and returns the adito login name
*/
function _getTitleCalenderUser(pCalendarUser)
{
var data = text.decodeMS(pCalendarUser)
for ( var i = 0; i < data.length; i++ )
{
//if login changes we have to check calendarid
if ( data[i].substr(0, "mailto:".length).toUpperCase() == "MAILTO:" )
{
var user = tools.getUserByAttribute(tools.CALENDARID, [data[i].substr("mailto:".length)]);
if (user != null )
return user[tools.TITLE];
}
if ( data[i].substr(0, 3).toUpperCase() == "CN:" )
return data[i].substr(3);
}
return "";
}
<?xml version="1.0" encoding="UTF-8"?>
<process xmlns="http://www.adito.de/2018/ao/Model" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" VERSION="1.2.2" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/process/1.2.2">
<name>DataTimeshift_lib</name>
<majorModelMode>DISTRIBUTED</majorModelMode>
<process>%aditoprj%/process/DataTimeshift_lib/process.js</process>
<alias>Data_alias</alias>
<variants>
<element>LIBRARY</element>
</variants>
</process>
import("Util_lib");
import("Sql_lib");
import("system.translate");
import("system.SQLTYPES");
import("system.logging");
import("system.tools");
import("system.calendars");
import("system.datetime");
import("system.eMath");
import("system.db");
import("system.project");
import("DatabaseSupplier_lib");
import("Supplier_lib");
/**
* Object for helping to shift DATE/TIMESTAMP-databasefields of an alias.
* @param {String} pAlias <p/>database alias for the shifting transactions. Data will be read from this alias and written into this alias
* @throws Error when no alias is provided
*/
function TimeShifter(pAlias)
{
if (pAlias === undefined)
throw new Error("[TimeShifter.constructor]No alias is defined, but it is required");
this._alias = pAlias;
this._tablesToUpdate = new Set();
this._customTreatments = new Map();
this._defaulTreatmentFunc = function(pDifferenceMillis, pTablename, pPrimaryKeySupplier, pColumnsSupplier, pColumnTypeSupplier) {
var columnNames = pColumnsSupplier.get();
var columnTypes = pColumnTypeSupplier.get();
var pkField = pPrimaryKeySupplier.get();
var alias = this.getAlias();
if (!pkField)//we cannot update if there is no primary key field
return;
var dateCols = [];
var dateColTypes = [];
for (var i = 0, l = columnNames.length; i < l; i++)
{
if (columnTypes[i] == SQLTYPES.TIMESTAMP || columnTypes[i] == SQLTYPES.DATE)
{
dateCols.push(columnNames[i]);
dateColTypes.push(columnTypes[i]);
}
}
if (dateCols.length == 0)
return;
var queryFields = [pkField].concat(dateCols);
var dataQuery = newSelect(queryFields, this.getAlias())
.from(pTablename)
//some databases need a sorting for correct paging
.orderBy(pkField);
dataQuery.pageSize(500);
dataQuery.forEachPage(function (pData){
if (pData.length == 0)
return;
var updateStatements = [];
for (var i = 0, l = pData.length; i < l; i++)
{
var vals = pData[i].slice(1);//first values is always the primary key
//reduce the array to an object where the key is the columnname and the value is the target value for the update
vals = vals.reduce(function(pUpdateObject, pValueToAdd, pIndex){
//empty timestamp should be ignored
if (pValueToAdd == "")
return pUpdateObject;
var column = dateCols[pIndex];
pUpdateObject[column] = eMath.addInt(pValueToAdd, pDifferenceMillis);
return pUpdateObject;
}, {});
//if a field is empty it is not added so it can happen that the object is empty when there are no date fields filled
if ( !Utils.isEmpty( vals))
{
updateStatements.push(
newWhere([pTablename, pkField], pData[i][0], null, null, alias)
.buildUpdateStatement(vals, pTablename)
);
}
}
if (updateStatements.length > 0)
db.updates(updateStatements, alias);
},null, null, this);
};
}
/**
*
* @return {String} returns the alias that is set for the TimeShifter-object
*/
TimeShifter.prototype.getAlias = function(){
return this._alias;
};
/**
*
* @return {Set} <p/>returns a Set where the tables that shall be shiftet can be added
*
* @example
* var exampleShifter = new TimeShifter("example_dbalias");
* exampleShifter.getTablesToShift().add("EXAMPLE_TABLE");
*/
TimeShifter.prototype.getTablesToShift = function(){
return this._tablesToUpdate;
};
TimeShifter.prototype.setTablesToShift = function( pTablesToUpdate){
this._tablesToUpdate = pTablesToUpdate;
}
/**
*
* @return {Map} <p/>returns a Map where the key is the tablename and the value is a callback function that performs the shifting with the following
* params:
* <ul>
* <li>{String} timeDiff - the difference in milliseconds where the shift has to be performed</li>
* <li>{String} tableName - name of the table that is shifted</li>
* <li>{CachedSupplierWrapper} pkSupplier - a cached "PrimaryKeyNameSupplier" to retrieve the primary key name of the table</li>
* <li>{CachedSupplierWrapper} columnSupplier - a cached "ColumnSupplier" to retrieve all columns of the table</li>
* <li>{CachedSupplierWrapper} columnTypesSupplier - a cached "ColumnTypesSupplier" to retrieve all columntypes of the table</li>
* </ul>
* <p/>You can access the TimeShifter-object via the "this" reference
*
* @example
* var exampleShifter = new TimeShifter("example_dbalias");
* exampleShifter.getTablesToShift().add("EXAMPLE_TABLE");
* exampleShifter.getCustomTreatments().set("EXAMPLE_TABLE", function(pTimeDiff, pTable, pPkSupplier, pColumnSupplier, pColumnTypeSupplier){
* var primaryKeyField = pPkSupplier.get();
* //code for shifting ......
* );
*/
TimeShifter.prototype.getCustomTreatments = function(){
return this._customTreatments;
};
/**
* After configurating the TimeShifter object the data can be shiftet with the timeshift-function.
*
* @param {String|Number} pOffsetInMillis <p/>The amount of milliseconds for shifting the data, if this is e.g. one day (86400000) every date
* will be shiftet one day in the future
*/
TimeShifter.prototype.timeshift = function (pOffsetInMillis)
{
var timeDiff = pOffsetInMillis;
this.getTablesToShift().forEach(function (tableName) {
var pkSupplier = new CachedSupplierWrapper(new PrimaryKeyNameSupplier(this.getAlias(), tableName));
var columnSupplier = new CachedSupplierWrapper(new ColumnSupplier(this.getAlias(), tableName));
var columnTypesSupplier = new CachedSupplierWrapper(new ColumnTypesSupplier(this.getAlias(), tableName, columnSupplier));
if (this.getCustomTreatments().has(tableName))
{
var customTreamentFunc = this.getCustomTreatments().get(tableName);
//assertions
customTreamentFunc.call(this, timeDiff, tableName, pkSupplier, columnSupplier, columnTypesSupplier);
}
else
{
this._defaulTreatmentFunc.call(this, timeDiff, tableName, pkSupplier, columnSupplier, columnTypesSupplier);
}
}, this);
this.setTablesToShift( new Set())
};
/**
* Provides static utility functions for shifting timestamp database ifelds
* Don't instanciate TimeShiftUtils.
*/
function TimeShiftUtils(){}
/**
*
* @return Title of the user that is used for timeshifting-actions
*/
TimeShiftUtils.getTimeShiftingServiceUserTitle = function (){
return "timeshiftService";
}
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<process xmlns="http://www.adito.de/2018/ao/Model" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" VERSION="1.2.2" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/process/1.2.2">
<name>DatabaseSupplier_lib</name>
<majorModelMode>DISTRIBUTED</majorModelMode>
<process>%aditoprj%/process/DatabaseSupplier_lib/process.js</process>
<variants>
<element>LIBRARY</element>
</variants>
</process>
import("system.logging");
import("system.db");
import("system.project");
/*
* Database supplier lib, supplies a unified object structure for loading table definitions
*/
function AbstractDbTableBasedSupplier(pAlias, pTable)
{
this._alias = pAlias;
this._table = pTable;
}
AbstractDbTableBasedSupplier.prototype.get = function()
{
throw new Error("[AbstractDbTableBasedSupplier.get]Not implemented because it is abstract.");
};
//Supplier for pks
function PrimaryKeyNameSupplier(pAlias, pTable)
{
AbstractDbTableBasedSupplier.call(this, pAlias, pTable);
}
PrimaryKeyNameSupplier.prototype = Object.create(AbstractDbTableBasedSupplier.prototype);
PrimaryKeyNameSupplier.prototype.constructor = AbstractDbTableBasedSupplier;
//uses alias definitions to return the primary key name for pTable defined in the constructor
PrimaryKeyNameSupplier.prototype.get = function()
{
if (!this.hasOwnProperty(this._resultCache))
{
var struct = project.getAliasDefinitionStructure(this._alias, this._table);
var tableStruct = struct.tables[ this._table.toUpperCase()];
if (tableStruct == undefined){
this._resultCache = "";
logging.log( this._table +"'s alias definition could not be found, primary key could not be established");
}else
this._resultCache = tableStruct.idColumn || "";
}
return this._resultCache;
};
//Supplier for column names
function ColumnSupplier(pAlias, pTable)
{
AbstractDbTableBasedSupplier.call(this, pAlias, pTable);
}
ColumnSupplier.prototype = Object.create(AbstractDbTableBasedSupplier.prototype);
ColumnSupplier.prototype.constructor = AbstractDbTableBasedSupplier;
//returns all column names of a table pTable defined in the constructor
ColumnSupplier.prototype.get = function()
{
var columns = db.getColumns(this._table, this._alias);
return columns;
};
//Supplier for column types
function ColumnTypesSupplier(pAlias, pTable, pColumnSupplier)
{
AbstractDbTableBasedSupplier.call(this, pAlias, pTable);
this._columnSuppier = pColumnSupplier;
}
ColumnTypesSupplier.prototype = Object.create(AbstractDbTableBasedSupplier.prototype);
ColumnTypesSupplier.prototype.constructor = AbstractDbTableBasedSupplier;
//returns column types for all columns of table pTable defined in the constructor
ColumnTypesSupplier.prototype.get = function()
{
var columns = this._columnSuppier.get();
var columnTypes = db.getColumnTypes(this._table, columns, this._alias);
return columnTypes;
};
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<process xmlns="http://www.adito.de/2018/ao/Model" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" VERSION="1.2.2" xsi:schemaLocation="http://www.adito.de/2018/ao/Model adito://models/xsd/process/1.2.2">
<name>Supplier_lib</name>
<majorModelMode>DISTRIBUTED</majorModelMode>
<process>%aditoprj%/process/Supplier_lib/process.js</process>
<variants>
<element>LIBRARY</element>
</variants>
</process>
/*
* Wrapper object to cache and load suppliers (see DatabaseSupplier_lib)
*
*/
function CachedSupplierWrapper(pSupplier)
{
//todo: assertion
this._supplier = pSupplier;
this._valueLoaded = false;
this._cache = null;
}
CachedSupplierWrapper.prototype.get = function()
{
if (!this._valueLoaded)
{
this._cache = this.getSupplier().get();
this._valueLoaded = true;
}
return this._cache;
};
CachedSupplierWrapper.prototype.getSupplier = function ()
{
return this._supplier;
};
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment