@nymphjs/tilmeld 1.0.0-beta.5 → 1.0.0-beta.50

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/src/Group.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  import {
2
2
  EntityData,
3
- EntityInterface,
4
3
  EntityJson,
5
4
  EntityPatch,
6
5
  Options,
@@ -10,7 +9,7 @@ import {
10
9
  import md5 from 'crypto-js/md5';
11
10
  import { difference } from 'lodash';
12
11
 
13
- import type Tilmeld from './Tilmeld';
12
+ import { enforceTilmeld } from './enforceTilmeld';
14
13
  import AbleObject from './AbleObject';
15
14
  import {
16
15
  BadDataError,
@@ -85,7 +84,6 @@ export default class Group extends AbleObject<GroupData> {
85
84
  /**
86
85
  * The instance of Tilmeld to use for queries.
87
86
  */
88
- static tilmeld: Tilmeld;
89
87
  static ETYPE = 'tilmeld_group';
90
88
  static class = 'Group';
91
89
 
@@ -121,7 +119,7 @@ export default class Group extends AbleObject<GroupData> {
121
119
  }
122
120
 
123
121
  static async factoryGroupname(
124
- groupname?: string
122
+ groupname?: string,
125
123
  ): Promise<Group & GroupData> {
126
124
  const entity = new this();
127
125
  if (groupname != null) {
@@ -132,7 +130,7 @@ export default class Group extends AbleObject<GroupData> {
132
130
  {
133
131
  type: '&',
134
132
  ilike: ['groupname', groupname.replace(/([\\%_])/g, (s) => `\\${s}`)],
135
- }
133
+ },
136
134
  );
137
135
  if (entity != null) {
138
136
  return entity;
@@ -141,19 +139,17 @@ export default class Group extends AbleObject<GroupData> {
141
139
  return entity;
142
140
  }
143
141
 
144
- static factorySync(guid?: string): Group & GroupData {
145
- return super.factorySync(guid) as Group & GroupData;
142
+ static factorySync(): Group & GroupData {
143
+ return super.factorySync() as Group & GroupData;
146
144
  }
147
145
 
148
- constructor(guid?: string) {
149
- super(guid);
146
+ constructor() {
147
+ super();
150
148
 
151
- if (this.guid == null) {
152
- // Defaults.
153
- this.$data.enabled = true;
154
- this.$data.abilities = [];
155
- this.$updateDataProtection();
156
- }
149
+ // Defaults.
150
+ this.$data.enabled = true;
151
+ this.$data.abilities = [];
152
+ this.$updateDataProtection();
157
153
  }
158
154
 
159
155
  /**
@@ -165,16 +161,17 @@ export default class Group extends AbleObject<GroupData> {
165
161
  */
166
162
  public static async getPrimaryGroups(
167
163
  options?: Options,
168
- selectors?: Selector[]
164
+ selectors?: Selector[],
169
165
  ) {
170
- if (!this.tilmeld.gatekeeper('tilmeld/admin')) {
166
+ const tilmeld = enforceTilmeld(this);
167
+ if (!tilmeld.gatekeeper('tilmeld/admin')) {
171
168
  throw new Error("You don't have permission to do that.");
172
169
  }
173
170
 
174
171
  return await this.getAssignableGroups(
175
- this.tilmeld.config.highestPrimary,
176
- { ...options, class: this.tilmeld.Group, return: 'entity' },
177
- [...(selectors ?? [])]
172
+ tilmeld.config.highestPrimary,
173
+ { ...options, class: tilmeld.Group, return: 'entity' },
174
+ [...(selectors ?? [])],
178
175
  );
179
176
  }
180
177
 
@@ -187,24 +184,26 @@ export default class Group extends AbleObject<GroupData> {
187
184
  */
188
185
  public static async getSecondaryGroups(
189
186
  options?: Options,
190
- selectors?: Selector[]
187
+ selectors?: Selector[],
191
188
  ) {
192
- if (!this.tilmeld.gatekeeper('tilmeld/admin')) {
189
+ const tilmeld = enforceTilmeld(this);
190
+ if (!tilmeld.gatekeeper('tilmeld/admin')) {
193
191
  throw new Error("You don't have permission to do that.");
194
192
  }
195
193
 
196
194
  return await this.getAssignableGroups(
197
- this.tilmeld.config.highestSecondary,
198
- { ...options, class: this.tilmeld.Group, return: 'entity' },
199
- [...(selectors ?? [])]
195
+ tilmeld.config.highestSecondary,
196
+ { ...options, class: tilmeld.Group, return: 'entity' },
197
+ [...(selectors ?? [])],
200
198
  );
201
199
  }
202
200
 
203
201
  private static async getAssignableGroups(
204
202
  highestParent: string | boolean,
205
203
  options: Options<typeof Group>,
206
- selectors: Selector[]
204
+ selectors: Selector[],
207
205
  ) {
206
+ const tilmeld = enforceTilmeld(this);
208
207
  let assignableGroups: (Group & GroupData)[] = [];
209
208
 
210
209
  if (highestParent === false) {
@@ -212,20 +211,28 @@ export default class Group extends AbleObject<GroupData> {
212
211
  }
213
212
 
214
213
  assignableGroups = await this.nymph.getEntities(
215
- { ...options, class: this.tilmeld.Group },
216
- ...selectors
214
+ { ...options, class: tilmeld.Group },
215
+ ...selectors,
217
216
  );
218
217
  if (highestParent !== true) {
219
- assignableGroups = assignableGroups.filter((group) => {
220
- let curGroup = group;
221
- while (curGroup.parent != null && curGroup.parent.cdate != null) {
222
- if (curGroup.parent.guid === highestParent) {
223
- return true;
224
- }
225
- curGroup = curGroup.parent;
226
- }
227
- return false;
228
- });
218
+ assignableGroups = (
219
+ await Promise.all(
220
+ assignableGroups.map(async (group) => {
221
+ let curGroup = group;
222
+ let parent = curGroup.parent;
223
+ await parent?.$wake();
224
+ while (parent != null && parent?.cdate != null) {
225
+ if (parent?.guid === highestParent) {
226
+ return group;
227
+ }
228
+ curGroup = parent;
229
+ parent = curGroup.parent;
230
+ await parent?.$wake();
231
+ }
232
+ return null;
233
+ }),
234
+ )
235
+ ).filter((group) => group != null) as (Group & GroupData)[];
229
236
  }
230
237
  return assignableGroups;
231
238
  }
@@ -245,17 +252,42 @@ export default class Group extends AbleObject<GroupData> {
245
252
  }
246
253
 
247
254
  public $jsonAcceptData(input: EntityJson, allowConflict = false) {
248
- const tilmeld = this.$nymph.tilmeld as Tilmeld;
249
- this.$referenceWake();
255
+ const tilmeld = enforceTilmeld(this);
256
+ this.$check();
257
+
258
+ if (
259
+ 'abilities' in input.data &&
260
+ input.data.abilities?.includes('system/admin') !==
261
+ this.$data.abilities?.includes('system/admin') &&
262
+ (!tilmeld.currentUser ||
263
+ !tilmeld.currentUser.abilities?.includes('system/admin'))
264
+ ) {
265
+ throw new BadDataError(
266
+ "You don't have the authority to grant or revoke system/admin.",
267
+ );
268
+ }
269
+
270
+ if (
271
+ 'abilities' in input.data &&
272
+ input.data.abilities?.includes('tilmeld/admin') !==
273
+ this.$data.abilities?.includes('tilmeld/admin') &&
274
+ (!tilmeld.currentUser ||
275
+ !tilmeld.currentUser.abilities?.includes('system/admin'))
276
+ ) {
277
+ throw new BadDataError(
278
+ "You don't have the authority to grant or revoke tilmeld/admin.",
279
+ );
280
+ }
250
281
 
251
282
  if (
252
- input.data.abilities?.indexOf('system/admin') !== -1 &&
253
- this.$data.abilities?.indexOf('system/admin') === -1 &&
254
- tilmeld.gatekeeper('tilmeld/admin') &&
255
- !tilmeld.gatekeeper('system/admin')
283
+ 'abilities' in input.data &&
284
+ input.data.abilities?.includes('tilmeld/switch') !==
285
+ this.$data.abilities?.includes('tilmeld/switch') &&
286
+ (!tilmeld.currentUser ||
287
+ !tilmeld.currentUser.abilities?.includes('system/admin'))
256
288
  ) {
257
289
  throw new BadDataError(
258
- "You don't have the authority to make this group a system admin."
290
+ "You don't have the authority to grant or revoke tilmeld/switch.",
259
291
  );
260
292
  }
261
293
 
@@ -263,17 +295,42 @@ export default class Group extends AbleObject<GroupData> {
263
295
  }
264
296
 
265
297
  public $jsonAcceptPatch(patch: EntityPatch, allowConflict = false) {
266
- const tilmeld = this.$nymph.tilmeld as Tilmeld;
267
- this.$referenceWake();
298
+ const tilmeld = enforceTilmeld(this);
299
+ this.$check();
268
300
 
269
301
  if (
270
- patch.set.abilities?.indexOf('system/admin') !== -1 &&
271
- this.$data.abilities?.indexOf('system/admin') === -1 &&
272
- tilmeld.gatekeeper('tilmeld/admin') &&
273
- !tilmeld.gatekeeper('system/admin')
302
+ 'abilities' in patch.set &&
303
+ patch.set.abilities?.includes('system/admin') !==
304
+ this.$data.abilities?.includes('system/admin') &&
305
+ (!tilmeld.currentUser ||
306
+ !tilmeld.currentUser.abilities?.includes('system/admin'))
274
307
  ) {
275
308
  throw new BadDataError(
276
- "You don't have the authority to make this group a system admin."
309
+ "You don't have the authority to grant or revoke system/admin.",
310
+ );
311
+ }
312
+
313
+ if (
314
+ 'abilities' in patch.set &&
315
+ patch.set.abilities?.includes('tilmeld/admin') !==
316
+ this.$data.abilities?.includes('tilmeld/admin') &&
317
+ (!tilmeld.currentUser ||
318
+ !tilmeld.currentUser.abilities?.includes('system/admin'))
319
+ ) {
320
+ throw new BadDataError(
321
+ "You don't have the authority to grant or revoke tilmeld/admin.",
322
+ );
323
+ }
324
+
325
+ if (
326
+ 'abilities' in patch.set &&
327
+ patch.set.abilities?.includes('tilmeld/switch') !==
328
+ this.$data.abilities?.includes('tilmeld/switch') &&
329
+ (!tilmeld.currentUser ||
330
+ !tilmeld.currentUser.abilities?.includes('system/admin'))
331
+ ) {
332
+ throw new BadDataError(
333
+ "You don't have the authority to grant or revoke tilmeld/switch.",
277
334
  );
278
335
  }
279
336
 
@@ -291,7 +348,7 @@ export default class Group extends AbleObject<GroupData> {
291
348
  * @param givenUser User to update protection for. If undefined, will use the currently logged in user.
292
349
  */
293
350
  public $updateDataProtection(givenUser?: User & UserData) {
294
- const tilmeld = this.$nymph.tilmeld as Tilmeld;
351
+ const tilmeld = enforceTilmeld(this);
295
352
  let user = givenUser ?? tilmeld.User.current();
296
353
 
297
354
  this.$privateData = [...Group.DEFAULT_PRIVATE_DATA];
@@ -312,7 +369,7 @@ export default class Group extends AbleObject<GroupData> {
312
369
  } else if (
313
370
  this.$data.user != null &&
314
371
  user != null &&
315
- this.$data.user.$is(user)
372
+ this.$data.user.guid === user.guid
316
373
  ) {
317
374
  // Users can see their group's data.
318
375
  this.$privateData = [];
@@ -325,11 +382,13 @@ export default class Group extends AbleObject<GroupData> {
325
382
  * @param group The group, or the group's GUID.
326
383
  * @returns True or false.
327
384
  */
328
- public $isDescendant(givenGroup: (Group & GroupData) | string): boolean {
329
- const tilmeld = this.$nymph.tilmeld as Tilmeld;
385
+ public async $isDescendant(
386
+ givenGroup: (Group & GroupData) | string,
387
+ ): Promise<boolean> {
388
+ const tilmeld = enforceTilmeld(this);
330
389
  let group: Group & GroupData;
331
390
  if (typeof givenGroup === 'string') {
332
- group = tilmeld.Group.factorySync(givenGroup);
391
+ group = await tilmeld.Group.factory(givenGroup);
333
392
  } else {
334
393
  group = givenGroup;
335
394
  }
@@ -337,13 +396,15 @@ export default class Group extends AbleObject<GroupData> {
337
396
  return false;
338
397
  }
339
398
  // Check to see if the group is a descendant of the given group.
340
- if (this.$data.parent == null) {
399
+ const parent = this.$data.parent;
400
+ await parent?.$wake();
401
+ if (parent == null) {
341
402
  return false;
342
403
  }
343
- if (this.$data.parent.$is(group)) {
404
+ if (parent.$is(group)) {
344
405
  return true;
345
406
  }
346
- if (this.$data.parent.$isDescendant(group)) {
407
+ if (await parent.$isDescendant(group)) {
347
408
  return true;
348
409
  }
349
410
  return false;
@@ -355,14 +416,14 @@ export default class Group extends AbleObject<GroupData> {
355
416
  * @returns An array of groups.
356
417
  */
357
418
  public async $getChildren() {
358
- const tilmeld = this.$nymph.tilmeld as Tilmeld;
419
+ const tilmeld = enforceTilmeld(this);
359
420
  return await this.$nymph.getEntities(
360
421
  { class: tilmeld.Group },
361
422
  {
362
423
  type: '&',
363
424
  equal: ['enabled', true],
364
425
  ref: ['parent', this],
365
- }
426
+ },
366
427
  );
367
428
  }
368
429
 
@@ -373,9 +434,9 @@ export default class Group extends AbleObject<GroupData> {
373
434
  * @returns An array of groups.
374
435
  */
375
436
  public async $getDescendants(
376
- andSelf = false
437
+ andSelf = false,
377
438
  ): Promise<(Group & GroupData)[]> {
378
- const tilmeld = this.$nymph.tilmeld as Tilmeld;
439
+ const tilmeld = enforceTilmeld(this);
379
440
  let groups: (Group & GroupData)[] = [];
380
441
  const entities = await this.$nymph.getEntities(
381
442
  { class: tilmeld.Group },
@@ -383,7 +444,7 @@ export default class Group extends AbleObject<GroupData> {
383
444
  type: '&',
384
445
  equal: ['enabled', true],
385
446
  ref: ['parent', this],
386
- }
447
+ },
387
448
  );
388
449
  for (let entity of entities) {
389
450
  groups = (await entity.$getDescendants(true)).concat(groups);
@@ -394,37 +455,6 @@ export default class Group extends AbleObject<GroupData> {
394
455
  return groups;
395
456
  }
396
457
 
397
- /**
398
- * Gets an array of the group's descendant groups.
399
- *
400
- * @param andSelf Include this group in the returned array.
401
- * @returns An array of groups.
402
- */
403
- public $getDescendantsSync(andSelf = false): (Group & GroupData)[] {
404
- const tilmeld = this.$nymph.tilmeld as Tilmeld;
405
- let groups: (Group & GroupData)[] = [];
406
- let entity: EntityInterface | null;
407
- let offset = 0;
408
- do {
409
- entity = this.$nymph.driver.getEntitySync(
410
- { class: tilmeld.Group, offset },
411
- {
412
- type: '&',
413
- equal: ['enabled', true],
414
- ref: ['parent', this],
415
- }
416
- );
417
- if (entity != null) {
418
- groups = entity.$getDescendantsSync(true).concat(groups);
419
- }
420
- offset++;
421
- } while (entity != null);
422
- if (andSelf) {
423
- groups.push(this);
424
- }
425
- return groups;
426
- }
427
-
428
458
  /**
429
459
  * Get the number of parents the group has.
430
460
  *
@@ -436,13 +466,17 @@ export default class Group extends AbleObject<GroupData> {
436
466
  *
437
467
  * @returns The level of the group.
438
468
  */
439
- public $getLevel() {
440
- const tilmeld = this.$nymph.tilmeld as Tilmeld;
441
- let group = tilmeld.Group.factorySync(this.guid ?? undefined);
469
+ public async $getLevel() {
470
+ const tilmeld = enforceTilmeld(this);
471
+ let group = await tilmeld.Group.factory(this.guid ?? undefined);
472
+ let parent = group.parent;
473
+ await parent?.$wake();
442
474
  let level = 0;
443
- while (group.parent != null && group.parent.cdate != null && level < 1024) {
475
+ while (parent != null && parent.cdate != null && level < 1024) {
444
476
  level++;
445
- group = group.parent;
477
+ group = parent;
478
+ parent = group.parent;
479
+ await parent?.$wake();
446
480
  }
447
481
  return level;
448
482
  }
@@ -458,9 +492,9 @@ export default class Group extends AbleObject<GroupData> {
458
492
  public async $getUsers(
459
493
  descendants = false,
460
494
  limit?: number,
461
- offset?: number
495
+ offset?: number,
462
496
  ): Promise<(User & UserData)[]> {
463
- const tilmeld = this.$nymph.tilmeld as Tilmeld;
497
+ const tilmeld = enforceTilmeld(this);
464
498
  let groups: (Group & GroupData)[] = [];
465
499
  if (descendants) {
466
500
  groups = await this.$getDescendants();
@@ -489,7 +523,7 @@ export default class Group extends AbleObject<GroupData> {
489
523
  group,
490
524
  ]),
491
525
  ],
492
- }
526
+ },
493
527
  );
494
528
  }
495
529
 
@@ -502,15 +536,21 @@ export default class Group extends AbleObject<GroupData> {
502
536
  result: boolean;
503
537
  message: string;
504
538
  }> {
505
- const tilmeld = this.$nymph.tilmeld as Tilmeld;
539
+ const tilmeld = enforceTilmeld(this);
506
540
  if (!tilmeld.config.emailUsernames) {
507
541
  if (this.$data.groupname == null || !this.$data.groupname.length) {
508
542
  return { result: false, message: 'Please specify a groupname.' };
509
543
  }
510
- if (
511
- tilmeld.config.maxUsernameLength > 0 &&
512
- this.$data.groupname.length > tilmeld.config.maxUsernameLength
513
- ) {
544
+ if (this.$data.groupname.length < tilmeld.config.minUsernameLength) {
545
+ return {
546
+ result: false,
547
+ message:
548
+ 'Groupnames must be at least ' +
549
+ tilmeld.config.minUsernameLength +
550
+ ' characters.',
551
+ };
552
+ }
553
+ if (this.$data.groupname.length > tilmeld.config.maxUsernameLength) {
514
554
  return {
515
555
  result: false,
516
556
  message:
@@ -522,7 +562,7 @@ export default class Group extends AbleObject<GroupData> {
522
562
  if (
523
563
  difference(
524
564
  this.$data.groupname.split(''),
525
- tilmeld.config.validChars.split('')
565
+ tilmeld.config.validChars.split(''),
526
566
  ).length
527
567
  ) {
528
568
  return {
@@ -549,7 +589,7 @@ export default class Group extends AbleObject<GroupData> {
549
589
  }
550
590
  const test = await this.$nymph.getEntity(
551
591
  { class: tilmeld.Group, skipAc: true },
552
- selector
592
+ selector,
553
593
  );
554
594
  if (test != null) {
555
595
  return { result: false, message: 'That groupname is taken.' };
@@ -565,10 +605,16 @@ export default class Group extends AbleObject<GroupData> {
565
605
  if (this.$data.groupname == null || !this.$data.groupname.length) {
566
606
  return { result: false, message: 'Please specify an email.' };
567
607
  }
568
- if (
569
- tilmeld.config.maxUsernameLength > 0 &&
570
- this.$data.groupname.length > tilmeld.config.maxUsernameLength
571
- ) {
608
+ if (this.$data.groupname.length < tilmeld.config.minUsernameLength) {
609
+ return {
610
+ result: false,
611
+ message:
612
+ 'Emails must be at least ' +
613
+ tilmeld.config.minUsernameLength +
614
+ ' characters.',
615
+ };
616
+ }
617
+ if (this.$data.groupname.length > tilmeld.config.maxUsernameLength) {
572
618
  return {
573
619
  result: false,
574
620
  message:
@@ -588,7 +634,7 @@ export default class Group extends AbleObject<GroupData> {
588
634
  * @returns An object with a boolean 'result' entry and a 'message' entry.
589
635
  */
590
636
  public async $checkEmail(): Promise<{ result: boolean; message: string }> {
591
- const tilmeld = this.$nymph.tilmeld as Tilmeld;
637
+ const tilmeld = enforceTilmeld(this);
592
638
  if (this.$data.email === '') {
593
639
  return { result: true, message: '' };
594
640
  }
@@ -610,7 +656,7 @@ export default class Group extends AbleObject<GroupData> {
610
656
  }
611
657
  const test = await this.$nymph.getEntity(
612
658
  { class: tilmeld.Group, skipAc: true },
613
- selector
659
+ selector,
614
660
  );
615
661
  if (test != null) {
616
662
  return {
@@ -627,7 +673,7 @@ export default class Group extends AbleObject<GroupData> {
627
673
  }
628
674
 
629
675
  public async $save() {
630
- const tilmeld = this.$nymph.tilmeld as Tilmeld;
676
+ const tilmeld = enforceTilmeld(this);
631
677
  if (this.$data.groupname == null || !this.$data.groupname.trim().length) {
632
678
  return false;
633
679
  }
@@ -671,10 +717,12 @@ export default class Group extends AbleObject<GroupData> {
671
717
  delete this.$data.unverifiedSecondary;
672
718
  }
673
719
 
674
- // Groups should never have 'system/admin' or 'tilmeld/admin' abilities.
720
+ // Groups should never have 'system/admin', 'tilmeld/admin', or
721
+ // 'tilmeld/switch' abilities.
675
722
  this.$data.abilities = difference(this.$data.abilities ?? [], [
676
723
  'system/admin',
677
724
  'tilmeld/admin',
725
+ 'tilmeld/switch',
678
726
  null,
679
727
  undefined,
680
728
  ]) as string[];
@@ -692,14 +740,16 @@ export default class Group extends AbleObject<GroupData> {
692
740
  }
693
741
 
694
742
  // Validate group parent. Make sure it's not a descendant of this group.
743
+ const parent = this.$data.parent;
744
+ await parent?.$wake();
695
745
  if (
696
- this.$data.parent &&
697
- (this.$data.parent.cdate == null ||
698
- this.$is(this.$data.parent) ||
699
- this.$data.parent.$isDescendant(this))
746
+ parent &&
747
+ (parent.cdate == null ||
748
+ this.$is(parent) ||
749
+ (await parent.$isDescendant(this)))
700
750
  ) {
701
751
  throw new BadDataError(
702
- "Group parent can't be itself or descendant of itself."
752
+ "Group parent can't be itself or descendant of itself.",
703
753
  );
704
754
  }
705
755
 
@@ -713,14 +763,14 @@ export default class Group extends AbleObject<GroupData> {
713
763
  if (this.$data.defaultPrimary) {
714
764
  const currentPrimary = await this.$nymph.getEntity(
715
765
  { class: tilmeld.Group },
716
- { type: '&', truthy: 'defaultPrimary' }
766
+ { type: '&', truthy: 'defaultPrimary' },
717
767
  );
718
768
  if (currentPrimary != null && !this.$is(currentPrimary)) {
719
769
  currentPrimary.defaultPrimary = false;
720
770
  if (!(await currentPrimary.$save())) {
721
771
  throw new CouldNotChangeDefaultPrimaryGroupError(
722
772
  'Could not change new user primary group from ' +
723
- `${currentPrimary.groupname}.`
773
+ `${currentPrimary.groupname}.`,
724
774
  );
725
775
  }
726
776
  }
@@ -746,7 +796,7 @@ export default class Group extends AbleObject<GroupData> {
746
796
  }
747
797
 
748
798
  public async $delete() {
749
- let tilmeld = this.$nymph.tilmeld as Tilmeld;
799
+ let tilmeld = enforceTilmeld(this);
750
800
  if (!tilmeld.gatekeeper('tilmeld/admin')) {
751
801
  throw new BadDataError("You don't have the authority to delete groups.");
752
802
  }
@@ -755,7 +805,7 @@ export default class Group extends AbleObject<GroupData> {
755
805
  const nymph = this.$nymph;
756
806
  const tnymph = await nymph.startTransaction(transaction);
757
807
  this.$nymph = tnymph;
758
- tilmeld = this.$nymph.tilmeld as Tilmeld;
808
+ tilmeld = enforceTilmeld(this);
759
809
 
760
810
  // Delete descendants.
761
811
  const descendants = await this.$getDescendants();
@@ -778,7 +828,7 @@ export default class Group extends AbleObject<GroupData> {
778
828
  {
779
829
  type: '&',
780
830
  ref: ['group', this],
781
- }
831
+ },
782
832
  );
783
833
  for (let user of primaryUsers) {
784
834
  delete user.group;
@@ -798,7 +848,7 @@ export default class Group extends AbleObject<GroupData> {
798
848
  {
799
849
  type: '&',
800
850
  ref: ['groups', this],
801
- }
851
+ },
802
852
  );
803
853
  for (let user of secondaryUsers) {
804
854
  user.$delGroup(this);