@memberjunction/data-context 4.0.0 → 4.1.0

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.
Files changed (2) hide show
  1. package/package.json +4 -4
  2. package/readme.md +314 -164
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@memberjunction/data-context",
3
3
  "type": "module",
4
- "version": "4.0.0",
4
+ "version": "4.1.0",
5
5
  "description": "This library provides a set of objects that handle run-time loading of data contexts as well as types to be able to use across application tiers for interacting with data contexts.",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
@@ -20,9 +20,9 @@
20
20
  "typescript": "^5.9.3"
21
21
  },
22
22
  "dependencies": {
23
- "@memberjunction/global": "4.0.0",
24
- "@memberjunction/core-entities": "4.0.0",
25
- "@memberjunction/core": "4.0.0"
23
+ "@memberjunction/global": "4.1.0",
24
+ "@memberjunction/core-entities": "4.1.0",
25
+ "@memberjunction/core": "4.1.0"
26
26
  },
27
27
  "repository": {
28
28
  "type": "git",
package/readme.md CHANGED
@@ -1,15 +1,49 @@
1
1
  # @memberjunction/data-context
2
2
 
3
- The `@memberjunction/data-context` library provides a comprehensive framework for managing data contexts in MemberJunction applications. It enables developers to define, load, and manipulate collections of related data items across different tiers of an application.
4
-
5
- ## Overview
6
-
7
- A Data Context in MemberJunction represents a collection of data items that can include:
8
- - **Views**: Filtered and customized views of entity data
9
- - **Queries**: Pre-defined SQL queries registered in the system
10
- - **Full Entities**: Complete data sets from an entity
11
- - **Single Records**: Individual entity records with optional related data
12
- - **SQL Statements**: Direct SQL queries (server-side only)
3
+ The `@memberjunction/data-context` library provides a metadata-driven framework for managing collections of related data items in MemberJunction applications. It enables developers to define, load, persist, and manipulate data from multiple sources -- views, queries, entities, single records, and raw SQL -- through a unified, type-safe API that works across both client and server tiers.
4
+
5
+ ## Architecture Overview
6
+
7
+ ```mermaid
8
+ graph TD
9
+ subgraph Consumer["Consumer Code"]
10
+ style Consumer fill:#2d6a9f,stroke:#1a4971,color:#fff
11
+ A["Application / Agent / Workflow"]
12
+ end
13
+
14
+ subgraph DataContextPkg["@memberjunction/data-context"]
15
+ style DataContextPkg fill:#7c5295,stroke:#563a6b,color:#fff
16
+ DC["DataContext"]
17
+ DCI["DataContextItem"]
18
+ DCFI["DataContextFieldInfo"]
19
+ end
20
+
21
+ subgraph Sources["Data Sources"]
22
+ style Sources fill:#2d8659,stroke:#1a5c3a,color:#fff
23
+ V["Views (RunView)"]
24
+ Q["Queries (RunQuery)"]
25
+ E["Full Entities"]
26
+ SR["Single Records"]
27
+ SQL["SQL Statements"]
28
+ end
29
+
30
+ subgraph Persistence["Persistence Layer"]
31
+ style Persistence fill:#b8762f,stroke:#8a5722,color:#fff
32
+ DCE["DataContextEntity"]
33
+ DCIE["DataContextItemEntity"]
34
+ end
35
+
36
+ A --> DC
37
+ DC -->|"manages"| DCI
38
+ DCI -->|"field metadata"| DCFI
39
+ DCI -->|"loads from"| V
40
+ DCI -->|"loads from"| Q
41
+ DCI -->|"loads from"| E
42
+ DCI -->|"loads from"| SR
43
+ DCI -->|"loads from"| SQL
44
+ DC -->|"persists via"| DCE
45
+ DC -->|"persists items via"| DCIE
46
+ ```
13
47
 
14
48
  ## Installation
15
49
 
@@ -17,54 +51,167 @@ A Data Context in MemberJunction represents a collection of data items that can
17
51
  npm install @memberjunction/data-context
18
52
  ```
19
53
 
20
- ## Key Features
54
+ ## Key Concepts
55
+
56
+ A **Data Context** is a named collection of **Data Context Items**, each of which represents a distinct data source. An item can be one of five types:
57
+
58
+ | Type | Description | Required Fields |
59
+ |-----------------|--------------------------------------------------|----------------------------------|
60
+ | `view` | A saved MemberJunction User View | `ViewID`, `EntityID` |
61
+ | `query` | A registered MemberJunction Query | `QueryID` |
62
+ | `full_entity` | All records from an entity | `EntityID` |
63
+ | `single_record` | One record, optionally with related entity data | `EntityID`, `RecordID` |
64
+ | `sql` | A raw SQL statement (server-side only) | `SQL` |
65
+
66
+ ## Class Hierarchy
67
+
68
+ ```mermaid
69
+ classDiagram
70
+ class DataContext {
71
+ +ID: string
72
+ +DataContextEntity: DataContextEntity
73
+ +Items: DataContextItem[]
74
+ +LoadMetadata(id, contextUser, provider) Promise~boolean~
75
+ +LoadData(dataSource, forceRefresh, ...) Promise~boolean~
76
+ +Load(id, dataSource, ...) Promise~boolean~
77
+ +SaveItems(contextUser, persistItemData) Promise~boolean~
78
+ +AddDataContextItem() DataContextItem
79
+ +ValidateDataExists(ignoreFailedLoadItems) boolean
80
+ +ConvertToSimpleObject(itemPrefix, includeFailedLoadItems) object
81
+ +CreateSimpleObjectTypeDefinition(itemPrefix, includeFailedLoadItems) string
82
+ +LoadDataFromObject(data) boolean
83
+ +Clone(context, includeData, contextUser)$ Promise~DataContext~
84
+ +FromRawData(rawData)$ Promise~DataContext~
85
+ +CreateDataContextItem()$ DataContextItem
86
+ +MapEntityFieldsToDataContextFields(entity)$ DataContextFieldInfo[]
87
+ }
88
+
89
+ class DataContextItem {
90
+ +Type: ItemType
91
+ +RecordID: string
92
+ +EntityID: string
93
+ +ViewID: string
94
+ +QueryID: string
95
+ +RecordName: string
96
+ +SQL: string
97
+ +CodeName: string
98
+ +EntityName: string
99
+ +Fields: DataContextFieldInfo[]
100
+ +DataContextItemID: string
101
+ +Data: object[]
102
+ +DataLoaded: boolean
103
+ +DataLoadingError: string
104
+ +Description: string
105
+ +AdditionalDescription: string
106
+ +LoadData(dataSource, ...) Promise~boolean~
107
+ +LoadDataFromObject(data) boolean
108
+ +ValidateDataExists(ignoreFailedLoad) boolean
109
+ +FromViewEntity(viewEntity)$ DataContextItem
110
+ +FromSingleRecord(record)$ DataContextItem
111
+ +FromQuery(query)$ DataContextItem
112
+ +FromFullEntity(entity)$ DataContextItem
113
+ +FromRawItem(rawItem)$ DataContextItem
114
+ #LoadFromView(contextUser) Promise~boolean~
115
+ #LoadFromFullEntity(contextUser) Promise~boolean~
116
+ #LoadFromSingleRecord(contextUser, ...) Promise~boolean~
117
+ #LoadFromQuery(contextUser) Promise~boolean~
118
+ #LoadFromSQL(dataSource, contextUser) Promise~boolean~
119
+ }
120
+
121
+ class DataContextFieldInfo {
122
+ +Name: string
123
+ +Type: string
124
+ +Description: string
125
+ }
126
+
127
+ class DataContextItemServer {
128
+ #LoadFromSQL(dataSource, contextUser) Promise~boolean~
129
+ }
130
+
131
+ DataContext "1" --> "*" DataContextItem : contains
132
+ DataContextItem "1" --> "*" DataContextFieldInfo : describes fields
133
+ DataContextItemServer --|> DataContextItem : extends
134
+ ```
135
+
136
+ ## Data Lifecycle
137
+
138
+ ```mermaid
139
+ flowchart LR
140
+ subgraph Step1["1. Create"]
141
+ style Step1 fill:#2d6a9f,stroke:#1a4971,color:#fff
142
+ C1["new DataContext()"]
143
+ end
144
+
145
+ subgraph Step2["2. Load Metadata"]
146
+ style Step2 fill:#7c5295,stroke:#563a6b,color:#fff
147
+ C2["LoadMetadata(id)"]
148
+ end
21
149
 
22
- - **Type-safe data context management**: Full TypeScript support with proper typing
23
- - **Flexible data loading**: Support for various data sources including views, queries, entities, and SQL
24
- - **Metadata-driven**: Automatic loading of metadata from the MemberJunction system
25
- - **Transaction support**: Save multiple data context items in a single transaction
26
- - **Cross-tier compatibility**: Works across server and client tiers with appropriate implementations
27
- - **Data persistence**: Optional saving of loaded data to the database
150
+ subgraph Step3["3. Load Data"]
151
+ style Step3 fill:#2d8659,stroke:#1a5c3a,color:#fff
152
+ C3["LoadData(dataSource)"]
153
+ end
154
+
155
+ subgraph Step4["4. Process"]
156
+ style Step4 fill:#b8762f,stroke:#8a5722,color:#fff
157
+ C4["ValidateDataExists()\nConvertToSimpleObject()"]
158
+ end
159
+
160
+ subgraph Step5["5. Persist"]
161
+ style Step5 fill:#2d6a9f,stroke:#1a4971,color:#fff
162
+ C5["SaveItems(contextUser)"]
163
+ end
164
+
165
+ Step1 --> Step2 --> Step3 --> Step4 --> Step5
166
+ ```
28
167
 
29
168
  ## Usage
30
169
 
31
- ### Basic Example
170
+ ### Loading an Existing Data Context
171
+
172
+ Use `Load()` to fetch both metadata and data in a single call:
32
173
 
33
174
  ```typescript
34
- import { DataContext, DataContextItem } from '@memberjunction/data-context';
35
- import { Metadata } from '@memberjunction/core';
175
+ import { DataContext } from '@memberjunction/data-context';
36
176
 
37
- // Create a new data context
38
177
  const context = new DataContext();
39
-
40
- // Load metadata and data for an existing data context
41
- const dataContextID = 'your-data-context-id';
42
178
  const loaded = await context.Load(
43
- dataContextID,
44
- dataSource, // Required for SQL type items (server-side only)
45
- false, // forceRefresh
46
- true, // loadRelatedDataOnSingleRecords
47
- 10, // maxRecordsPerRelationship
48
- userInfo // contextUser
179
+ dataContextID, // ID of the data context record
180
+ dataSource, // Required only for SQL-type items (server-side)
181
+ false, // forceRefresh -- reload even if data is cached
182
+ true, // loadRelatedDataOnSingleRecords
183
+ 10, // maxRecordsPerRelationship
184
+ contextUser // required on server-side
49
185
  );
50
186
 
51
187
  if (loaded) {
52
- // Access the loaded data
53
188
  context.Items.forEach(item => {
54
- console.log(`Item: ${item.Description}`);
55
- console.log(`Data rows: ${item.Data?.length || 0}`);
189
+ console.log(`${item.Description}: ${item.Data?.length ?? 0} rows`);
56
190
  });
57
191
  }
58
192
  ```
59
193
 
60
- ### Creating Data Context Items
194
+ Alternatively, call `LoadMetadata()` and `LoadData()` separately for finer control:
195
+
196
+ ```typescript
197
+ const context = new DataContext();
198
+ await context.LoadMetadata(dataContextID, contextUser);
199
+
200
+ // Inspect or modify items before loading data
201
+ console.log(`Items to load: ${context.Items.length}`);
202
+
203
+ await context.LoadData(dataSource, false, true, 10, contextUser);
204
+ ```
205
+
206
+ ### Creating Items Programmatically
207
+
208
+ Each item type has a dedicated static factory method.
61
209
 
62
210
  #### From a View
63
211
 
64
212
  ```typescript
65
213
  import { UserViewEntityExtended } from '@memberjunction/core-entities';
66
214
 
67
- // Assuming you have a view entity loaded
68
215
  const viewEntity: UserViewEntityExtended = await md.GetEntityObject<UserViewEntityExtended>('User Views');
69
216
  await viewEntity.Load(viewID);
70
217
 
@@ -77,7 +224,6 @@ context.Items.push(viewItem);
77
224
  ```typescript
78
225
  import { BaseEntity } from '@memberjunction/core';
79
226
 
80
- // Assuming you have an entity record loaded
81
227
  const record: BaseEntity = await md.GetEntityObject('Customers');
82
228
  await record.Load(recordID);
83
229
 
@@ -90,8 +236,7 @@ context.Items.push(recordItem);
90
236
  ```typescript
91
237
  import { QueryInfo } from '@memberjunction/core';
92
238
 
93
- // Get query info from metadata
94
- const queryInfo = md.Queries.find(q => q.Name === 'My Query');
239
+ const queryInfo = md.Queries.find(q => q.Name === 'Monthly Revenue');
95
240
  if (queryInfo) {
96
241
  const queryItem = DataContextItem.FromQuery(queryInfo);
97
242
  context.Items.push(queryItem);
@@ -103,7 +248,6 @@ if (queryInfo) {
103
248
  ```typescript
104
249
  import { EntityInfo } from '@memberjunction/core';
105
250
 
106
- // Get entity info from metadata
107
251
  const entityInfo = md.Entities.find(e => e.Name === 'Products');
108
252
  if (entityInfo) {
109
253
  const entityItem = DataContextItem.FromFullEntity(entityInfo);
@@ -115,75 +259,85 @@ if (entityInfo) {
115
259
 
116
260
  ```typescript
117
261
  // Load data for all items in the context
118
- const dataLoaded = await context.LoadData(
119
- dataSource, // Required for SQL type items
262
+ const allLoaded = await context.LoadData(
263
+ dataSource, // required for SQL-type items
120
264
  false, // forceRefresh
121
265
  true, // loadRelatedDataOnSingleRecords
122
- 10 // maxRecordsPerRelationship
266
+ 10 // maxRecordsPerRelationship
123
267
  );
124
268
 
125
- // Or load data for a specific item
269
+ // Or load data for a single item
126
270
  const itemLoaded = await context.Items[0].LoadData(
127
- dataSource,
128
- false,
129
- true,
130
- 10
271
+ dataSource, false, true, 10, contextUser
131
272
  );
132
273
  ```
133
274
 
275
+ ### Loading Pre-fetched Data
276
+
277
+ If you already have the data available (for example, from a cache or external API), load it directly:
278
+
279
+ ```typescript
280
+ // Load into a single item
281
+ const item = context.Items[0];
282
+ item.LoadDataFromObject(myDataArray);
283
+
284
+ // Load into all items at once (2D array, one sub-array per item in order)
285
+ context.LoadDataFromObject([
286
+ rowsForItem0,
287
+ rowsForItem1,
288
+ rowsForItem2
289
+ ]);
290
+ ```
291
+
134
292
  ### Saving Data Context Items
135
293
 
136
294
  ```typescript
137
- // Save all items in the context to the database
138
295
  const saved = await context.SaveItems(
139
- userInfo, // contextUser
140
- true // persistItemData - saves actual data, not just metadata
296
+ contextUser, // context user for server-side operations
297
+ true // persistItemData -- also save loaded data as JSON
141
298
  );
142
299
 
143
300
  if (saved) {
144
- console.log('Data context items saved successfully');
145
301
  // Each item now has a DataContextItemID populated
302
+ context.Items.forEach(item => {
303
+ console.log(`Saved item ${item.DataContextItemID}`);
304
+ });
146
305
  }
147
306
  ```
148
307
 
149
- ### Working with Data
308
+ Items are saved in a single transaction group -- either all succeed or none are committed.
309
+
310
+ ### Working with Loaded Data
150
311
 
151
312
  ```typescript
152
- // Validate that all items have data loaded
313
+ // Validate all items have data
153
314
  if (context.ValidateDataExists()) {
154
- // Convert to a simple object for easier manipulation
155
- const simpleData = context.ConvertToSimpleObject('item_', false);
315
+ // Convert to a flat keyed object
316
+ const simpleObj = context.ConvertToSimpleObject('item_', false);
156
317
  // Result: { item_0: [...], item_1: [...], ... }
157
-
158
- // Get type definition for the data structure
318
+
319
+ // Generate a TypeScript-style type definition string
159
320
  const typeDef = context.CreateSimpleObjectTypeDefinition('item_');
160
- console.log(typeDef);
161
321
  // Output: {item_0: []; // View: Customer List, From Entity: Customers\n...}
162
322
  }
163
323
 
164
- // Access individual item data
324
+ // Check individual items for errors
165
325
  context.Items.forEach(item => {
166
326
  if (item.DataLoaded && item.Data) {
167
327
  console.log(`${item.Description}: ${item.Data.length} rows`);
168
-
169
- // Process the data
170
- item.Data.forEach(row => {
171
- // Work with row data
172
- });
173
328
  } else if (item.DataLoadingError) {
174
- console.error(`Error loading ${item.Description}: ${item.DataLoadingError}`);
329
+ console.error(`Failed: ${item.DataLoadingError}`);
175
330
  }
176
331
  });
177
332
  ```
178
333
 
179
- ### Cloning Data Contexts
334
+ ### Cloning a Data Context
180
335
 
181
336
  ```typescript
182
- // Clone an existing data context
183
337
  const clonedContext = await DataContext.Clone(
184
338
  originalContext,
185
- true, // includeData - copies the data along with metadata
186
- userInfo // contextUser
339
+ true, // includeData -- copy loaded data into the clone
340
+ contextUser
187
341
  );
188
342
 
189
343
  if (clonedContext) {
@@ -191,116 +345,112 @@ if (clonedContext) {
191
345
  }
192
346
  ```
193
347
 
194
- ## API Reference
195
-
196
- ### DataContext Class
197
-
198
- #### Properties
199
-
200
- - `ID: string` - The unique identifier of the data context
201
- - `DataContextEntity: DataContextEntity` - The metadata entity for the data context
202
- - `Items: DataContextItem[]` - Array of data context items
203
-
204
- #### Methods
205
-
206
- - `async LoadMetadata(DataContextID: string, contextUser?: UserInfo, provider?: IMetadataProvider): Promise<boolean>`
207
- - Loads only the metadata for the data context and its items
208
-
209
- - `async LoadData(dataSource: any, forceRefresh?: boolean, loadRelatedDataOnSingleRecords?: boolean, maxRecordsPerRelationship?: number, contextUser?: UserInfo): Promise<boolean>`
210
- - Loads data for all items in the context
211
-
212
- - `async Load(DataContextID: string, dataSource: any, forceRefresh?: boolean, loadRelatedDataOnSingleRecords?: boolean, maxRecordsPerRelationship?: number, contextUser?: UserInfo): Promise<boolean>`
213
- - Loads both metadata and data in one operation
214
-
215
- - `async SaveItems(contextUser?: UserInfo, persistItemData?: boolean): Promise<boolean>`
216
- - Saves all data context items to the database
217
-
218
- - `AddDataContextItem(): DataContextItem`
219
- - Creates and adds a new item to the context
220
-
221
- - `ValidateDataExists(ignoreFailedLoadItems?: boolean): boolean`
222
- - Checks if all items have data loaded
223
-
224
- - `ConvertToSimpleObject(itemPrefix?: string, includeFailedLoadItems?: boolean): any`
225
- - Converts the context to a simple object structure
348
+ ### Reconstructing from Raw Data
226
349
 
227
- - `CreateSimpleObjectTypeDefinition(itemPrefix?: string, includeFailedLoadItems?: boolean): string`
228
- - Generates TypeScript type definition for the data structure
229
-
230
- - `LoadDataFromObject(data: any[][]): boolean`
231
- - Loads pre-fetched data into the context
232
-
233
- - `static async Clone(context: DataContext, includeData?: boolean, contextUser?: UserInfo): Promise<DataContext>`
234
- - Creates a deep copy of a data context
235
-
236
- - `static async FromRawData(rawData: any): Promise<DataContext>`
237
- - Creates a context from raw data object
238
-
239
- ### DataContextItem Class
240
-
241
- #### Properties
242
-
243
- - `Type: 'view' | 'query' | 'full_entity' | 'sql' | 'single_record'` - The type of data item
244
- - `RecordID: string` - Primary key for single_record types
245
- - `EntityID?: string` - Entity identifier
246
- - `ViewID?: string` - View identifier
247
- - `QueryID?: string` - Query identifier
248
- - `RecordName: string` - Name of the view, query, or entity
249
- - `SQL?: string` - SQL statement for 'sql' type
250
- - `EntityName?: string` - Name of the entity
251
- - `Fields: DataContextFieldInfo[]` - Field metadata
252
- - `Data: any[]` - The loaded data
253
- - `DataLoaded: boolean` - Indicates if data has been loaded
254
- - `DataLoadingError?: string` - Error message if loading failed
255
- - `Description: string` - Auto-generated description
256
- - `AdditionalDescription?: string` - Optional custom description
257
-
258
- #### Methods
259
-
260
- - `async LoadData(dataSource: any, forceRefresh?: boolean, loadRelatedDataOnSingleRecords?: boolean, maxRecordsPerRelationship?: number, contextUser?: UserInfo): Promise<boolean>`
261
- - Loads data for this specific item
350
+ ```typescript
351
+ const context = await DataContext.FromRawData(rawObject);
352
+ // rawObject should have { ID, Items: [ { Type, RecordID, ... }, ... ] }
353
+ ```
262
354
 
263
- - `LoadDataFromObject(data: any[]): boolean`
264
- - Loads pre-fetched data into the item
355
+ ## Server-Side SQL Support
265
356
 
266
- - `ValidateDataExists(ignoreFailedLoad?: boolean): boolean`
267
- - Validates that data has been loaded
357
+ The base `DataContextItem` class throws an error when `LoadFromSQL()` is called because raw SQL execution is inherently a server-side operation. The companion package `@memberjunction/data-context-server` provides `DataContextItemServer`, which overrides this method using an `mssql` connection pool:
268
358
 
269
- - `static FromViewEntity(viewEntity: UserViewEntityExtended): DataContextItem`
270
- - `static FromSingleRecord(singleRecord: BaseEntity): DataContextItem`
271
- - `static FromQuery(query: QueryInfo): DataContextItem`
272
- - `static FromFullEntity(entity: EntityInfo): DataContextItem`
273
- - `static FromRawItem(rawItem: any): DataContextItem`
359
+ ```mermaid
360
+ flowchart TD
361
+ subgraph Client["Client Tier"]
362
+ style Client fill:#2d6a9f,stroke:#1a4971,color:#fff
363
+ DCI_C["DataContextItem\n(base class)"]
364
+ end
274
365
 
275
- ### DataContextFieldInfo Class
366
+ subgraph Server["Server Tier"]
367
+ style Server fill:#2d8659,stroke:#1a5c3a,color:#fff
368
+ DCI_S["DataContextItemServer\n(@RegisterClass override)"]
369
+ MSSQL["mssql ConnectionPool"]
370
+ end
276
371
 
277
- ```typescript
278
- class DataContextFieldInfo {
279
- Name: string;
280
- Type: string;
281
- Description?: string;
282
- }
372
+ DCI_C -->|"LoadFromSQL() throws error"| X["Not supported client-side"]
373
+ DCI_S -->|"LoadFromSQL() executes via"| MSSQL
374
+ DCI_S -.->|"extends"| DCI_C
283
375
  ```
284
376
 
285
- ## Server-Side Considerations
377
+ `DataContextItemServer` is registered via `@RegisterClass` with a higher priority, so the MemberJunction class factory automatically returns the server variant when the server package is included. No code changes are needed -- just include `@memberjunction/data-context-server` in your server project's dependencies.
378
+
379
+ ## API Reference
286
380
 
287
- For SQL type data context items, you'll need to use the server-side implementation from `@memberjunction/data-context-server` which properly handles SQL execution with appropriate security and data source management.
381
+ ### DataContext
382
+
383
+ | Method | Returns | Description |
384
+ |--------|---------|-------------|
385
+ | `LoadMetadata(id, contextUser?, provider?)` | `Promise<boolean>` | Loads metadata for the context and all its items from the database |
386
+ | `LoadData(dataSource, forceRefresh?, loadRelated?, maxRecords?, contextUser?)` | `Promise<boolean>` | Loads data for all items; must call `LoadMetadata()` first |
387
+ | `Load(id, dataSource, forceRefresh?, loadRelated?, maxRecords?, contextUser?)` | `Promise<boolean>` | Combined metadata + data load in one call |
388
+ | `SaveItems(contextUser?, persistItemData?)` | `Promise<boolean>` | Persists all items to the database in a single transaction |
389
+ | `AddDataContextItem()` | `DataContextItem` | Creates a new item and adds it to `Items` |
390
+ | `ValidateDataExists(ignoreFailedLoadItems?)` | `boolean` | Returns true if all items have their `Data` property set |
391
+ | `ConvertToSimpleObject(itemPrefix?, includeFailedLoadItems?)` | `object` | Converts context to a flat keyed object |
392
+ | `CreateSimpleObjectTypeDefinition(itemPrefix?, includeFailedLoadItems?)` | `string` | Generates a type definition string for the simple object |
393
+ | `LoadDataFromObject(data)` | `boolean` | Loads pre-fetched data as a 2D array mapped by item index |
394
+ | `Clone(context, includeData?, contextUser?)` (static) | `Promise<DataContext>` | Deep-clones a context and its items into new database records |
395
+ | `FromRawData(rawData)` (static) | `Promise<DataContext>` | Reconstructs a `DataContext` from a plain object |
396
+ | `CreateDataContextItem()` (static) | `DataContextItem` | Creates a new item via the class factory (does not add to context) |
397
+ | `MapEntityFieldsToDataContextFields(entity)` (static) | `DataContextFieldInfo[]` | Maps `EntityInfo` fields to simplified field info objects |
398
+
399
+ ### DataContextItem
400
+
401
+ | Property | Type | Description |
402
+ |----------|------|-------------|
403
+ | `Type` | `'view' \| 'query' \| 'full_entity' \| 'sql' \| 'single_record'` | The data source type |
404
+ | `RecordID` | `string` | Primary key for `single_record` items (comma-separated for composite keys) |
405
+ | `EntityID` | `string` | Entity identifier (not used for `query` or `sql` types) |
406
+ | `ViewID` | `string` | View identifier (only for `view` type) |
407
+ | `QueryID` | `string` | Query identifier (only for `query` type) |
408
+ | `RecordName` | `string` | Display name of the view, query, or entity |
409
+ | `SQL` | `string` | SQL statement (only for `sql` type) |
410
+ | `CodeName` | `string` | System-generated unique code name within the context |
411
+ | `EntityName` | `string` | Entity name (not used for `query` or `sql` types) |
412
+ | `Fields` | `DataContextFieldInfo[]` | Field metadata for the item |
413
+ | `DataContextItemID` | `string` | Database record ID (populated after save) |
414
+ | `Data` | `object[]` | The loaded data rows |
415
+ | `DataLoaded` | `boolean` | Whether data has been successfully loaded |
416
+ | `DataLoadingError` | `string` | Error message if loading failed |
417
+ | `Description` | `string` (readonly) | Auto-generated description based on type |
418
+ | `AdditionalDescription` | `string` | Optional supplementary description |
419
+
420
+ | Static Factory Method | Description |
421
+ |----------------------|-------------|
422
+ | `FromViewEntity(viewEntity)` | Creates an item from a `UserViewEntityExtended` |
423
+ | `FromSingleRecord(record)` | Creates an item from a `BaseEntity` instance |
424
+ | `FromQuery(query)` | Creates an item from a `QueryInfo` |
425
+ | `FromFullEntity(entity)` | Creates an item from an `EntityInfo` |
426
+ | `FromRawItem(rawItem)` | Creates an item from a plain object |
427
+
428
+ ### DataContextFieldInfo
429
+
430
+ | Property | Type | Description |
431
+ |----------|------|-------------|
432
+ | `Name` | `string` | Field name |
433
+ | `Type` | `string` | Field data type |
434
+ | `Description` | `string` (optional) | Field description |
288
435
 
289
436
  ## Dependencies
290
437
 
291
- - `@memberjunction/global`: Core global utilities and class factory
292
- - `@memberjunction/core-entities`: Entity definitions for MemberJunction
293
- - `@memberjunction/core`: Core MemberJunction functionality
438
+ | Package | Purpose |
439
+ |---------|---------|
440
+ | `@memberjunction/global` | Class factory and `@RegisterClass` decorator |
441
+ | `@memberjunction/core` | Core framework: `Metadata`, `RunView`, `RunQuery`, `BaseEntity`, `EntityInfo`, `UserInfo` |
442
+ | `@memberjunction/core-entities` | Generated entity classes: `DataContextEntity`, `DataContextItemEntity`, `UserViewEntityExtended` |
294
443
 
295
444
  ## Best Practices
296
445
 
297
- 1. **Always load metadata before data**: Use `LoadMetadata()` before `LoadData()` or simply use `Load()` which does both
298
- 2. **Handle loading errors**: Check `DataLoaded` and `DataLoadingError` properties on items
299
- 3. **Use appropriate data sources**: SQL type items require server-side data sources
300
- 4. **Consider performance**: Use `maxRecordsPerRelationship` to limit related data loading
301
- 5. **Validate before use**: Call `ValidateDataExists()` before processing data
302
- 6. **Use transactions**: When saving multiple items, they're automatically wrapped in a transaction
446
+ 1. **Always load metadata before data.** Use `LoadMetadata()` then `LoadData()`, or use the combined `Load()` method.
447
+ 2. **Check `DataLoaded` and `DataLoadingError`** on each item after loading. `LoadData()` does not throw exceptions for individual item failures -- it returns `false` and populates the error properties.
448
+ 3. **Pass `contextUser` on the server side.** Server code serves multiple users concurrently and must include the context user for proper security and audit tracking.
449
+ 4. **Use `maxRecordsPerRelationship`** to limit the amount of related data loaded for `single_record` items, especially in production environments.
450
+ 5. **Validate before processing.** Call `ValidateDataExists()` before working with data to ensure all items are ready.
451
+ 6. **Leverage transactions.** `SaveItems()` automatically wraps all item saves in a transaction group for atomicity.
452
+ 7. **Include `@memberjunction/data-context-server`** in server projects if any items use the `sql` type. The class factory handles the override automatically.
303
453
 
304
454
  ## License
305
455
 
306
- ISC
456
+ ISC