@memberjunction/server 2.112.0 → 2.113.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 (250) hide show
  1. package/dist/agents/skip-agent.d.ts +4 -4
  2. package/dist/agents/skip-agent.d.ts.map +1 -1
  3. package/dist/agents/skip-agent.js +951 -808
  4. package/dist/agents/skip-agent.js.map +1 -1
  5. package/dist/agents/skip-sdk.d.ts +1 -1
  6. package/dist/agents/skip-sdk.d.ts.map +1 -1
  7. package/dist/agents/skip-sdk.js +43 -53
  8. package/dist/agents/skip-sdk.js.map +1 -1
  9. package/dist/apolloServer/index.js +1 -1
  10. package/dist/auth/AuthProviderFactory.d.ts +1 -1
  11. package/dist/auth/AuthProviderFactory.d.ts.map +1 -1
  12. package/dist/auth/AuthProviderFactory.js +3 -1
  13. package/dist/auth/AuthProviderFactory.js.map +1 -1
  14. package/dist/auth/BaseAuthProvider.d.ts +1 -1
  15. package/dist/auth/BaseAuthProvider.d.ts.map +1 -1
  16. package/dist/auth/BaseAuthProvider.js +2 -3
  17. package/dist/auth/BaseAuthProvider.js.map +1 -1
  18. package/dist/auth/IAuthProvider.d.ts +1 -1
  19. package/dist/auth/IAuthProvider.d.ts.map +1 -1
  20. package/dist/auth/exampleNewUserSubClass.d.ts.map +1 -1
  21. package/dist/auth/exampleNewUserSubClass.js +1 -1
  22. package/dist/auth/exampleNewUserSubClass.js.map +1 -1
  23. package/dist/auth/index.d.ts +1 -1
  24. package/dist/auth/index.d.ts.map +1 -1
  25. package/dist/auth/index.js +6 -6
  26. package/dist/auth/index.js.map +1 -1
  27. package/dist/auth/initializeProviders.js +1 -1
  28. package/dist/auth/initializeProviders.js.map +1 -1
  29. package/dist/auth/newUsers.d.ts +1 -1
  30. package/dist/auth/newUsers.d.ts.map +1 -1
  31. package/dist/auth/newUsers.js +7 -7
  32. package/dist/auth/newUsers.js.map +1 -1
  33. package/dist/auth/providers/Auth0Provider.d.ts +1 -1
  34. package/dist/auth/providers/Auth0Provider.d.ts.map +1 -1
  35. package/dist/auth/providers/Auth0Provider.js +1 -1
  36. package/dist/auth/providers/Auth0Provider.js.map +1 -1
  37. package/dist/auth/providers/CognitoProvider.d.ts +1 -1
  38. package/dist/auth/providers/CognitoProvider.d.ts.map +1 -1
  39. package/dist/auth/providers/CognitoProvider.js +6 -3
  40. package/dist/auth/providers/CognitoProvider.js.map +1 -1
  41. package/dist/auth/providers/GoogleProvider.d.ts +1 -1
  42. package/dist/auth/providers/GoogleProvider.d.ts.map +1 -1
  43. package/dist/auth/providers/GoogleProvider.js +1 -1
  44. package/dist/auth/providers/GoogleProvider.js.map +1 -1
  45. package/dist/auth/providers/MSALProvider.d.ts +1 -1
  46. package/dist/auth/providers/MSALProvider.d.ts.map +1 -1
  47. package/dist/auth/providers/MSALProvider.js +1 -1
  48. package/dist/auth/providers/MSALProvider.js.map +1 -1
  49. package/dist/auth/providers/OktaProvider.d.ts +1 -1
  50. package/dist/auth/providers/OktaProvider.d.ts.map +1 -1
  51. package/dist/auth/providers/OktaProvider.js +1 -1
  52. package/dist/auth/providers/OktaProvider.js.map +1 -1
  53. package/dist/config.d.ts.map +1 -1
  54. package/dist/config.js +10 -22
  55. package/dist/config.js.map +1 -1
  56. package/dist/context.d.ts +1 -1
  57. package/dist/context.d.ts.map +1 -1
  58. package/dist/context.js +7 -9
  59. package/dist/context.js.map +1 -1
  60. package/dist/entitySubclasses/entityPermissions.server.d.ts +1 -1
  61. package/dist/entitySubclasses/entityPermissions.server.d.ts.map +1 -1
  62. package/dist/entitySubclasses/entityPermissions.server.js +1 -1
  63. package/dist/entitySubclasses/entityPermissions.server.js.map +1 -1
  64. package/dist/generated/generated.d.ts +788 -658
  65. package/dist/generated/generated.d.ts.map +1 -1
  66. package/dist/generated/generated.js +2050 -3054
  67. package/dist/generated/generated.js.map +1 -1
  68. package/dist/generic/KeyInputOutputTypes.d.ts +1 -1
  69. package/dist/generic/KeyInputOutputTypes.d.ts.map +1 -1
  70. package/dist/generic/KeyInputOutputTypes.js +1 -1
  71. package/dist/generic/KeyInputOutputTypes.js.map +1 -1
  72. package/dist/generic/ResolverBase.d.ts +1 -1
  73. package/dist/generic/ResolverBase.d.ts.map +1 -1
  74. package/dist/generic/ResolverBase.js +10 -15
  75. package/dist/generic/ResolverBase.js.map +1 -1
  76. package/dist/generic/RunViewResolver.d.ts +1 -1
  77. package/dist/generic/RunViewResolver.d.ts.map +1 -1
  78. package/dist/generic/RunViewResolver.js +15 -15
  79. package/dist/generic/RunViewResolver.js.map +1 -1
  80. package/dist/index.d.ts.map +1 -1
  81. package/dist/index.js +13 -18
  82. package/dist/index.js.map +1 -1
  83. package/dist/resolvers/ActionResolver.d.ts +2 -2
  84. package/dist/resolvers/ActionResolver.d.ts.map +1 -1
  85. package/dist/resolvers/ActionResolver.js +30 -28
  86. package/dist/resolvers/ActionResolver.js.map +1 -1
  87. package/dist/resolvers/AskSkipResolver.d.ts +2 -2
  88. package/dist/resolvers/AskSkipResolver.d.ts.map +1 -1
  89. package/dist/resolvers/AskSkipResolver.js +50 -60
  90. package/dist/resolvers/AskSkipResolver.js.map +1 -1
  91. package/dist/resolvers/ComponentRegistryResolver.d.ts.map +1 -1
  92. package/dist/resolvers/ComponentRegistryResolver.js +38 -36
  93. package/dist/resolvers/ComponentRegistryResolver.js.map +1 -1
  94. package/dist/resolvers/CreateQueryResolver.d.ts +1 -1
  95. package/dist/resolvers/CreateQueryResolver.d.ts.map +1 -1
  96. package/dist/resolvers/CreateQueryResolver.js +40 -43
  97. package/dist/resolvers/CreateQueryResolver.js.map +1 -1
  98. package/dist/resolvers/DatasetResolver.d.ts.map +1 -1
  99. package/dist/resolvers/DatasetResolver.js +1 -1
  100. package/dist/resolvers/DatasetResolver.js.map +1 -1
  101. package/dist/resolvers/EntityRecordNameResolver.d.ts +1 -1
  102. package/dist/resolvers/EntityRecordNameResolver.d.ts.map +1 -1
  103. package/dist/resolvers/EntityRecordNameResolver.js +1 -1
  104. package/dist/resolvers/EntityRecordNameResolver.js.map +1 -1
  105. package/dist/resolvers/EntityResolver.d.ts.map +1 -1
  106. package/dist/resolvers/EntityResolver.js +1 -1
  107. package/dist/resolvers/EntityResolver.js.map +1 -1
  108. package/dist/resolvers/FileCategoryResolver.js +1 -1
  109. package/dist/resolvers/FileCategoryResolver.js.map +1 -1
  110. package/dist/resolvers/FileResolver.js +1 -1
  111. package/dist/resolvers/FileResolver.js.map +1 -1
  112. package/dist/resolvers/GetDataContextDataResolver.d.ts +1 -1
  113. package/dist/resolvers/GetDataContextDataResolver.d.ts.map +1 -1
  114. package/dist/resolvers/GetDataContextDataResolver.js +5 -5
  115. package/dist/resolvers/GetDataContextDataResolver.js.map +1 -1
  116. package/dist/resolvers/GetDataResolver.d.ts.map +1 -1
  117. package/dist/resolvers/GetDataResolver.js +6 -8
  118. package/dist/resolvers/GetDataResolver.js.map +1 -1
  119. package/dist/resolvers/MergeRecordsResolver.d.ts +3 -3
  120. package/dist/resolvers/MergeRecordsResolver.d.ts.map +1 -1
  121. package/dist/resolvers/MergeRecordsResolver.js +3 -3
  122. package/dist/resolvers/MergeRecordsResolver.js.map +1 -1
  123. package/dist/resolvers/PotentialDuplicateRecordResolver.d.ts +1 -1
  124. package/dist/resolvers/PotentialDuplicateRecordResolver.d.ts.map +1 -1
  125. package/dist/resolvers/PotentialDuplicateRecordResolver.js +1 -1
  126. package/dist/resolvers/PotentialDuplicateRecordResolver.js.map +1 -1
  127. package/dist/resolvers/QueryResolver.d.ts.map +1 -1
  128. package/dist/resolvers/QueryResolver.js +11 -11
  129. package/dist/resolvers/QueryResolver.js.map +1 -1
  130. package/dist/resolvers/ReportResolver.js +1 -1
  131. package/dist/resolvers/ReportResolver.js.map +1 -1
  132. package/dist/resolvers/RunAIAgentResolver.d.ts.map +1 -1
  133. package/dist/resolvers/RunAIAgentResolver.js +28 -27
  134. package/dist/resolvers/RunAIAgentResolver.js.map +1 -1
  135. package/dist/resolvers/RunAIPromptResolver.d.ts.map +1 -1
  136. package/dist/resolvers/RunAIPromptResolver.js +31 -31
  137. package/dist/resolvers/RunAIPromptResolver.js.map +1 -1
  138. package/dist/resolvers/RunTemplateResolver.d.ts.map +1 -1
  139. package/dist/resolvers/RunTemplateResolver.js +9 -9
  140. package/dist/resolvers/RunTemplateResolver.js.map +1 -1
  141. package/dist/resolvers/SqlLoggingConfigResolver.d.ts.map +1 -1
  142. package/dist/resolvers/SqlLoggingConfigResolver.js +10 -10
  143. package/dist/resolvers/SqlLoggingConfigResolver.js.map +1 -1
  144. package/dist/resolvers/SyncDataResolver.d.ts +1 -1
  145. package/dist/resolvers/SyncDataResolver.d.ts.map +1 -1
  146. package/dist/resolvers/SyncDataResolver.js +14 -15
  147. package/dist/resolvers/SyncDataResolver.js.map +1 -1
  148. package/dist/resolvers/SyncRolesUsersResolver.d.ts +1 -1
  149. package/dist/resolvers/SyncRolesUsersResolver.d.ts.map +1 -1
  150. package/dist/resolvers/SyncRolesUsersResolver.js +44 -48
  151. package/dist/resolvers/SyncRolesUsersResolver.js.map +1 -1
  152. package/dist/resolvers/TaskResolver.d.ts.map +1 -1
  153. package/dist/resolvers/TaskResolver.js +7 -7
  154. package/dist/resolvers/TaskResolver.js.map +1 -1
  155. package/dist/resolvers/TransactionGroupResolver.d.ts +1 -1
  156. package/dist/resolvers/TransactionGroupResolver.d.ts.map +1 -1
  157. package/dist/resolvers/TransactionGroupResolver.js +12 -12
  158. package/dist/resolvers/TransactionGroupResolver.js.map +1 -1
  159. package/dist/resolvers/UserFavoriteResolver.d.ts +1 -1
  160. package/dist/resolvers/UserFavoriteResolver.d.ts.map +1 -1
  161. package/dist/resolvers/UserFavoriteResolver.js +1 -1
  162. package/dist/resolvers/UserFavoriteResolver.js.map +1 -1
  163. package/dist/resolvers/UserViewResolver.d.ts.map +1 -1
  164. package/dist/resolvers/UserViewResolver.js.map +1 -1
  165. package/dist/rest/EntityCRUDHandler.d.ts +1 -1
  166. package/dist/rest/EntityCRUDHandler.d.ts.map +1 -1
  167. package/dist/rest/EntityCRUDHandler.js +16 -14
  168. package/dist/rest/EntityCRUDHandler.js.map +1 -1
  169. package/dist/rest/RESTEndpointHandler.d.ts.map +1 -1
  170. package/dist/rest/RESTEndpointHandler.js +25 -23
  171. package/dist/rest/RESTEndpointHandler.js.map +1 -1
  172. package/dist/rest/ViewOperationsHandler.d.ts +1 -1
  173. package/dist/rest/ViewOperationsHandler.d.ts.map +1 -1
  174. package/dist/rest/ViewOperationsHandler.js +21 -17
  175. package/dist/rest/ViewOperationsHandler.js.map +1 -1
  176. package/dist/scheduler/LearningCycleScheduler.d.ts.map +1 -1
  177. package/dist/scheduler/LearningCycleScheduler.js.map +1 -1
  178. package/dist/services/ScheduledJobsService.d.ts.map +1 -1
  179. package/dist/services/ScheduledJobsService.js +6 -4
  180. package/dist/services/ScheduledJobsService.js.map +1 -1
  181. package/dist/services/TaskOrchestrator.d.ts +1 -1
  182. package/dist/services/TaskOrchestrator.d.ts.map +1 -1
  183. package/dist/services/TaskOrchestrator.js +30 -30
  184. package/dist/services/TaskOrchestrator.js.map +1 -1
  185. package/dist/types.d.ts +3 -3
  186. package/dist/types.d.ts.map +1 -1
  187. package/dist/types.js +1 -0
  188. package/dist/types.js.map +1 -1
  189. package/dist/util.d.ts +1 -1
  190. package/dist/util.d.ts.map +1 -1
  191. package/dist/util.js +2 -2
  192. package/dist/util.js.map +1 -1
  193. package/package.json +39 -36
  194. package/src/agents/skip-agent.ts +1200 -1067
  195. package/src/agents/skip-sdk.ts +851 -877
  196. package/src/apolloServer/index.ts +2 -2
  197. package/src/auth/AuthProviderFactory.ts +14 -8
  198. package/src/auth/BaseAuthProvider.ts +4 -5
  199. package/src/auth/IAuthProvider.ts +2 -2
  200. package/src/auth/exampleNewUserSubClass.ts +2 -9
  201. package/src/auth/index.ts +26 -31
  202. package/src/auth/initializeProviders.ts +3 -3
  203. package/src/auth/newUsers.ts +134 -166
  204. package/src/auth/providers/Auth0Provider.ts +5 -5
  205. package/src/auth/providers/CognitoProvider.ts +10 -7
  206. package/src/auth/providers/GoogleProvider.ts +5 -4
  207. package/src/auth/providers/MSALProvider.ts +5 -5
  208. package/src/auth/providers/OktaProvider.ts +7 -6
  209. package/src/config.ts +54 -63
  210. package/src/context.ts +30 -42
  211. package/src/entitySubclasses/entityPermissions.server.ts +3 -3
  212. package/src/generated/generated.ts +40442 -48106
  213. package/src/generic/KeyInputOutputTypes.ts +6 -3
  214. package/src/generic/ResolverBase.ts +78 -119
  215. package/src/generic/RunViewResolver.ts +23 -27
  216. package/src/index.ts +48 -66
  217. package/src/resolvers/ActionResolver.ts +57 -46
  218. package/src/resolvers/AskSkipResolver.ts +533 -607
  219. package/src/resolvers/ComponentRegistryResolver.ts +562 -547
  220. package/src/resolvers/CreateQueryResolver.ts +655 -683
  221. package/src/resolvers/DatasetResolver.ts +6 -5
  222. package/src/resolvers/EntityCommunicationsResolver.ts +1 -1
  223. package/src/resolvers/EntityRecordNameResolver.ts +5 -9
  224. package/src/resolvers/EntityResolver.ts +7 -9
  225. package/src/resolvers/FileCategoryResolver.ts +2 -2
  226. package/src/resolvers/FileResolver.ts +4 -4
  227. package/src/resolvers/GetDataContextDataResolver.ts +118 -106
  228. package/src/resolvers/GetDataResolver.ts +205 -194
  229. package/src/resolvers/MergeRecordsResolver.ts +5 -5
  230. package/src/resolvers/PotentialDuplicateRecordResolver.ts +1 -1
  231. package/src/resolvers/QueryResolver.ts +78 -95
  232. package/src/resolvers/ReportResolver.ts +2 -2
  233. package/src/resolvers/RunAIAgentResolver.ts +828 -818
  234. package/src/resolvers/RunAIPromptResolver.ts +709 -693
  235. package/src/resolvers/RunTemplateResolver.ts +103 -105
  236. package/src/resolvers/SqlLoggingConfigResolver.ts +72 -69
  237. package/src/resolvers/SyncDataResolver.ts +352 -386
  238. package/src/resolvers/SyncRolesUsersResolver.ts +350 -387
  239. package/src/resolvers/TaskResolver.ts +115 -110
  240. package/src/resolvers/TransactionGroupResolver.ts +138 -143
  241. package/src/resolvers/UserFavoriteResolver.ts +8 -17
  242. package/src/resolvers/UserViewResolver.ts +12 -17
  243. package/src/rest/EntityCRUDHandler.ts +268 -291
  244. package/src/rest/RESTEndpointHandler.ts +776 -782
  245. package/src/rest/ViewOperationsHandler.ts +195 -191
  246. package/src/scheduler/LearningCycleScheduler.ts +52 -8
  247. package/src/services/ScheduledJobsService.ts +132 -129
  248. package/src/services/TaskOrchestrator.ts +776 -792
  249. package/src/types.ts +9 -15
  250. package/src/util.ts +109 -112
@@ -1,6 +1,6 @@
1
1
  import { Arg, Ctx, Field, InputType, Mutation, ObjectType, registerEnumType } from 'type-graphql';
2
2
  import { AppContext, UserPayload } from '../types.js';
3
- import { EntityDeleteOptions, EntitySaveOptions, LogError, Metadata, RunView, UserInfo } from '@memberjunction/global';
3
+ import { EntityDeleteOptions, EntitySaveOptions, LogError, Metadata, RunView, UserInfo } from '@memberjunction/core';
4
4
  import { RequireSystemUser } from '../directives/RequireSystemUser.js';
5
5
  import { RoleEntity, UserEntity, UserRoleEntity } from '@memberjunction/core-entities';
6
6
  import { UserCache } from '@memberjunction/sqlserver-dataprovider';
@@ -11,440 +11,403 @@ export class SyncRolesAndUsersResultType {
11
11
  Success: boolean;
12
12
  }
13
13
 
14
+
14
15
  @InputType()
15
16
  export class RoleInputType {
16
- @Field(() => String)
17
- ID: string;
18
-
19
- @Field(() => String)
20
- Name: string;
21
-
22
- @Field(() => String, { nullable: true })
23
- Description: string;
17
+ @Field(() => String)
18
+ ID: string;
19
+
20
+ @Field(() => String)
21
+ Name: string;
22
+
23
+ @Field(() => String, {nullable: true})
24
+ Description: string;
24
25
  }
25
26
 
27
+
26
28
  export enum UserType {
27
- Owner = 'Owner',
28
- User = 'User',
29
+ Owner = "Owner",
30
+ User = "User",
29
31
  }
30
32
 
31
33
  registerEnumType(UserType, {
32
- name: 'UserType', // GraphQL Enum Name
33
- description: 'Defines whether a user is an Owner or a User',
34
+ name: "UserType", // GraphQL Enum Name
35
+ description: "Defines whether a user is an Owner or a User",
34
36
  });
35
37
 
36
38
  @InputType()
37
39
  export class UserInputType {
38
- @Field(() => String)
39
- ID!: string;
40
+ @Field(() => String)
41
+ ID!: string;
40
42
 
41
- @Field(() => String)
42
- Name!: string;
43
+ @Field(() => String)
44
+ Name!: string;
43
45
 
44
- @Field(() => String)
45
- Email!: string;
46
+ @Field(() => String)
47
+ Email!: string;
46
48
 
47
- // the next field needs to have GraphQL enum with only Owner or User being allowed
48
- @Field(() => UserType)
49
- Type!: UserType;
49
+ // the next field needs to have GraphQL enum with only Owner or User being allowed
50
+ @Field(() => UserType)
51
+ Type!: UserType;
50
52
 
51
- @Field(() => String, { nullable: true })
52
- FirstName: string;
53
+ @Field(() => String, {nullable: true})
54
+ FirstName: string;
53
55
 
54
- @Field(() => String, { nullable: true })
55
- LastName: string;
56
+ @Field(() => String, {nullable: true})
57
+ LastName: string;
58
+
59
+ @Field(() => String, {nullable: true})
60
+ Title: string;
56
61
 
57
- @Field(() => String, { nullable: true })
58
- Title: string;
59
-
60
- @Field(() => [RoleInputType], { nullable: true })
61
- Roles?: RoleInputType[];
62
+ @Field(() => [RoleInputType], {nullable: true})
63
+ Roles?: RoleInputType[];
62
64
  }
63
65
 
66
+
64
67
  @InputType()
65
68
  export class RolesAndUsersInputType {
66
- @Field(() => [UserInputType])
67
- public Users: UserInputType[];
68
-
69
- @Field(() => [RoleInputType])
70
- public Roles: RoleInputType[];
69
+ @Field(() => [UserInputType])
70
+ public Users: UserInputType[];
71
+
72
+ @Field(() => [RoleInputType])
73
+ public Roles: RoleInputType[];
71
74
  }
72
75
 
76
+
73
77
  export class SyncRolesAndUsersResolver {
74
- /**
75
- * This mutation will sync both the roles and the users, and the user/role relationships in the system with the data provided in the input.
76
- * Roles are matched by the name (case insensitive) and users are matched by email
77
- * @param data
78
- */
79
- @RequireSystemUser()
80
- @Mutation(() => SyncRolesAndUsersResultType)
81
- async SyncRolesAndUsers(@Arg('data', () => RolesAndUsersInputType) data: RolesAndUsersInputType, @Ctx() context: AppContext) {
82
- try {
83
- // first we sync the roles, then the users
84
- const roleResult = await this.SyncRoles(data.Roles, context);
85
- if (roleResult?.Success) {
86
- const usersResult = await this.SyncUsers(data.Users, context);
87
- if (usersResult?.Success) {
88
- // refresh the user cache, don't set an auto-refresh
89
- // interval here becuase that is alreayd done at startup
90
- // and will keep going on its own as per the config. This is a
91
- // special one-time refresh since we made changes here.
92
- await UserCache.Instance.Refresh(context.dataSource);
78
+ /**
79
+ * This mutation will sync both the roles and the users, and the user/role relationships in the system with the data provided in the input.
80
+ * Roles are matched by the name (case insensitive) and users are matched by email
81
+ * @param data
82
+ */
83
+ @RequireSystemUser()
84
+ @Mutation(() => SyncRolesAndUsersResultType)
85
+ async SyncRolesAndUsers(
86
+ @Arg('data', () => RolesAndUsersInputType ) data: RolesAndUsersInputType,
87
+ @Ctx() context: AppContext
88
+ ) {
89
+ try {
90
+ // first we sync the roles, then the users
91
+ const roleResult = await this.SyncRoles(data.Roles, context);
92
+ if (roleResult?.Success) {
93
+ const usersResult = await this.SyncUsers(data.Users, context);
94
+ if (usersResult?.Success) {
95
+ // refresh the user cache, don't set an auto-refresh
96
+ // interval here becuase that is alreayd done at startup
97
+ // and will keep going on its own as per the config. This is a
98
+ // special one-time refresh since we made changes here.
99
+ await UserCache.Instance.Refresh(context.dataSource);
100
+ }
101
+ return usersResult;
102
+ }
103
+ else {
104
+ return roleResult;
105
+ }
106
+ }
107
+ catch (err) {
108
+ LogError(err);
109
+ throw new Error('Error syncing roles and users\n\n' + err);
93
110
  }
94
- return usersResult;
95
- } else {
96
- return roleResult;
97
- }
98
- } catch (err) {
99
- LogError(err);
100
- throw new Error('Error syncing roles and users\n\n' + err);
101
111
  }
102
- }
103
-
104
- /**
105
- * This mutation will sync the roles in the system with the data provided in the input, using the role name for matching (case insensitive)
106
- * @param data
107
- */
108
- @RequireSystemUser()
109
- @Mutation(() => SyncRolesAndUsersResultType)
110
- async SyncRoles(
112
+
113
+ /**
114
+ * This mutation will sync the roles in the system with the data provided in the input, using the role name for matching (case insensitive)
115
+ * @param data
116
+ */
117
+ @RequireSystemUser()
118
+ @Mutation(() => SyncRolesAndUsersResultType)
119
+ async SyncRoles(
111
120
  @Arg('roles', () => [RoleInputType]) roles: RoleInputType[],
112
121
  @Ctx() context: AppContext
113
- ): Promise<SyncRolesAndUsersResultType> {
114
- try {
115
- // we iterate through the provided roles and we remove roles that are not in the input and add roles that are new
116
- // and update roles that already exist
117
- const rv = new RunView();
118
- const result = await rv.RunView<RoleEntity>(
119
- {
120
- EntityName: 'Roles',
121
- ResultType: 'entity_object',
122
- },
123
- context.userPayload.userRecord
124
- );
125
-
126
- if (result && result.Success) {
127
- const currentRoles = result.Results;
128
- if (await this.DeleteRemovedRoles(currentRoles, roles, context.userPayload.userRecord, context.userPayload)) {
129
- if (await this.AddNewRoles(currentRoles, roles, context.userPayload.userRecord, context.userPayload)) {
130
- return await this.UpdateExistingRoles(currentRoles, roles, context.userPayload);
131
- }
132
- }
133
- }
122
+ ) : Promise<SyncRolesAndUsersResultType> {
123
+ try {
124
+ // we iterate through the provided roles and we remove roles that are not in the input and add roles that are new
125
+ // and update roles that already exist
126
+ const rv = new RunView();
127
+ const result = await rv.RunView<RoleEntity>({
128
+ EntityName: "Roles",
129
+ ResultType: 'entity_object'
130
+ }, context.userPayload.userRecord);
131
+
132
+ if (result && result.Success) {
133
+ const currentRoles = result.Results;
134
+ if (await this.DeleteRemovedRoles(currentRoles, roles, context.userPayload.userRecord, context.userPayload)) {
135
+ if ( await this.AddNewRoles(currentRoles, roles, context.userPayload.userRecord, context.userPayload)) {
136
+ return await this.UpdateExistingRoles(currentRoles, roles, context.userPayload);
137
+ }
138
+ }
139
+ }
134
140
 
135
- return { Success: false }; // if we get here, something went wrong
136
- } catch (err) {
137
- LogError(err);
138
- throw new Error('Error syncing roles and users\n\n' + err);
141
+ return { Success: false }; // if we get here, something went wrong
142
+ } catch (err) {
143
+ LogError(err);
144
+ throw new Error('Error syncing roles and users\n\n' + err);
145
+ }
139
146
  }
140
- }
141
-
142
- protected async UpdateExistingRoles(
143
- currentRoles: RoleEntity[],
144
- futureRoles: RoleInputType[],
145
- userPayload: UserPayload
146
- ): Promise<SyncRolesAndUsersResultType> {
147
- // go through the future roles and update any that are in the current roles
148
- const md = new Metadata();
149
- let ok: boolean = true;
150
-
151
- for (const update of futureRoles) {
152
- const currentRole = currentRoles.find((r) => r.Name.trim().toLowerCase() === update.Name.trim().toLowerCase());
153
- if (currentRole) {
154
- currentRole.Description = update.Description;
155
- ok = ok && (await currentRole.Save());
156
- }
147
+
148
+ protected async UpdateExistingRoles(currentRoles: RoleEntity[], futureRoles: RoleInputType[], userPayload: UserPayload): Promise<SyncRolesAndUsersResultType> {
149
+ // go through the future roles and update any that are in the current roles
150
+ const md = new Metadata();
151
+ let ok: boolean = true;
152
+
153
+ for (const update of futureRoles) {
154
+ const currentRole = currentRoles.find(r => r.Name.trim().toLowerCase() === update.Name.trim().toLowerCase());
155
+ if (currentRole) {
156
+ currentRole.Description = update.Description;
157
+ ok = ok && await currentRole.Save();
158
+ }
159
+ }
160
+ return { Success: ok };
157
161
  }
158
- return { Success: ok };
159
- }
160
-
161
- protected async AddNewRoles(
162
- currentRoles: RoleEntity[],
163
- futureRoles: RoleInputType[],
164
- user: UserInfo,
165
- userPayload: UserPayload
166
- ): Promise<boolean> {
167
- // go through the future roles and add any that are not in the current roles
168
- const md = new Metadata();
169
- let ok: boolean = true;
170
-
171
- for (const add of futureRoles) {
172
- if (!currentRoles.find((r) => r.Name.trim().toLowerCase() === add.Name.trim().toLowerCase())) {
173
- const role = await md.GetEntityObject<RoleEntity>('Roles', user);
174
- role.Name = add.Name;
175
- role.Description = add.Description;
176
- ok = ok && (await role.Save());
177
- }
162
+
163
+ protected async AddNewRoles(currentRoles: RoleEntity[], futureRoles: RoleInputType[], user: UserInfo, userPayload: UserPayload): Promise<boolean> {
164
+ // go through the future roles and add any that are not in the current roles
165
+ const md = new Metadata();
166
+ let ok: boolean = true;
167
+
168
+ for (const add of futureRoles) {
169
+ if (!currentRoles.find(r => r.Name.trim().toLowerCase() === add.Name.trim().toLowerCase())) {
170
+ const role = await md.GetEntityObject<RoleEntity>("Roles", user);
171
+ role.Name = add.Name;
172
+ role.Description = add.Description;
173
+ ok = ok && await role.Save();
174
+ }
175
+ }
176
+ return ok;
178
177
  }
179
- return ok;
180
- }
181
-
182
- protected async DeleteRemovedRoles(
183
- currentRoles: RoleEntity[],
184
- futureRoles: RoleInputType[],
185
- user: UserInfo,
186
- userPayload: UserPayload
187
- ): Promise<boolean> {
188
- const rv = new RunView();
189
- let ok: boolean = true;
190
-
191
- // iterate through the existing roles and remove any that are not in the input
192
- for (const remove of currentRoles) {
193
- if (!this.IsStandardRole(remove.Name)) {
194
- if (!futureRoles.find((r) => r.Name.trim().toLowerCase() === remove.Name.trim().toLowerCase())) {
195
- ok = ok && (await this.DeleteSingleRole(remove, rv, user, userPayload));
178
+
179
+
180
+ protected async DeleteRemovedRoles(currentRoles: RoleEntity[], futureRoles: RoleInputType[], user: UserInfo, userPayload: UserPayload): Promise<boolean> {
181
+ const rv = new RunView();
182
+ let ok: boolean = true;
183
+
184
+ // iterate through the existing roles and remove any that are not in the input
185
+ for (const remove of currentRoles) {
186
+ if (!this.IsStandardRole(remove.Name)) {
187
+ if (!futureRoles.find(r => r.Name.trim().toLowerCase() === remove.Name.trim().toLowerCase())) {
188
+ ok = ok && await this.DeleteSingleRole(remove, rv, user, userPayload);
189
+ }
190
+ }
196
191
  }
197
- }
192
+ return ok;
198
193
  }
199
- return ok;
200
- }
201
-
202
- public get StandardRoles(): string[] {
203
- return ['Developer', 'Integration', 'UI'];
204
- }
205
- public IsStandardRole(roleName: string): boolean {
206
- return this.StandardRoles.find((r) => r.toLowerCase() === roleName.toLowerCase()) !== undefined;
207
- }
208
-
209
- protected async DeleteSingleRole(role: RoleEntity, rv: RunView, user: UserInfo, userPayload: UserPayload): Promise<boolean> {
210
- // first, remove all the UserRole records that match this role
211
- let ok: boolean = true;
212
-
213
- const r2 = await rv.RunView<UserRoleEntity>(
214
- {
215
- EntityName: 'User Roles',
216
- ExtraFilter: "RoleID = '" + role.ID + "'",
217
- ResultType: 'entity_object',
218
- },
219
- user
220
- );
221
- if (r2.Success) {
222
- for (const ur of r2.Results) {
223
- ok = ok && (await ur.Delete()); // remove the user role
224
- }
194
+
195
+ public get StandardRoles(): string[] {
196
+ return ['Developer', 'Integration', 'UI']
197
+ }
198
+ public IsStandardRole(roleName: string): boolean {
199
+ return this.StandardRoles.find(r => r.toLowerCase() === roleName.toLowerCase()) !== undefined;
225
200
  }
226
201
 
227
- return ok && role.Delete(); // remove the role
228
- }
229
-
230
- /**
231
- * This mutation will sync the just the users in the system with the data provided in the input, matches existing users by email
232
- * @important This method will NOT work if the roles are not already in sync, meaning if User/Role relationships exist in the input data where the Role doesn't already exist in this system the sync will fail
233
- * @param data
234
- */
235
- @RequireSystemUser()
236
- @Mutation(() => SyncRolesAndUsersResultType)
237
- async SyncUsers(
238
- @Arg('users', () => [UserInputType]) users: UserInputType[],
239
- @Ctx() context: AppContext
240
- ): Promise<SyncRolesAndUsersResultType> {
241
- try {
242
- // first, we sync up the users and then the user roles.
243
- // for syncing users we first remove users that are no longer in the input, then we add new users and update existing users
244
- const rv = new RunView();
245
- const result = await rv.RunView<UserEntity>(
246
- {
247
- EntityName: 'Users',
248
- ResultType: 'entity_object',
249
- },
250
- context.userPayload.userRecord
251
- );
252
- if (result && result.Success) {
253
- // go through current users and remove those that are not in the input
254
- const currentUsers = result.Results;
255
- if (await this.DeleteRemovedUsers(currentUsers, users, context.userPayload.userRecord, context.userPayload)) {
256
- if (await this.AddNewUsers(currentUsers, users, context.userPayload)) {
257
- if (await this.UpdateExistingUsers(currentUsers, users, context.userPayload)) {
258
- if (await this.SyncUserRoles(users, context.userPayload.userRecord, context.userPayload)) {
259
- return { Success: true };
260
- }
202
+ protected async DeleteSingleRole(role: RoleEntity, rv: RunView, user: UserInfo, userPayload: UserPayload): Promise<boolean> {
203
+ // first, remove all the UserRole records that match this role
204
+ let ok: boolean = true;
205
+
206
+ const r2 = await rv.RunView<UserRoleEntity>({
207
+ EntityName: "User Roles",
208
+ ExtraFilter: "RoleID = '" + role.ID + "'",
209
+ ResultType: 'entity_object'
210
+ }, user);
211
+ if (r2.Success) {
212
+ for (const ur of r2.Results) {
213
+ ok = ok && await ur.Delete(); // remove the user role
261
214
  }
262
- }
263
215
  }
264
- }
265
216
 
266
- return { Success: false }; // if we get here, something went wrong
267
- } catch (err) {
268
- LogError(err);
269
- throw new Error('Error syncing roles and users\n\n' + err);
217
+ return ok && role.Delete(); // remove the role
270
218
  }
271
- }
272
-
273
- protected async UpdateExistingUsers(
274
- currentUsers: UserEntity[],
275
- futureUsers: UserInputType[],
276
- userPayload: UserPayload
277
- ): Promise<boolean> {
278
- // go through the future users and update any that are in the current users
279
- let ok: boolean = true;
280
-
281
- for (const update of futureUsers) {
282
- const current = currentUsers.find((c) => c.Email?.trim().toLowerCase() === update.Email?.trim().toLowerCase());
283
- if (current) {
284
- current.Name = update.Name;
285
- current.Type = update.Type;
286
- current.FirstName = update.FirstName;
287
- current.LastName = update.LastName;
288
- current.Title = update.Title;
289
- ok = ok && (await current.Save());
290
- }
291
- }
292
- return ok;
293
- }
294
- protected async AddNewUsers(currentUsers: UserEntity[], futureUsers: UserInputType[], userPayload: UserPayload): Promise<boolean> {
295
- // add users that are not in the current users
296
- const md = new Metadata();
297
- let ok: boolean = true;
298
-
299
- for (const add of futureUsers) {
300
- const match = currentUsers.find((currentUser) => currentUser.Email?.trim().toLowerCase() === add.Email?.trim().toLowerCase());
301
- if (match) {
302
- // make sure the IsActive bit is set to true
303
- match.IsActive = true;
304
- ok = ok && (await match.Save());
305
- } else {
306
- const user = await md.GetEntityObject<UserEntity>('Users', userPayload.userRecord);
307
- user.Name = add.Name;
308
- user.Type = add.Type;
309
- user.Email = add.Email;
310
- user.FirstName = add.FirstName;
311
- user.LastName = add.LastName;
312
- user.Title = add.Title;
313
- user.IsActive = true;
314
-
315
- ok = ok && (await user.Save());
316
- }
219
+
220
+ /**
221
+ * This mutation will sync the just the users in the system with the data provided in the input, matches existing users by email
222
+ * @important This method will NOT work if the roles are not already in sync, meaning if User/Role relationships exist in the input data where the Role doesn't already exist in this system the sync will fail
223
+ * @param data
224
+ */
225
+ @RequireSystemUser()
226
+ @Mutation(() => SyncRolesAndUsersResultType)
227
+ async SyncUsers(
228
+ @Arg('users', () => [UserInputType]) users: UserInputType[],
229
+ @Ctx() context: AppContext
230
+ ) : Promise<SyncRolesAndUsersResultType> {
231
+ try {
232
+ // first, we sync up the users and then the user roles.
233
+ // for syncing users we first remove users that are no longer in the input, then we add new users and update existing users
234
+ const rv = new RunView();
235
+ const result = await rv.RunView<UserEntity>({
236
+ EntityName: "Users",
237
+ ResultType: 'entity_object'
238
+ }, context.userPayload.userRecord);
239
+ if (result && result.Success) {
240
+ // go through current users and remove those that are not in the input
241
+ const currentUsers = result.Results;
242
+ if (await this.DeleteRemovedUsers(currentUsers, users, context.userPayload.userRecord, context.userPayload)) {
243
+ if (await this.AddNewUsers(currentUsers, users, context.userPayload)) {
244
+ if (await this.UpdateExistingUsers(currentUsers, users, context.userPayload)) {
245
+ if (await this.SyncUserRoles(users, context.userPayload.userRecord, context.userPayload)) {
246
+ return { Success: true };
247
+ }
248
+ }
249
+ }
250
+ }
251
+ }
252
+
253
+ return { Success: false }; // if we get here, something went wrong
254
+ } catch (err) {
255
+ LogError(err);
256
+ throw new Error('Error syncing roles and users\n\n' + err);
257
+ }
317
258
  }
318
- return ok;
319
- }
320
-
321
- protected async DeleteRemovedUsers(
322
- currentUsers: UserEntity[],
323
- futureUsers: UserInputType[],
324
- u: UserInfo,
325
- userPayload: UserPayload
326
- ): Promise<boolean> {
327
- // remove users that are not in the future users
328
- const rv = new RunView();
329
- const md = new Metadata();
330
-
331
- let ok: boolean = true;
332
- //const tg = await md.CreateTransactionGroup(); HAVING PROBLEMS with this, so skipping for now, I think the entire thing is wrapped in a transaction and that's causing issues with two styles of trans wrappers
333
- for (const remove of currentUsers) {
334
- if (remove.Type.trim().toLowerCase() !== 'owner') {
335
- if (!futureUsers.find((r) => r.Email.trim().toLowerCase() === remove.Email.trim().toLowerCase())) {
336
- ok = ok && (await this.DeleteSingleUser(remove, rv, u, userPayload));
259
+
260
+ protected async UpdateExistingUsers(currentUsers: UserEntity[], futureUsers: UserInputType[], userPayload: UserPayload): Promise<boolean> {
261
+ // go through the future users and update any that are in the current users
262
+ let ok: boolean = true;
263
+
264
+ for (const update of futureUsers) {
265
+ const current = currentUsers.find(c => c.Email?.trim().toLowerCase() === update.Email?.trim().toLowerCase());
266
+ if (current) {
267
+ current.Name = update.Name;
268
+ current.Type = update.Type;
269
+ current.FirstName = update.FirstName;
270
+ current.LastName = update.LastName;
271
+ current.Title = update.Title;
272
+ ok = ok && await current.Save();
273
+ }
337
274
  }
338
- }
275
+ return ok;
339
276
  }
340
- return ok;
341
- }
342
-
343
- protected async DeleteSingleUser(user: UserEntity, rv: RunView, u: UserInfo, userPayload: UserPayload): Promise<boolean> {
344
- // first, remove all the UserRole records that match this user
345
- let ok: boolean = true;
346
-
347
- const r2 = await rv.RunView<UserRoleEntity>(
348
- {
349
- EntityName: 'User Roles',
350
- ExtraFilter: "UserID = '" + user.ID + "'",
351
- ResultType: 'entity_object',
352
- },
353
- u
354
- );
355
- if (r2.Success) {
356
- for (const ur of r2.Results) {
357
- //ur.TransactionGroup = tg;
358
- ok = ok && (await ur.Delete()); // remove the user role
359
- }
277
+ protected async AddNewUsers(currentUsers: UserEntity[], futureUsers: UserInputType[], userPayload: UserPayload): Promise<boolean> {
278
+ // add users that are not in the current users
279
+ const md = new Metadata();
280
+ let ok: boolean = true;
281
+
282
+ for (const add of futureUsers) {
283
+ const match = currentUsers.find(currentUser => currentUser.Email?.trim().toLowerCase() === add.Email?.trim().toLowerCase());
284
+ if (match) {
285
+ // make sure the IsActive bit is set to true
286
+ match.IsActive = true;
287
+ ok = ok && await match.Save();
288
+ }
289
+ else {
290
+ const user = await md.GetEntityObject<UserEntity>("Users", userPayload.userRecord);
291
+ user.Name = add.Name;
292
+ user.Type = add.Type;
293
+ user.Email = add.Email;
294
+ user.FirstName = add.FirstName;
295
+ user.LastName = add.LastName;
296
+ user.Title = add.Title;
297
+ user.IsActive = true;
298
+
299
+ ok = ok && await user.Save();
300
+ }
301
+ }
302
+ return ok;
360
303
  }
361
- if (await user.Delete()) {
362
- return ok;
363
- } else {
364
- // in some cases there are a lot of fkey constraints that prevent the user from being deleted, so we mark the user as inactive instead
365
- user.IsActive = false;
366
- return (await user.Save()) && ok;
304
+
305
+ protected async DeleteRemovedUsers(currentUsers: UserEntity[], futureUsers: UserInputType[], u: UserInfo, userPayload: UserPayload): Promise<boolean> {
306
+ // remove users that are not in the future users
307
+ const rv = new RunView();
308
+ const md = new Metadata();
309
+
310
+ let ok: boolean = true;
311
+ //const tg = await md.CreateTransactionGroup(); HAVING PROBLEMS with this, so skipping for now, I think the entire thing is wrapped in a transaction and that's causing issues with two styles of trans wrappers
312
+ for (const remove of currentUsers) {
313
+ if (remove.Type.trim().toLowerCase() !== 'owner') {
314
+ if (!futureUsers.find(r => r.Email.trim().toLowerCase() === remove.Email.trim().toLowerCase())) {
315
+ ok = ok && await this.DeleteSingleUser(remove, rv, u, userPayload);
316
+ }
317
+ }
318
+ }
319
+ return ok;
367
320
  }
368
- }
369
-
370
- protected async SyncUserRoles(users: UserInputType[], u: UserInfo, userPayload: UserPayload): Promise<boolean> {
371
- // for each user in the users array, make sure there is a User Role that matches. First, get a list of all DATABASE user and roels so we have that for fast lookup in memory
372
- const rv = new RunView();
373
- const md = new Metadata();
374
-
375
- const p1 = rv.RunView<UserEntity>(
376
- {
377
- EntityName: 'Users',
378
- ResultType: 'entity_object',
379
- },
380
- u
381
- );
382
- const p2 = rv.RunView<RoleEntity>(
383
- {
384
- EntityName: 'Roles',
385
- ResultType: 'entity_object',
386
- },
387
- u
388
- );
389
- const p3 = rv.RunView<UserRoleEntity>(
390
- {
391
- EntityName: 'User Roles',
392
- ResultType: 'entity_object',
393
- },
394
- u
395
- );
396
-
397
- // await both
398
- const [uResult, rResult, urResult] = await Promise.all([p1, p2, p3]);
399
-
400
- if (uResult.Success && rResult.Success && urResult.Success) {
401
- // we have the DB users and roles, and user roles
402
- const dbUsers = uResult.Results;
403
- const dbRoles = rResult.Results;
404
- const dbUserRoles = urResult.Results;
405
- let ok: boolean = true;
406
-
407
- // now, we can do lookups in memory from those DB roles and Users for their ID values
408
- // now we will iterate through the users input type and for each role, make sure it is in there
409
- //const tg = await md.CreateTransactionGroup();
410
- for (const user of users) {
411
- const dbUser = dbUsers.find((u) => u.Email.trim().toLowerCase() === user.Email.trim().toLowerCase());
412
- if (dbUser) {
413
- for (const role of user.Roles) {
414
- const dbRole = dbRoles.find((r) => r.Name.trim().toLowerCase() === role.Name.trim().toLowerCase());
415
- if (dbRole) {
416
- // now we need to make sure there is a user role that matches this user and role
417
- if (!dbUserRoles.find((ur) => ur.UserID === dbUser.ID && ur.RoleID === dbRole.ID)) {
418
- // we need to add a user role
419
- const ur = await md.GetEntityObject<UserRoleEntity>('User Roles', u);
420
- ur.UserID = dbUser.ID;
421
- ur.RoleID = dbRole.ID;
422
- ok = ok && (await ur.Save());
423
- }
321
+
322
+ protected async DeleteSingleUser(user: UserEntity, rv: RunView, u: UserInfo, userPayload: UserPayload): Promise<boolean> {
323
+ // first, remove all the UserRole records that match this user
324
+ let ok: boolean = true;
325
+
326
+ const r2 = await rv.RunView<UserRoleEntity>({
327
+ EntityName: "User Roles",
328
+ ExtraFilter: "UserID = '" + user.ID + "'",
329
+ ResultType: 'entity_object'
330
+ }, u);
331
+ if (r2.Success) {
332
+ for (const ur of r2.Results) {
333
+ //ur.TransactionGroup = tg;
334
+ ok = ok && await ur.Delete(); // remove the user role
424
335
  }
425
- }
426
- // now, we check for DB user roles that are NOT in the user.Roles property as they are no longer part of the user's roles
427
- const thisUserDBRoles = dbUserRoles.filter((ur) => ur.UserID === dbUser.ID);
428
- for (const dbUserRole of thisUserDBRoles) {
429
- const role = user.Roles.find(
430
- (r) =>
431
- r.Name.trim().toLowerCase() ===
432
- dbRoles
433
- .find((rr) => rr.ID === dbUserRole.RoleID)
434
- ?.Name.trim()
435
- .toLowerCase()
436
- );
437
- if (!role && !this.IsStandardRole(dbUserRole.Role)) {
438
- // this user role is no longer in the user's roles, we need to remove it
439
- //dbUserRole.TransactionGroup = tg;
440
- ok = ok && (await dbUserRole.Delete()); // remove the user role - we use await for the DELETE, not the save
336
+ }
337
+ if (await user.Delete()) {
338
+ return ok;
339
+ }
340
+ else {
341
+ // in some cases there are a lot of fkey constraints that prevent the user from being deleted, so we mark the user as inactive instead
342
+ user.IsActive = false;
343
+ return await user.Save() && ok;
344
+ }
345
+ }
346
+
347
+ protected async SyncUserRoles(users: UserInputType[], u: UserInfo, userPayload: UserPayload): Promise<boolean> {
348
+ // for each user in the users array, make sure there is a User Role that matches. First, get a list of all DATABASE user and roels so we have that for fast lookup in memory
349
+ const rv = new RunView();
350
+ const md = new Metadata();
351
+
352
+ const p1 = rv.RunView<UserEntity>({
353
+ EntityName: "Users",
354
+ ResultType: 'entity_object'
355
+ }, u);
356
+ const p2 = rv.RunView<RoleEntity>({
357
+ EntityName: "Roles",
358
+ ResultType: 'entity_object'
359
+ }, u);
360
+ const p3 = rv.RunView<UserRoleEntity>({
361
+ EntityName: "User Roles",
362
+ ResultType: 'entity_object'
363
+ }, u);
364
+
365
+ // await both
366
+ const [uResult,rResult, urResult] = await Promise.all([p1, p2, p3]);
367
+
368
+ if (uResult.Success && rResult.Success && urResult.Success) {
369
+ // we have the DB users and roles, and user roles
370
+ const dbUsers = uResult.Results;
371
+ const dbRoles = rResult.Results;
372
+ const dbUserRoles = urResult.Results;
373
+ let ok: boolean = true;
374
+
375
+ // now, we can do lookups in memory from those DB roles and Users for their ID values
376
+ // now we will iterate through the users input type and for each role, make sure it is in there
377
+ //const tg = await md.CreateTransactionGroup();
378
+ for (const user of users) {
379
+ const dbUser = dbUsers.find(u => u.Email.trim().toLowerCase() === user.Email.trim().toLowerCase());
380
+ if (dbUser) {
381
+ for (const role of user.Roles) {
382
+ const dbRole = dbRoles.find(r => r.Name.trim().toLowerCase() === role.Name.trim().toLowerCase());
383
+ if (dbRole) {
384
+ // now we need to make sure there is a user role that matches this user and role
385
+ if (!dbUserRoles.find(ur => ur.UserID === dbUser.ID && ur.RoleID === dbRole.ID)) {
386
+ // we need to add a user role
387
+ const ur = await md.GetEntityObject<UserRoleEntity>("User Roles", u);
388
+ ur.UserID = dbUser.ID;
389
+ ur.RoleID = dbRole.ID;
390
+ ok = ok && await ur.Save();
391
+ }
392
+ }
393
+ }
394
+ // now, we check for DB user roles that are NOT in the user.Roles property as they are no longer part of the user's roles
395
+ const thisUserDBRoles = dbUserRoles.filter(ur => ur.UserID === dbUser.ID);
396
+ for (const dbUserRole of thisUserDBRoles) {
397
+ const role = user.Roles.find(r => r.Name.trim().toLowerCase() === dbRoles.find(rr => rr.ID === dbUserRole.RoleID)?.Name.trim().toLowerCase());
398
+ if (!role && !this.IsStandardRole(dbUserRole.Role)) {
399
+ // this user role is no longer in the user's roles, we need to remove it
400
+ //dbUserRole.TransactionGroup = tg;
401
+ ok = ok && await dbUserRole.Delete(); // remove the user role - we use await for the DELETE, not the save
402
+ }
403
+ }
404
+ }
441
405
  }
442
- }
406
+ return ok;
407
+ }
408
+ else {
409
+ return false;
443
410
  }
444
- }
445
- return ok;
446
- } else {
447
- return false;
448
411
  }
449
- }
450
412
  }
413
+