spitfirepm
Version:
Client API Tools for Spitfire Project Management
1,013 lines (1,012 loc) • 326 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.sfRestClient = exports.InvokeOptions = exports.WCCData = exports.NVPair = exports.SFFileUploadContext = exports.ValidationMode = exports.LoggingLevels = void 0;
const SwaggerClients_1 = require("./SwaggerClients");
const _SwaggerClientExports = require("./SwaggerClients");
const BrowserExtensionChecker_1 = require("./BrowserExtensionChecker");
const RESTClientBase = require("./APIClientBase"); // avoid conflict with same in SwaggerClient when loaded by classic UI
const string_extensions_1 = require("./string.extensions");
//import {dialog} from "jquery-ui";
const ClientPackageVersion = "23.9330.5";
// originally modified for typescript and linter requirements by Uladzislau Kumakou of XB Software
var LoggingLevels;
(function (LoggingLevels) {
LoggingLevels[LoggingLevels["None"] = 0] = "None";
LoggingLevels[LoggingLevels["Normal"] = 1] = "Normal";
LoggingLevels[LoggingLevels["Verbose"] = 2] = "Verbose";
LoggingLevels[LoggingLevels["Debug"] = 9] = "Debug";
LoggingLevels[LoggingLevels["VerboseDebug"] = 92] = "VerboseDebug";
})(LoggingLevels = exports.LoggingLevels || (exports.LoggingLevels = {}));
var ValidationMode;
(function (ValidationMode) {
ValidationMode["none"] = "0";
/** 1,T*,Y* are true, empty, 0,F*,N* are false */
ValidationMode["boolean"] = "1";
ValidationMode["numeric"] = "2";
ValidationMode["positive"] = "5";
ValidationMode["integer"] = "6";
ValidationMode["positiveinteger"] = "7";
ValidationMode["date"] = "3";
ValidationMode["futuredate"] = "4";
/** sort of like past date */
ValidationMode["untildate"] = "8";
/** non empty, can be combined with other validations */
ValidationMode["required"] = "16";
ValidationMode["filename"] = "32";
ValidationMode["url"] = "64";
})(ValidationMode = exports.ValidationMode || (exports.ValidationMode = {}));
class _SessionClientGetWCCShare {
constructor(apiPromise, forPageHash) {
this.APIResult = null;
this.AsOf = Date.now();
this.IsResolved = false;
this.APIResult = apiPromise;
this.ForNavHash = forPageHash;
apiPromise.finally(() => {
this.IsResolved = true;
this.AsOf = Date.now();
});
}
AppliesFor(testHash) {
return !this.Expired() && (this.ForNavHash === testHash);
}
Expired() {
return (this.IsResolved && (Date.now() - this.AsOf) > 3210);
}
}
class SFFileUploadContext {
constructor(mode) {
this.mode = mode;
}
}
exports.SFFileUploadContext = SFFileUploadContext;
class PartStorageData {
CFGLoader() {
if (!this._InitializationResultPromise)
throw new Error("This part was never initialized: " + this._ReferenceKey);
return this._InitializationResultPromise;
}
static ClearCache() { this._LoadedParts = new Map(); }
/**
*
* @param client sfRestClient
* @param partName something like ActionItems or ProjectCA
* @param forDocType GUID
* @param forProject project ID; if empty client.GetPageProjectKey() is used
* @param context
* @param usingCfg if supplied, stored, otherwise resolved
* @returns
* @see RegisterRestoredCFG
*/
static PartStorageDataFactory(client, partName, forDocType, forProject, context, usingCfg) {
if (!forProject)
forProject = client.GetPageProjectKey();
const ReferenceKey = PartStorageData.GetPartContextKey(partName, forDocType, forProject, context);
let thisPart;
if (PartStorageData._LoadedParts.has(ReferenceKey))
thisPart = PartStorageData._LoadedParts.get(ReferenceKey);
else {
thisPart = new PartStorageData(client, partName, forDocType, forProject, context);
if (usingCfg && usingCfg.PartName === partName) {
thisPart._InitializationResultPromise = new Promise((preloaded) => {
thisPart.CFG = usingCfg;
preloaded(usingCfg);
});
return thisPart;
}
const api = new SwaggerClients_1.UICFGClient(PartStorageData._SiteURL);
try {
thisPart._InitializationResultPromise = api.getLiveDisplay(partName, forDocType, forProject, context);
if (thisPart._InitializationResultPromise) {
thisPart._InitializationResultPromise.then((r) => {
thisPart.CFG = r;
});
}
}
catch (error) {
console.warn(error);
thisPart.CFG = new SwaggerClients_1.UIDisplayPart();
throw error;
}
}
return thisPart;
}
/**
*
* @param client
* @param lookupName
* @param usingCfg
* @returns
* @see RegisterRestoredLookupCFG
*/
static PartStorageDataLookupFactory(client, lookupName, usingCfg) {
var ReferenceKey = PartStorageData.GetPartContextKey(lookupName, "lookup", "lookup", "lookup");
var thisPart;
if (PartStorageData._LoadedParts.has(ReferenceKey))
thisPart = PartStorageData._LoadedParts.get(ReferenceKey);
else {
thisPart = new PartStorageData(client, lookupName, "lookup", "lookup", "lookup");
if (usingCfg && usingCfg.PartName === lookupName) {
thisPart._InitializationResultPromise = new Promise((preloaded) => {
thisPart.CFG = usingCfg;
preloaded(usingCfg);
});
return thisPart;
}
var api = new SwaggerClients_1.UICFGClient(PartStorageData._SiteURL);
thisPart._InitializationResultPromise = api.getLookupDisplay(lookupName);
if (thisPart._InitializationResultPromise) {
thisPart._InitializationResultPromise.then((r) => {
thisPart.CFG = r;
});
}
}
return thisPart;
}
static GetPartContextKey(partName, forDocType, forProject, context) {
return `${partName}[${context}]::${forDocType}#${forProject}`; // "{0}[{2}]::{1}".sfFormat(partName, forDocType, context);
}
GetDataModelBuildContextKey() {
PartStorageData._DMCount++;
return `DVM-${this._ForPartName}#${PartStorageData._DMCount}`;
}
constructor(client, partName, forDocType, forProject, context) {
this.CFG = null;
this.DataModels = new Map();
this.RestClient = client;
this._PromiseList = null;
this._ReferenceKey = PartStorageData.GetPartContextKey(partName, forDocType, forProject, context);
this._ForPartName = partName;
PartStorageData._LoadedParts.set(this._ReferenceKey, this);
this._InitializationResultPromise = null;
if (!PartStorageData._SiteURL) {
var ApplicationPath = window.__HTTPApplicationName();
if (ApplicationPath.toLocaleLowerCase() === "powerux")
ApplicationPath = 'sfPMS';
PartStorageData._SiteURL = `${window.location.origin}/${ApplicationPath || 'sfPMS'}`;
}
}
}
PartStorageData._DMCount = 0;
PartStorageData._LoadedParts = new Map();
/**
* Options for Query Alias Popup ( QAPopInfo )
*/
class QAInfoOptions {
// URLCallback: () => string
//dvFor:string // "*";
/**
* Finds closest element with .sfUIPopQA and its matching .uiQA-xxx and creates options class from CSS -- attributes
*
*
* .sfQA-RoleRouteInfo {
*
* --dialog-title:"Routes that Reference Role" ;
* --dialog-class:sfUIShowInfo;
* --dialog-value-class: false
* --dialog-empty-text:"No route references were found." ;
* --dialogQueryL:"util/jstNodes.ashx/qa/RoleMemberInfo/$K";
* }
*/
static QAInfoOptionsFromCSSFactory(forElement) {
var thisPart;
var title = "", cssClass = "sfUIShowInfo", emptyText = "Nothing to see here!", queryURL = "", ValueCSS = "";
if (!forElement.hasClass("sfUIPopQA"))
forElement = forElement.closest(".sfUIPopQA");
if (forElement.length === 0) {
console.log("QAInfoOptionsFromCSSFactory() could not find .sfUIPopQA ");
return null;
}
title = QAInfoOptions.CSSPropertyValueOrEmpty(forElement, "--dialog-title");
cssClass = QAInfoOptions.CSSPropertyValueOrEmpty(forElement, "--dialog-class", cssClass);
ValueCSS = QAInfoOptions.CSSPropertyValueOrEmpty(forElement, "--dialog-value-class");
emptyText = QAInfoOptions.CSSPropertyValueOrEmpty(forElement, "--dialog-empty-text", emptyText);
queryURL = QAInfoOptions.CSSPropertyValueOrEmpty(forElement, "--dialog-query");
thisPart = new QAInfoOptions(title, cssClass, emptyText, queryURL);
if (ValueCSS)
thisPart.ValueCSS = ValueCSS.trim();
thisPart.LoadFromDataAttributes(forElement);
return thisPart;
}
/**
*
* @param fromElement
* @param cssName
* @param defaultValue Specify if default is not empty string
* @returns
*/
static CSSPropertyValueOrEmpty(fromElement, cssName, defaultValue) {
let CSSValue = fromElement.css(cssName);
if (CSSValue) {
CSSValue = CSSValue.trim();
if (CSSValue.startsWith("'") || CSSValue.startsWith("\"")) {
try {
// CSSValue = eval(CSSValue);
// Instead of eval, use string replace
CSSValue = CSSValue.replace(/['"]/g, '').trim();
CSSValue = CSSValue.trim();
}
catch (ex) {
console.warn("CSSPropertyValueOrEmpty could not EVAL {0}".sfFormat(CSSValue));
}
}
}
return CSSValue?.trim() ?? defaultValue ?? "";
}
LoadFromDataAttributes(fromElement) {
var elementData = fromElement.data();
if ("dialogtitle" in elementData)
this.DialogTitle = elementData.dialogtitle;
if ("dialogcss" in elementData)
this.DialogCSS = elementData.dialogcss.trim();
if ("valuecss" in elementData)
this.ValueCSS = elementData.valuecss.trim();
if ("emptydialogtext" in elementData)
this.EmptyDialogText = elementData.emptydialogtext.trim();
if ("dialogtitle" in elementData) {
this.DialogTitle = elementData.dialogtitle;
}
}
constructor(title, cssClass, emptyText, queryURL) {
//, urlCallback: () => string ) {
this.DialogTitle = title; // "Routes that Reference Role" ;
this.DialogCSS = "sfUIShowInfo";
if (cssClass)
this.DialogCSS = cssClass.trim(); // "sfUIShowInfo";
this.EmptyDialogText = emptyText; // "No route references were found." ;
//this.dvFor = "*";
//this.URLCallback = urlCallback;
this.QueryURL = queryURL;
if (!this.DialogCSS)
this.DialogCSS = "sfUIShowInfo";
if (!this.EmptyDialogText)
this.EmptyDialogText = "Nothing to see here...";
}
}
class NVPair {
}
exports.NVPair = NVPair;
class WCCData {
}
exports.WCCData = WCCData;
class InvokeOptions {
}
exports.InvokeOptions = InvokeOptions;
;
;
/** Spitfire PM Client
* @example top.sfClient.GetDV(...)
* @example Access static methods or shared properties from console: top.staticBase._IconMap */
class sfRestClient {
ServerVersion() {
var result = "";
if (sfRestClient._WCC.Version)
result = sfRestClient._WCC.Version;
return result;
}
/**
* Applies removals, then changes, then additions
* @param rawData array of Data Model or View Model
* @param keyName name of key field in this data model
* @param changes (Add,Change,Remote)
* @returns merged rawData
* @see BuildDataSummary
*/
ApplyDataChanges(rawData, keyName, changes) {
var RESTClient = this;
var StartAtTicks = Date.now();
var RemoveCount = 0, ChangeCount = 0, AddCount = 0;
if (!rawData)
rawData = [];
if (changes.Remove) {
changes.Remove.forEach(element => {
var foundRow = RESTClient.FindRowIndexByKey(rawData, keyName, element);
if (typeof foundRow === "number" && foundRow >= 0) {
rawData.splice(foundRow, 1);
RemoveCount++;
}
else if (sfRestClient._Options.LogLevel >= LoggingLevels.Verbose)
console.log("ApplyDataChanges(REMOVE) did not find a row with key {0}".sfFormat(element));
});
}
if (changes.Change) {
changes.Change.forEach((element) => {
var thisKey = element[keyName];
var foundRow = RESTClient.FindRowIndexByKey(rawData, keyName, thisKey);
if (typeof foundRow === "number" && foundRow >= 0) {
rawData[foundRow] = element;
ChangeCount++;
}
else {
if (sfRestClient._Options.LogLevel >= LoggingLevels.Verbose)
console.log("ApplyDataChanges(CHANGE) did not find a row with key {0} (changed to add)".sfFormat(element));
changes.Add?.push(element); // !!! does this work?
}
});
}
if (changes.Add) {
changes.Add.forEach((element) => {
var thisKey = element[keyName];
var foundRow = RESTClient.FindRowIndexByKey(rawData, keyName, thisKey);
if (typeof foundRow !== "number" || foundRow < 0) {
rawData.push(element);
AddCount++;
}
else {
rawData[foundRow] = element;
ChangeCount++;
}
});
}
if (sfRestClient._Options.LogLevel >= LoggingLevels.Verbose)
console.log("ApplyDataChanges({0}) removed {1}, changed {2}, added {3} in {4}t ".sfFormat(keyName, RemoveCount, ChangeCount, AddCount, Date.now() - StartAtTicks));
return rawData;
}
/**
* Extracts key, etag pairs from a set of data
* @param rawData array of Data Models or View Models, must have etag
* @param keyName
* @returns array of key, etag pairs, suitable for passing to getChange* API endpoints
* @see ApplyDataChanges
*/
BuildDataSummary(rawData, keyName) {
var result = [];
if (!rawData)
return result;
if (!Array.isArray(rawData)) {
var SingleElementArrayOfRawData = [];
SingleElementArrayOfRawData.push(rawData);
rawData = SingleElementArrayOfRawData;
}
else {
if (rawData.length === 0)
return result;
}
if (!(keyName in rawData[0])) {
console.warn("BuildDataSummary() - rawData does not include keyName {0} ".sfFormat(keyName));
return result;
}
if (!("ETag" in rawData[0])) {
console.warn("BuildDataSummary() - rawData does not include ETag".sfFormat(keyName));
return result;
}
var RESTClient = this;
rawData.forEach(function (row) {
if (row[keyName] && !result.find(el => el.RowKey === row[keyName])) {
result.push(new _SwaggerClientExports.CurrentDataSummary({ "RowKey": row[keyName], "ETag": row["ETag"] }));
}
});
return result;
}
/**
* Async builds a View Model for the rawData, given part context. - use .then()
*/
BuildViewModelForContext(partName, context, forDocType, rawData) {
if (!sfRestClient._z.WCCLoaded)
this.LoadUserSessionInfo();
var RESTClient = this;
var thisPart = PartStorageData.PartStorageDataFactory(this, partName, forDocType, "", context);
if (!thisPart) {
console.warn("Count not resolve part {0}".sfFormat(PartStorageData.GetPartContextKey(partName, forDocType, "", context)));
var EmptyPromise = new Promise((resolve) => resolve(rawData));
return EmptyPromise;
}
var FinalViewModelPromise = new Promise((finalResolve) => {
thisPart.CFGLoader().then(() => {
var ViewModelPromise = this._ConstructViewModel(thisPart, rawData);
ViewModelPromise.then((r) => {
finalResolve(r);
const RawResultIsArray = (r && Array.isArray(r));
let rowCount = 1;
if (!RawResultIsArray) {
if (sfRestClient._Options.LogLevel >= LoggingLevels.VerboseDebug)
console.log(`BuildViewModelForContext GA ${partName} ${typeof r} isArray ${Array.isArray(r)} - single row`, r);
}
else
rowCount = r.length;
RESTClient.GAViewModelEvent(partName, rowCount);
});
});
});
return FinalViewModelPromise;
}
// /**
// * Legacy version of BuildViewModelForContext - use .done()
// * @deprecated use BuildViewModelForContext()
// */
// BuildViewModel(partName: string, context: string, rawData: any, unusedCfgData: undefined, forDocType: GUID | undefined): JQueryPromise<any> {
// if (!sfRestClient._z.WCCLoaded) this.LoadUserSessionInfo();
// var thisPart: PartStorageData | undefined = PartStorageData.PartStorageDataFactory(this, partName, forDocType, "",context);
// var darnSoon = $.Deferred();
// var ResultReady = darnSoon.promise();
// // what purpose would this serve?? if (cfg) thisPart.CFGLoader = cfg;
// thisPart.CFGLoader().then(() => {
// this._ConstructViewModel(thisPart!, rawData)
// .then((r) => darnSoon.resolve(r))
// }
// );
// return ResultReady;
// }
/**
* Updates row visibility on the server and updates the in-memory flags for display of this row
* @param partName ProjTeam or ProjectPublicInfo
* @param rawData ViewModel containing raw row data
*/
ToggleRowVisibility(partName, rawData) {
var DataField = "TeamList";
var api;
const FlagVisibleFieldName = "_DefaultRowVisible";
var ServerUpdatePromise;
var RowKey = "";
var NewValue = false;
var APIContext = "";
if (partName === "ProjTeam") {
DataField = "TeamList";
api = new SwaggerClients_1.ProjectTeamClient(this._SiteURL);
NewValue = !(rawData[FlagVisibleFieldName]);
RowKey = rawData["UserProjectKey"];
APIContext = this.GetPageProjectKey();
ServerUpdatePromise = api.patchProjectTeam(APIContext, RowKey, DataField, NewValue.toString());
}
else if (partName === "ProjectPublicInfo") {
DataField = "UserList";
api = new SwaggerClients_1.ProjectsClient(this._SiteURL);
NewValue = !(rawData[FlagVisibleFieldName]);
RowKey = rawData["UserProjectKey"];
APIContext = this.EmptyKey; // future! get current user key
ServerUpdatePromise = api.patchUserProjectList(APIContext, RowKey, DataField, NewValue.toString());
}
else {
console.warn("ToggleRowVisibility - Unsupported: ", partName);
ServerUpdatePromise = undefined;
}
var FinalPromise = new Promise((finalResolve) => {
if (!ServerUpdatePromise) {
finalResolve(false);
return;
}
ServerUpdatePromise.then(() => {
rawData[FlagVisibleFieldName] = NewValue;
rawData[DataField] = NewValue;
finalResolve(true);
}).catch((reason) => {
console.warn("ToggleRowVisibility({0},{1}) Failed persist {2} to {3}, {4}".sfFormat(partName, RowKey, DataField, NewValue, reason));
finalResolve(false);
});
});
return FinalPromise;
}
/** Writes to console log
* @argument msg text to write
* @argument level msg is suppressed if logging level is less than specified, default is verbose
*/
Log(msg, level = LoggingLevels.Verbose) {
if (sfRestClient._Options.LogLevel >= level)
console.log(msg);
}
/**
* Applies CFG data to raw Data Model, returns promise that resolves when View Model is ready
*/
_ConstructViewModel(thisPart, rawData) {
if (!thisPart || !thisPart.CFG || !thisPart.CFG.UIItems) {
console.error('_ConstructViewModel requires CFG and UI', thisPart);
throw `Cannot construct requested ViewModel`;
}
var StartAtTicks = Date.now();
var DataModelBuildKey = thisPart.GetDataModelBuildContextKey();
var FailCount = 0;
var SingleInstanceMode = false;
if (!Array.isArray(rawData)) {
SingleInstanceMode = true;
var SingleElementArrayOfRawData = [];
SingleElementArrayOfRawData.push(rawData);
rawData = SingleElementArrayOfRawData;
}
thisPart.DataModels.set(DataModelBuildKey, rawData);
thisPart._PromiseList = [];
// this loop builds PromiseList
thisPart.CFG.UIItems.forEach(element => thisPart.RestClient._ApplyUICFGtoRawData(element, thisPart, DataModelBuildKey));
var ViewModelPromise = new Promise((resolve) => {
$.when.apply($, thisPart._PromiseList)
.done(function () {
var FinalData = thisPart.DataModels.get(DataModelBuildKey);
if (SingleInstanceMode)
FinalData = FinalData[0];
resolve(FinalData);
thisPart.DataModels.delete(DataModelBuildKey);
if (sfRestClient._Options.LogLevel >= LoggingLevels.Verbose)
console.log("ViewModel {0} complete in {1}t".sfFormat(DataModelBuildKey, Date.now() - StartAtTicks));
}).fail(function (jqXHR, textStatus, errorThrown) {
if (FailCount === 0) {
var FinalData = thisPart.DataModels.get(DataModelBuildKey);
if (SingleInstanceMode)
FinalData = FinalData[0];
resolve(FinalData);
thisPart.DataModels.delete(DataModelBuildKey);
FailCount++;
}
console.warn("ViewModel {0} failed to complete cleanly; {2} fails; {1}t".sfFormat(DataModelBuildKey, Date.now() - StartAtTicks, FailCount));
});
});
return ViewModelPromise;
}
async WaitForSessionLoaded() {
if (!sfRestClient._z.WCCLoaded)
try {
await this.LoadUserSessionInfo();
}
catch (ex) {
console.warn('IsSessionLoaded()', ex);
}
}
/**
* async: returns an numeric bit-flag indicating the user's permission level (R=1,I=2,U=4,D=8,S=16)
* When the user has admin permissions, these are blended (unless ucModule = WORK)
* @param ucModule WORK,SYS, PAGE, PART,LIST
* @param ucFunction see xsfUCFunction
* @param optionalDTK guid or undefined
* @param optionalProject
*/
CheckPermit(ucModule, ucFunction, optionalDTK, optionalProject, optionalReference) {
var RESTClient = this;
//was $.Deferred();
var DeferredPermitResult = new Promise(async (ResolveThisPermit, rejectThisPermit) => {
if (!sfRestClient._z.WCCLoaded) {
let retryCount = 0;
while (!sfRestClient._z.WCCLoaded && retryCount < 9)
try {
const usePageName = ((retryCount++ < 3) &&
(sfRestClient.ResolvedPageInfo.LastResolvedPageTypeName & RESTClient.PageTypeNames.Unauthenticated) === RESTClient.PageTypeNames.Unauthenticated)
? `${string_extensions_1.sfApplicationRootPath}/wx/#!/main/home` : undefined;
await RESTClient.LoadUserSessionInfo(false, usePageName);
}
catch (ex) {
rejectThisPermit(ex.message);
return;
}
}
if (typeof optionalDTK !== "string")
optionalDTK = "";
if (typeof optionalReference !== "string")
optionalReference = "";
if (typeof optionalProject !== "string" || !optionalProject)
optionalProject = "0";
var PermitCacheID = ucModule + "_" + ucFunction
+ "_T" + optionalDTK.replaceAll("-", "")
+ "_R" + optionalReference
+ "_P" + optionalProject;
var ValueFromCache = sfRestClient._UserPermitResultCache.get(PermitCacheID);
if (typeof ValueFromCache === "number") {
if (sfRestClient._Options.LogLevel >= LoggingLevels.Debug)
console.log(`CheckPermit#${RESTClient.ThisInstanceID}(${ucModule}:${ucFunction},${optionalProject}) = ${ValueFromCache}; from static cache `);
ResolveThisPermit(ValueFromCache);
return;
}
var UCFK = "";
var ThisProjectPermitSet;
var UCFKDeferredResult = $.Deferred();
var UCFKPromise = UCFKDeferredResult.promise();
var PPSDeferredResult = $.Deferred();
var PPSPromise = PPSDeferredResult.promise();
if (!sfRestClient.PermitMapLoaded()) {
// new approach, lets wait for a map
await RESTClient.LoadUCFunctionMap();
if (!sfRestClient.PermitMapLoaded()) {
// still no map!?
console.warn("CheckPermits() could not load Permit Map!!");
}
}
if (ucModule.length === 36) {
UCFK = ucModule;
UCFKDeferredResult.resolve(UCFK);
}
else if (sfRestClient._UCPermitMap && ucModule in sfRestClient._UCPermitMap
&& ucFunction in sfRestClient._UCPermitMap[ucModule]) {
UCFK = sfRestClient._UCPermitMap[ucModule][ucFunction];
UCFKDeferredResult.resolve(UCFK);
}
else
UCFKDeferredResult.resolve(RESTClient.EmptyKey);
if (!UCFK) {
if (sfRestClient._WCC.UserKey === RESTClient.EmptyKey)
console.warn(`CheckPermit(): >>>> No user/session!! <<<< Therefore no permission for ${ucModule}|${ucFunction}! LOGIN AGAIN!`);
else
console.warn(`CheckPermit could not find ${ucModule}|${ucFunction} - verify proper case/trim!`);
ResolveThisPermit(0);
return;
}
if (!sfRestClient.GlobalPermitAPIPromise) {
if (!(sfRestClient._LoadedPermits.has("0"))) { // global permissions
var api = new SwaggerClients_1.SessionClient(this._SiteURL);
sfRestClient.GlobalPermitAPIPromise = api.getProjectPermits("0");
sfRestClient._LoadedPermits.set("0", new _SwaggerClientExports.UCPermitSet()); // this prevents repeat requests
if (sfRestClient.GlobalPermitAPIPromise) {
sfRestClient.GlobalPermitAPIPromise.then((r) => {
if (r) {
//if (sfRestClient._Options.LogLevel >= LoggingLevels.Debug) console.log(`CheckPermit#${RESTClient.ThisInstanceID}() Received Global Permits from server...`);
sfRestClient._LoadedPermits.set("0", r);
if (sfRestClient._Options.LogLevel >= LoggingLevels.Verbose)
console.log(`CheckPermit#${RESTClient.ThisInstanceID}() Loaded Global Permits from server...`);
}
});
}
}
}
if (sfRestClient.GlobalPermitAPIPromise) {
//if (sfRestClient._Options.LogLevel >= LoggingLevels.Debug) console.log(`CheckPermit#${RESTClient.ThisInstanceID}() Waiting for Global Permits from server...`);
await sfRestClient.GlobalPermitAPIPromise;
if (sfRestClient._Options.LogLevel >= LoggingLevels.VerboseDebug)
console.log(`CheckPermit#${RESTClient.ThisInstanceID}() Global Permits ready() `, sfRestClient._LoadedPermits.get(optionalProject));
}
if (!(sfRestClient._LoadedPermits.has(optionalProject))) {
var apiResult;
var MyAPIRequest = false;
if (!sfRestClient._LoadingPermitRequests.has(optionalProject)) {
var api = new SwaggerClients_1.SessionClient(this._SiteURL);
apiResult = api.getProjectPermits(optionalProject);
sfRestClient._LoadingPermitRequests.set(optionalProject, apiResult);
MyAPIRequest = true;
}
else
apiResult = sfRestClient._LoadingPermitRequests.get(optionalProject);
if (apiResult) {
apiResult.then((r) => {
if (r) {
if (sfRestClient._Options.LogLevel >= LoggingLevels.Debug)
console.log(`CheckPermit#${RESTClient.ThisInstanceID}() Loaded Project ${optionalProject} Permits from server...`);
sfRestClient._LoadedPermits.set(optionalProject, r);
ThisProjectPermitSet = r;
PPSDeferredResult.resolve(r);
if (MyAPIRequest && optionalProject)
sfRestClient._LoadingPermitRequests.delete(optionalProject);
}
});
}
}
else {
ThisProjectPermitSet = sfRestClient._LoadedPermits.get(optionalProject);
PPSDeferredResult.resolve(ThisProjectPermitSet);
}
var finalCheck = [PPSPromise, UCFKPromise];
$.when.apply($, finalCheck).done(function () {
var finalPermit = 0;
var GlobalPermits = sfRestClient._LoadedPermits.get("0")?.Permits;
$.each([ThisProjectPermitSet?.Permits, GlobalPermits], function CheckOneSource(sourceIdx, thisSource) {
if (sfRestClient._Options.LogLevel >= LoggingLevels.Debug)
console.log(`CheckPermit#${RESTClient.ThisInstanceID}(${ucModule}:${ucFunction},${optionalProject}) checking ${thisSource === ThisProjectPermitSet?.Permits ? "project" : "global"}`);
$.each(thisSource, function OneCapabilityCheck(ThisUCFK, capabilitySet) {
if (ThisUCFK === UCFK) {
if (sfRestClient._Options.LogLevel >= LoggingLevels.Debug)
console.log(`CheckPermit#${RESTClient.ThisInstanceID}(${ucModule}:${ucFunction},${optionalProject}) UCFK ${UCFK}, cl:`, capabilitySet);
$.each(capabilitySet, function OnePermitCheck(_n, p) {
if (sfRestClient._Options.LogLevel >= LoggingLevels.VerboseDebug)
console.log(`CheckPermit#${RESTClient.ThisInstanceID}(${ucModule}:${ucFunction},${optionalProject}) UCFK ${UCFK}, p:`, p);
var thisPermitValue = 0;
if (p.IsGlobal || RESTClient._PermitMatches(p, optionalDTK, optionalReference)) {
if (p.ReadOK)
thisPermitValue += RESTClient.PermissionFlags.Read;
if (p.InsOK)
thisPermitValue += RESTClient.PermissionFlags.Insert;
if (p.UpdOK)
thisPermitValue += RESTClient.PermissionFlags.Update;
if (p.DelOK)
thisPermitValue += RESTClient.PermissionFlags.Delete;
if (p.BlanketOK)
thisPermitValue += RESTClient.PermissionFlags.Special;
}
finalPermit |= thisPermitValue;
return (finalPermit !== 31);
});
}
return (finalPermit !== 31);
});
});
if (ucModule !== "WORK")
finalPermit = finalPermit | sfRestClient._WCC.AdminLevel;
sfRestClient._UserPermitResultCache.set(PermitCacheID, finalPermit);
if (sfRestClient._Options.LogLevel >= LoggingLevels.Debug)
console.log(`CheckPermit#${RESTClient.ThisInstanceID}(${ucModule}:${ucFunction},${optionalProject}) = Usr:${finalPermit},Adm:${sfRestClient._WCC.AdminLevel}; static cache set `);
ResolveThisPermit(finalPermit);
// we do have global permits above: what use case was this for???
// if (finalPermit === 31) {
// RESTClient._UserPermitResultCache.set(PermitCacheID, finalPermit);
// ResolveThisPermit(finalPermit);
// }
// else {
// // not ideal: future we need a way to hold global permits here too
// var api : SessionClient = RESTClient.NewAPIClient("SessionClient");
// api.getCapabilityPermits(ucModule,ucFunction.replaceAll(".","!").replaceAll("/","@"),optionalProject,optionalDTK).then((r)=>{
// RESTClient._UserPermitResultCache.set(PermitCacheID, r);
// ResolveThisPermit(r);
// });
// }
});
});
return DeferredPermitResult; // wait for .then, use (r)
}
/**
* Resolves list of part names with permissions
* @returns object with part names and permission flags
*/
GetPagePartPermits(forPageName, pageKey) {
if (typeof pageKey === "undefined")
pageKey = "0";
if (typeof forPageName === "undefined") {
forPageName = this.ResolvePageTypeName();
}
var DeferredPermitResult = new Promise((ResolveList) => {
var finalCheck = [];
var PartNameList = [];
var PageParts = {};
if (forPageName === this.PageTypeNames.HomeDashboard) {
PartNameList = ["ActionItems", "ProjectList", "AlertList"];
}
else if (forPageName === this.PageTypeNames.ProjectDashboard) {
PartNameList = ["ProjTeam", "ProjectKPI", "ProjLinks", "ProjectCA", "ProjNote", "ProjPhoto", "ProjWeather"];
if (pageKey.length <= 1)
pageKey = this.GetPageProjectKey();
// special case for project dashboards: ProjDocMenu
PageParts["ProjDocMenu"] = 1;
}
else if (forPageName === this.PageTypeNames.ExecutiveDashboard) {
PartNameList = ["ProjectRES"];
//if (pageKey!.length <= 1) pageKey = this.GetPageProjectKey();
// special case for project dashboards: ProjDocMenu
}
else if (forPageName === this.PageTypeNames.Catalog) {
PartNameList = ["DocSearch", "CatVersions", "Folders"];
}
else {
console.warn("GetPagePartPermits() does not (yet) support ", forPageName);
}
PartNameList.forEach(element => {
var OnePartPermitDeferred = $.Deferred();
var OnePartPermitDeferredPromise = OnePartPermitDeferred.promise();
finalCheck.push(OnePartPermitDeferredPromise);
if (!pageKey && typeof pageKey === "string")
pageKey = undefined;
this.CheckPermit("PART", element, undefined, pageKey).then((r) => {
PageParts[element] = r;
OnePartPermitDeferred.resolve(r);
});
});
$.when.apply($, finalCheck).done(function () {
ResolveList(PageParts);
});
});
return DeferredPermitResult;
}
/**
* Returns set of choices for an auto-complete based on the seed value
* @param lookupName
* @param seedValue 0 or more characters which will limit the suggestions
* @param dependsOn 0 to 4 values required by the lookup for context; @see sfRestClient.GatherDependsOnValues to process string
* @param limit a reasonable number of suggestions to be returned; zero or negative replaced with option.SuggestionLimit
* @returns promise of suggestions. reuses a matching promise from withing about 4 minutes
* @description context is provided from GetPageContextValue("dsCacheKey")
*/
GetLookupSuggestions(lookupName, seedValue, dependsOn, limit = -1) {
var apiResultPromise;
var RESTClient = this;
var api = new SwaggerClients_1.LookupClient();
var DependsOnSet = ["", "", "", "", ""];
if (limit <= 0)
limit = sfRestClient._Options.SuggestionLimit;
if (Array.isArray(dependsOn)) {
$.each(dependsOn, function (i, v) { DependsOnSet[i] = v; });
}
else if (dependsOn) {
DependsOnSet[0] = dependsOn;
}
//DependsOnSet = [1, "a", 2.5, true, null, undefined, {key: "value"}];
DependsOnSet = DependsOnSet.map(String); // makes sure all the elements are strings
const suggestionGroupKey = `${lookupName}[${seedValue.sfHashCode()}]@${DependsOnSet[0].sfHashCode()}x${DependsOnSet[1].sfHashCode()}x${DependsOnSet[2].sfHashCode()}x${DependsOnSet[3].sfHashCode()}x`;
const TimeNow = Date.now();
if (sfRestClient.RecentSuggestionAsOf.has(suggestionGroupKey)) {
if (sfRestClient.RecentSuggestionAsOf.get(suggestionGroupKey) < TimeNow) {
// expired
sfRestClient.RecentSuggestionAsOf.delete(suggestionGroupKey);
}
else {
if (sfRestClient.RecentSuggestionResultMap.has(suggestionGroupKey)) {
apiResultPromise = sfRestClient.RecentSuggestionResultMap.get(suggestionGroupKey);
if (apiResultPromise)
return apiResultPromise;
}
}
}
//var apiResultPromise: Promise<Suggestion[] | null> = api.getSuggestions4(lookupName, "1", DependsOnSet[0], DependsOnSet[1], DependsOnSet[2], DependsOnSet[3],seedValue,limit);
var SuggestionContext = new _SwaggerClientExports.QueryFilters();
SuggestionContext.DependsOn = DependsOnSet;
SuggestionContext.MatchingSeed = seedValue;
SuggestionContext.ResultLimit = limit;
apiResultPromise = api.getSuggestionsWithContext(lookupName, RESTClient.GetPageDataContext(), SuggestionContext);
sfRestClient.RecentSuggestionAsOf.set(suggestionGroupKey, TimeNow + sfRestClient.suggestionCacheLifespan);
sfRestClient.RecentSuggestionResultMap.set(suggestionGroupKey, apiResultPromise);
return apiResultPromise;
}
/** returns GetPageContextValue("dsCacheKey") */
GetPageDataContext() {
let result = this.GetPageContextValue("dsCacheKey");
return result;
}
;
/** Returns list of recent documents from server as updated by PopDoc() calls */
GetRecentDocuments() {
return sfRestClient.RecentDocumentList;
}
/**
* Returns a View Model constructured for the result rows matching specified lookup context and filters
* @param lookupName
* @param filterValues default to {} can include NVPs and zero to 4 dependsOn strings
* @returns
*/
GetLookupResults(lookupName,
/**
* either a string or string array (up to 4 elements);
*/
filterValues) {
var apiResultPromise;
var RESTClient = this;
var api = new SwaggerClients_1.LookupClient();
var FinalViewModelPromise = new Promise((finalResolve) => {
apiResultPromise = api.getLookupResultAll(lookupName, RESTClient.GetPageDataContext(), filterValues);
apiResultPromise.then((lookupResultData) => {
var thisPart = PartStorageData.PartStorageDataLookupFactory(this, lookupName);
thisPart.CFGLoader().then(() => {
var ViewModelPromise = this._ConstructViewModel(thisPart, lookupResultData);
ViewModelPromise.then((r) => finalResolve(r));
});
});
});
return FinalViewModelPromise;
}
/** removes an exact match from the session DV cache */
ClearDV(displayName, keyValue,
/**
* either a string or string array (up to 4 elements)
*/
dependsOn) {
const requestData = this._getDVRequestString(displayName, keyValue, dependsOn);
const cacheKey = `GetDV:L${requestData.length}H${requestData.sfHashCode()}`;
try {
var result = sessionStorage.getItem(cacheKey);
if (typeof result === "string") {
sessionStorage.removeItem(cacheKey);
return true;
}
}
catch (err2) {
throw `ClearDV() cache error: ${err2.message}`;
}
return false;
}
/**
* Get Display Value using DV-Name and key value, with 0 to 4 dependencies.
* @param displayName the name of a display value rule (eg sfUser, RoleName, etc)
* @param keyValue the primary or most significant key
* @param dependsOn optional array of context values (multi-part key); 0 to 4 elements allowed; hint: use 'empty' for an empty value
* @param autoVary force bypass of cache
* @returns Promise for String
*/
GetDV(displayName, keyValue,
/**
* either a string or string array (up to 4 elements)
*/
dependsOn,
/**
* when true, a unique value is added to defeat cache
*/
autoVary) {
var apiResultPromise;
if (!keyValue || keyValue === this.EmptyKey)
return new Promise((resolve) => resolve(""));
if (displayName === "0")
return new Promise((resolve) => resolve(keyValue));
var requestData = this._getDVRequestString(displayName, keyValue, dependsOn);
if (autoVary)
requestData += `?${this._getVaryByQValue()}`;
const cacheKey = `GetDV:L${requestData.length}H${requestData.sfHashCode()}`;
try {
var result = sessionStorage.getItem(cacheKey);
if (!result) {
// continues below - must get value
}
if (typeof result === "string") {
var CacheResult = JSON.parse(result);
if ((Date.now() - CacheResult.w) < sfRestClient._Options.DVCacheLife) {
apiResultPromise = new Promise((resolve) => resolve(CacheResult.v));
return apiResultPromise;
}
}
// if falls through, we get a fresh value
}
catch (err2) {
new Error("GetDV() cache error: " + err2.message);
}
if (this._CachedDVRequests.has(cacheKey)) {
if (sfRestClient._Options.LogLevel >= LoggingLevels.VerboseDebug)
console.log(`GetDV(${displayName}:${keyValue}) reused pending request `);
return this._CachedDVRequests.get(cacheKey); // already requested, still pending or not doesn't matter
}
var RESTClient = this;
var api = new SwaggerClients_1.LookupClient();
var DependsOnSet = ["undefined", "undefined", "undefined", "undefined"];
if (Array.isArray(dependsOn)) {
$.each(dependsOn, function (i, v) {
if (v !== undefined)
DependsOnSet[i] = v;
});
}
else if (dependsOn)
DependsOnSet[0] = dependsOn;
//removed all "undefined" from the end, leaving at least one
for (let index = DependsOnSet.length - 1; index >= 1; index--) {
if (DependsOnSet[index - 1] != "undefined")
break;
if (DependsOnSet[index] == "undefined")
DependsOnSet.pop();
}
var DVFilters = new _SwaggerClientExports.DVRequest();
DVFilters.DVName = displayName;
DVFilters.MatchingValue = keyValue;
DVFilters.DependsOn = DependsOnSet;
DVFilters.RequestID = cacheKey;
//DVFilters.DataContext = "1";
var apiResultPromise = this._ThrottleDVRequests(api, DVFilters);
if (apiResultPromise) {
apiResultPromise.then((dvResult) => {
if (dvResult) {
sessionStorage.setItem(cacheKey, JSON.stringify({ v: dvResult, w: Date.now() }));
if (RESTClient._CachedDVRequests.has(cacheKey))
RESTClient._CachedDVRequests.delete(cacheKey);
}
});
}
this._CachedDVRequests.set(cacheKey, apiResultPromise);
return apiResultPromise;
}
/**
* Throttles and batches requests for display values (DVs) to improve performance.
* If there are pending DV requests, it adds the new request to a queue and sets up a timer to batch the requests.
* Otherwise, it directly calls the `getDisplayableValue` method on the `LookupClient` API.
*
* @param api - The `LookupClient` API instance to use for making DV requests.
* @param dvRequest - The DV request object containing the necessary parameters.
* @returns A Promise that resolves to the requested display value, or `null` if the request fails.
*/
_ThrottleDVRequests(api, dvRequest) {
var DVResultPromise;
var RESTClient = this;
if (sfRestClient._Options.LogLevel >= LoggingLevels.VerboseDebug)
console.log(`ThrottleDV(${dvRequest.DVName}:${dvRequest.MatchingValue}) with ${this._CachedDVRequests.size} pending `);
if (this._CachedDVRequests.size > 0) {
DVResultPromise = new Promise((resolvedTo) => {
if (!RESTClient._DVRequestTimerHandle) {
if (sfRestClient._Options.LogLevel >= LoggingLevels.VerboseDebug)
console.log(`ThrottleDV() created BatchDV `);
RESTClient._DVRequestTimerHandle = setTimeout(() => {
var thisGroup = [];
Object.assign(thisGroup, RESTClient._DVRequestQueue);
RESTClient._DVRequestQueue = [];
RESTClient._DVRequestTimerHandle = undefined;
if (sfRestClient._Options.LogLevel >= LoggingLevels.VerboseDebug)
console.log(`BatchDV() with ${thisGroup.length} in group `);
api.getDisplayValueCollection(thisGroup)
.then((dvList) => {
if (sfRestClient._Options.LogLevel >= LoggingLevels.VerboseDebug)
console.log(`BatchDV().THEN `, dvList);
if (dvList)
dvList.forEach(element => {
if (element.value) {
var DVDonePromise = this._DVThrottledResolvers.get(element.value);
if (DVDonePromise) {
DVDonePromise(element.label);
this._DVThrottledResolvers.delete(element.value);
}
else
console.warn("Batch DV could not find element", element);
}
else
console.warn("Batch DV could not find cacheId", element);
});