@mj-biz-apps/common-entities 5.4.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.
package/README.md ADDED
@@ -0,0 +1,289 @@
1
+ # MemberJunction Generated Entities
2
+
3
+ This package contains automatically generated TypeScript entity classes that provide strongly-typed representations of database tables in the MemberJunction ecosystem.
4
+
5
+ ## Overview
6
+
7
+ `mj_generatedentities` contains entity subclasses that are generated and maintained by MemberJunction's code generation system. These entities map directly to database tables and views, providing type-safe access to your data while abstracting away the complexity of data access.
8
+
9
+ Key features:
10
+ - **Type-safe data access**: All entity properties are strongly typed to match database columns
11
+ - **Generated code**: Automatically maintained by MemberJunction to stay in sync with database schema
12
+ - **Schema validation**: Uses Zod for runtime validation of entity data
13
+ - **Extensible**: Base classes for extending with custom business logic
14
+ - **MemberJunction integration**: Works seamlessly with MemberJunction's data context and API
15
+ - **Webpack tree-shaking support**: Includes `LoadGeneratedEntities()` function to ensure proper bundling
16
+
17
+ ## Installation
18
+
19
+ This package is a private package used internally within MemberJunction applications:
20
+
21
+ ```bash
22
+ # Within the MemberJunction monorepo workspace
23
+ npm install
24
+ ```
25
+
26
+ Note: This package is marked as private (`"private": true`) in package.json and is not published to npm.
27
+
28
+ ## Dependencies
29
+
30
+ - `@memberjunction/core` (v2.43.0): Core MemberJunction functionality including BaseEntity
31
+ - `@memberjunction/global` (v2.43.0): Global utilities and constants
32
+ - `zod` (v3.23.8): Schema validation library
33
+
34
+ ## Package Structure
35
+
36
+ ```
37
+ mj_generatedentities/
38
+ ├── src/
39
+ │ ├── index.ts # Main export file
40
+ │ ├── generated/
41
+ │ │ └── entity_subclasses.ts # Auto-generated entity classes
42
+ │ └── demo/
43
+ │ └── demoContactEntitySubclass.ts # Example of extending entities
44
+ ├── dist/ # Compiled JavaScript output
45
+ ├── package.json
46
+ ├── tsconfig.json
47
+ └── README.md
48
+ ```
49
+
50
+ ## Usage
51
+
52
+ ### Important: MemberJunction Entity Creation Pattern
53
+
54
+ **Never directly instantiate entity classes**. Always use the Metadata system to ensure proper class registration:
55
+
56
+ ```typescript
57
+ import { Metadata } from '@memberjunction/core';
58
+
59
+ // ❌ Wrong - bypasses MJ class system
60
+ const entity = new UserEntity();
61
+
62
+ // ✅ Correct - uses MJ metadata system
63
+ const md = new Metadata();
64
+ const entity = await md.GetEntityObject<UserEntity>('Users');
65
+ ```
66
+
67
+ ### Loading Generated Entities for Webpack
68
+
69
+ To ensure generated entities are included in webpack builds (avoiding tree-shaking issues):
70
+
71
+ ```typescript
72
+ import { LoadGeneratedEntities } from 'mj_generatedentities';
73
+
74
+ // Call this function early in your application initialization
75
+ LoadGeneratedEntities();
76
+ ```
77
+
78
+ ### Working with Entity Classes
79
+
80
+ ```typescript
81
+ import { Metadata, RunView } from '@memberjunction/core';
82
+
83
+ // Load a single entity by ID
84
+ async function getUserById(userId: string): Promise<UserEntity | null> {
85
+ const md = new Metadata();
86
+ const user = await md.GetEntityObject<UserEntity>('Users');
87
+
88
+ if (await user.Load(userId)) {
89
+ return user;
90
+ }
91
+ return null;
92
+ }
93
+
94
+ // Load multiple entities with RunView
95
+ async function getActiveUsers(): Promise<UserEntity[]> {
96
+ const rv = new RunView();
97
+ const result = await rv.RunView<UserEntity>({
98
+ EntityName: 'Users',
99
+ ExtraFilter: `IsActive = 1`,
100
+ OrderBy: 'CreatedAt DESC',
101
+ ResultType: 'entity_object' // Returns entity objects, not raw data
102
+ });
103
+
104
+ return result.Results;
105
+ }
106
+
107
+ // Update an entity
108
+ async function updateUser(userId: string, updates: Partial<UserEntity>): Promise<boolean> {
109
+ const md = new Metadata();
110
+ const user = await md.GetEntityObject<UserEntity>('Users');
111
+
112
+ if (await user.Load(userId)) {
113
+ // Apply updates
114
+ Object.assign(user, updates);
115
+
116
+ // Save changes
117
+ const result = await user.Save();
118
+ return result.Success;
119
+ }
120
+
121
+ return false;
122
+ }
123
+ ```
124
+
125
+ ### Entity Relationships
126
+
127
+ ```typescript
128
+ import { Metadata, RunView } from '@memberjunction/core';
129
+
130
+ async function getUserWithRoles(userId: string) {
131
+ const md = new Metadata();
132
+ const user = await md.GetEntityObject<UserEntity>('Users');
133
+
134
+ if (await user.Load(userId)) {
135
+ // Load related entities using RunView
136
+ const rv = new RunView();
137
+ const rolesResult = await rv.RunView<RoleEntity>({
138
+ EntityName: 'User Roles',
139
+ ExtraFilter: `UserID = '${userId}'`,
140
+ ResultType: 'entity_object'
141
+ });
142
+
143
+ return {
144
+ user,
145
+ roles: rolesResult.Results
146
+ };
147
+ }
148
+
149
+ return null;
150
+ }
151
+ ```
152
+
153
+ ### Extending Generated Entities
154
+
155
+ You can extend the generated entities with custom business logic. Use the `@RegisterClass` decorator to ensure proper registration:
156
+
157
+ ```typescript
158
+ import { RegisterClass } from '@memberjunction/global';
159
+ import { BaseEntity } from '@memberjunction/core';
160
+
161
+ // Example from demo/demoContactEntitySubclass.ts
162
+ @RegisterClass(BaseEntity, 'Contacts', 1)
163
+ export class ContactEntity extends ContactBaseEntity {
164
+ // Override property getters/setters (must override both)
165
+ get FirstName(): string {
166
+ console.log("Getting FirstName from subclass");
167
+ return super.FirstName;
168
+ }
169
+
170
+ set FirstName(value: string) {
171
+ super.FirstName = value;
172
+ console.log("Setting FirstName from subclass");
173
+ }
174
+
175
+ // Add custom methods
176
+ async getFullName(): Promise<string> {
177
+ return `${this.FirstName} ${this.LastName}`;
178
+ }
179
+
180
+ // Add custom validation
181
+ override async Validate(): Promise<ValidationResult> {
182
+ const result = await super.Validate();
183
+
184
+ if (this.Email && !this.Email.includes('@')) {
185
+ result.Success = false;
186
+ result.Errors.push({
187
+ Source: 'Email',
188
+ Message: 'Email must contain @ symbol',
189
+ Type: ValidationErrorType.Failure
190
+ });
191
+ }
192
+
193
+ return result;
194
+ }
195
+ }
196
+ ```
197
+
198
+ ### Important Notes on Extending Entities
199
+
200
+ 1. **Always use `@RegisterClass` decorator** to register your subclass with MemberJunction
201
+ 2. **When overriding property getters/setters**, you MUST override both getter and setter
202
+ 3. **Call super methods** when overriding to maintain base functionality
203
+ 4. **Version parameter** in `@RegisterClass` allows for entity versioning
204
+
205
+ ### Zod Schema Validation
206
+
207
+ Generated entities use Zod for schema validation:
208
+
209
+ ```typescript
210
+ import { z } from 'zod';
211
+
212
+ // Each generated entity includes a Zod schema
213
+ const userSchema = z.object({
214
+ ID: z.string(),
215
+ FirstName: z.string(),
216
+ LastName: z.string(),
217
+ Email: z.string().email(),
218
+ IsActive: z.boolean(),
219
+ CreatedAt: z.date(),
220
+ UpdatedAt: z.date()
221
+ });
222
+
223
+ // Validate data before creating entities
224
+ async function createUserFromData(userData: unknown) {
225
+ const result = userSchema.safeParse(userData);
226
+
227
+ if (result.success) {
228
+ const md = new Metadata();
229
+ const user = await md.GetEntityObject<UserEntity>('Users');
230
+ await user.LoadFromData(result.data);
231
+
232
+ const saveResult = await user.Save();
233
+ return saveResult.Success;
234
+ } else {
235
+ console.error('Validation errors:', result.error.format());
236
+ return false;
237
+ }
238
+ }
239
+ ```
240
+
241
+ ## Entity Base Class Methods
242
+
243
+ All generated entities inherit from `BaseEntity` and provide these key methods:
244
+
245
+ | Method | Description |
246
+ |--------|-------------|
247
+ | `Load(id: CompositeKey)` | Loads an entity by its primary key |
248
+ | `LoadFromData(data: any)` | Populates entity from a data object |
249
+ | `Save(options?: EntitySaveOptions)` | Saves changes to the database |
250
+ | `Delete()` | Deletes the entity from the database |
251
+ | `Validate()` | Validates the entity, returns ValidationResult |
252
+ | `GetFieldValue(fieldName: string)` | Gets the value of a specific field |
253
+ | `SetFieldValue(fieldName: string, value: any)` | Sets the value of a specific field |
254
+ | `TransactionMode` | Property to control transaction behavior |
255
+
256
+ ## Build Scripts
257
+
258
+ ```bash
259
+ # Build the package
260
+ npm run build
261
+
262
+ # Development mode with auto-reload
263
+ npm run start
264
+
265
+ # Run tests (not implemented yet)
266
+ npm test
267
+ ```
268
+
269
+ ## Code Generation
270
+
271
+ This package is automatically maintained by MemberJunction's code generation system. The `entity_subclasses.ts` file is generated based on your database schema.
272
+
273
+ **Important**: Do not manually edit files in the `generated/` directory as they will be overwritten during the next code generation cycle.
274
+
275
+ To regenerate entities:
276
+ 1. Ensure your database schema is up to date
277
+ 2. Run the MemberJunction code generation tool from the appropriate package
278
+ 3. The generated files will be automatically updated
279
+
280
+ ## Development Notes
281
+
282
+ - The package includes TypeScript declaration files (`*.d.ts`) in the dist folder
283
+ - Source maps are generated for debugging
284
+ - The `loadModule` export in entity_subclasses.ts ensures the module is valid even when empty
285
+ - Use the demo file as a reference for creating custom entity subclasses
286
+
287
+ ## License
288
+
289
+ ISC
@@ -0,0 +1,11 @@
1
+ import { BaseEntity } from "@memberjunction/core";
2
+ declare class ContactBaseEntityStub extends BaseEntity {
3
+ get FirstName(): string;
4
+ set FirstName(value: string);
5
+ }
6
+ export declare class ContactDemoEntity extends ContactBaseEntityStub {
7
+ get FirstName(): string;
8
+ set FirstName(value: string);
9
+ }
10
+ export {};
11
+ //# sourceMappingURL=demoContactEntitySubclass.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"demoContactEntitySubclass.d.ts","sourceRoot":"","sources":["../../src/demo/demoContactEntitySubclass.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAKlD,cAAM,qBAAsB,SAAQ,UAAU;IAC1C,IAAI,SAAS,IAAI,MAAM,CAEtB;IACD,IAAI,SAAS,CAAC,KAAK,EAAE,MAAM,EAC1B;CACJ;AAKD,qBACa,iBAAkB,SAAQ,qBAAqB;IACxD,IAAI,SAAS,IAAI,MAAM,CAGtB;IACD,IAAI,SAAS,CAAC,KAAK,EAAE,MAAM,EAG1B;CACJ"}
@@ -0,0 +1,35 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ 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;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ //import { ContactBaseEntity } from "../generated/entity_subclasses.js";
8
+ import { BaseEntity } from "@memberjunction/core";
9
+ import { RegisterClass } from "@memberjunction/global";
10
+ // STUB base class- you would DELETE this and use a real base class from ../generated/entity_subclasses instead
11
+ class ContactBaseEntityStub extends BaseEntity {
12
+ get FirstName() {
13
+ return "";
14
+ }
15
+ set FirstName(value) {
16
+ }
17
+ }
18
+ // Super simple example of a sub-class for an example Contacts entity doesn't do anything but you can see here you can do stuff with sub-class overrides
19
+ // Also, important, if you're going to override the getter/setter for a property, you MUST call super.propertyName in the getter/setter AND you must also
20
+ // override BOTH the getter and setter, otherwise you'll get a runtime error! This is because the getter/setter is actually a single property on the object
21
+ let ContactDemoEntity = class ContactDemoEntity extends ContactBaseEntityStub /*here you would change this to sub-class the real base class from ../generated/entity_subclasses */ {
22
+ get FirstName() {
23
+ console.log("I'm getting the FirstName property from the sub-class!");
24
+ return super.FirstName;
25
+ }
26
+ set FirstName(value) {
27
+ super.FirstName = value;
28
+ console.log("I'm setting the FirstName property from the sub-class!");
29
+ }
30
+ };
31
+ ContactDemoEntity = __decorate([
32
+ RegisterClass(BaseEntity, 'Contacts', 1)
33
+ ], ContactDemoEntity);
34
+ export { ContactDemoEntity };
35
+ //# sourceMappingURL=demoContactEntitySubclass.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"demoContactEntitySubclass.js","sourceRoot":"","sources":["../../src/demo/demoContactEntitySubclass.ts"],"names":[],"mappings":";;;;;;AAAA,qEAAqE;AACrE,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAGvD,gHAAgH;AAChH,MAAM,qBAAsB,SAAQ,UAAU;IAC1C,IAAI,SAAS;QACT,OAAO,EAAE,CAAC;IACd,CAAC;IACD,IAAI,SAAS,CAAC,KAAa;IAC3B,CAAC;CACJ;AAED,yJAAyJ;AACzJ,yJAAyJ;AACzJ,2JAA2J;AAEpJ,IAAM,iBAAiB,GAAvB,MAAM,iBAAkB,SAAQ,qBAAqB,CAAC,oGAAoG;IAC7J,IAAI,SAAS;QACT,OAAO,CAAC,GAAG,CAAC,wDAAwD,CAAC,CAAA;QACrE,OAAO,KAAK,CAAC,SAAS,CAAC;IAC3B,CAAC;IACD,IAAI,SAAS,CAAC,KAAa;QACvB,KAAK,CAAC,SAAS,GAAG,KAAK,CAAC;QACxB,OAAO,CAAC,GAAG,CAAC,wDAAwD,CAAC,CAAA;IACzE,CAAC;CACJ,CAAA;AATY,iBAAiB;IAD7B,aAAa,CAAC,UAAU,EAAE,UAAU,EAAE,CAAC,CAAC;GAC5B,iBAAiB,CAS7B"}