@memberjunction/server 2.76.0 → 2.78.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 (120) hide show
  1. package/README.md +61 -2
  2. package/dist/apolloServer/index.d.ts.map +1 -1
  3. package/dist/apolloServer/index.js +1 -3
  4. package/dist/apolloServer/index.js.map +1 -1
  5. package/dist/auth/exampleNewUserSubClass.d.ts +1 -1
  6. package/dist/auth/exampleNewUserSubClass.d.ts.map +1 -1
  7. package/dist/auth/exampleNewUserSubClass.js +1 -1
  8. package/dist/auth/exampleNewUserSubClass.js.map +1 -1
  9. package/dist/auth/newUsers.js.map +1 -1
  10. package/dist/context.d.ts +5 -0
  11. package/dist/context.d.ts.map +1 -1
  12. package/dist/context.js +34 -3
  13. package/dist/context.js.map +1 -1
  14. package/dist/entitySubclasses/entityPermissions.server.d.ts +2 -2
  15. package/dist/entitySubclasses/entityPermissions.server.d.ts.map +1 -1
  16. package/dist/entitySubclasses/entityPermissions.server.js +2 -2
  17. package/dist/entitySubclasses/entityPermissions.server.js.map +1 -1
  18. package/dist/generated/generated.d.ts +1781 -1738
  19. package/dist/generated/generated.d.ts.map +1 -1
  20. package/dist/generated/generated.js +6073 -5327
  21. package/dist/generated/generated.js.map +1 -1
  22. package/dist/generic/ResolverBase.d.ts +20 -21
  23. package/dist/generic/ResolverBase.d.ts.map +1 -1
  24. package/dist/generic/ResolverBase.js +75 -59
  25. package/dist/generic/ResolverBase.js.map +1 -1
  26. package/dist/generic/RunViewResolver.d.ts +8 -8
  27. package/dist/generic/RunViewResolver.d.ts.map +1 -1
  28. package/dist/generic/RunViewResolver.js +50 -48
  29. package/dist/generic/RunViewResolver.js.map +1 -1
  30. package/dist/index.d.ts +1 -1
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.js +1 -1
  33. package/dist/index.js.map +1 -1
  34. package/dist/resolvers/AskSkipResolver.d.ts +6 -6
  35. package/dist/resolvers/AskSkipResolver.d.ts.map +1 -1
  36. package/dist/resolvers/AskSkipResolver.js +33 -21
  37. package/dist/resolvers/AskSkipResolver.js.map +1 -1
  38. package/dist/resolvers/CreateQueryResolver.d.ts +1 -1
  39. package/dist/resolvers/CreateQueryResolver.d.ts.map +1 -1
  40. package/dist/resolvers/CreateQueryResolver.js +15 -9
  41. package/dist/resolvers/CreateQueryResolver.js.map +1 -1
  42. package/dist/resolvers/EntityResolver.d.ts +1 -2
  43. package/dist/resolvers/EntityResolver.d.ts.map +1 -1
  44. package/dist/resolvers/EntityResolver.js +17 -9
  45. package/dist/resolvers/EntityResolver.js.map +1 -1
  46. package/dist/resolvers/FileCategoryResolver.d.ts +1 -1
  47. package/dist/resolvers/FileCategoryResolver.d.ts.map +1 -1
  48. package/dist/resolvers/FileCategoryResolver.js +9 -9
  49. package/dist/resolvers/FileCategoryResolver.js.map +1 -1
  50. package/dist/resolvers/FileResolver.d.ts.map +1 -1
  51. package/dist/resolvers/FileResolver.js +3 -1
  52. package/dist/resolvers/FileResolver.js.map +1 -1
  53. package/dist/resolvers/MergeRecordsResolver.d.ts.map +1 -1
  54. package/dist/resolvers/MergeRecordsResolver.js +2 -1
  55. package/dist/resolvers/MergeRecordsResolver.js.map +1 -1
  56. package/dist/resolvers/ReportResolver.js.map +1 -1
  57. package/dist/resolvers/SyncDataResolver.d.ts +8 -8
  58. package/dist/resolvers/SyncDataResolver.d.ts.map +1 -1
  59. package/dist/resolvers/SyncDataResolver.js +19 -19
  60. package/dist/resolvers/SyncDataResolver.js.map +1 -1
  61. package/dist/resolvers/SyncRolesUsersResolver.d.ts +10 -10
  62. package/dist/resolvers/SyncRolesUsersResolver.d.ts.map +1 -1
  63. package/dist/resolvers/SyncRolesUsersResolver.js +19 -19
  64. package/dist/resolvers/SyncRolesUsersResolver.js.map +1 -1
  65. package/dist/resolvers/TransactionGroupResolver.d.ts.map +1 -1
  66. package/dist/resolvers/TransactionGroupResolver.js.map +1 -1
  67. package/dist/resolvers/UserFavoriteResolver.d.ts +2 -2
  68. package/dist/resolvers/UserFavoriteResolver.d.ts.map +1 -1
  69. package/dist/resolvers/UserFavoriteResolver.js +7 -4
  70. package/dist/resolvers/UserFavoriteResolver.js.map +1 -1
  71. package/dist/resolvers/UserResolver.d.ts +3 -3
  72. package/dist/resolvers/UserResolver.d.ts.map +1 -1
  73. package/dist/resolvers/UserResolver.js +10 -6
  74. package/dist/resolvers/UserResolver.js.map +1 -1
  75. package/dist/resolvers/UserViewResolver.d.ts +2 -2
  76. package/dist/resolvers/UserViewResolver.d.ts.map +1 -1
  77. package/dist/resolvers/UserViewResolver.js +11 -6
  78. package/dist/resolvers/UserViewResolver.js.map +1 -1
  79. package/dist/scheduler/LearningCycleScheduler.d.ts.map +1 -1
  80. package/dist/scheduler/LearningCycleScheduler.js +7 -1
  81. package/dist/scheduler/LearningCycleScheduler.js.map +1 -1
  82. package/dist/types.d.ts +7 -4
  83. package/dist/types.d.ts.map +1 -1
  84. package/dist/types.js +4 -0
  85. package/dist/types.js.map +1 -1
  86. package/dist/util.d.ts +8 -1
  87. package/dist/util.d.ts.map +1 -1
  88. package/dist/util.js +28 -0
  89. package/dist/util.js.map +1 -1
  90. package/package.json +34 -34
  91. package/src/apolloServer/index.ts +3 -3
  92. package/src/auth/exampleNewUserSubClass.ts +3 -2
  93. package/src/auth/newUsers.ts +1 -1
  94. package/src/context.ts +49 -9
  95. package/src/entitySubclasses/entityPermissions.server.ts +3 -3
  96. package/src/generated/generated.ts +5709 -5049
  97. package/src/generic/ResolverBase.ts +103 -86
  98. package/src/generic/RunViewResolver.ts +55 -54
  99. package/src/index.ts +1 -1
  100. package/src/resolvers/AskSkipResolver.ts +44 -23
  101. package/src/resolvers/CreateQueryResolver.ts +19 -11
  102. package/src/resolvers/EntityResolver.ts +18 -9
  103. package/src/resolvers/FileCategoryResolver.ts +12 -9
  104. package/src/resolvers/FileResolver.ts +4 -2
  105. package/src/resolvers/MergeRecordsResolver.ts +2 -1
  106. package/src/resolvers/ReportResolver.ts +1 -1
  107. package/src/resolvers/SyncDataResolver.ts +21 -21
  108. package/src/resolvers/SyncRolesUsersResolver.ts +24 -21
  109. package/src/resolvers/TransactionGroupResolver.ts +1 -1
  110. package/src/resolvers/UserFavoriteResolver.ts +7 -5
  111. package/src/resolvers/UserResolver.ts +10 -6
  112. package/src/resolvers/UserViewResolver.ts +13 -7
  113. package/src/scheduler/LearningCycleScheduler.ts +10 -4
  114. package/src/types.ts +14 -4
  115. package/src/util.ts +45 -2
  116. package/dist/apolloServer/TransactionPlugin.d.ts +0 -4
  117. package/dist/apolloServer/TransactionPlugin.d.ts.map +0 -1
  118. package/dist/apolloServer/TransactionPlugin.js +0 -46
  119. package/dist/apolloServer/TransactionPlugin.js.map +0 -1
  120. package/src/apolloServer/TransactionPlugin.ts +0 -53
@@ -1,6 +1,6 @@
1
1
  import { Arg, Ctx, Field, InputType, Mutation, ObjectType, registerEnumType } from 'type-graphql';
2
- import { AppContext } from '../types.js';
3
- import { LogError, Metadata, RunView, UserInfo } from '@memberjunction/core';
2
+ import { AppContext, UserPayload } from '../types.js';
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';
@@ -131,9 +131,9 @@ export class SyncRolesAndUsersResolver {
131
131
 
132
132
  if (result && result.Success) {
133
133
  const currentRoles = result.Results;
134
- if (await this.DeleteRemovedRoles(currentRoles, roles, context.userPayload.userRecord)) {
135
- if ( await this.AddNewRoles(currentRoles, roles, context.userPayload.userRecord)) {
136
- return await this.UpdateExistingRoles(currentRoles, roles, context.userPayload.userRecord);
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
137
  }
138
138
  }
139
139
  }
@@ -145,7 +145,7 @@ export class SyncRolesAndUsersResolver {
145
145
  }
146
146
  }
147
147
 
148
- protected async UpdateExistingRoles(currentRoles: RoleEntity[], futureRoles: RoleInputType[], user: UserInfo): Promise<SyncRolesAndUsersResultType> {
148
+ protected async UpdateExistingRoles(currentRoles: RoleEntity[], futureRoles: RoleInputType[], userPayload: UserPayload): Promise<SyncRolesAndUsersResultType> {
149
149
  // go through the future roles and update any that are in the current roles
150
150
  const md = new Metadata();
151
151
  let ok: boolean = true;
@@ -160,7 +160,7 @@ export class SyncRolesAndUsersResolver {
160
160
  return { Success: ok };
161
161
  }
162
162
 
163
- protected async AddNewRoles(currentRoles: RoleEntity[], futureRoles: RoleInputType[], user: UserInfo): Promise<boolean> {
163
+ protected async AddNewRoles(currentRoles: RoleEntity[], futureRoles: RoleInputType[], user: UserInfo, userPayload: UserPayload): Promise<boolean> {
164
164
  // go through the future roles and add any that are not in the current roles
165
165
  const md = new Metadata();
166
166
  let ok: boolean = true;
@@ -177,7 +177,7 @@ export class SyncRolesAndUsersResolver {
177
177
  }
178
178
 
179
179
 
180
- protected async DeleteRemovedRoles(currentRoles: RoleEntity[], futureRoles: RoleInputType[], user: UserInfo): Promise<boolean> {
180
+ protected async DeleteRemovedRoles(currentRoles: RoleEntity[], futureRoles: RoleInputType[], user: UserInfo, userPayload: UserPayload): Promise<boolean> {
181
181
  const rv = new RunView();
182
182
  let ok: boolean = true;
183
183
 
@@ -185,7 +185,7 @@ export class SyncRolesAndUsersResolver {
185
185
  for (const remove of currentRoles) {
186
186
  if (!this.IsStandardRole(remove.Name)) {
187
187
  if (!futureRoles.find(r => r.Name.trim().toLowerCase() === remove.Name.trim().toLowerCase())) {
188
- ok = ok && await this.DeleteSingleRole(remove, rv, user);
188
+ ok = ok && await this.DeleteSingleRole(remove, rv, user, userPayload);
189
189
  }
190
190
  }
191
191
  }
@@ -199,9 +199,10 @@ export class SyncRolesAndUsersResolver {
199
199
  return this.StandardRoles.find(r => r.toLowerCase() === roleName.toLowerCase()) !== undefined;
200
200
  }
201
201
 
202
- protected async DeleteSingleRole(role: RoleEntity, rv: RunView, user: UserInfo): Promise<boolean> {
202
+ protected async DeleteSingleRole(role: RoleEntity, rv: RunView, user: UserInfo, userPayload: UserPayload): Promise<boolean> {
203
203
  // first, remove all the UserRole records that match this role
204
204
  let ok: boolean = true;
205
+
205
206
  const r2 = await rv.RunView<UserRoleEntity>({
206
207
  EntityName: "User Roles",
207
208
  ExtraFilter: "RoleID = '" + role.ID + "'",
@@ -238,10 +239,10 @@ export class SyncRolesAndUsersResolver {
238
239
  if (result && result.Success) {
239
240
  // go through current users and remove those that are not in the input
240
241
  const currentUsers = result.Results;
241
- if (await this.DeleteRemovedUsers(currentUsers, users, context.userPayload.userRecord)) {
242
- if (await this.AddNewUsers(currentUsers, users, context.userPayload.userRecord)) {
243
- if (await this.UpdateExistingUsers(currentUsers, users, context.userPayload.userRecord)) {
244
- if (await this.SyncUserRoles(users, context.userPayload.userRecord)) {
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)) {
245
246
  return { Success: true };
246
247
  }
247
248
  }
@@ -256,9 +257,10 @@ export class SyncRolesAndUsersResolver {
256
257
  }
257
258
  }
258
259
 
259
- protected async UpdateExistingUsers(currentUsers: UserEntity[], futureUsers: UserInputType[], u: UserInfo): Promise<boolean> {
260
+ protected async UpdateExistingUsers(currentUsers: UserEntity[], futureUsers: UserInputType[], userPayload: UserPayload): Promise<boolean> {
260
261
  // go through the future users and update any that are in the current users
261
262
  let ok: boolean = true;
263
+
262
264
  for (const update of futureUsers) {
263
265
  const current = currentUsers.find(c => c.Email?.trim().toLowerCase() === update.Email?.trim().toLowerCase());
264
266
  if (current) {
@@ -272,7 +274,7 @@ export class SyncRolesAndUsersResolver {
272
274
  }
273
275
  return ok;
274
276
  }
275
- protected async AddNewUsers(currentUsers: UserEntity[], futureUsers: UserInputType[], u: UserInfo): Promise<boolean> {
277
+ protected async AddNewUsers(currentUsers: UserEntity[], futureUsers: UserInputType[], userPayload: UserPayload): Promise<boolean> {
276
278
  // add users that are not in the current users
277
279
  const md = new Metadata();
278
280
  let ok: boolean = true;
@@ -285,7 +287,7 @@ export class SyncRolesAndUsersResolver {
285
287
  ok = ok && await match.Save();
286
288
  }
287
289
  else {
288
- const user = await md.GetEntityObject<UserEntity>("Users", u);
290
+ const user = await md.GetEntityObject<UserEntity>("Users", userPayload.userRecord);
289
291
  user.Name = add.Name;
290
292
  user.Type = add.Type;
291
293
  user.Email = add.Email;
@@ -300,7 +302,7 @@ export class SyncRolesAndUsersResolver {
300
302
  return ok;
301
303
  }
302
304
 
303
- protected async DeleteRemovedUsers(currentUsers: UserEntity[], futureUsers: UserInputType[], u: UserInfo): Promise<boolean> {
305
+ protected async DeleteRemovedUsers(currentUsers: UserEntity[], futureUsers: UserInputType[], u: UserInfo, userPayload: UserPayload): Promise<boolean> {
304
306
  // remove users that are not in the future users
305
307
  const rv = new RunView();
306
308
  const md = new Metadata();
@@ -310,16 +312,17 @@ export class SyncRolesAndUsersResolver {
310
312
  for (const remove of currentUsers) {
311
313
  if (remove.Type.trim().toLowerCase() !== 'owner') {
312
314
  if (!futureUsers.find(r => r.Email.trim().toLowerCase() === remove.Email.trim().toLowerCase())) {
313
- ok = ok && await this.DeleteSingleUser(remove, rv, u);
315
+ ok = ok && await this.DeleteSingleUser(remove, rv, u, userPayload);
314
316
  }
315
317
  }
316
318
  }
317
319
  return ok;
318
320
  }
319
321
 
320
- protected async DeleteSingleUser(user: UserEntity, rv: RunView, u: UserInfo): Promise<boolean> {
322
+ protected async DeleteSingleUser(user: UserEntity, rv: RunView, u: UserInfo, userPayload: UserPayload): Promise<boolean> {
321
323
  // first, remove all the UserRole records that match this user
322
324
  let ok: boolean = true;
325
+
323
326
  const r2 = await rv.RunView<UserRoleEntity>({
324
327
  EntityName: "User Roles",
325
328
  ExtraFilter: "UserID = '" + user.ID + "'",
@@ -341,7 +344,7 @@ export class SyncRolesAndUsersResolver {
341
344
  }
342
345
  }
343
346
 
344
- protected async SyncUserRoles(users: UserInputType[], u: UserInfo): Promise<boolean> {
347
+ protected async SyncUserRoles(users: UserInputType[], u: UserInfo, userPayload: UserPayload): Promise<boolean> {
345
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
346
349
  const rv = new RunView();
347
350
  const md = new Metadata();
@@ -1,6 +1,6 @@
1
1
  import { Arg, Ctx, Field, InputType, Int, Mutation, ObjectType, registerEnumType } from 'type-graphql';
2
2
  import { AppContext } from '../types.js';
3
- import { CompositeKey, KeyValuePair, LogError, Metadata, TransactionVariable, BaseEntity } from '@memberjunction/core';
3
+ import { CompositeKey, KeyValuePair, LogError, Metadata, TransactionVariable, BaseEntity, EntityDeleteOptions, EntitySaveOptions } from '@memberjunction/core';
4
4
  import { SafeJSONParse } from '@memberjunction/global';
5
5
 
6
6
  export enum TransactionVariableType {
@@ -14,9 +14,9 @@ import {
14
14
  Resolver,
15
15
  } from '@memberjunction/server';
16
16
  import { UserCache } from '@memberjunction/sqlserver-dataprovider';
17
- import { UserFavoriteEntity } from '@memberjunction/core-entities';
18
17
 
19
18
  import { UserFavorite_, UserFavoriteResolverBase } from '../generated/generated.js';
19
+ import { GetReadOnlyProvider } from '../util.js';
20
20
 
21
21
  //****************************************************************************
22
22
  // INPUT TYPE for User Favorite Queries
@@ -69,13 +69,15 @@ export class UserFavoriteResult {
69
69
  @Resolver(UserFavorite_)
70
70
  export class UserFavoriteResolver extends UserFavoriteResolverBase {
71
71
  @Query(() => [UserFavorite_])
72
- async UserFavoritesByUserID(@Arg('UserID', () => Int) UserID: number, @Ctx() { dataSource, userPayload }: AppContext) {
73
- return await this.findBy(dataSource, 'User Favorites', { UserID }, userPayload.userRecord);
72
+ async UserFavoritesByUserID(@Arg('UserID', () => Int) UserID: number, @Ctx() { providers, userPayload }: AppContext) {
73
+ const provider = GetReadOnlyProvider(providers, {allowFallbackToReadWrite: true})
74
+ return await this.findBy(provider, 'User Favorites', { UserID }, userPayload.userRecord);
74
75
  }
75
76
 
76
77
  @Query(() => [UserFavorite_])
77
- async UserFavoriteSearchByParams(@Arg('params', () => Int) params: UserFavoriteSearchParams, @Ctx() { dataSource, userPayload }: AppContext) {
78
- return await this.findBy(dataSource, 'User Favorites', params, userPayload.userRecord);
78
+ async UserFavoriteSearchByParams(@Arg('params', () => Int) params: UserFavoriteSearchParams, @Ctx() { providers, userPayload }: AppContext) {
79
+ const provider = GetReadOnlyProvider(providers, {allowFallbackToReadWrite: true})
80
+ return await this.findBy(provider, 'User Favorites', params, userPayload.userRecord);
79
81
  }
80
82
 
81
83
  @Query(() => UserFavoriteResult)
@@ -1,5 +1,6 @@
1
1
  import { AppContext, Arg, Ctx, Int, Query, Resolver } from '@memberjunction/server';
2
2
  import { User_, UserResolverBase } from '../generated/generated.js';
3
+ import { GetReadOnlyProvider } from '../util.js';
3
4
 
4
5
  @Resolver(User_)
5
6
  export class UserResolver extends UserResolverBase {
@@ -11,22 +12,25 @@ export class UserResolver extends UserResolverBase {
11
12
  }
12
13
 
13
14
  @Query(() => User_)
14
- async UserByID(@Arg('ID', () => Int) ID: number, @Ctx() { dataSource, userPayload }: AppContext) {
15
- const retVal = super.safeFirstArrayElement(await this.findBy(dataSource, 'Users', { ID }, userPayload.userRecord));
15
+ async UserByID(@Arg('ID', () => Int) ID: number, @Ctx() { providers, userPayload }: AppContext) {
16
+ const provider = GetReadOnlyProvider(providers, {allowFallbackToReadWrite: true})
17
+ const retVal = super.safeFirstArrayElement(await this.findBy(provider, 'Users', { ID }, userPayload.userRecord));
16
18
  return this.MapFieldNamesToCodeNames('Users', retVal);
17
19
  }
18
20
 
19
21
  @Query(() => User_)
20
- async UserByEmployeeID(@Arg('EmployeeID', () => Int) EmployeeID: number, @Ctx() { dataSource, userPayload }: AppContext) {
21
- const retVal = super.safeFirstArrayElement(await this.findBy(dataSource, 'Users', { EmployeeID }, userPayload.userRecord));
22
+ async UserByEmployeeID(@Arg('EmployeeID', () => Int) EmployeeID: number, @Ctx() { providers, userPayload }: AppContext) {
23
+ const provider = GetReadOnlyProvider(providers, {allowFallbackToReadWrite: true})
24
+ const retVal = super.safeFirstArrayElement(await this.findBy(provider, 'Users', { EmployeeID }, userPayload.userRecord));
22
25
  return this.MapFieldNamesToCodeNames('Users', retVal);
23
26
  }
24
27
 
25
28
  @Query(() => User_)
26
- async UserByEmail(@Arg('Email', () => String) Email: string, @Ctx() { dataSource, userPayload }: AppContext) {
29
+ async UserByEmail(@Arg('Email', () => String) Email: string, @Ctx() { providers, userPayload }: AppContext) {
27
30
  // const searchEmail = userEmailMap[Email] ?? Email;
28
31
  const searchEmail = Email;
29
- const returnVal = super.safeFirstArrayElement(await this.findBy(dataSource, 'Users', { Email: searchEmail }, userPayload.userRecord));
32
+ const provider = GetReadOnlyProvider(providers, {allowFallbackToReadWrite: true})
33
+ const returnVal = super.safeFirstArrayElement(await this.findBy(provider, 'Users', { Email: searchEmail }, userPayload.userRecord));
30
34
  return this.MapFieldNamesToCodeNames('Users', returnVal);
31
35
  }
32
36
  }
@@ -1,29 +1,33 @@
1
1
  /* eslint-disable @typescript-eslint/no-explicit-any */
2
- import { Metadata } from '@memberjunction/core';
2
+ import { EntitySaveOptions, Metadata } from '@memberjunction/core';
3
3
  import { AppContext, Arg, Ctx, Int, Query, Resolver, UserPayload } from '@memberjunction/server';
4
4
  import { UserView_, UserViewResolverBase } from '../generated/generated.js';
5
5
  import { UserResolver } from './UserResolver.js';
6
6
  import { UserViewEntity, UserViewEntityExtended } from '@memberjunction/core-entities';
7
+ import { GetReadOnlyProvider } from '../util.js';
7
8
 
8
9
  @Resolver(UserView_)
9
10
  export class UserViewResolver extends UserViewResolverBase {
10
11
  @Query(() => [UserView_])
11
- async UserViewsByUserID(@Arg('UserID', () => Int) UserID: number, @Ctx() { dataSource, userPayload }: AppContext) {
12
- return await this.findBy(dataSource, 'User Views', { UserID }, userPayload.userRecord);
12
+ async UserViewsByUserID(@Arg('UserID', () => Int) UserID: number, @Ctx() { providers, userPayload }: AppContext) {
13
+ const provider = GetReadOnlyProvider(providers, {allowFallbackToReadWrite: true})
14
+ return await this.findBy(provider, 'User Views', { UserID }, userPayload.userRecord);
13
15
  }
14
16
 
15
17
  @Query(() => [UserView_])
16
18
  async DefaultViewByUserAndEntity(
17
19
  @Arg('UserID', () => Int) UserID: number,
18
20
  @Arg('EntityID', () => Int) EntityID: number,
19
- @Ctx() { dataSource, userPayload }: AppContext
21
+ @Ctx() { providers, userPayload }: AppContext
20
22
  ) {
21
- return await this.findBy(dataSource, 'User Views', { UserID, EntityID, IsDefault: true }, userPayload.userRecord);
23
+ const provider = GetReadOnlyProvider(providers, {allowFallbackToReadWrite: true})
24
+ return await this.findBy(provider, 'User Views', { UserID, EntityID, IsDefault: true }, userPayload.userRecord);
22
25
  }
23
26
 
24
27
  @Query(() => [UserView_])
25
28
  async CurrentUserDefaultViewByEntityID(@Arg('EntityID', () => Int) EntityID: number, @Ctx() context: AppContext) {
26
- return await this.findBy(context.dataSource, 'User Views', {
29
+ const provider = GetReadOnlyProvider(context.providers, {allowFallbackToReadWrite: true})
30
+ return await this.findBy(provider, 'User Views', {
27
31
  UserID: await this.getCurrentUserID(context),
28
32
  EntityID,
29
33
  IsDefault: true,
@@ -38,7 +42,8 @@ export class UserViewResolver extends UserViewResolverBase {
38
42
 
39
43
  @Query(() => [UserView_])
40
44
  async CurrentUserUserViewsByEntityID(@Arg('EntityID', () => Int) EntityID: number, @Ctx() context: AppContext) {
41
- return this.findBy(context.dataSource, 'User Views', { UserID: await this.getCurrentUserID(context), EntityID}, context.userPayload.userRecord);
45
+ const provider = GetReadOnlyProvider(context.providers, {allowFallbackToReadWrite: true})
46
+ return this.findBy(provider, 'User Views', { UserID: await this.getCurrentUserID(context), EntityID}, context.userPayload.userRecord);
42
47
  }
43
48
 
44
49
  @Query(() => [UserView_])
@@ -52,6 +57,7 @@ export class UserViewResolver extends UserViewResolverBase {
52
57
  const viewEntity = <UserViewEntityExtended>await md.GetEntityObject('User Views', u);
53
58
  await viewEntity.Load(ID);
54
59
  viewEntity.UpdateWhereClause();
60
+
55
61
  if (await viewEntity.Save()) {
56
62
  return viewEntity.GetAll();
57
63
  } else {
@@ -1,10 +1,11 @@
1
1
  import { LogStatus, LogError } from '@memberjunction/core';
2
- import { UserCache } from '@memberjunction/sqlserver-dataprovider';
2
+ import { SQLServerDataProvider, SQLServerProviderConfigData, UserCache } from '@memberjunction/sqlserver-dataprovider';
3
3
  import { GetReadWriteDataSource } from '../util.js';
4
4
  import { AskSkipResolver } from '../resolvers/AskSkipResolver.js';
5
- import { DataSourceInfo } from '../types.js';
5
+ import { AppContext, DataSourceInfo } from '../types.js';
6
6
  import { getSystemUser } from '../auth/index.js';
7
7
  import { BaseSingleton } from '@memberjunction/global';
8
+ import { mj_core_schema } from '../config.js';
8
9
 
9
10
  /**
10
11
  * A simple scheduler for the Skip AI learning cycle
@@ -89,7 +90,11 @@ export class LearningCycleScheduler extends BaseSingleton<LearningCycleScheduler
89
90
  }
90
91
 
91
92
  // Create context for the resolver
92
- const context = {
93
+ const config = new SQLServerProviderConfigData(dataSource, mj_core_schema, 0, undefined, undefined, false);
94
+ const p = new SQLServerDataProvider();
95
+ await p.Config(config);
96
+
97
+ const context: AppContext = {
93
98
  dataSource: dataSource,
94
99
  dataSources: this.dataSources,
95
100
  userPayload: {
@@ -97,7 +102,8 @@ export class LearningCycleScheduler extends BaseSingleton<LearningCycleScheduler
97
102
  sessionId: `scheduler_${Date.now()}`,
98
103
  userRecord: systemUser,
99
104
  isSystemUser: true
100
- }
105
+ },
106
+ providers: [{provider: p, type: 'Read-Write'}]
101
107
  };
102
108
 
103
109
  // Execute the learning cycle
package/src/types.ts CHANGED
@@ -1,10 +1,11 @@
1
- import { UserInfo } from '@memberjunction/core';
1
+ import { DatabaseProviderBase, UserInfo } from '@memberjunction/core';
2
2
  import { UserViewEntity } from '@memberjunction/core-entities';
3
3
  import { GraphQLSchema } from 'graphql';
4
4
  import { PubSubEngine } from 'type-graphql';
5
5
  import sql from 'mssql';
6
6
  import { getSystemUser } from './auth/index.js';
7
7
  import { MJEvent, MJEventType, MJGlobal } from '@memberjunction/global';
8
+ import { SQLServerDataProvider } from '@memberjunction/sqlserver-dataprovider';
8
9
 
9
10
  export type UserPayload = {
10
11
  email: string;
@@ -28,8 +29,18 @@ export type AppContext = {
28
29
  * Array of connection pools that have additional information about their intended use e.g. Admin, Read-Write, Read-Only.
29
30
  */
30
31
  dataSources: DataSourceInfo[];
32
+
33
+ /**
34
+ * Per-request DatabaseProviderBase instances
35
+ */
36
+ providers: Array<ProviderInfo>;
31
37
  };
32
38
 
39
+ export class ProviderInfo {
40
+ provider: DatabaseProviderBase;
41
+ type: 'Admin' | 'Read-Write' | 'Read-Only' | 'Other';
42
+ }
43
+
33
44
  export class DataSourceInfo {
34
45
  dataSource: sql.ConnectionPool;
35
46
  host: string;
@@ -56,7 +67,7 @@ export type DirectiveBuilder = {
56
67
 
57
68
  export type RunViewGenericParams = {
58
69
  viewInfo: UserViewEntity;
59
- dataSource: sql.ConnectionPool;
70
+ provider: DatabaseProviderBase;
60
71
  extraFilter: string;
61
72
  orderBy: string;
62
73
  userSearchString: string;
@@ -69,8 +80,7 @@ export type RunViewGenericParams = {
69
80
  forceAuditLog?: boolean;
70
81
  auditLogDescription?: string;
71
82
  resultType?: string;
72
- userPayload?: UserPayload;
73
- pubSub: PubSubEngine;
83
+ userPayload?: UserPayload;
74
84
  };
75
85
 
76
86
 
package/src/util.ts CHANGED
@@ -4,8 +4,9 @@ import { gzip as gzipCallback, createGunzip } from 'zlib';
4
4
  import { promisify } from 'util';
5
5
  import { URL } from 'url';
6
6
  import { z } from 'zod';
7
- import { DataSourceInfo } from './types';
7
+ import { DataSourceInfo, ProviderInfo } from './types';
8
8
  import sql from 'mssql';
9
+ import { DatabaseProviderBase } from '@memberjunction/core';
9
10
 
10
11
  const gzip = promisify(gzipCallback);
11
12
 
@@ -29,7 +30,7 @@ export async function sendPostRequest(url: string, payload: any, useCompression:
29
30
  let data;
30
31
  if (useCompression) {
31
32
  try {
32
- data = await gzip(Buffer.from(JSON.stringify(payload)));
33
+ data = await gzip(Buffer.from(JSON.stringify(payload)) as any);
33
34
  headers = headers || {}; // Ensure headers is an object
34
35
  headers['Content-Encoding'] = 'gzip';
35
36
  } catch (error) {
@@ -136,6 +137,48 @@ export async function sendPostRequest(url: string, payload: any, useCompression:
136
137
  throw new Error('No suitable data source found');
137
138
  }
138
139
 
140
+ /**
141
+ * Returns the read-only provider if it exists, otherwise returns the original provider if options is not provided or if options.allowFallbackToReadWrite is true.
142
+ * @param options
143
+ * @returns
144
+ */
145
+ export function GetReadOnlyProvider(providers: Array<ProviderInfo>, options?: {allowFallbackToReadWrite: boolean}): DatabaseProviderBase {
146
+ if (!providers || providers.length === 0)
147
+ return null; // no providers available
148
+
149
+ const readOnlyProvider = providers.find((p) => p.type === 'Read-Only');
150
+ if (readOnlyProvider) {
151
+ return readOnlyProvider.provider;
152
+ }
153
+ else if (options?.allowFallbackToReadWrite) {
154
+ return providers[0].provider; // if no read-only provider is provided, use the original provider since we are allowed to fallback to read-write
155
+ }
156
+ else {
157
+ return null; // no read only provider available and we are not allowed to fallback to read-write
158
+ }
159
+ }
160
+
161
+ /**
162
+ * Returns the read-write provider if it exists, otherwise returns the original provider if options is not provided or if options.allowFallbackToReadOnly is true.
163
+ * @param options
164
+ * @returns
165
+ */
166
+ export function GetReadWriteProvider(providers: Array<ProviderInfo>, options?: {allowFallbackToReadOnly: boolean}): DatabaseProviderBase {
167
+ if (!providers || providers.length === 0)
168
+ return null; // no providers available
169
+
170
+ const readWriteProvider = providers.find((p) => p.type === 'Read-Write');
171
+ if (readWriteProvider) {
172
+ return readWriteProvider.provider;
173
+ }
174
+ else if (options?.allowFallbackToReadOnly) {
175
+ return GetReadOnlyProvider(providers, { allowFallbackToReadWrite: false }); // if no read-write provider is provided, use the read-only provider since we are allowed to fallback to read-only
176
+ }
177
+ else {
178
+ return null; // no read-write provider available and we are not allowed to fallback to read-only
179
+ }
180
+ }
181
+
139
182
  /**
140
183
  * Returns the read-write data source if it exists, otherwise throws an error.
141
184
  * @param dataSources
@@ -1,4 +0,0 @@
1
- import { ApolloServerPlugin } from '@apollo/server';
2
- import { AppContext } from '../types.js';
3
- export declare const TransactionPlugin: ApolloServerPlugin<AppContext>;
4
- //# sourceMappingURL=TransactionPlugin.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"TransactionPlugin.d.ts","sourceRoot":"","sources":["../../src/apolloServer/TransactionPlugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAgF,MAAM,gBAAgB,CAAC;AAElI,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,eAAO,MAAM,iBAAiB,EAAE,kBAAkB,CAAC,UAAU,CAgD5D,CAAC"}
@@ -1,46 +0,0 @@
1
- import sql from 'mssql';
2
- export const TransactionPlugin = {
3
- async requestDidStart(requestContext) {
4
- const start = Date.now();
5
- const query = requestContext.request.query || '';
6
- const isMutation = /^\s*mutation\b/i.test(query);
7
- if (!isMutation) {
8
- return null;
9
- }
10
- const pool = requestContext.contextValue.dataSource;
11
- const transaction = new sql.Transaction(pool);
12
- requestContext.contextValue.transaction = transaction;
13
- console.log('Starting transaction wrapper, time spent: ', Date.now() - start, 'ms ');
14
- await transaction.begin();
15
- return {
16
- didEncounterErrors: async (requestContext) => {
17
- console.log('Error in transaction wrapper: ' + requestContext.errors, 'time spent: ', Date.now() - start, 'ms');
18
- },
19
- executionDidStart: async () => {
20
- return {
21
- executionDidEnd: async (err) => {
22
- try {
23
- if (err) {
24
- console.log('Error in transaction, rolling back, time spent: ', Date.now() - start, 'ms ');
25
- console.error('Rolling back transaction', err);
26
- await transaction.rollback();
27
- }
28
- else {
29
- console.log('Committing transaction, time spent: ', Date.now() - start, 'ms ');
30
- await transaction.commit();
31
- }
32
- }
33
- catch (execErr) {
34
- console.log('Execution Error, time spent: ', Date.now() - start, 'ms ');
35
- console.error(execErr);
36
- }
37
- finally {
38
- console.log('Transaction complete, time spent: ', Date.now() - start, 'ms ');
39
- }
40
- },
41
- };
42
- },
43
- };
44
- },
45
- };
46
- //# sourceMappingURL=TransactionPlugin.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"TransactionPlugin.js","sourceRoot":"","sources":["../../src/apolloServer/TransactionPlugin.ts"],"names":[],"mappings":"AACA,OAAO,GAAG,MAAM,OAAO,CAAC;AAGxB,MAAM,CAAC,MAAM,iBAAiB,GAAmC;IAC/D,KAAK,CAAC,eAAe,CAAC,cAAc;QAClC,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACzB,MAAM,KAAK,GAAG,cAAc,CAAC,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC;QACjD,MAAM,UAAU,GAAG,iBAAiB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAEjD,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,OAAO,IAAI,CAAC;QACd,CAAC;QAKD,MAAM,IAAI,GAAuB,cAAc,CAAC,YAAY,CAAC,UAAU,CAAC;QACxE,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;QAG7C,cAAc,CAAC,YAAoB,CAAC,WAAW,GAAG,WAAW,CAAC;QAC/D,OAAO,CAAC,GAAG,CAAC,4CAA4C,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,EAAE,KAAK,CAAC,CAAC;QACrF,MAAM,WAAW,CAAC,KAAK,EAAE,CAAC;QAE1B,OAAO;YACL,kBAAkB,EAAE,KAAK,EAAE,cAAc,EAAE,EAAE;gBAC3C,OAAO,CAAC,GAAG,CAAC,gCAAgC,GAAG,cAAc,CAAC,MAAM,EAAE,cAAc,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,EAAE,IAAI,CAAC,CAAC;YAClH,CAAC;YACD,iBAAiB,EAAE,KAAK,IAAI,EAAE;gBAC5B,OAAO;oBACL,eAAe,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;wBAC7B,IAAI,CAAC;4BACH,IAAI,GAAG,EAAE,CAAC;gCACR,OAAO,CAAC,GAAG,CAAC,kDAAkD,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,EAAE,KAAK,CAAC,CAAC;gCAC3F,OAAO,CAAC,KAAK,CAAC,0BAA0B,EAAE,GAAG,CAAC,CAAC;gCAC/C,MAAM,WAAW,CAAC,QAAQ,EAAE,CAAC;4BAC/B,CAAC;iCAAM,CAAC;gCACN,OAAO,CAAC,GAAG,CAAC,sCAAsC,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,EAAE,KAAK,CAAC,CAAC;gCAC/E,MAAM,WAAW,CAAC,MAAM,EAAE,CAAC;4BAC7B,CAAC;wBACH,CAAC;wBAAC,OAAO,OAAO,EAAE,CAAC;4BACjB,OAAO,CAAC,GAAG,CAAC,+BAA+B,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,EAAE,KAAK,CAAC,CAAC;4BACxE,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;wBACzB,CAAC;gCAAS,CAAC;4BACT,OAAO,CAAC,GAAG,CAAC,oCAAoC,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,EAAE,KAAK,CAAC,CAAC;wBAC/E,CAAC;oBACH,CAAC;iBACF,CAAC;YACJ,CAAC;SACF,CAAC;IACJ,CAAC;CACF,CAAC"}
@@ -1,53 +0,0 @@
1
- import { ApolloServerPlugin, GraphQLRequestContextDidEncounterErrors, GraphQLRequestListenerParsingDidEnd } from '@apollo/server';
2
- import sql from 'mssql';
3
- import { AppContext } from '../types.js';
4
-
5
- export const TransactionPlugin: ApolloServerPlugin<AppContext> = {
6
- async requestDidStart(requestContext) {
7
- const start = Date.now();
8
- const query = requestContext.request.query || '';
9
- const isMutation = /^\s*mutation\b/i.test(query);
10
-
11
- if (!isMutation) {
12
- return null;
13
- }
14
-
15
- // Start transaction, one or more mutations. If it is just one mutation, this trans wrapper isn't really needed
16
- // but there's no good way to know if it's one or more mutations, so we just start a transaction anyway and it isn't terribly expensive
17
- // to do so with SQL Server anyway.
18
- const pool: sql.ConnectionPool = requestContext.contextValue.dataSource;
19
- const transaction = new sql.Transaction(pool);
20
-
21
- // Store transaction in context for resolvers to use
22
- (requestContext.contextValue as any).transaction = transaction;
23
- console.log('Starting transaction wrapper, time spent: ', Date.now() - start, 'ms ');
24
- await transaction.begin();
25
-
26
- return {
27
- didEncounterErrors: async (requestContext) => {
28
- console.log('Error in transaction wrapper: ' + requestContext.errors, 'time spent: ', Date.now() - start, 'ms');
29
- },
30
- executionDidStart: async () => {
31
- return {
32
- executionDidEnd: async (err) => {
33
- try {
34
- if (err) {
35
- console.log('Error in transaction, rolling back, time spent: ', Date.now() - start, 'ms ');
36
- console.error('Rolling back transaction', err);
37
- await transaction.rollback();
38
- } else {
39
- console.log('Committing transaction, time spent: ', Date.now() - start, 'ms ');
40
- await transaction.commit();
41
- }
42
- } catch (execErr) {
43
- console.log('Execution Error, time spent: ', Date.now() - start, 'ms ');
44
- console.error(execErr);
45
- } finally {
46
- console.log('Transaction complete, time spent: ', Date.now() - start, 'ms ');
47
- }
48
- },
49
- };
50
- },
51
- };
52
- },
53
- };