@memberjunction/core
Advanced tools
Comparing version 1.8.1 to 2.0.0
@@ -5,2 +5,3 @@ import { BaseInfo } from './baseInfo'; | ||
export declare class ApplicationSettingInfo extends BaseInfo { | ||
ID: string; | ||
ApplicationName: string; | ||
@@ -15,4 +16,5 @@ Name: string; | ||
export declare class ApplicationEntityInfo extends BaseInfo { | ||
ID: string; | ||
ApplicationName: string; | ||
EntityID: number; | ||
EntityID: string; | ||
Sequence: number; | ||
@@ -35,2 +37,3 @@ DefaultForNewUser: boolean; | ||
export declare class ApplicationInfo extends BaseInfo { | ||
ID: string; | ||
Name: string; | ||
@@ -37,0 +40,0 @@ Description: string; |
@@ -8,2 +8,3 @@ "use strict"; | ||
super(); | ||
this.ID = null; | ||
this.ApplicationName = null; | ||
@@ -28,2 +29,3 @@ this.Name = null; | ||
super(); | ||
this.ID = null; | ||
this.ApplicationName = null; | ||
@@ -56,2 +58,3 @@ this.EntityID = null; | ||
super(); | ||
this.ID = null; | ||
this.Name = null; | ||
@@ -58,0 +61,0 @@ this.Description = null; |
@@ -1,2 +0,2 @@ | ||
import { BaseSingleton } from "@memberjunction/global"; | ||
import { BaseSingleton, MJEvent } from "@memberjunction/global"; | ||
import { BehaviorSubject } from "rxjs"; | ||
@@ -6,2 +6,3 @@ import { UserInfo } from "./securityInfo"; | ||
import { BaseInfo } from "./baseInfo"; | ||
import { BaseEntityEvent } from "./baseEntity"; | ||
/** | ||
@@ -53,2 +54,9 @@ * Property configuration for the BaseEngine class to automatically load/set properties on the class. | ||
AddToObject?: boolean; | ||
/** | ||
* Optional, defaults to true. If set to false, AutoRefresh for this item will be disabled. By default, whenever a BaseEntity event is emitted for a save/delete, if the entity name | ||
* for this config matches the BaseEntity's entity name, the config will be refreshed. If this is set to false, that will not happen. NOTE: This is not a network notification mechanism, | ||
* it only works within the local tier, so for example within a browser application, that brower's engine sub-classes will be updated when changes are made to entities in that application | ||
* environment, and the same is true for MJAPI/Server based environments. If you need network based notification, additional infrastructure will be needed to implement that. | ||
*/ | ||
AutoRefresh?: boolean; | ||
constructor(init?: Partial<BaseEnginePropertyConfig>); | ||
@@ -69,3 +77,8 @@ } | ||
private _expirationTimers; | ||
private _entityEventSubjects; | ||
/** | ||
* Returns a COPY of the metadata configs array for the engine. This is a copy so you can't modify the original configs by modifying this array. | ||
*/ | ||
get Configs(): BaseEnginePropertyConfig[]; | ||
/** | ||
* Configures the engine by loading metadata from the database. | ||
@@ -82,3 +95,48 @@ */ | ||
protected Load(configs: Partial<BaseEnginePropertyConfig>[], forceRefresh?: boolean, contextUser?: UserInfo): Promise<void>; | ||
private _eventListener; | ||
/** | ||
* This method is responsible for registering for MJGlobal events and listening for BaseEntity events where those | ||
* BaseEntity are related to the engine's configuration metadata. The idea is to auto-refresh the releated configs | ||
* when the BaseEntity is updated. | ||
*/ | ||
protected SetupGlobalEventListener(): Promise<boolean>; | ||
/** | ||
* Subclasses of BaseEngine can override this method to handle individual MJGlobal events. This is typically done to optimize | ||
* the way refreshes are done when a BaseEntity is updated. If you are interested in only BaseEntity events, override | ||
* the HandleIndividualBaseEntityEvent method instead as this method primarily serves to filter all the events we get from MJGlobal | ||
* and only pass on BaseEntity events to HandleIndividualBaseEntityEvent. | ||
* @param event | ||
*/ | ||
protected HandleIndividualEvent(event: MJEvent): Promise<boolean>; | ||
/** | ||
* This method handles the individual base entity event and just checks to see if it is a delete or save event and if so, debounces it. | ||
* Override this method if you want to have a different handling for the filtering of events that are debounced or if you don't want to debounce | ||
* at all you can do that in an override of this method. | ||
* @param event | ||
*/ | ||
protected HandleIndividualBaseEntityEvent(event: BaseEntityEvent): Promise<boolean>; | ||
/** | ||
* This method handles the debouncing process, by default using the EntityEventDebounceTime property to set the debounce time. Debouncing is | ||
* done on a per-entity basis, meaning that if the debounce time passes for a specific entity name, the event will be processed. This is done to | ||
* prevent multiple events from being processed in quick succession for a single entity which would cause a lot of wasted processing. | ||
* | ||
* Override this method if you want to change how debouncing time such as having variable debounce times per-entity, etc. | ||
* @param event | ||
* @returns | ||
*/ | ||
protected DebounceIndividualBaseEntityEvent(event: BaseEntityEvent): Promise<boolean>; | ||
private _entityEventDebounceTime; | ||
/** | ||
* Overridable property to set the debounce time for entity events. Default is 5000 milliseconds (5 seconds). | ||
*/ | ||
protected get EntityEventDebounceTime(): number; | ||
/** | ||
* This method does the actual work of processing the entity event. It is not directly called from the event handler because we want to first debounce the events | ||
* which also introduces a delay which is usually desirable so that our processing is typically outside of the scope of any transaction processing that would have | ||
* originated the event. | ||
* | ||
* This is the best method to override if you want to change the actual processing of an entity event but do NOT want to modify the debouncing behavior. | ||
*/ | ||
protected ProcessEntityEvent(event: BaseEntityEvent): Promise<void>; | ||
/** | ||
* Utility method to upgrade an object to a BaseEnginePropertyConfig object. | ||
@@ -85,0 +143,0 @@ * @param obj |
@@ -6,2 +6,3 @@ "use strict"; | ||
const rxjs_1 = require("rxjs"); | ||
const operators_1 = require("rxjs/operators"); | ||
const runView_1 = require("../views/runView"); | ||
@@ -12,2 +13,3 @@ const logging_1 = require("./logging"); | ||
const baseInfo_1 = require("./baseInfo"); | ||
const baseEntity_1 = require("./baseEntity"); | ||
/** | ||
@@ -29,2 +31,9 @@ * Property configuration for the BaseEngine class to automatically load/set properties on the class. | ||
this.DatasetResultHandling = 'single_property'; | ||
/** | ||
* Optional, defaults to true. If set to false, AutoRefresh for this item will be disabled. By default, whenever a BaseEntity event is emitted for a save/delete, if the entity name | ||
* for this config matches the BaseEntity's entity name, the config will be refreshed. If this is set to false, that will not happen. NOTE: This is not a network notification mechanism, | ||
* it only works within the local tier, so for example within a browser application, that brower's engine sub-classes will be updated when changes are made to entities in that application | ||
* environment, and the same is true for MJAPI/Server based environments. If you need network based notification, additional infrastructure will be needed to implement that. | ||
*/ | ||
this.AutoRefresh = true; | ||
// now copy the values from init to this object | ||
@@ -50,4 +59,13 @@ if (init) | ||
this._expirationTimers = new Map(); | ||
this._entityEventSubjects = new Map(); | ||
this._entityEventDebounceTime = 5000; // Default debounce time in milliseconds (5 seconds) | ||
} | ||
/** | ||
* Returns a COPY of the metadata configs array for the engine. This is a copy so you can't modify the original configs by modifying this array. | ||
*/ | ||
get Configs() { | ||
// do a full deep copy of the array to ensure no tampering | ||
return JSON.parse(JSON.stringify(this._metadataConfigs)); | ||
} | ||
/** | ||
* This method should be called by sub-classes to load up their specific metadata requirements. For more complex metadata | ||
@@ -78,2 +96,3 @@ * loading or for post-processing of metadata loading done here, overide the AdditionalLoading method to add your logic. | ||
await this.AdditionalLoading(contextUser); // Call the additional loading method | ||
await this.SetupGlobalEventListener(); | ||
this._loaded = true; | ||
@@ -90,2 +109,116 @@ } | ||
/** | ||
* This method is responsible for registering for MJGlobal events and listening for BaseEntity events where those | ||
* BaseEntity are related to the engine's configuration metadata. The idea is to auto-refresh the releated configs | ||
* when the BaseEntity is updated. | ||
*/ | ||
async SetupGlobalEventListener() { | ||
try { | ||
if (!this._eventListener) { | ||
this._eventListener = global_1.MJGlobal.Instance.GetEventListener(false); | ||
this._eventListener.subscribe(async (event) => { | ||
await this.HandleIndividualEvent(event); | ||
}); | ||
} | ||
} | ||
catch (e) { | ||
(0, logging_1.LogError)(e); | ||
return false; | ||
} | ||
} | ||
/** | ||
* Subclasses of BaseEngine can override this method to handle individual MJGlobal events. This is typically done to optimize | ||
* the way refreshes are done when a BaseEntity is updated. If you are interested in only BaseEntity events, override | ||
* the HandleIndividualBaseEntityEvent method instead as this method primarily serves to filter all the events we get from MJGlobal | ||
* and only pass on BaseEntity events to HandleIndividualBaseEntityEvent. | ||
* @param event | ||
*/ | ||
async HandleIndividualEvent(event) { | ||
// base class algo is simple - we just check to see if the event's entity name matches any of our entity names | ||
// and if so, we refresh the data by calling | ||
if (event.event === global_1.MJEventType.ComponentEvent && event.eventCode === baseEntity_1.BaseEntity.BaseEventCode) { | ||
// we have an entity event | ||
const baseEntityEvent = event.args; | ||
return await this.HandleIndividualBaseEntityEvent(baseEntityEvent); | ||
} | ||
} | ||
/** | ||
* This method handles the individual base entity event and just checks to see if it is a delete or save event and if so, debounces it. | ||
* Override this method if you want to have a different handling for the filtering of events that are debounced or if you don't want to debounce | ||
* at all you can do that in an override of this method. | ||
* @param event | ||
*/ | ||
async HandleIndividualBaseEntityEvent(event) { | ||
try { | ||
if (event.type === 'delete' || event.type === 'save') { | ||
const eName = event.baseEntity.EntityInfo.Name.toLowerCase().trim(); | ||
const matchingAutoRefreshConfig = this.Configs.some(c => c.AutoRefresh && c.EntityName.trim().toLowerCase() === eName); | ||
if (matchingAutoRefreshConfig) | ||
return this.DebounceIndividualBaseEntityEvent(event); | ||
} | ||
return true; | ||
} | ||
catch (e) { | ||
(0, logging_1.LogError)(e); | ||
return false; | ||
} | ||
} | ||
/** | ||
* This method handles the debouncing process, by default using the EntityEventDebounceTime property to set the debounce time. Debouncing is | ||
* done on a per-entity basis, meaning that if the debounce time passes for a specific entity name, the event will be processed. This is done to | ||
* prevent multiple events from being processed in quick succession for a single entity which would cause a lot of wasted processing. | ||
* | ||
* Override this method if you want to change how debouncing time such as having variable debounce times per-entity, etc. | ||
* @param event | ||
* @returns | ||
*/ | ||
async DebounceIndividualBaseEntityEvent(event) { | ||
try { | ||
const entityName = event.baseEntity.EntityInfo.Name.toLowerCase().trim(); | ||
if (!this._entityEventSubjects.has(entityName)) { | ||
const subject = new rxjs_1.Subject(); | ||
subject.pipe((0, operators_1.debounceTime)(this.EntityEventDebounceTime)).subscribe(async (e) => { | ||
await this.ProcessEntityEvent(e); | ||
}); | ||
this._entityEventSubjects.set(entityName, subject); | ||
} | ||
this._entityEventSubjects.get(entityName).next(event); | ||
return true; | ||
} | ||
catch (e) { | ||
(0, logging_1.LogError)(e); | ||
return false; | ||
} | ||
} | ||
/** | ||
* Overridable property to set the debounce time for entity events. Default is 5000 milliseconds (5 seconds). | ||
*/ | ||
get EntityEventDebounceTime() { | ||
return this._entityEventDebounceTime; | ||
} | ||
/** | ||
* This method does the actual work of processing the entity event. It is not directly called from the event handler because we want to first debounce the events | ||
* which also introduces a delay which is usually desirable so that our processing is typically outside of the scope of any transaction processing that would have | ||
* originated the event. | ||
* | ||
* This is the best method to override if you want to change the actual processing of an entity event but do NOT want to modify the debouncing behavior. | ||
*/ | ||
async ProcessEntityEvent(event) { | ||
const entityName = event.baseEntity.EntityInfo.Name.toLowerCase().trim(); | ||
let refreshCount = 0; | ||
for (const config of this.Configs) { | ||
if (config.AutoRefresh && config.EntityName.trim().toLowerCase() === entityName) { | ||
(0, logging_1.LogStatus)(`>>> Refreshing metadata for ${config.PropertyName} due to BaseEntity ${event.type} event for: ${event.baseEntity.EntityInfo.Name}, pkey: ${event.baseEntity.PrimaryKey.ToString()}`); | ||
await this.LoadSingleConfig(config, this._contextUser); | ||
refreshCount++; | ||
} | ||
} | ||
if (refreshCount > 0) { | ||
// we need to call AdditionalLoading here - because in many cases engine sub-classes do various kinds of data mashups | ||
// after we have loaded - for example the TemplateEngine takes the TemplateContents and TemplateParams and stuffs them | ||
// into arrays in each template to make it easier to get Params/Contents for each Template. Such operations are common | ||
// and need to be done after the initial load and after any refreshes. | ||
await this.AdditionalLoading(this._contextUser); | ||
} | ||
} | ||
/** | ||
* Utility method to upgrade an object to a BaseEnginePropertyConfig object. | ||
@@ -92,0 +225,0 @@ * @param obj |
@@ -78,4 +78,4 @@ import { EntityFieldInfo, EntityInfo, EntityFieldTSType, EntityPermissionType, RecordChange, ValidationResult, EntityRelationshipInfo } from './entityInfo'; | ||
name: string; | ||
actionId: number; | ||
modelId: number; | ||
actionId: string; | ||
modelId: string; | ||
systemPrompt: string; | ||
@@ -134,2 +134,3 @@ userMessage: string; | ||
* Event type that is used to raise events and provided structured callbacks for any caller that is interested in registering for events. | ||
* This type is also used for whenever a BaseEntity instance raises an event with MJGlobal. | ||
*/ | ||
@@ -176,3 +177,9 @@ export declare class BaseEntityEvent { | ||
RaiseReadyForTransaction(): void; | ||
private static _baseEventCode; | ||
/** | ||
* When a BaseEntity class raises an event with MJGlobal, the eventCode property is set to this value. This is used to identify events that are raised by BaseEntity objects. | ||
* Any MJGlobal event that is raised by a BaseEntity class will use a BaseEntityEvent type as the args parameter | ||
*/ | ||
static get BaseEventCode(): string; | ||
/** | ||
* Used for raising events within the BaseEntity and can be used by sub-classes to raise events that are specific to the entity. | ||
@@ -179,0 +186,0 @@ */ |
@@ -253,2 +253,3 @@ "use strict"; | ||
* Event type that is used to raise events and provided structured callbacks for any caller that is interested in registering for events. | ||
* This type is also used for whenever a BaseEntity instance raises an event with MJGlobal. | ||
*/ | ||
@@ -302,6 +303,28 @@ class BaseEntityEvent { | ||
/** | ||
* When a BaseEntity class raises an event with MJGlobal, the eventCode property is set to this value. This is used to identify events that are raised by BaseEntity objects. | ||
* Any MJGlobal event that is raised by a BaseEntity class will use a BaseEntityEvent type as the args parameter | ||
*/ | ||
static get BaseEventCode() { | ||
return BaseEntity._baseEventCode; | ||
} | ||
/** | ||
* Used for raising events within the BaseEntity and can be used by sub-classes to raise events that are specific to the entity. | ||
*/ | ||
RaiseEvent(type, payload) { | ||
// this is the local event handler that is specific to THIS instance of the entity object | ||
this._eventSubject.next({ type: type, payload: payload, baseEntity: this }); | ||
// this next call is to MJGlobal to let everyone who cares knows that we had an event on an entity object | ||
// at the moment we only broadcast save/delete not the others | ||
if (type === 'save' || type === 'delete') { | ||
const event = new BaseEntityEvent(); | ||
event.baseEntity = this; | ||
event.payload = null; | ||
event.type = type; | ||
global_1.MJGlobal.Instance.RaiseEvent({ | ||
component: this, | ||
event: global_1.MJEventType.ComponentEvent, | ||
eventCode: BaseEntity.BaseEventCode, | ||
args: event | ||
}); | ||
} | ||
} | ||
@@ -785,3 +808,3 @@ /** | ||
if (!valResult || !valResult.IsValid) | ||
throw new Error(`Invalid CompositeKey passed to BaseEntity.Load(${this.EntityInfo.Name})`); | ||
throw new Error(`Invalid CompositeKey passed to BaseEntity.Load(${this.EntityInfo.Name}): ${valResult.ErrorMessage}`); | ||
this.CheckPermissions(entityInfo_1.EntityPermissionType.Read, true); // this will throw an error and exit out if we don't have permission | ||
@@ -942,3 +965,4 @@ if (!this.IsSaved) { | ||
exports.BaseEntity = BaseEntity; | ||
BaseEntity._baseEventCode = 'BaseEntityEvent'; | ||
BaseEntity._globalProviderKey = 'MJ_BaseEntityProvider'; | ||
//# sourceMappingURL=baseEntity.js.map |
@@ -5,5 +5,6 @@ export declare abstract class BaseInfo { | ||
*/ | ||
ID: number; | ||
ID: any; | ||
protected copyInitData(initData: any): void; | ||
constructor(initData?: any); | ||
} | ||
//# sourceMappingURL=baseInfo.d.ts.map |
@@ -6,8 +6,2 @@ "use strict"; | ||
class BaseInfo { | ||
constructor() { | ||
/** | ||
* Primary Key | ||
*/ | ||
this.ID = null; | ||
} | ||
copyInitData(initData) { | ||
@@ -34,4 +28,13 @@ if (initData) { | ||
} | ||
constructor(initData = null) { | ||
/** | ||
* Primary Key | ||
*/ | ||
this.ID = null; | ||
if (initData) { | ||
this.copyInitData(initData); | ||
} | ||
} | ||
} | ||
exports.BaseInfo = BaseInfo; | ||
//# sourceMappingURL=baseInfo.js.map |
@@ -140,2 +140,3 @@ import { EntityField } from "./baseEntity"; | ||
export declare class CompositeKey extends FieldValueCollection { | ||
constructor(keyValuePairs?: KeyValuePair[]); | ||
/** | ||
@@ -142,0 +143,0 @@ * Utility function to compare this composite key to another |
@@ -264,2 +264,5 @@ "use strict"; | ||
class CompositeKey extends FieldValueCollection { | ||
constructor(keyValuePairs) { | ||
super(keyValuePairs); | ||
} | ||
/** | ||
@@ -266,0 +269,0 @@ * Utility function to compare this composite key to another |
@@ -19,3 +19,4 @@ import { BaseInfo } from "./baseInfo"; | ||
export declare class RecordChange extends BaseInfo { | ||
EntityID: number; | ||
ID: string; | ||
EntityID: string; | ||
RecordID: any; | ||
@@ -37,5 +38,6 @@ ChangedAt: Date; | ||
export declare class EntityRelationshipInfo extends BaseInfo { | ||
EntityID: number; | ||
ID: string; | ||
EntityID: string; | ||
Sequence: number; | ||
RelatedEntityID: number; | ||
RelatedEntityID: string; | ||
BundleInAPI: boolean; | ||
@@ -54,4 +56,4 @@ IncludeInParentAllQuery: boolean; | ||
DisplayIcon: string; | ||
DisplayUserViewGUID: string; | ||
DisplayComponentID: number; | ||
DisplayUserViewID: string; | ||
DisplayComponentID: string; | ||
DisplayComponentConfiguration: string; | ||
@@ -70,3 +72,2 @@ __mj_CreatedAt: Date; | ||
DisplayUserViewName: string; | ||
DisplayUserViewID: number; | ||
DisplayComponent: string; | ||
@@ -83,2 +84,3 @@ constructor(initData: any); | ||
export declare class EntityUserPermissionInfo { | ||
ID: string; | ||
Entity: EntityInfo; | ||
@@ -92,4 +94,5 @@ User: UserInfo; | ||
export declare class EntityPermissionInfo extends BaseInfo { | ||
EntityID: number; | ||
RoleName: string; | ||
ID: string; | ||
EntityID: string; | ||
RoleID: string; | ||
CanCreate: boolean; | ||
@@ -99,9 +102,10 @@ CanRead: boolean; | ||
CanDelete: boolean; | ||
ReadRLSFilterID: number; | ||
CreateRLSFilterID: number; | ||
UpdateRLSFilterID: number; | ||
DeleteRLSFilterID: number; | ||
ReadRLSFilterID: string; | ||
CreateRLSFilterID: string; | ||
UpdateRLSFilterID: string; | ||
DeleteRLSFilterID: string; | ||
__mj_CreatedAt: Date; | ||
__mj_UpdatedAt: Date; | ||
Entity: string; | ||
Role: string; | ||
RoleSQLName: string; | ||
@@ -141,4 +145,4 @@ ReadRLSFilter: string; | ||
export declare class EntityFieldValueInfo extends BaseInfo { | ||
EntityID: number; | ||
EntityFieldName: string; | ||
ID: string; | ||
EntityFieldID: string; | ||
Sequence: number; | ||
@@ -162,6 +166,7 @@ Value: string; | ||
export declare class EntityFieldInfo extends BaseInfo { | ||
ID: string; | ||
/** | ||
* Foreign key to the Entities entity. | ||
*/ | ||
EntityID: number; | ||
EntityID: string; | ||
/** | ||
@@ -208,3 +213,3 @@ * The sequence of the field within the entity, typically the intended display order | ||
IsNameField: boolean; | ||
RelatedEntityID: number; | ||
RelatedEntityID: string; | ||
RelatedEntityFieldName: string; | ||
@@ -214,2 +219,3 @@ IncludeRelatedEntityNameFieldInBaseView: boolean; | ||
RelatedEntityDisplayType: 'Search' | 'Dropdown'; | ||
EntityIDFieldName: string; | ||
__mj_CreatedAt: Date; | ||
@@ -264,4 +270,4 @@ __mj_UpdatedAt: Date; | ||
/** | ||
* Helper method that returns true if the field is one of the special reserved MJ date fields for tracking CreatedAt and UpdatedAt timestamps. This is only used when the | ||
* entity has TrackRecordChanges=1 | ||
* Helper method that returns true if the field is one of the special reserved MJ date fields for tracking CreatedAt and UpdatedAt timestamps as well as the DeletedAt timestamp used for entities that | ||
* have DeleteType=Soft. This is only used when the entity has TrackRecordChanges=1 or for entities where DeleteType=Soft | ||
*/ | ||
@@ -278,6 +284,9 @@ get IsSpecialDateField(): boolean; | ||
/** | ||
* Returns true if the field is the DeletedAt field, a special field that is used to track the deletion date of a record. This is only used when the entity has DeleteType=Soft | ||
*/ | ||
get IsDeletedAtField(): boolean; | ||
/** | ||
* Returns true if the field is a "special" field (see list below) and is handled inside the DB layer and should be ignored in validation by the BaseEntity architecture | ||
* Also, we skip validation if we have a field that is: | ||
* - the primary key | ||
* - a uniqueidentifier | ||
* - an autoincrement field | ||
@@ -309,2 +318,3 @@ * - the field is virtual | ||
export declare class EntityDocumentTypeInfo extends BaseInfo { | ||
ID: string; | ||
Name: string; | ||
@@ -320,3 +330,4 @@ Description: string; | ||
export declare class EntitySettingInfo extends BaseInfo { | ||
EntityID: number; | ||
ID: string; | ||
EntityID: string; | ||
Name: string; | ||
@@ -333,6 +344,7 @@ Value: string; | ||
export declare class EntityInfo extends BaseInfo { | ||
ID: string; | ||
/** | ||
* Reserved for future use | ||
*/ | ||
ParentID: number; | ||
ParentID: string; | ||
/** | ||
@@ -373,2 +385,6 @@ * Unique name of the entity | ||
CascadeDeletes: boolean; | ||
DeleteType: 'Hard' | 'Soft'; | ||
AllowRecordMerge: boolean; | ||
spMatch: string; | ||
RelationshipDefaultDisplayType: 'Search' | 'Dropdown'; | ||
UserFormGenerated: boolean; | ||
@@ -416,2 +432,3 @@ EntityObjectSubclassName: string; | ||
private static __updatedAtFieldName; | ||
private static __deletedAtFieldName; | ||
/** | ||
@@ -426,2 +443,6 @@ * Returns the name of the special reserved field that is used to store the CreatedAt timestamp across all of MJ. This is only used when an entity has TrackRecordChanges turned on | ||
/** | ||
* Returns the name of the special reserved field that is used to store the DeletedAt timestamp across all of MJ. This is only used when an entity has DeleteType=Soft | ||
*/ | ||
static get DeletedAtFieldName(): string; | ||
/** | ||
* @returns The BaseTable but with spaces inbetween capital letters | ||
@@ -535,5 +556,5 @@ * */ | ||
/** | ||
* The value of the primary key field in the parent record. At present, MemberJunction supports composite(multi-field) primary keys. However, foreign keys only support links to single-valued primary keys in their linked entity. | ||
* The value of the primary key field in the parent record. MemberJunction supports composite(multi-field) primary keys. However, foreign keys only support links to single-valued primary keys in their linked entity. | ||
*/ | ||
CompositeKey: CompositeKey; | ||
PrimaryKey: CompositeKey; | ||
} | ||
@@ -600,3 +621,3 @@ /** | ||
*/ | ||
RecordMergeLogID: number | null; | ||
RecordMergeLogID: string | null; | ||
/** | ||
@@ -603,0 +624,0 @@ * The details of the merge operation, including the status of each record that was merged |
@@ -31,2 +31,3 @@ "use strict"; | ||
super(); | ||
this.ID = null; | ||
this.EntityID = null; | ||
@@ -50,2 +51,3 @@ this.RecordID = null; | ||
super(); | ||
this.ID = null; | ||
this.EntityID = null; | ||
@@ -67,3 +69,3 @@ this.Sequence = null; | ||
this.DisplayIcon = null; | ||
this.DisplayUserViewGUID = null; | ||
this.DisplayUserViewID = null; | ||
this.DisplayComponentID = null; | ||
@@ -84,3 +86,2 @@ this.DisplayComponentConfiguration = null; | ||
this.DisplayUserViewName = null; | ||
this.DisplayUserViewID = null; | ||
this.DisplayComponent = null; | ||
@@ -98,2 +99,5 @@ this.copyInitData(initData); | ||
class EntityUserPermissionInfo { | ||
constructor() { | ||
this.ID = null; | ||
} | ||
} | ||
@@ -115,3 +119,3 @@ exports.EntityUserPermissionInfo = EntityUserPermissionInfo; | ||
RLSFilter(type) { | ||
let fID = 0; | ||
let fID = ""; | ||
switch (type) { | ||
@@ -131,3 +135,3 @@ case exports.EntityPermissionType.Read: | ||
} | ||
if (fID > 0) | ||
if (fID && fID.length > 0) | ||
return metadata_1.Metadata.Provider.RowLevelSecurityFilters.find(f => f.ID === fID); | ||
@@ -137,4 +141,5 @@ } | ||
super(); | ||
this.ID = null; | ||
this.EntityID = null; | ||
this.RoleName = null; | ||
this.RoleID = null; | ||
this.CanCreate = null; | ||
@@ -152,2 +157,3 @@ this.CanRead = null; | ||
this.Entity = null; | ||
this.Role = null; | ||
this.RoleSQLName = null; | ||
@@ -183,4 +189,4 @@ this.ReadRLSFilter = null; | ||
super(); | ||
this.EntityID = null; | ||
this.EntityFieldName = null; | ||
this.ID = null; | ||
this.EntityFieldID = null; // EntityFieldID is a uniqueidentifier column | ||
this.Sequence = null; | ||
@@ -305,11 +311,10 @@ this.Value = null; | ||
this.IsPrimaryKey || | ||
this.Type.toLowerCase() === 'uniqueidentifier' || | ||
this.IsSpecialDateField; | ||
} | ||
/** | ||
* Helper method that returns true if the field is one of the special reserved MJ date fields for tracking CreatedAt and UpdatedAt timestamps. This is only used when the | ||
* entity has TrackRecordChanges=1 | ||
* Helper method that returns true if the field is one of the special reserved MJ date fields for tracking CreatedAt and UpdatedAt timestamps as well as the DeletedAt timestamp used for entities that | ||
* have DeleteType=Soft. This is only used when the entity has TrackRecordChanges=1 or for entities where DeleteType=Soft | ||
*/ | ||
get IsSpecialDateField() { | ||
return this.IsCreatedAtField || this.IsUpdatedAtField; | ||
return this.IsCreatedAtField || this.IsUpdatedAtField || this.IsDeletedAtField; | ||
} | ||
@@ -329,6 +334,11 @@ /** | ||
/** | ||
* Returns true if the field is the DeletedAt field, a special field that is used to track the deletion date of a record. This is only used when the entity has DeleteType=Soft | ||
*/ | ||
get IsDeletedAtField() { | ||
return this.Name.trim().toLowerCase() === EntityInfo.DeletedAtFieldName.trim().toLowerCase(); | ||
} | ||
/** | ||
* Returns true if the field is a "special" field (see list below) and is handled inside the DB layer and should be ignored in validation by the BaseEntity architecture | ||
* Also, we skip validation if we have a field that is: | ||
* - the primary key | ||
* - a uniqueidentifier | ||
* - an autoincrement field | ||
@@ -343,3 +353,2 @@ * - the field is virtual | ||
this.IsPrimaryKey || | ||
this.Type.trim().toLowerCase() === 'uniqueidentifier' || | ||
this.AutoIncrement === true || | ||
@@ -369,2 +378,3 @@ this.IsVirtual === true || | ||
super(); | ||
this.ID = null; | ||
/** | ||
@@ -420,2 +430,3 @@ * Foreign key to the Entities entity. | ||
this.RelatedEntityDisplayType = null; | ||
this.EntityIDFieldName = null; | ||
this.__mj_CreatedAt = null; | ||
@@ -462,2 +473,3 @@ this.__mj_UpdatedAt = null; | ||
super(); | ||
this.ID = null; | ||
this.Name = null; | ||
@@ -477,2 +489,3 @@ this.Description = null; | ||
super(); | ||
this.ID = null; | ||
this.EntityID = null; | ||
@@ -509,3 +522,3 @@ this.Name = null; | ||
get ForeignKeys() { | ||
return this.Fields.filter((f) => f.RelatedEntityID > 0); | ||
return this.Fields.filter((f) => f.RelatedEntityID && f.RelatedEntityID.length > 0); | ||
} | ||
@@ -537,2 +550,8 @@ get Fields() { | ||
/** | ||
* Returns the name of the special reserved field that is used to store the DeletedAt timestamp across all of MJ. This is only used when an entity has DeleteType=Soft | ||
*/ | ||
static get DeletedAtFieldName() { | ||
return EntityInfo.__deletedAtFieldName; | ||
} | ||
/** | ||
* @returns The BaseTable but with spaces inbetween capital letters | ||
@@ -566,3 +585,3 @@ * */ | ||
const ep = this.Permissions[j]; | ||
const roleMatch = user.UserRoles.find((r) => r.RoleName.trim().toLowerCase() === ep.RoleName.trim().toLowerCase()); | ||
const roleMatch = user.UserRoles.find((r) => r.RoleID === ep.RoleID); | ||
if (roleMatch) // user has this role | ||
@@ -602,3 +621,3 @@ permissionList.push(ep); | ||
const ep = this.Permissions[j]; | ||
const roleMatch = user.UserRoles.find((r) => r.RoleName.trim().toLowerCase() === ep.RoleName.trim().toLowerCase()); | ||
const roleMatch = user.UserRoles.find((r) => r.RoleID === ep.RoleID); | ||
if (roleMatch) { // user has this role | ||
@@ -637,3 +656,3 @@ switch (type) { | ||
const ep = this.Permissions[j]; | ||
const roleMatch = user.UserRoles.find((r) => r.RoleName.trim().toLowerCase() === ep.RoleName.trim().toLowerCase()); | ||
const roleMatch = user.UserRoles.find((r) => r.RoleID === ep.RoleID); | ||
if (roleMatch) { // user has this role | ||
@@ -710,3 +729,4 @@ let matchObject = null; | ||
keyValue = firstKey.Value; | ||
quotes = firstKey.NeedsQuotes ? "'" : ''; | ||
//When creating a new record, the keyValue is null and the quotes are not needed | ||
quotes = keyValue && firstKey.NeedsQuotes ? "'" : ''; | ||
} | ||
@@ -723,5 +743,5 @@ if (relationship.Type.trim().toLowerCase() === 'one to many') { | ||
params.ExtraFilter = `(${params.ExtraFilter}) AND (${filter})`; // caller provided their own filter, so AND it in with the relationship filter we have here | ||
if (relationship.DisplayUserViewGUID && relationship.DisplayUserViewGUID.length > 0) { | ||
if (relationship.DisplayUserViewID && relationship.DisplayUserViewID.length > 0) { | ||
// we have been given a specific view to run, use it | ||
params.ViewID = relationship.DisplayUserViewID; // virtual field - the durable key is the GUID, but the base view for entityrelationship brings in view name and ID | ||
params.ViewID = relationship.DisplayUserViewID; | ||
} | ||
@@ -753,2 +773,3 @@ else { | ||
super(); | ||
this.ID = null; | ||
/** | ||
@@ -793,2 +814,6 @@ * Reserved for future use | ||
this.CascadeDeletes = null; | ||
this.DeleteType = 'Hard'; | ||
this.AllowRecordMerge = null; | ||
this.spMatch = null; | ||
this.RelationshipDefaultDisplayType = null; | ||
this.UserFormGenerated = null; | ||
@@ -907,2 +932,3 @@ this.EntityObjectSubclassName = null; | ||
EntityInfo.__updatedAtFieldName = '__mj_UpdatedAt'; | ||
EntityInfo.__deletedAtFieldName = '__mj_DeletedAt'; | ||
exports.ValidationErrorType = { | ||
@@ -909,0 +935,0 @@ Failure: 'Failure', |
@@ -12,2 +12,3 @@ import { BaseEntity } from "./baseEntity"; | ||
import { CompositeKey } from "./compositeKey"; | ||
import { ExplorerNavigationItem } from "./explorerNavigationItem"; | ||
export declare class ProviderConfigDataBase { | ||
@@ -25,5 +26,6 @@ private _includeSchemas; | ||
export declare class MetadataInfo { | ||
ID: number; | ||
ID: string; | ||
Type: string; | ||
UpdatedAt: Date; | ||
RowCount: number; | ||
} | ||
@@ -42,7 +44,7 @@ export declare const ProviderType: { | ||
**/ | ||
EntityID: number; | ||
EntityID: string; | ||
/** | ||
* The ID of the List entity to use | ||
**/ | ||
ListID: number; | ||
ListID: string; | ||
/** | ||
@@ -56,3 +58,3 @@ * The Primary Key values of each record | ||
**/ | ||
EntityDocumentID?: number; | ||
EntityDocumentID?: string; | ||
/** | ||
@@ -68,6 +70,7 @@ * The minimum score in order to consider a record a potential duplicate | ||
export declare class PotentialDuplicateResult { | ||
EntityID: number; | ||
constructor(); | ||
EntityID: string; | ||
RecordCompositeKey: CompositeKey; | ||
Duplicates: PotentialDuplicate[]; | ||
DuplicateRunDetailMatchRecordIDs: number[]; | ||
DuplicateRunDetailMatchRecordIDs: string[]; | ||
} | ||
@@ -157,2 +160,4 @@ export declare class PotentialDuplicateResponse { | ||
get Libraries(): LibraryInfo[]; | ||
get VisibleExplorerNavigationItems(): ExplorerNavigationItem[]; | ||
get AllExplorerNavigationItems(): ExplorerNavigationItem[]; | ||
get LatestRemoteMetadata(): MetadataInfo[]; | ||
@@ -212,4 +217,4 @@ get LatestLocalMetadata(): MetadataInfo[]; | ||
GetEntityRecordNames(info: EntityRecordNameInput[]): Promise<EntityRecordNameResult[]>; | ||
GetRecordFavoriteStatus(userId: number, entityName: string, CompositeKey: CompositeKey): Promise<boolean>; | ||
SetRecordFavoriteStatus(userId: number, entityName: string, CompositeKey: CompositeKey, isFavorite: boolean, contextUser: UserInfo): Promise<void>; | ||
GetRecordFavoriteStatus(userId: string, entityName: string, CompositeKey: CompositeKey): Promise<boolean>; | ||
SetRecordFavoriteStatus(userId: string, entityName: string, CompositeKey: CompositeKey, isFavorite: boolean, contextUser: UserInfo): Promise<void>; | ||
CreateTransactionGroup(): Promise<TransactionGroupBase>; | ||
@@ -303,3 +308,3 @@ Refresh(): Promise<boolean>; | ||
*/ | ||
UserViewRunID?: number; | ||
UserViewRunID?: string; | ||
/** | ||
@@ -327,5 +332,6 @@ * Number of rows returned in the Results[] array | ||
RunView<T = any>(params: RunViewParams, contextUser?: UserInfo): Promise<RunViewResult<T>>; | ||
RunViews<T = any>(params: RunViewParams[], contextUser?: UserInfo): Promise<RunViewResult<T>[]>; | ||
} | ||
export type RunQueryResult = { | ||
QueryID: number; | ||
QueryID: string; | ||
Success: boolean; | ||
@@ -342,3 +348,3 @@ Results: any[]; | ||
export type RunReportResult = { | ||
ReportID: number; | ||
ReportID: string; | ||
Success: boolean; | ||
@@ -355,3 +361,3 @@ Results: any[]; | ||
export type DatasetResultType = { | ||
DatasetID: number; | ||
DatasetID: string; | ||
DatasetName: string; | ||
@@ -366,3 +372,3 @@ Success: boolean; | ||
EntityName: string; | ||
EntityID: number; | ||
EntityID: string; | ||
Results: any[]; | ||
@@ -375,3 +381,3 @@ }; | ||
export type DatasetStatusResultType = { | ||
DatasetID: number; | ||
DatasetID: string; | ||
DatasetName: string; | ||
@@ -385,5 +391,6 @@ Success: boolean; | ||
EntityName: string; | ||
EntityID: number; | ||
EntityID: string; | ||
UpdateDate: Date; | ||
RowCount: number; | ||
}; | ||
//# sourceMappingURL=interfaces.d.ts.map |
@@ -45,2 +45,7 @@ "use strict"; | ||
class PotentialDuplicateResult { | ||
constructor() { | ||
this.RecordCompositeKey = new compositeKey_1.CompositeKey(); | ||
this.Duplicates = []; | ||
this.DuplicateRunDetailMatchRecordIDs = []; | ||
} | ||
} | ||
@@ -47,0 +52,0 @@ exports.PotentialDuplicateResult = PotentialDuplicateResult; |
@@ -6,2 +6,3 @@ import { BaseInfo } from "./baseInfo"; | ||
export declare class LibraryInfo extends BaseInfo { | ||
ID: string; | ||
/** | ||
@@ -8,0 +9,0 @@ * Name of the library - used for import statements and within package.json |
@@ -22,2 +22,3 @@ "use strict"; | ||
super(); | ||
this.ID = null; | ||
/** | ||
@@ -24,0 +25,0 @@ * Name of the library - used for import statements and within package.json |
@@ -10,2 +10,3 @@ import { DatasetItemFilterType, DatasetResultType, DatasetStatusResultType, EntityRecordNameInput, EntityRecordNameResult, ILocalStorageProvider, IMetadataProvider, PotentialDuplicateRequest, PotentialDuplicateResponse, ProviderConfigDataBase, ProviderType } from "./interfaces"; | ||
import { CompositeKey } from "./compositeKey"; | ||
import { ExplorerNavigationItem } from "./explorerNavigationItem"; | ||
/** | ||
@@ -40,3 +41,3 @@ * Class used to access a wide array of MemberJunction metadata, to instantiate derived classes of BaseEntity for record access and manipulation and more. This class uses a provider model where different providers transparently plug-in to implement the functionality needed based on where the code is running. The provider in use is generally not of any importance to users of the class and code can be written indepdenent of tier/provider. | ||
*/ | ||
EntityByID(entityID: number): EntityInfo; | ||
EntityByID(entityID: string): EntityInfo; | ||
get Queries(): QueryInfo[]; | ||
@@ -55,2 +56,10 @@ get QueryFields(): QueryFieldInfo[]; | ||
/** | ||
* Returns all of the ExplorerNavigationItems that are visible to the user, sorted by Sequence. Filtered by the IsActive bit. | ||
*/ | ||
get VisibleExplorerNavigationItems(): ExplorerNavigationItem[]; | ||
/** | ||
* Returns all of the ExplorerNavigationItems, including those that are not visible. This is useful for admin tools and other places where you need to see all of the navigation items, not just the ones that are visible to the user. | ||
*/ | ||
get AllExplorerNavigationItems(): ExplorerNavigationItem[]; | ||
/** | ||
* Helper function to return an Entity Name from a given Entity ID. | ||
@@ -60,3 +69,3 @@ * @param entityName | ||
*/ | ||
EntityIDFromName(entityName: string): number; | ||
EntityIDFromName(entityName: string): string; | ||
/** | ||
@@ -67,3 +76,3 @@ * Helper function to return an Entity Name from an Entity ID | ||
*/ | ||
EntityNameFromID(entityID: number): string; | ||
EntityNameFromID(entityID: string): string; | ||
/** | ||
@@ -73,3 +82,3 @@ * Helper function to return an EntityInfo from an Entity ID | ||
*/ | ||
EntityFromEntityID(entityID: number): EntityInfo | null; | ||
EntityFromEntityID(entityID: string): EntityInfo | null; | ||
/** | ||
@@ -82,3 +91,3 @@ * Returns true if the combination of userId/entityName/KeyValuePairs has a favorite status on (meaning the user has marked the record as a "favorite" for easy access) | ||
*/ | ||
GetRecordFavoriteStatus(userId: number, entityName: string, primaryKey: CompositeKey): Promise<boolean>; | ||
GetRecordFavoriteStatus(userId: string, entityName: string, primaryKey: CompositeKey): Promise<boolean>; | ||
/** | ||
@@ -92,3 +101,3 @@ * Sets the favorite status for a given user for a specific entityName/KeyValuePairs | ||
*/ | ||
SetRecordFavoriteStatus(userId: number, entityName: string, primaryKey: CompositeKey, isFavorite: boolean, contextUser?: UserInfo): Promise<void>; | ||
SetRecordFavoriteStatus(userId: string, entityName: string, primaryKey: CompositeKey, isFavorite: boolean, contextUser?: UserInfo): Promise<void>; | ||
/** | ||
@@ -95,0 +104,0 @@ * Returns a list of dependencies - records that are linked to the specified Entity/Primary Key Value combination. A dependency is as defined by the relationships in the database. The MemberJunction metadata that is used |
@@ -90,2 +90,14 @@ "use strict"; | ||
/** | ||
* Returns all of the ExplorerNavigationItems that are visible to the user, sorted by Sequence. Filtered by the IsActive bit. | ||
*/ | ||
get VisibleExplorerNavigationItems() { | ||
return Metadata.Provider.VisibleExplorerNavigationItems; | ||
} | ||
/** | ||
* Returns all of the ExplorerNavigationItems, including those that are not visible. This is useful for admin tools and other places where you need to see all of the navigation items, not just the ones that are visible to the user. | ||
*/ | ||
get AllExplorerNavigationItems() { | ||
return Metadata.Provider.AllExplorerNavigationItems; | ||
} | ||
/** | ||
* Helper function to return an Entity Name from a given Entity ID. | ||
@@ -195,3 +207,7 @@ * @param entityName | ||
async MergeRecords(request, contextUser) { | ||
return await Metadata.Provider.MergeRecords(request, contextUser); | ||
const e = this.EntityByName(request.EntityName); | ||
if (e.AllowRecordMerge) | ||
return await Metadata.Provider.MergeRecords(request, contextUser); | ||
else | ||
throw new Error(`Entity ${request.EntityName} does not allow record merging, check the AllowRecordMerge property in the entity metadata`); | ||
} | ||
@@ -198,0 +214,0 @@ /** |
@@ -10,2 +10,3 @@ import { BaseEntity } from "./baseEntity"; | ||
import { CompositeKey } from "./compositeKey"; | ||
import { ExplorerNavigationItem } from "./explorerNavigationItem"; | ||
/** | ||
@@ -28,2 +29,3 @@ * AllMetadata is used to pass all metadata around in a single object for convenience and type safety. | ||
AllLibraries: LibraryInfo[]; | ||
AllExplorerNavigationItems: ExplorerNavigationItem[]; | ||
static FromSimpleObject(data: any, md: IMetadataProvider): AllMetadata; | ||
@@ -42,2 +44,5 @@ } | ||
key: string; | ||
class: typeof QueryFieldInfo; | ||
} | { | ||
key: string; | ||
class: typeof QueryPermissionInfo; | ||
@@ -65,4 +70,4 @@ } | { | ||
abstract GetEntityRecordNames(info: EntityRecordNameInput[]): Promise<EntityRecordNameResult[]>; | ||
abstract GetRecordFavoriteStatus(userId: number, entityName: string, CompositeKey: CompositeKey): Promise<boolean>; | ||
abstract SetRecordFavoriteStatus(userId: number, entityName: string, CompositeKey: CompositeKey, isFavorite: boolean, contextUser: UserInfo): Promise<void>; | ||
abstract GetRecordFavoriteStatus(userId: string, entityName: string, CompositeKey: CompositeKey): Promise<boolean>; | ||
abstract SetRecordFavoriteStatus(userId: string, entityName: string, CompositeKey: CompositeKey, isFavorite: boolean, contextUser: UserInfo): Promise<void>; | ||
/******** END - ABSTRACT SECTION ****************************************************************** */ | ||
@@ -88,2 +93,5 @@ Config(data: ProviderConfigDataBase): Promise<boolean>; | ||
get Libraries(): LibraryInfo[]; | ||
get AllExplorerNavigationItems(): ExplorerNavigationItem[]; | ||
private _cachedVisibleExplorerNavigationItems; | ||
get VisibleExplorerNavigationItems(): ExplorerNavigationItem[]; | ||
Refresh(): Promise<boolean>; | ||
@@ -90,0 +98,0 @@ CheckToSeeIfRefreshNeeded(): Promise<boolean>; |
@@ -13,2 +13,3 @@ "use strict"; | ||
const libraryInfo_1 = require("./libraryInfo"); | ||
const explorerNavigationItem_1 = require("./explorerNavigationItem"); | ||
/** | ||
@@ -33,2 +34,3 @@ * AllMetadata is used to pass all metadata around in a single object for convenience and type safety. | ||
this.AllLibraries = []; | ||
this.AllExplorerNavigationItems = []; | ||
} | ||
@@ -69,3 +71,4 @@ // Create a new instance of AllMetadata from a simple object | ||
{ key: 'AllEntityDocumentTypes', class: entityInfo_1.EntityDocumentTypeInfo }, | ||
{ key: 'AllLibraries', class: libraryInfo_1.LibraryInfo } | ||
{ key: 'AllLibraries', class: libraryInfo_1.LibraryInfo }, | ||
{ key: 'AllExplorerNavigationItems', class: explorerNavigationItem_1.ExplorerNavigationItem } | ||
]; | ||
@@ -76,2 +79,3 @@ class ProviderBase { | ||
this._refresh = false; | ||
this._cachedVisibleExplorerNavigationItems = null; | ||
} | ||
@@ -87,2 +91,3 @@ /******** END - ABSTRACT SECTION ****************************************************************** */ | ||
this._refresh = false; | ||
this._cachedVisibleExplorerNavigationItems = null; // reset this so it gets rebuilt next time it is requested | ||
const start = new Date().getTime(); | ||
@@ -145,4 +150,4 @@ const res = await this.GetAllMetadata(); | ||
simpleMetadata.AllApplications = simpleMetadata.Applications.map((a) => { | ||
a.ApplicationEntities = simpleMetadata.ApplicationEntities.filter((ae) => ae.ApplicationName.trim().toLowerCase() === a.Name.trim().toLowerCase()); | ||
a.ApplicationSettings = simpleMetadata.ApplicationSettings.filter((as) => as.ApplicationName.trim().toLowerCase() === a.Name.trim().toLowerCase()); | ||
a.ApplicationEntities = simpleMetadata.ApplicationEntities.filter((ae) => ae.ApplicationID === a.ID); | ||
a.ApplicationSettings = simpleMetadata.ApplicationSettings.filter((as) => as.ApplicationID === a.ID); | ||
return new applicationInfo_1.ApplicationInfo(a, this); | ||
@@ -185,3 +190,3 @@ }); | ||
// populate the field values for each field, if we have them | ||
f.EntityFieldValues = fieldValues.filter(fv => fv.EntityID === f.EntityID && fv.EntityFieldName.trim().toLowerCase() === f.Name.trim().toLowerCase()); | ||
f.EntityFieldValues = fieldValues.filter(fv => fv.EntityFieldID === f.ID); | ||
} | ||
@@ -236,2 +241,11 @@ for (let e of entities) { | ||
} | ||
get AllExplorerNavigationItems() { | ||
return this._localMetadata.AllExplorerNavigationItems; | ||
} | ||
get VisibleExplorerNavigationItems() { | ||
// filter and sort once and cache | ||
if (!this._cachedVisibleExplorerNavigationItems) | ||
this._cachedVisibleExplorerNavigationItems = this._localMetadata.AllExplorerNavigationItems.filter(e => e.IsActive).sort((a, b) => a.Sequence - b.Sequence); | ||
return this._cachedVisibleExplorerNavigationItems; | ||
} | ||
async Refresh() { | ||
@@ -362,3 +376,24 @@ // do nothing here, but set a _refresh flag for next time things are requested | ||
const localTimestamp = new Date(val); | ||
return localTimestamp.getTime() >= serverTimestamp; | ||
if (localTimestamp.getTime() >= serverTimestamp) { | ||
// this situation means our local cache timestamp is >= the server timestamp, so we're most likely up to date | ||
// in this situation, the last thing we check is for each entity, if the rowcount is the same as the server, if it is, we're good | ||
// iterate through all of the entities and check the row counts | ||
const localDataset = await this.GetCachedDataset(datasetName, itemFilters); | ||
for (const eu of status.EntityUpdateDates) { | ||
const localEntity = localDataset.Results.find(e => e.EntityID === eu.EntityID); | ||
if (localEntity && localEntity.Results.length === eu.RowCount) { | ||
return true; | ||
} | ||
else { | ||
// we either couldn't find the entity in the local cache or the row count is different, so we're out of date | ||
// the RowCount being different picks up on DELETED rows. The UpdatedAt check which is handled above would pick up | ||
// on any new rows or updated rows. This approach makes sure we detect deleted rows and refresh the cache. | ||
return false; | ||
} | ||
} | ||
} | ||
else { | ||
// our local cache timestamp is < the server timestamp, so we're out of date | ||
return false; | ||
} | ||
} | ||
@@ -467,3 +502,4 @@ else { | ||
Type: e.EntityName, | ||
UpdatedAt: e.UpdateDate | ||
UpdatedAt: e.UpdateDate, | ||
RowCount: e.RowCount | ||
}; | ||
@@ -473,5 +509,6 @@ }); | ||
ret.push({ | ||
ID: -1, | ||
ID: "", | ||
Type: 'All Entity Metadata', | ||
UpdatedAt: d.LatestUpdateDate | ||
UpdatedAt: d.LatestUpdateDate, | ||
RowCount: d.EntityUpdateDates.reduce((a, b) => a + b.RowCount, 0) | ||
}); | ||
@@ -518,2 +555,9 @@ return ret; | ||
} | ||
else { | ||
// here we have a match for the local and remote timestamps, so we need to check the row counts | ||
// if the row counts are different, we're obsolete | ||
if (l.RowCount !== mdRemote[i].RowCount) { | ||
return true; | ||
} | ||
} | ||
} | ||
@@ -520,0 +564,0 @@ else |
@@ -7,5 +7,6 @@ import { BaseInfo } from "./baseInfo"; | ||
export declare class QueryInfo extends BaseInfo { | ||
ID: string; | ||
Name: string; | ||
Description: string; | ||
CategoryID: number; | ||
CategoryID: string; | ||
SQL: string; | ||
@@ -26,4 +27,5 @@ OriginalSQL: string; | ||
export declare class QueryCategoryInfo extends BaseInfo { | ||
ID: string; | ||
Name: string; | ||
ParentID: number; | ||
ParentID: string; | ||
Description: string; | ||
@@ -39,3 +41,3 @@ __mj_CreatedAt: Date; | ||
Name: string; | ||
QueryID: number; | ||
QueryID: string; | ||
Description: string; | ||
@@ -51,3 +53,3 @@ Sequence: number; | ||
SQLFullType: string; | ||
SourceEntityID: number; | ||
SourceEntityID: string; | ||
SourceFieldName: string; | ||
@@ -66,3 +68,3 @@ IsComputed: boolean; | ||
export declare class QueryPermissionInfo extends BaseInfo { | ||
QueryID: number; | ||
QueryID: string; | ||
RoleName: string; | ||
@@ -69,0 +71,0 @@ Query: string; |
@@ -21,2 +21,3 @@ "use strict"; | ||
super(); | ||
this.ID = null; | ||
this.Name = null; | ||
@@ -56,2 +57,3 @@ this.Description = null; | ||
super(); | ||
this.ID = null; | ||
this.Name = null; | ||
@@ -58,0 +60,0 @@ this.ParentID = null; |
import { IRunQueryProvider, RunQueryResult } from './interfaces'; | ||
import { UserInfo } from './securityInfo'; | ||
export type RunQueryParams = { | ||
QueryID: number; | ||
QueryID: string; | ||
}; | ||
@@ -6,0 +6,0 @@ /** |
import { IRunReportProvider, RunReportResult } from './interfaces'; | ||
import { UserInfo } from './securityInfo'; | ||
export type RunReportParams = { | ||
ReportID: number; | ||
ReportID: string; | ||
}; | ||
@@ -6,0 +6,0 @@ /** |
@@ -7,2 +7,3 @@ import { BaseInfo } from "./baseInfo"; | ||
export declare class UserInfo extends BaseInfo { | ||
ID: string; | ||
Name: string; | ||
@@ -36,7 +37,8 @@ FirstName: string; | ||
export declare class UserRoleInfo extends BaseInfo { | ||
UserID: number; | ||
RoleName: string; | ||
UserID: string; | ||
RoleID: string; | ||
__mj_CreatedAt: Date; | ||
__mj_UpdatedAt: Date; | ||
User: string; | ||
Role: string; | ||
private _RoleInfo; | ||
@@ -51,5 +53,6 @@ get RoleInfo(): RoleInfo; | ||
export declare class RoleInfo extends BaseInfo { | ||
ID: string; | ||
Name: string; | ||
Description: string; | ||
AzureID: string; | ||
DirectoryID: string; | ||
SQLName: string; | ||
@@ -61,2 +64,3 @@ __mj_CreatedAt: Date; | ||
export declare class RowLevelSecurityFilterInfo extends BaseInfo { | ||
ID: string; | ||
Name: string; | ||
@@ -76,7 +80,8 @@ Description: string; | ||
export declare class AuthorizationInfo extends BaseInfo { | ||
ID: string; | ||
/** | ||
* The unique identifier for the parent authorization, if applicable. | ||
* @type {number|null} | ||
* @type {string|null} | ||
*/ | ||
ParentID: number; | ||
ParentID: string; | ||
Name: string; | ||
@@ -128,4 +133,5 @@ /** | ||
export declare class AuthorizationRoleInfo extends BaseInfo { | ||
AuthorizationName: string; | ||
RoleName: string; | ||
ID: string; | ||
AuthorizationID: string; | ||
RoleID: string; | ||
Type: string; | ||
@@ -166,3 +172,4 @@ __mj_CreatedAt: Date; | ||
export declare class AuditLogTypeInfo extends BaseInfo { | ||
ParentID: number; | ||
ID: string; | ||
ParentID: string; | ||
Name: string; | ||
@@ -169,0 +176,0 @@ Description: string; |
@@ -15,2 +15,3 @@ "use strict"; | ||
super(); | ||
this.ID = null; | ||
/* Name of the user that is used in various places in UIs/etc, can be anything including FirstLast, Email, a Handle, etc */ | ||
@@ -50,3 +51,3 @@ this.Name = null; | ||
this._UserRoles.push(uri); | ||
const match = mdRoles.find(r => r.Name.trim().toLowerCase() == uri.RoleName.trim().toLowerCase()); | ||
const match = mdRoles.find(r => r.ID == uri.RoleID); | ||
if (match) | ||
@@ -72,3 +73,3 @@ uri._setRole(match); | ||
this.UserID = null; | ||
this.RoleName = null; | ||
this.RoleID = null; | ||
this.__mj_CreatedAt = null; | ||
@@ -78,2 +79,3 @@ this.__mj_UpdatedAt = null; | ||
this.User = null; | ||
this.Role = null; | ||
this._RoleInfo = null; | ||
@@ -90,5 +92,6 @@ this.copyInitData(initData); | ||
super(); | ||
this.ID = null; | ||
this.Name = null; | ||
this.Description = null; | ||
this.AzureID = null; | ||
this.DirectoryID = null; | ||
this.SQLName = null; | ||
@@ -104,2 +107,3 @@ this.__mj_CreatedAt = null; | ||
super(); | ||
this.ID = null; | ||
this.Name = null; | ||
@@ -139,5 +143,6 @@ this.Description = null; | ||
super(); | ||
this.ID = null; | ||
/** | ||
* The unique identifier for the parent authorization, if applicable. | ||
* @type {number|null} | ||
* @type {string|null} | ||
*/ | ||
@@ -178,3 +183,3 @@ this.ParentID = null; | ||
this._AuthorizationRoles.push(ari); | ||
const match = mdRoles.find(r => r.Name.trim().toLowerCase() == ari.RoleName.trim().toLowerCase()); | ||
const match = mdRoles.find(r => r.ID === ari.RoleID); | ||
if (match) | ||
@@ -194,3 +199,3 @@ ari._setRole(match); | ||
for (let i = 0; i < user.UserRoles.length; i++) { | ||
const matchingRole = this.Roles.find(r => r.RoleName == user.UserRoles[i].RoleName); | ||
const matchingRole = this.Roles.find(r => r.ID === user.UserRoles[i].RoleID); | ||
if (matchingRole) | ||
@@ -210,3 +215,3 @@ return true; // as soon as we find a single matching role we can bail out as the user can execute | ||
if (this.IsActive) { | ||
return this.Roles.find(r => r.RoleName == role.Name) != null; | ||
return this.Roles.find(r => r.ID === role.ID) != null; | ||
} | ||
@@ -233,4 +238,5 @@ return false; | ||
super(); | ||
this.AuthorizationName = null; | ||
this.RoleName = null; | ||
this.ID = null; | ||
this.AuthorizationID = null; | ||
this.RoleID = null; | ||
this.Type = null; | ||
@@ -291,2 +297,3 @@ this.__mj_CreatedAt = null; | ||
super(); | ||
this.ID = null; | ||
this.ParentID = null; | ||
@@ -293,0 +300,0 @@ this.Name = null; |
@@ -14,3 +14,3 @@ import { IRunViewProvider, RunViewResult } from '../generic/interfaces'; | ||
*/ | ||
ViewID?: number; | ||
ViewID?: string; | ||
/** | ||
@@ -59,3 +59,3 @@ * optional - Name of the UserView record to run, if you are using this, make sure to use a naming convention | ||
*/ | ||
ExcludeUserViewRunID?: number; | ||
ExcludeUserViewRunID?: string; | ||
/** | ||
@@ -89,2 +89,6 @@ * optional - if set to true, the resulting data will filter out ANY records that were ever returned by this view, when the SaveViewResults property was set to true. | ||
/** | ||
* optional - if provided, this value will be used to offset the rows returned. | ||
*/ | ||
StartRow?: number; | ||
/** | ||
* optional - if set to true, the view run will ALWAYS be logged to the Audit Log, regardless of the entity's property settings for logging view runs. | ||
@@ -116,2 +120,3 @@ */ | ||
RunView<T = any>(params: RunViewParams, contextUser?: UserInfo): Promise<RunViewResult<T>>; | ||
RunViews<T = any>(params: RunViewParams[], contextUser?: UserInfo): Promise<RunViewResult<T>[]>; | ||
private static _globalProviderKey; | ||
@@ -118,0 +123,0 @@ static get Provider(): IRunViewProvider; |
@@ -46,2 +46,36 @@ "use strict"; | ||
} | ||
async RunViews(params, contextUser) { | ||
let md = null; | ||
for (const param of params) { | ||
// FIRST, if the resultType is entity_object, we need to run the view with ALL fields in the entity | ||
// so that we can get the data to populate the entity object with. | ||
if (param.ResultType === 'entity_object') { | ||
// we need to get the entity definition and then get all the fields for it | ||
md = md || new metadata_1.Metadata(); | ||
const entity = md.Entities.find(e => e.Name.trim().toLowerCase() === param.EntityName.trim().toLowerCase()); | ||
if (!entity) { | ||
throw new Error(`Entity ${param.EntityName} not found in metadata`); | ||
} | ||
param.Fields = entity.Fields.map(f => f.Name); // just override whatever was passed in with all the fields - or if nothing was passed in, we set it. For loading the entity object, we need ALL the fields. | ||
} | ||
} | ||
// NOW, run the view | ||
const results = await RunView.Provider.RunViews(params, contextUser); | ||
for (const [index, result] of results.entries()) { | ||
const param = params[index]; | ||
// FINALLY, if needed, transform the result set into BaseEntity-derived objects | ||
if (param.ResultType === 'entity_object' && result && result.Success) { | ||
// we need to transform each of the items in the result set into a BaseEntity-derived object | ||
md = md || new metadata_1.Metadata(); | ||
const newItems = []; | ||
for (const item of result.Results) { | ||
const entity = await md.GetEntityObject(param.EntityName, contextUser); | ||
entity.LoadFromData(item); | ||
newItems.push(entity); | ||
} | ||
result.Results = newItems; | ||
} | ||
} | ||
return results; | ||
} | ||
static get Provider() { | ||
@@ -81,3 +115,3 @@ const g = global_1.MJGlobal.Instance.GetGlobalObjectStore(); | ||
EntityName: "User Views", | ||
ExtraFilter: params.ViewID ? `ID = ${params.ViewID}` : `Name = '${params.ViewName}'`, | ||
ExtraFilter: params.ViewID ? `ID = '${params.ViewID}'` : `Name = '${params.ViewName}'`, | ||
ResultType: 'entity_object' | ||
@@ -84,0 +118,0 @@ }); |
@@ -33,2 +33,3 @@ import { BaseInfo } from '../generic/baseInfo'; | ||
export declare class ViewInfo extends BaseInfo { | ||
ID: string; | ||
UserID: number; | ||
@@ -35,0 +36,0 @@ EntityID: number; |
@@ -96,2 +96,3 @@ "use strict"; | ||
super(); | ||
this.ID = null; | ||
this.UserID = null; | ||
@@ -98,0 +99,0 @@ this.EntityID = null; |
{ | ||
"name": "@memberjunction/core", | ||
"version": "1.8.1", | ||
"version": "2.0.0", | ||
"description": "MemberJunction: Core Library including Metadata, Application, Entity Retrieval and Manipulation, and Utilities", | ||
@@ -22,5 +22,5 @@ "main": "dist/index.js", | ||
"dependencies": { | ||
"@memberjunction/global": "1.8.1", | ||
"@memberjunction/global": "2.0.0", | ||
"rxjs": "^7.8.1" | ||
} | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
575048
86
8298
+ Added@memberjunction/global@2.0.0(transitive)
- Removed@memberjunction/global@1.8.1(transitive)
Updated@memberjunction/global@2.0.0