@memberjunction/server 5.29.0 → 5.30.1

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.
@@ -1,4 +1,4 @@
1
- import { ApplicationInfo, EntitySaveOptions, LogError, LogStatus, Metadata, RunView, RunViewResult, UserInfo } from "@memberjunction/core";
1
+ import { ApplicationInfo, DatabaseProviderBase, EntitySaveOptions, LogError, LogStatus, Metadata, RunView, RunViewResult, UserInfo } from "@memberjunction/core";
2
2
  import { RegisterClass } from "@memberjunction/global";
3
3
  import { UserCache } from "@memberjunction/sqlserver-dataprovider";
4
4
  import { configInfo } from "../config.js";
@@ -43,81 +43,72 @@ export class NewUserBase {
43
43
  user.LinkedEntityRecordID = linkedEntityRecordId;
44
44
  }
45
45
 
46
- const saveResult: boolean = await user.Save();
47
- if(!saveResult){
48
- LogError(`Failed to create new user ${firstName} ${lastName} ${email}:`, undefined, user.LatestResult);
49
- return null;
50
- }
46
+ // Create the user and all of its role/application/app-entity records atomically.
47
+ // If any Save fails partway through, the whole provisioning rolls back so we never
48
+ // leave a half-created user with partial roles/applications behind.
49
+ const provider = Metadata.Provider as DatabaseProviderBase;
50
+ await provider.BeginTransaction();
51
+ try {
52
+ if (!await user.Save()) {
53
+ throw new Error(`Failed to create new user ${firstName} ${lastName} ${email}: ${user.LatestResult?.CompleteMessage ?? 'unknown error'}`);
54
+ }
51
55
 
52
- if(configInfo.userHandling && configInfo.userHandling.newUserRoles){
53
- // user created, now create however many roles we need to create for this user based on the config settings
54
- LogStatus(`User ${user.Email} created, assigning roles`);
55
- for (const role of configInfo.userHandling.newUserRoles) {
56
- const userRoleEntity: MJUserRoleEntity = await md.GetEntityObject<MJUserRoleEntity>('MJ: User Roles', contextUser);
57
- userRoleEntity.NewRecord();
58
- userRoleEntity.UserID = user.ID;
59
- const userRole = md.Roles.find(r => r.Name === role);
60
-
61
- if (!userRole) {
62
- LogError(`Role ${role} not found in the database, cannot assign to new user ${user.Name}`);
63
- continue;
64
- }
56
+ if(configInfo.userHandling && configInfo.userHandling.newUserRoles){
57
+ LogStatus(`User ${user.Email} created, assigning roles`);
58
+ for (const role of configInfo.userHandling.newUserRoles) {
59
+ const userRole = md.Roles.find(r => r.Name === role);
60
+ if (!userRole) {
61
+ LogError(`Role ${role} not found in the database, cannot assign to new user ${user.Name}`);
62
+ continue;
63
+ }
65
64
 
66
- userRoleEntity.RoleID = userRole.ID;
67
- const roleSaveResult: boolean = await userRoleEntity.Save();
68
- if(roleSaveResult){
65
+ const userRoleEntity: MJUserRoleEntity = await md.GetEntityObject<MJUserRoleEntity>('MJ: User Roles', contextUser);
66
+ userRoleEntity.NewRecord();
67
+ userRoleEntity.UserID = user.ID;
68
+ userRoleEntity.RoleID = userRole.ID;
69
+ if (!await userRoleEntity.Save()) {
70
+ throw new Error(`Failed to assign role ${role} to new user ${user.Name}: ${userRoleEntity.LatestResult?.CompleteMessage ?? 'unknown error'}`);
71
+ }
69
72
  LogStatus(`Assigned role ${role} to new user ${user.Name}`);
70
73
  }
71
- else{
72
- LogError(`Failed to assign role ${role} to new user ${user.Name}:`, undefined, userRoleEntity.LatestResult);
73
- }
74
-
75
74
  }
76
- }
77
75
 
78
- // Create UserApplication records if specified in the config
79
- if (configInfo.userHandling && configInfo.userHandling.CreateUserApplicationRecords) {
80
- LogStatus("Creating User Applications for new user: " + user.Name);
81
-
82
- // Determine which applications to create UserApplication records for
83
- // If UserApplications config array has entries, use those
84
- // Otherwise, fall back to applications with DefaultForNewUser = true
85
- let applicationsToCreate: ApplicationInfo[] = [];
86
-
87
- if (configInfo.userHandling.UserApplications && configInfo.userHandling.UserApplications.length > 0) {
88
- // Use explicitly configured applications
89
- for (const appName of configInfo.userHandling.UserApplications) {
90
- const toLowerCase: string = appName.trim().toLocaleLowerCase();
91
- const application: ApplicationInfo | undefined = md.Applications.find(a => a.Name.trim().toLocaleLowerCase() === toLowerCase);
92
- if (application) {
93
- applicationsToCreate.push(application);
94
- } else {
95
- LogError(`Application ${appName} not found in the Metadata, cannot assign to new user ${user.Name}`);
76
+ if (configInfo.userHandling && configInfo.userHandling.CreateUserApplicationRecords) {
77
+ LogStatus("Creating User Applications for new user: " + user.Name);
78
+
79
+ let applicationsToCreate: ApplicationInfo[] = [];
80
+
81
+ if (configInfo.userHandling.UserApplications && configInfo.userHandling.UserApplications.length > 0) {
82
+ for (const appName of configInfo.userHandling.UserApplications) {
83
+ const toLowerCase: string = appName.trim().toLocaleLowerCase();
84
+ const application: ApplicationInfo | undefined = md.Applications.find(a => a.Name.trim().toLocaleLowerCase() === toLowerCase);
85
+ if (application) {
86
+ applicationsToCreate.push(application);
87
+ } else {
88
+ LogError(`Application ${appName} not found in the Metadata, cannot assign to new user ${user.Name}`);
89
+ }
96
90
  }
91
+ } else {
92
+ LogStatus(`No UserApplications configured, using DefaultForNewUser applications for new user ${user.Name}`);
93
+ applicationsToCreate = md.Applications
94
+ .filter(a => a.DefaultForNewUser)
95
+ .sort((a, b) => (a.DefaultSequence ?? 100) - (b.DefaultSequence ?? 100));
96
+ LogStatus(`Found ${applicationsToCreate.length} applications with DefaultForNewUser=true`);
97
97
  }
98
- } else {
99
- // Fall back to DefaultForNewUser applications from metadata, sorted by DefaultSequence
100
- LogStatus(`No UserApplications configured, using DefaultForNewUser applications for new user ${user.Name}`);
101
- applicationsToCreate = md.Applications
102
- .filter(a => a.DefaultForNewUser)
103
- .sort((a, b) => (a.DefaultSequence ?? 100) - (b.DefaultSequence ?? 100));
104
- LogStatus(`Found ${applicationsToCreate.length} applications with DefaultForNewUser=true`);
105
- }
106
98
 
107
- // Create UserApplication records for each application
108
- for (const [appIndex, application] of applicationsToCreate.entries()) {
109
- const userApplication: MJUserApplicationEntity = await md.GetEntityObject<MJUserApplicationEntity>('MJ: User Applications', contextUser);
110
- userApplication.NewRecord();
111
- userApplication.UserID = user.ID;
112
- userApplication.ApplicationID = application.ID;
113
- userApplication.Sequence = appIndex; // Set sequence based on order
114
- userApplication.IsActive = true;
115
-
116
- const userApplicationSaveResult: boolean = await userApplication.Save();
117
- if(userApplicationSaveResult){
99
+ for (const [appIndex, application] of applicationsToCreate.entries()) {
100
+ const userApplication: MJUserApplicationEntity = await md.GetEntityObject<MJUserApplicationEntity>('MJ: User Applications', contextUser);
101
+ userApplication.NewRecord();
102
+ userApplication.UserID = user.ID;
103
+ userApplication.ApplicationID = application.ID;
104
+ userApplication.Sequence = appIndex;
105
+ userApplication.IsActive = true;
106
+
107
+ if (!await userApplication.Save()) {
108
+ throw new Error(`Failed to create User Application ${application.Name} for new user ${user.Name}: ${userApplication.LatestResult?.CompleteMessage ?? 'unknown error'}`);
109
+ }
118
110
  LogStatus(`Created User Application ${application.Name} for new user ${user.Name}`);
119
111
 
120
- //now create a MJUserApplicationEntity records for each entity in the application
121
112
  const rv: RunView = new RunView();
122
113
  const rvResult: RunViewResult<MJApplicationEntityEntityType> = await rv.RunView({
123
114
  EntityName: 'MJ: Application Entities',
@@ -138,19 +129,19 @@ export class NewUserBase {
138
129
  userAppEntity.EntityID = appEntity.EntityID;
139
130
  userAppEntity.Sequence = index;
140
131
 
141
- const userAppEntitySaveResult: boolean = await userAppEntity.Save();
142
- if(userAppEntitySaveResult){
143
- LogStatus(`Created User Application Entity ${appEntity.Entity} for new user ${user.Name}`);
144
- }
145
- else{
146
- LogError(`Failed to create User Application Entity for new user ${user.Name}:`, undefined, userAppEntity.LatestResult);
132
+ if (!await userAppEntity.Save()) {
133
+ throw new Error(`Failed to create User Application Entity for new user ${user.Name}: ${userAppEntity.LatestResult?.CompleteMessage ?? 'unknown error'}`);
147
134
  }
135
+ LogStatus(`Created User Application Entity ${appEntity.Entity} for new user ${user.Name}`);
148
136
  }
149
137
  }
150
- else{
151
- LogError(`Failed to create User Application ${application.Name} for new user ${user.Name}:`, undefined, userApplication.LatestResult);
152
- }
153
138
  }
139
+
140
+ await provider.CommitTransaction();
141
+ } catch (txErr) {
142
+ await provider.RollbackTransaction();
143
+ LogError(txErr);
144
+ return null;
154
145
  }
155
146
 
156
147
  return user;