Name
global.CSMRelationshipServiceSNC
Description
Service script-include for CSM Relationship framework, only for OOB implementation.
Script
var CSMRelationshipServiceSNC = Class.create();
CSMRelationshipServiceSNC.prototype = {
initialize: function() {
this.context = {};
},
/*
* Return true if this SI is able to handle the services required for given relationship.
*/
canProcess: function(relationship, additionalParams) {
return false;
},
/*
*
* @relationship: String - The table that represents this relationship
* @accessLevel: String - The level of access; READ/WRITE/CREATE/DELETE/NOTIFY/FULL
*
*/
hasAccess: function(current, relationship, accessLevel, additionalParams) {
if(!this.__isValidRelationship(relationship) || !this.canProcess(relationship, additionalParams))
return false;
this.__setContext(current, relationship, null, accessLevel, additionalParams);
if(!this._isAllowedToFetchEntities())
return false;
return this.__hasAccess(current, relationship, accessLevel);
},
/*
*
* @relationship: String - The table that represents this relationship
* @accessLevel: String - The level of access; READ/WRITE/CREATE/DELETE/NOTIFY/FULL
* @targetEntity: String - The entity where the the given 'accessLevel' operation to be performed
* @applicableTo - filter responsibilities by limiting them to specific personas.
* @restrictAccessTo - filter by restricting access to specific set of records (contact/consumer) of accessible entities.
*
*/
getResponsibilities: function(relationship, accessLevel, targetEntity, applicableTo, restrictAccessTo) {
targetEntity = targetEntity || this.__getAdditionalParam(global.CSMRelationshipConstants.TARGET_ENTITY);
applicableTo = applicableTo || this.__getAdditionalParam(global.CSMRelationshipConstants.APPLICABLE_TO);
restrictAccessTo = restrictAccessTo || this.__getAdditionalParam(global.CSMRelationshipConstants.RESTRICT_ACCESS_TO);
var skipRoleCheck = this.__getAdditionalParam(global.CSMRelationshipConstants.SKIP_ROLE_CHECK);
return CSMResponsibilityModelUtil.getResponsibilities(
relationship, accessLevel, null, skipRoleCheck, targetEntity, applicableTo, restrictAccessTo
);
},
/*
*
* @current: GlideRecord - Current glide record; Mostly it would be an entity glide-record on which
* 1) the dynamic filter is being applied OR 2) this method invoked.
* @relationship: String - The table that represents this relationship
* @entityName: String - The field name in the m2m table that represents the required entity
* @accessLevel: String - The level of the access; READ/WRITE/NOTIFY/FULL/AUDIT
* @additionalParams: JSON Object - Object of additional params
* @additionalParams.targetEntity - The entity where the the given 'accessLevel' operation to be performed
* @additionalParams.applicableTo - filter responsibilities by limiting them to specific personas.
* @additionalParams.restrictAccessTo - filter by restricting access to specific set of records (contact/consumer) of accessible entities.
*
*/
getMyEntities: function(current, relationship, entityName, accessLevel, additionalParams) {
if(!this.__isValidRelationship(relationship) || !this.canProcess(relationship, additionalParams))
return [];
this.__setContext(current, relationship, entityName, accessLevel, additionalParams);
if(!this._isAllowedToFetchEntities())
return [];
return this.__getMyEntities(current, relationship, entityName, accessLevel, additionalParams);
},
/*** Private Methods (Do not invoke these methods out side of this SI/child SI) ***/
__isValidRelationship: function(relationship) {
if (this.__hasValidAccessConfig(relationship))
return true;
// fallback to SI configurations
return (Object.keys(global.CSMRelationshipConstants.RELATIONSHIPS)|| []).indexOf(relationship) > -1;
},
__hasValidAccessConfig: function(relationship) {
if (gs.nil(relationship))
return false;
return !gs.nil(new global.ResponsibilityAccessConfigCacheUtil().getAppliesToRelationshipFromCache(relationship));
},
__setContext: function(current, relationship, entityName, accessLevel, additionalParams) {
this.context.current = current;
this.context.relationship = relationship;
this.context.entityName = entityName;
this.context.accessLevel = accessLevel;
this.context.additionalParams = additionalParams;
},
__updateContext: function(key, value) {
if (!gs.nil(key))
this.context[key] = value;
},
__getAdditionalParam: function(key) {
if(!key || !this.context || !this.context.additionalParams)
return null;
return this.context.additionalParams[key];
},
__getMyEntities: function(current, relationship, entityName, accessLevel, additionalParams) {
var entities = {};
var relationshipGR = new GlideRecord(relationship);
if (!relationshipGR.isValid()) return;
if (!this.__getAdditionalParam(global.CSMRelationshipConstants.NO_PERSONA_QUERY))
this._addPersonaCriteria(relationshipGR);
if(this._enforceResponsibilities())
this._addResponsibilitiesCriteria(relationshipGR);
this._addEncodedQuery(relationshipGR);
this._addAdditionalCriteria(relationshipGR);
if(this.__getAdditionalParam(global.CSMRelationshipConstants.SKIP_BEFORE_QUERY_FILTER)){
relationshipGR._skip_before_query_filter = true;
}
relationshipGR.query();
while(relationshipGR.next()) {
var entity = this._getEntity(relationshipGR);
if(!gs.nil(entity)){
entities[entity] = relationshipGR.getValue(global.CSMRelationshipConstants.RESPONSIBILITY);
}
}
this._addAdditionalEntities(entities);
return Object.keys(entities); //set of entity sys-ids
},
__hasAccess: function(current, relationship, accessLevel) {
var relationshipGR = new GlideRecord(relationship);
if (!relationshipGR.isValid()) return;
if (!this.__getAdditionalParam(global.CSMRelationshipConstants.NO_PERSONA_QUERY))
this._addPersonaCriteria(relationshipGR);
if(this._enforceResponsibilities())
this._addResponsibilitiesCriteria(relationshipGR);
this._addAdditionalCriteria(relationshipGR);
relationshipGR.setLimit(1);
relationshipGR.query();
return relationshipGR.hasNext();
},
/*
* This method inserts/updates/deletes given value in Glide List field.
*
* Only addPersonaId is passed -> Insert operation
* Only removePersonaId is passed -> Delete operation
* Both are passed -> Replace operation
*
* Example:
* syncRelationshipGlideListWithEntity(SOLD_PRODUCT_TABLE, SYS_ID, 'additional_consumers', consumerId, null)
*
* Above example adds given consumer Id to additional consumers glide list field in Sold Product table
*/
syncRelationshipGlideListWithEntity: function (tableName, sysId, glideListFieldName, addPersonaId, removePersonaId){
return this.__mutateGlideList(tableName, sysId, glideListFieldName, addPersonaId, removePersonaId, 3);
},
/*
* This method is util method for _updatePersonaInReadAccessList
* This method tries to mutate glide list while avoiding collisions.
* makes given number of attempts
*/
__mutateGlideList: function (tableName, sysId, glideListFieldName, addPersonaId, removePersonaId, attempts){
if(gs.nil(attempts) || attempts <= 0)
return false;
if(gs.nil(tableName) || gs.nil(sysId) || gs.nil(glideListFieldName))
return false;
if(gs.nil(addPersonaId) && gs.nil(removePersonaId))
return false;
addPersonaId = addPersonaId || '';
removePersonaId = removePersonaId || '';
var gr = new GlideRecord(tableName);
if(!gr.get(sysId))
return false;
var values = (gr.getValue(glideListFieldName)) || '';
var addIndex = -1;
var removeIndex = -1;
if(gs.nil(removePersonaId)){
addIndex = values.indexOf(addPersonaId+'');
} else if(gs.nil(addPersonaId)){
removeIndex = values.indexOf(removePersonaId+'');
} else{
removeIndex = values.indexOf(removePersonaId+'');
addIndex = values.indexOf(addPersonaId+'');
}
if(removeIndex == -1 && addIndex > -1)
return true;
else if(removeIndex == -1){
//Insert
if(gs.nil(values))
values = addPersonaId;
else
values = values + "," + addPersonaId;
}
else if(removeIndex > -1 && (gs.nil(addPersonaId) || addIndex > -1)){
//Remove
// if persona id we are going to remove is a middle element -
// it would leave ',,'. If it is a first element or last -
// it would leave a trailing or starting comma.
// The regex replaces first or last comma
values = values.replace(removePersonaId, '').replace(',,', ',').replace(/^,|,$/g, '');
}
else
//Replace
values.replace(removePersonaId, addPersonaId);
//trying to persist read access list with latest value
var rec = new GlideRecord(tableName);
rec.addQuery("sys_id", sysId);
//we are querying sys_mod_count to be sure that we are not overwriting the other changes made to the same record.
rec.addQuery("sys_mod_count", gr.sys_mod_count);
rec.query();
if(rec.next()){
rec.setValue(glideListFieldName, values+'');
rec.update();
//Checking if change is done
gr.initialize();
gr.addQuery("sys_id", sysId);
if(removeIndex == -1)
gr.addQuery(glideListFieldName, "CONTAINS", addPersonaId);
else if(removeIndex > -1 && (gs.nil(addPersonaId) || addIndex > -1))
gr.addQuery(glideListFieldName, "DOES NOT CONTAIN", removePersonaId);
else{
gr.addQuery(glideListFieldName, "CONTAINS", addPersonaId);
gr.addQuery(glideListFieldName, "DOES NOT CONTAIN", removePersonaId);
}
gr.query();
if(gr.hasNext())
return true;
}
//Change did not persist, may be because of collision, reattempting
attempts--;
return this._mutateGlideList(tableName, sysId, glideListFieldName, addPersonaId, removePersonaId, attempts);
},
/*** Protected Methods (Are subject to override only, do not invoke these methods out side of this SI/child SI) ***/
//Note: use this.context object to get the required parameters.
_addPersonaCriteria: function(relationshipGR) {
relationshipGR.addQuery(global.CSMRelationshipConstants.DEFAULT_PERSONA_FIELD, gs.getUserID());
},
_enforceResponsibilities: function() {
return true;
},
_addResponsibilitiesCriteria: function(relationshipGR) {
var responsibilities = this.getResponsibilities(this.context.relationship, this.context.accessLevel);
relationshipGR.addQuery(global.CSMRelationshipConstants.RESPONSIBILITY, "IN", responsibilities);
},
_addAdditionalCriteria: function(relationshipGR) {
},
_addEncodedQuery : function(relationshipGR) {
var additionalEncodedQuery = this.__getAdditionalParam(global.CSMRelationshipConstants.ADDITIONAL_ENCODED_QUERY);
if (!gs.nil(additionalEncodedQuery))
relationshipGR.addEncodedQuery(additionalEncodedQuery);
},
_addAdditionalEntities: function(entitiesMap) {
},
_getEntity: function(relationshipGR) {
return relationshipGR.getElement(this.context.entityName) + '';
},
_isAllowedToFetchEntities: function() {
return true;
},
_isChildOf: function(parent, tableName) {
return new global.CSMRelationshipUtils().isChildOf(parent, tableName);
},
_getAccountHelper: function() {
return new global.Account();
},
_getSOHelper: function() {
return new global.ServiceOrganizationUtil();
},
type: 'CSMRelationshipServiceSNC'
};
Sys ID
0ce4b42d77427010d3ef07dc7d5a997a