@memberjunction/core-entities 0.9.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,178 @@
1
+ import { BaseInfo, EntityInfo, EntityFieldInfo, UserInfo, EntitySaveOptions } from "@memberjunction/core";
2
+ import { UserViewEntity } from "../generated/entity_subclasses";
3
+ export declare class UserViewEntityExtended extends UserViewEntity {
4
+ private _ViewEntityInfo;
5
+ /**
6
+ * This is a read-only property that returns the filters for this view. This information
7
+ * is persisted in a JSON format in the FilterState column of the UserViewEntity table. To access
8
+ * the filters easily, use this property.
9
+ * @readonly
10
+ * @type {ViewFilterInfo[]}
11
+ * @memberof UserViewEntitySubclass
12
+ */
13
+ get Filter(): ViewFilterInfo[];
14
+ /**
15
+ * This is a read-only property that returns the columns for this view. This information
16
+ * is persisted in a JSON format in the GridState column of the UserViewEntity table. To access
17
+ * the columns easily, use this property.
18
+ */
19
+ get Columns(): ViewColumnInfo[];
20
+ /**
21
+ * The entity info for the entity that this view is based on
22
+ * @readonly
23
+ * @type {EntityInfo}
24
+ * @memberof UserViewEntitySubclass
25
+ */
26
+ get ViewEntityInfo(): EntityInfo;
27
+ get ViewSortInfo(): ViewSortInfo[];
28
+ get OrderByClause(): string;
29
+ LoadFromData(data: any): boolean;
30
+ Load(ID: number, EntityRelationshipsToLoad?: string[]): Promise<boolean>;
31
+ Save(options?: EntitySaveOptions): Promise<boolean>;
32
+ SetDefaultsFromEntity(e: EntityInfo): Promise<void>;
33
+ NewRecord(): boolean;
34
+ /**
35
+ * This method is used to update the view's where clause based on the following logic.
36
+ * 1) If the view has a regular Filter State (which is typically set by an end-user in a UI), the FilterState will be processed and a WHERE clause will be generated
37
+ * 2) If SmartFilterEnabled === 1 and the view has a SmartFilterPrompt, the SmartFilterPrompt will be processed by AI and the SmartFilterWhereClause will be generated. SmartFilterWhereClause will only be generated whenever the SmartFilterPrompt changes.
38
+ * 3) If CustomWhereClause === 1, this function will NOT modify the WhereClause because the sysadmin has set CustomWhereClause === 1 which means we don't want any changes to this particular view's WhereClause
39
+ * IMPORTANT NOTE: This method does not save the record. You still call .Save() to save the record as desired. If you want to get the new WhereClause based on the FilterState but not
40
+ * update the FilterState column, call the GenerateWhereClause() method.
41
+ * KEY ASSUMPTION: The server code must set a property in the MJGlobal.Properties array with a key of OPENAI_API_KEY to use the AI functionality. If this property is not set, the AI functionality will not work.
42
+ */
43
+ UpdateWhereClause(): Promise<void>;
44
+ /**
45
+ * This method will use AI to return a valid WHERE clause based on the provided prompt. This is automatically called at the right time if the view has SmartFilterEnabled turned on and the SmartFilterPrompt is set. If you want
46
+ * to call this directly to get back a WHERE clause for other purposes you can call this method directly and provide both a prompt and the entity that the view is based on.
47
+ * @param prompt
48
+ */
49
+ GenerateSmartFilterWhereClause(prompt: string, entityInfo: EntityInfo): Promise<string>;
50
+ Set(FieldName: string, Value: any): void;
51
+ /**
52
+ * Create a where clause for SQL from the structured filter state JSON information
53
+ * @param FilterState A string containing a valid Filter State JSON string - this uses the format that the Kendo Filter component uses which is generic and can be used anywhere
54
+ * with/without kendo
55
+ * @param EntityInfo The entity info for the entity that the UserView is based on
56
+ * @returns a string that represents a valid SQL WHERE clause
57
+ * @memberof UserViewEntitySubclass
58
+ * @example Example Filter State JSON below
59
+ FilterState = `{
60
+ "logic": "or",
61
+ "filters": [{
62
+ "field": "Name",
63
+ "operator": "startswith",
64
+ "value": "A"
65
+ }, {
66
+ "logic": "or",
67
+ "filters": [{
68
+ "field": "TotalRevenue",
69
+ "operator": "gt",
70
+ "value": 10000000
71
+ }, {
72
+ "field": "NumberEmployees",
73
+ "operator": "gte",
74
+ "value": 25
75
+ }, {
76
+ "field": "InformationTechnologyExpense",
77
+ "operator": "gte",
78
+ "value": 500000
79
+ }, {
80
+ "logic": "and",
81
+ "filters": [{
82
+ "field": "City",
83
+ "operator": "eq",
84
+ "value": "Chicago"
85
+ }, {
86
+ "field": "ActivityCount",
87
+ "operator": "gte",
88
+ "value": 5
89
+ }]
90
+ }]
91
+ }, {
92
+ "field": "LatestActivityDate",
93
+ "operator": "gte",
94
+ "value": "2023-01-01T06:00:00.000Z"
95
+ }]
96
+ }`;
97
+ */
98
+ protected GenerateWhereClause(FilterState: string, entityInfo: EntityInfo): string;
99
+ private wrapQuotes;
100
+ private convertFilterToSQL;
101
+ private processFilterGroup;
102
+ }
103
+ export declare class ViewInfo {
104
+ /**
105
+ * Returns a list of views for the specified user. If no user is specified, the current user is used.
106
+ * @param contextUser optional, the user to use for context when loading the view
107
+ * @param entityId optional, the entity ID to filter the views by, if not provided, there is no filter on EntityID and all views for the user are returned
108
+ * @returns an array of UserViewEntityBase objects
109
+ * @memberof ViewInfo
110
+ * @static
111
+ * @async
112
+ */
113
+ static GetViewsForUser(entityId?: number, contextUser?: UserInfo): Promise<UserViewEntityExtended[]>;
114
+ /**
115
+ * Returns a view ID for a given viewName
116
+ * @param viewName Name of the view to lookup the ID for
117
+ * @returns the ID of the User View record that matches the provided name, if found
118
+ */
119
+ static GetViewID(viewName: string): Promise<number>;
120
+ /**
121
+ * Loads a new entity object for User Views for the specified viewId and returns it if successful.
122
+ * @param viewId record ID for the view to load
123
+ * @param contextUser optional, the user to use for context when loading the view
124
+ * @returns UserViewEntityBase (or a subclass of it)
125
+ * @throws Error if the view cannot be loaded
126
+ * @memberof ViewInfo
127
+ * @static
128
+ * @async
129
+ */
130
+ static GetViewEntity(viewId: number, contextUser?: UserInfo): Promise<UserViewEntityExtended>;
131
+ /**
132
+ * Loads a new entity object for User Views for the specified viewName and returns it if successful.
133
+ * @param viewName name for the view to load
134
+ * @param contextUser optional, the user to use for context when loading the view
135
+ * @returns UserViewEntityBase (or a subclass of it)
136
+ * @throws Error if the view cannot be loaded
137
+ * @memberof ViewInfo
138
+ * @static
139
+ * @async
140
+ */
141
+ static GetViewEntityByName(viewName: string, contextUser?: UserInfo): Promise<UserViewEntityExtended>;
142
+ }
143
+ export declare class ViewColumnInfo extends BaseInfo {
144
+ ID: number;
145
+ Name: string;
146
+ DisplayName: string;
147
+ hidden: boolean;
148
+ width?: number;
149
+ orderIndex?: number;
150
+ EntityField: EntityFieldInfo;
151
+ constructor(initData?: any);
152
+ }
153
+ export declare enum ViewFilterLogicInfo {
154
+ And = 1,
155
+ Or = 2
156
+ }
157
+ export declare class ViewFilterInfo extends BaseInfo {
158
+ logicOperator: ViewFilterLogicInfo;
159
+ field: string;
160
+ operator: string;
161
+ value: string;
162
+ filters: ViewFilterInfo[];
163
+ constructor(initData?: any);
164
+ }
165
+ export declare enum ViewSortDirectionInfo {
166
+ Asc = 1,
167
+ Desc = 2
168
+ }
169
+ export declare class ViewSortInfo extends BaseInfo {
170
+ field: string;
171
+ direction: ViewSortDirectionInfo;
172
+ constructor(initData?: any);
173
+ }
174
+ export declare class ViewGridState {
175
+ sortSettings?: any;
176
+ columnSettings?: any;
177
+ filter?: any;
178
+ }
@@ -0,0 +1,506 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.ViewGridState = exports.ViewSortInfo = exports.ViewSortDirectionInfo = exports.ViewFilterInfo = exports.ViewFilterLogicInfo = exports.ViewColumnInfo = exports.ViewInfo = exports.UserViewEntityExtended = void 0;
10
+ const global_1 = require("@memberjunction/global");
11
+ const core_1 = require("@memberjunction/core");
12
+ const entity_subclasses_1 = require("../generated/entity_subclasses");
13
+ let UserViewEntityExtended = class UserViewEntityExtended extends entity_subclasses_1.UserViewEntity {
14
+ constructor() {
15
+ super(...arguments);
16
+ this._ViewEntityInfo = null;
17
+ }
18
+ /**
19
+ * This is a read-only property that returns the filters for this view. This information
20
+ * is persisted in a JSON format in the FilterState column of the UserViewEntity table. To access
21
+ * the filters easily, use this property.
22
+ * @readonly
23
+ * @type {ViewFilterInfo[]}
24
+ * @memberof UserViewEntitySubclass
25
+ */
26
+ get Filter() {
27
+ if (this.FilterState) {
28
+ return [new ViewFilterInfo(JSON.parse(this.FilterState))];
29
+ }
30
+ else
31
+ return [];
32
+ }
33
+ /**
34
+ * This is a read-only property that returns the columns for this view. This information
35
+ * is persisted in a JSON format in the GridState column of the UserViewEntity table. To access
36
+ * the columns easily, use this property.
37
+ */
38
+ get Columns() {
39
+ // now, we need to do some post-processing once we've loaded the raw data so that our
40
+ // columns and filters are set up correctly
41
+ if (this.GridState) {
42
+ const gridState = JSON.parse(this.GridState);
43
+ if (gridState && gridState.columnSettings) {
44
+ const columns = gridState.columnSettings.map(c => {
45
+ // find the entity field and put it in place inside the View Metadata for easy access
46
+ if (c) {
47
+ // check to make sure the current item is non-null to ensure metadata isn't messed up
48
+ const field = this._ViewEntityInfo.Fields.find(f => f.Name.trim().toLowerCase() == c.Name.trim().toLowerCase());
49
+ return new ViewColumnInfo({ ...c, EntityField: field });
50
+ }
51
+ else {
52
+ console.log('null column setting found in view grid state for columns - ViewID: ' + this.ID);
53
+ }
54
+ });
55
+ return columns;
56
+ }
57
+ }
58
+ // if we get here, we don't have column info, return an empty array
59
+ return [];
60
+ }
61
+ /**
62
+ * The entity info for the entity that this view is based on
63
+ * @readonly
64
+ * @type {EntityInfo}
65
+ * @memberof UserViewEntitySubclass
66
+ */
67
+ get ViewEntityInfo() {
68
+ return this._ViewEntityInfo;
69
+ }
70
+ get ViewSortInfo() {
71
+ if (this.SortState) {
72
+ const sortTemp = JSON.parse(this.SortState);
73
+ if (sortTemp && sortTemp.length > 0)
74
+ return sortTemp.map(s => new ViewSortInfo(s));
75
+ }
76
+ // if we get here return a blank array
77
+ return [];
78
+ }
79
+ get OrderByClause() {
80
+ if (this.ViewSortInfo && this.ViewSortInfo.length > 0) {
81
+ return this.ViewSortInfo.map(s => s.field + (s.direction === ViewSortDirectionInfo.Asc ? '' : ' DESC')).join(', ');
82
+ }
83
+ else
84
+ return '';
85
+ }
86
+ LoadFromData(data) {
87
+ // in this case we need to make sure we ge the _ViewEntityInfo property set up correctly
88
+ if (data && data.EntityID) {
89
+ const md = new core_1.Metadata();
90
+ const match = md.Entities.find(e => e.ID == data.EntityID);
91
+ if (match) {
92
+ this._ViewEntityInfo = match;
93
+ }
94
+ else {
95
+ throw new Error('Unable to find entity info for entity ID: ' + data.EntityID);
96
+ }
97
+ }
98
+ return super.LoadFromData(data);
99
+ }
100
+ async Load(ID, EntityRelationshipsToLoad) {
101
+ // first load up the view info, use the superclass to do this
102
+ const result = await super.Load(ID, EntityRelationshipsToLoad);
103
+ if (result) {
104
+ const md = new core_1.Metadata();
105
+ // first, cache a copy of the entity info for the entity that is used in this view
106
+ const match = md.Entities.find(e => e.ID == this.EntityID);
107
+ if (match)
108
+ this._ViewEntityInfo = match;
109
+ else
110
+ throw new Error('Unable to find entity info for entity ID: ' + this.EntityID);
111
+ }
112
+ return result;
113
+ }
114
+ async Save(options) {
115
+ // we want to preprocess the Save() call because we need to regenerate the WhereClause in some situations
116
+ if (options?.IgnoreDirtyState ||
117
+ this.Fields.find(c => c.Name.toLowerCase() == 'filterstate')?.Dirty ||
118
+ this.Fields.find(c => c.Name.toLowerCase() == 'smartfilterenabled')?.Dirty ||
119
+ this.Fields.find(c => c.Name.toLowerCase() == 'smartfilterprompt')?.Dirty) {
120
+ // either we're ignoring dirty state or the filter state is dirty, so we need to update the where clause
121
+ await this.UpdateWhereClause();
122
+ }
123
+ // now just call our superclass to do the actual save()
124
+ return super.Save(options);
125
+ }
126
+ async SetDefaultsFromEntity(e) {
127
+ this.EntityID = e.ID;
128
+ const newGridState = new ViewGridState();
129
+ newGridState.columnSettings = [];
130
+ e.Fields.filter(f => f.DefaultInView).forEach(f => {
131
+ newGridState.columnSettings.push({
132
+ ID: f.ID,
133
+ DisplayName: f.DisplayName,
134
+ Name: f.Name,
135
+ hidden: false,
136
+ width: f.DefaultColumnWidth,
137
+ orderIndex: newGridState.columnSettings.length
138
+ });
139
+ });
140
+ this.GridState = JSON.stringify(newGridState); // default columns for a view are the ones with DefaultInView turned on
141
+ }
142
+ NewRecord() {
143
+ const result = super.NewRecord();
144
+ if (result) {
145
+ if (this.ContextCurrentUser) {
146
+ this.UserID = this.ContextCurrentUser.ID;
147
+ }
148
+ else {
149
+ const md = new core_1.Metadata();
150
+ if (md.CurrentUser)
151
+ this.UserID = md.CurrentUser.ID;
152
+ else
153
+ throw new Error('Unable to determine current user for new view record');
154
+ }
155
+ this.Name = '';
156
+ this.IsShared = false;
157
+ this.IsDefault = false;
158
+ this.WhereClause = '';
159
+ this.Description = '';
160
+ this.FilterState = JSON.stringify({ "logic": "and", "filters": [] }); // blank default for filter
161
+ this.GridState = JSON.stringify({}); // blank object initially
162
+ this.CustomFilterState = false;
163
+ this.CustomWhereClause = false;
164
+ }
165
+ return result;
166
+ }
167
+ /**
168
+ * This method is used to update the view's where clause based on the following logic.
169
+ * 1) If the view has a regular Filter State (which is typically set by an end-user in a UI), the FilterState will be processed and a WHERE clause will be generated
170
+ * 2) If SmartFilterEnabled === 1 and the view has a SmartFilterPrompt, the SmartFilterPrompt will be processed by AI and the SmartFilterWhereClause will be generated. SmartFilterWhereClause will only be generated whenever the SmartFilterPrompt changes.
171
+ * 3) If CustomWhereClause === 1, this function will NOT modify the WhereClause because the sysadmin has set CustomWhereClause === 1 which means we don't want any changes to this particular view's WhereClause
172
+ * IMPORTANT NOTE: This method does not save the record. You still call .Save() to save the record as desired. If you want to get the new WhereClause based on the FilterState but not
173
+ * update the FilterState column, call the GenerateWhereClause() method.
174
+ * KEY ASSUMPTION: The server code must set a property in the MJGlobal.Properties array with a key of OPENAI_API_KEY to use the AI functionality. If this property is not set, the AI functionality will not work.
175
+ */
176
+ async UpdateWhereClause() {
177
+ if (this.CustomWhereClause && (this.CustomWhereClause === true || this.CustomWhereClause === 1))
178
+ // if the CustomWhereClause is set to true or 1, we don't want to update the WhereClause
179
+ return;
180
+ // if we get here, we need to update the WhereClause, first check to see if we have a Smart Filter or not
181
+ if (this.SmartFilterEnabled && (this.SmartFilterEnabled === true || this.SmartFilterEnabled === 1) &&
182
+ this.SmartFilterPrompt && this.SmartFilterPrompt.length > 0) {
183
+ // we have a smart filter prompt (e.g. a prompt for the AI to create the where clause)
184
+ // if the SmartFilterPrompt has changed, then we need to update the SmartFilterWhereClause using AI
185
+ // otherwise, we don't need to do anything other than just use the SmartFilterWhereClause as it is
186
+ if (this.Fields.find(c => c.Name.toLowerCase() == 'smartfilterprompt')?.Dirty) {
187
+ // the prompt has changed (or is newly populated, either way it is dirty) so use the AI to figure this out
188
+ this.SmartFilterWhereClause = await this.GenerateSmartFilterWhereClause(this.SmartFilterPrompt, this.ViewEntityInfo);
189
+ }
190
+ else
191
+ this.WhereClause = this.SmartFilterWhereClause;
192
+ }
193
+ else {
194
+ this.WhereClause = this.GenerateWhereClause(this.FilterState, this.ViewEntityInfo);
195
+ }
196
+ }
197
+ /**
198
+ * This method will use AI to return a valid WHERE clause based on the provided prompt. This is automatically called at the right time if the view has SmartFilterEnabled turned on and the SmartFilterPrompt is set. If you want
199
+ * to call this directly to get back a WHERE clause for other purposes you can call this method directly and provide both a prompt and the entity that the view is based on.
200
+ * @param prompt
201
+ */
202
+ async GenerateSmartFilterWhereClause(prompt, entityInfo) {
203
+ try {
204
+ const apiKey = global_1.MJGlobal.Instance.Properties['OPENAI_API_KEY'];
205
+ if (!apiKey)
206
+ throw new Error('Unable to find OPENAI_API_KEY property in MJGlobal.Properties array. This property is required to use the AI functionality.');
207
+ // we have our OpenAI API Key, let's prompt
208
+ return '';
209
+ }
210
+ catch (e) {
211
+ (0, core_1.LogError)(e);
212
+ throw e;
213
+ }
214
+ }
215
+ Set(FieldName, Value) {
216
+ // call the superclass first and set the value internally there
217
+ super.Set(FieldName, Value);
218
+ if (FieldName.toLowerCase() == 'entityid') {
219
+ // we're updating the entityID, need to upate the _ViewEntityInfo property so it is always in sync
220
+ const md = new core_1.Metadata();
221
+ const match = md.Entities.find(e => e.ID == Value);
222
+ if (match)
223
+ this._ViewEntityInfo = match;
224
+ else
225
+ throw new Error('Unable to find entity info for entity ID: ' + Value);
226
+ }
227
+ }
228
+ /**
229
+ * Create a where clause for SQL from the structured filter state JSON information
230
+ * @param FilterState A string containing a valid Filter State JSON string - this uses the format that the Kendo Filter component uses which is generic and can be used anywhere
231
+ * with/without kendo
232
+ * @param EntityInfo The entity info for the entity that the UserView is based on
233
+ * @returns a string that represents a valid SQL WHERE clause
234
+ * @memberof UserViewEntitySubclass
235
+ * @example Example Filter State JSON below
236
+ FilterState = `{
237
+ "logic": "or",
238
+ "filters": [{
239
+ "field": "Name",
240
+ "operator": "startswith",
241
+ "value": "A"
242
+ }, {
243
+ "logic": "or",
244
+ "filters": [{
245
+ "field": "TotalRevenue",
246
+ "operator": "gt",
247
+ "value": 10000000
248
+ }, {
249
+ "field": "NumberEmployees",
250
+ "operator": "gte",
251
+ "value": 25
252
+ }, {
253
+ "field": "InformationTechnologyExpense",
254
+ "operator": "gte",
255
+ "value": 500000
256
+ }, {
257
+ "logic": "and",
258
+ "filters": [{
259
+ "field": "City",
260
+ "operator": "eq",
261
+ "value": "Chicago"
262
+ }, {
263
+ "field": "ActivityCount",
264
+ "operator": "gte",
265
+ "value": 5
266
+ }]
267
+ }]
268
+ }, {
269
+ "field": "LatestActivityDate",
270
+ "operator": "gte",
271
+ "value": "2023-01-01T06:00:00.000Z"
272
+ }]
273
+ }`;
274
+ */
275
+ GenerateWhereClause(FilterState, entityInfo) {
276
+ return this.processFilterGroup(JSON.parse(FilterState), entityInfo);
277
+ }
278
+ wrapQuotes(value, needQuotes) {
279
+ return needQuotes ? `'${value}'` : value;
280
+ }
281
+ convertFilterToSQL(field, operator, value, entity) {
282
+ let op = '';
283
+ let bNeedsQuotes = false;
284
+ const f = entity.Fields.find((f) => f.Name.trim().toLowerCase() === field.trim().toLowerCase());
285
+ if (!f)
286
+ throw new Error('Unable to find field ' + field + ' in entity ' + entity.Name);
287
+ switch (f.Type.toLowerCase().trim()) {
288
+ case 'nvarchar':
289
+ case 'char':
290
+ case 'varchar':
291
+ case 'text':
292
+ case 'ntext':
293
+ case 'date':
294
+ case 'datetime':
295
+ case 'datetimeoffset':
296
+ case 'time':
297
+ case 'guid':
298
+ case 'uniqueidentifier':
299
+ bNeedsQuotes = true;
300
+ break;
301
+ // all other cases do not need quotes
302
+ }
303
+ switch (operator) {
304
+ case 'eq':
305
+ op = '= ' + this.wrapQuotes(value, bNeedsQuotes);
306
+ break;
307
+ case 'neq':
308
+ op = '<> ' + this.wrapQuotes(value, bNeedsQuotes);
309
+ break;
310
+ case 'gt':
311
+ op = '> ' + this.wrapQuotes(value, bNeedsQuotes);
312
+ break;
313
+ case 'gte':
314
+ op = '>= ' + this.wrapQuotes(value, bNeedsQuotes);
315
+ break;
316
+ case 'lt':
317
+ op = '< ' + this.wrapQuotes(value, bNeedsQuotes);
318
+ break;
319
+ case 'lte':
320
+ op = '<= ' + this.wrapQuotes(value, bNeedsQuotes);
321
+ break;
322
+ case 'startswith':
323
+ op = `LIKE '${value}%'`;
324
+ break;
325
+ case 'endswith':
326
+ op = `LIKE '%${value}'`;
327
+ break;
328
+ case 'contains':
329
+ op = `LIKE '%${value}%'`;
330
+ break;
331
+ case 'doesnotcontain':
332
+ op = `NOT LIKE '%${value}%'`;
333
+ break;
334
+ case 'isnull':
335
+ case 'isempty':
336
+ op = 'IS NULL';
337
+ break;
338
+ case 'isnotnull':
339
+ case 'isnotempty':
340
+ op = 'IS NOT NULL';
341
+ break;
342
+ }
343
+ return `[${field}] ${op}`;
344
+ }
345
+ processFilterGroup(filterGroup, entity) {
346
+ // each filter has two properties, logic and filters
347
+ // logic is either 'and' or 'or' and is what we use to determine the SQL logic operator
348
+ // filters is an array of filters, each filter has a field, operator, and value,
349
+ let whereClause = '';
350
+ let bFirst = true;
351
+ const logic = filterGroup.logic.toUpperCase();
352
+ for (const filter of filterGroup.filters) {
353
+ if (!bFirst)
354
+ whereClause += ` ${logic} `;
355
+ else
356
+ bFirst = false;
357
+ // if an individual filter has a "logic" property, it's a group and we need to process it with parenthesis and recurisely
358
+ if (filter.logic && filter.logic.length > 0) {
359
+ // this is a group, we process it with parenthesis
360
+ whereClause += `(${this.processFilterGroup(filter, entity)})`;
361
+ }
362
+ else {
363
+ // this is an individual filter, easy to process
364
+ whereClause += `(${this.convertFilterToSQL(filter.field, filter.operator, filter.value, entity)})`;
365
+ }
366
+ }
367
+ return whereClause;
368
+ }
369
+ };
370
+ exports.UserViewEntityExtended = UserViewEntityExtended;
371
+ exports.UserViewEntityExtended = UserViewEntityExtended = __decorate([
372
+ (0, global_1.RegisterClass)(core_1.BaseEntity, 'User Views', 2) // 2 priority so this gets used ahead of the generated sub-class
373
+ ], UserViewEntityExtended);
374
+ class ViewInfo {
375
+ /**
376
+ * Returns a list of views for the specified user. If no user is specified, the current user is used.
377
+ * @param contextUser optional, the user to use for context when loading the view
378
+ * @param entityId optional, the entity ID to filter the views by, if not provided, there is no filter on EntityID and all views for the user are returned
379
+ * @returns an array of UserViewEntityBase objects
380
+ * @memberof ViewInfo
381
+ * @static
382
+ * @async
383
+ */
384
+ static async GetViewsForUser(entityId, contextUser) {
385
+ const rv = new core_1.RunView();
386
+ const md = new core_1.Metadata();
387
+ const result = await rv.RunView({
388
+ EntityName: 'User Views',
389
+ ExtraFilter: `UserID = ${contextUser ? contextUser.ID : md.CurrentUser.ID}
390
+ ${entityId ? ` AND EntityID = ${entityId}` : ''}`
391
+ });
392
+ const rd = result?.Results;
393
+ if (result && result.Success && rd)
394
+ return rd;
395
+ }
396
+ /**
397
+ * Returns a view ID for a given viewName
398
+ * @param viewName Name of the view to lookup the ID for
399
+ * @returns the ID of the User View record that matches the provided name, if found
400
+ */
401
+ static async GetViewID(viewName) {
402
+ const rv = new core_1.RunView();
403
+ const result = await rv.RunView({ EntityName: 'User Views', ExtraFilter: `Name = '${viewName}'` });
404
+ const rd = result?.Results;
405
+ if (result && result.Success && rd && rd.length > 0) {
406
+ return rd[0].ID;
407
+ }
408
+ else {
409
+ throw new Error('Unable to find view with name: ' + viewName);
410
+ }
411
+ }
412
+ /**
413
+ * Loads a new entity object for User Views for the specified viewId and returns it if successful.
414
+ * @param viewId record ID for the view to load
415
+ * @param contextUser optional, the user to use for context when loading the view
416
+ * @returns UserViewEntityBase (or a subclass of it)
417
+ * @throws Error if the view cannot be loaded
418
+ * @memberof ViewInfo
419
+ * @static
420
+ * @async
421
+ */
422
+ static async GetViewEntity(viewId, contextUser) {
423
+ const md = new core_1.Metadata();
424
+ const view = await md.GetEntityObject('User Views', contextUser);
425
+ if (await view.Load(viewId))
426
+ return view;
427
+ else
428
+ throw new Error('Unable to load view with ID: ' + viewId);
429
+ }
430
+ /**
431
+ * Loads a new entity object for User Views for the specified viewName and returns it if successful.
432
+ * @param viewName name for the view to load
433
+ * @param contextUser optional, the user to use for context when loading the view
434
+ * @returns UserViewEntityBase (or a subclass of it)
435
+ * @throws Error if the view cannot be loaded
436
+ * @memberof ViewInfo
437
+ * @static
438
+ * @async
439
+ */
440
+ static async GetViewEntityByName(viewName, contextUser) {
441
+ const viewId = await ViewInfo.GetViewID(viewName);
442
+ if (viewId)
443
+ return await ViewInfo.GetViewEntity(viewId, contextUser);
444
+ else
445
+ throw new Error('Unable to find view with name: ' + viewName);
446
+ }
447
+ }
448
+ exports.ViewInfo = ViewInfo;
449
+ class ViewColumnInfo extends core_1.BaseInfo {
450
+ constructor(initData = null) {
451
+ super();
452
+ this.ID = null;
453
+ this.Name = null;
454
+ this.DisplayName = null;
455
+ this.hidden = null;
456
+ this.width = null;
457
+ this.orderIndex = null;
458
+ this.EntityField = null;
459
+ this.copyInitData(initData);
460
+ }
461
+ }
462
+ exports.ViewColumnInfo = ViewColumnInfo;
463
+ var ViewFilterLogicInfo;
464
+ (function (ViewFilterLogicInfo) {
465
+ ViewFilterLogicInfo[ViewFilterLogicInfo["And"] = 1] = "And";
466
+ ViewFilterLogicInfo[ViewFilterLogicInfo["Or"] = 2] = "Or";
467
+ })(ViewFilterLogicInfo || (exports.ViewFilterLogicInfo = ViewFilterLogicInfo = {}));
468
+ class ViewFilterInfo extends core_1.BaseInfo {
469
+ constructor(initData = null) {
470
+ super();
471
+ this.logicOperator = null;
472
+ this.field = null;
473
+ this.operator = null;
474
+ this.value = null;
475
+ this.filters = [];
476
+ this.copyInitData(initData);
477
+ if (initData && initData.logic) {
478
+ this.logicOperator = initData.logic.trim().toLowerCase() == 'and' ? ViewFilterLogicInfo.And : ViewFilterLogicInfo.Or;
479
+ }
480
+ if (initData && initData.filters) {
481
+ this.filters = initData.filters.map(f => new ViewFilterInfo(f));
482
+ }
483
+ }
484
+ }
485
+ exports.ViewFilterInfo = ViewFilterInfo;
486
+ var ViewSortDirectionInfo;
487
+ (function (ViewSortDirectionInfo) {
488
+ ViewSortDirectionInfo[ViewSortDirectionInfo["Asc"] = 1] = "Asc";
489
+ ViewSortDirectionInfo[ViewSortDirectionInfo["Desc"] = 2] = "Desc";
490
+ })(ViewSortDirectionInfo || (exports.ViewSortDirectionInfo = ViewSortDirectionInfo = {}));
491
+ class ViewSortInfo extends core_1.BaseInfo {
492
+ constructor(initData = null) {
493
+ super();
494
+ this.field = null;
495
+ this.direction = null;
496
+ this.copyInitData(initData);
497
+ if (initData && initData.dir && typeof initData.dir == 'string') {
498
+ this.direction = initData.dir.trim().toLowerCase() == 'asc' ? ViewSortDirectionInfo.Asc : ViewSortDirectionInfo.Desc;
499
+ }
500
+ }
501
+ }
502
+ exports.ViewSortInfo = ViewSortInfo;
503
+ class ViewGridState {
504
+ }
505
+ exports.ViewGridState = ViewGridState;
506
+ //# sourceMappingURL=UserViewEntity.js.map