• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

alkem-io / server / #8050

16 Aug 2024 11:21AM UTC coverage: 13.92%. First build
#8050

Pull #4411

travis-ci

Pull Request #4411: Type added to authorization policy entity

80 of 4158 branches covered (1.92%)

Branch coverage included in aggregate %.

61 of 116 new or added lines in 50 files covered. (52.59%)

1945 of 10389 relevant lines covered (18.72%)

3.01 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

16.51
/src/domain/collaboration/callout-framing/callout.framing.service.ts
1
import { ProfileService } from '@domain/common/profile/profile.service';
19✔
2
import { Injectable } from '@nestjs/common';
19✔
3
import { CreateCalloutFramingInput } from './dto/callout.framing.dto.create';
4
import { UpdateCalloutFramingInput } from './dto/callout.framing.dto.update';
5
import { ICalloutFraming } from './callout.framing.interface';
6
import { CalloutFraming } from './callout.framing.entity';
19✔
7
import { AuthorizationPolicy } from '@domain/common/authorization-policy/authorization.policy.entity';
19✔
8
import { InjectRepository } from '@nestjs/typeorm';
19✔
9
import { FindOneOptions, FindOptionsRelations, Repository } from 'typeorm';
19✔
10
import { EntityNotFoundException } from '@common/exceptions/entity.not.found.exception';
19✔
11
import { LogContext } from '@common/enums/logging.context';
19✔
12
import { IProfile } from '@domain/common/profile/profile.interface';
19✔
13
import { ProfileType } from '@common/enums';
14
import { WhiteboardService } from '@domain/common/whiteboard/whiteboard.service';
19✔
15
import { LinkService } from '@domain/collaboration/link/link.service';
19✔
16
import { IWhiteboard } from '@domain/common/whiteboard/whiteboard.interface';
17
import { MemoService } from '@domain/common/memo/memo.service';
19✔
18
import { VisualType } from '@common/enums/visual.type';
19
import { NamingService } from '@services/infrastructure/naming/naming.service';
19✔
20
import { CreateTagsetInput } from '@domain/common/tagset/dto/tagset.dto.create';
21
import { TagsetReservedName } from '@common/enums/tagset.reserved.name';
19✔
22
import { TagsetType } from '@common/enums/tagset.type';
19✔
23
import { AuthorizationPolicyService } from '@domain/common/authorization-policy/authorization.policy.service';
19✔
24
import { IStorageAggregator } from '@domain/storage/storage-aggregator/storage.aggregator.interface';
25
import { AuthorizationPolicyType } from '@common/enums/authorization.policy.type';
26
import { TagsetService } from '@domain/common/tagset/tagset.service';
19✔
27
import { CalloutFramingType } from '@common/enums/callout.framing.type';
28
import { CreateWhiteboardInput } from '@domain/common/whiteboard/types';
29
import { CreateLinkInput } from '@domain/collaboration/link/dto/link.dto.create';
19✔
30
import { ValidationException } from '@common/exceptions';
31
import { CreateMemoInput, IMemo } from '@domain/common/memo/types';
×
32
import { ILink } from '../link/link.interface';
×
33

×
34
@Injectable()
×
35
export class CalloutFramingService {
36
  constructor(
×
37
    private authorizationPolicyService: AuthorizationPolicyService,
38
    private profileService: ProfileService,
39
    private whiteboardService: WhiteboardService,
40
    private linkService: LinkService,
41
    private memoService: MemoService,
42
    private namingService: NamingService,
43
    private tagsetService: TagsetService,
44
    @InjectRepository(CalloutFraming)
45
    private calloutFramingRepository: Repository<CalloutFraming>
46
  ) {}
×
47

NEW
48
  public async createCalloutFraming(
×
49
    calloutFramingData: CreateCalloutFramingInput,
50
    storageAggregator: IStorageAggregator,
51
    userID?: string
52
  ): Promise<ICalloutFraming> {
×
53
    const calloutFraming: ICalloutFraming =
54
      CalloutFraming.create(calloutFramingData);
55

×
56
    calloutFraming.authorization = new AuthorizationPolicy(
57
      AuthorizationPolicyType.CALLOUT_FRAMING
58
    );
59

60
    const { profile: profileData, tags } = calloutFramingData;
61

62
    const defaultTagset: CreateTagsetInput = {
×
63
      name: TagsetReservedName.DEFAULT,
64
      type: TagsetType.FREEFORM,
65
      tags: tags,
×
66
    };
67

×
68
    const tagsetInputs = [defaultTagset];
69

70
    calloutFramingData.profile.tagsets = this.tagsetService.updateTagsetInputs(
71
      calloutFraming.profile.tagsets,
72
      tagsetInputs
73
    );
×
74

75
    calloutFraming.profile = await this.profileService.createProfile(
76
      profileData,
77
      ProfileType.CALLOUT_FRAMING,
78
      storageAggregator
79
    );
×
80

×
81
    calloutFraming.type = calloutFramingData.type ?? CalloutFramingType.NONE;
×
82

83
    if (calloutFraming.type === CalloutFramingType.WHITEBOARD) {
84
      if (calloutFramingData.whiteboard) {
85
        await this.createNewWhiteboardInCalloutFraming(
86
          calloutFraming,
×
87
          calloutFramingData.whiteboard,
88
          storageAggregator,
89
          userID
90
        );
91
      } else {
×
92
        throw new ValidationException(
93
          'Callout Framing of type WHITEBOARD requires whiteboard data.',
94
          LogContext.COLLABORATION
95
        );
96
      }
97
    }
×
98

99
    if (calloutFraming.type === CalloutFramingType.LINK) {
100
      if (calloutFramingData.link) {
101
        await this.createNewLinkInCalloutFraming(
102
          calloutFraming,
103
          calloutFramingData.link,
104
          storageAggregator
×
105
        );
×
106
      } else {
107
        throw new ValidationException(
108
          'Callout Framing of type LINK requires link data.',
109
          LogContext.COLLABORATION
110
        );
111
      }
×
112
    }
×
113

114
    if (calloutFraming.type === CalloutFramingType.MEMO) {
115
      if (calloutFramingData.memo) {
116
        await this.createNewMemoInCalloutFraming(
117
          calloutFraming,
118
          calloutFramingData.memo,
×
119
          storageAggregator,
×
120
          userID
121
        );
122
      } else {
123
        throw new ValidationException(
124
          'Callout Framing of type MEMO requires memo data.',
125
          LogContext.COLLABORATION
×
126
        );
×
127
      }
128
    }
129

130
    return calloutFraming;
131
  }
132

133
  private async createNewWhiteboardInCalloutFraming(
×
134
    calloutFraming: ICalloutFraming,
135
    whiteboardData: CreateWhiteboardInput,
136
    storageAggregator: IStorageAggregator,
137
    userID?: string
×
138
  ) {
×
139
    const reservedNameIDs: string[] = []; // no reserved nameIDs for framing
140
    whiteboardData.nameID =
141
      this.namingService.createNameIdAvoidingReservedNameIDs(
142
        `${whiteboardData.profile?.displayName ?? 'whiteboard'}`,
143
        reservedNameIDs
144
      );
145
    calloutFraming.whiteboard = await this.whiteboardService.createWhiteboard(
146
      whiteboardData,
147
      storageAggregator,
×
148
      userID
×
149
    );
150
  }
151

×
152
  private async createNewMemoInCalloutFraming(
×
153
    calloutFraming: ICalloutFraming,
154
    memoData: CreateMemoInput,
155
    storageAggregator: IStorageAggregator,
156
    userID?: string
157
  ) {
×
158
    const reservedNameIDs: string[] = []; // no reserved nameIDs for framing
×
159
    memoData.nameID = this.namingService.createNameIdAvoidingReservedNameIDs(
160
      `${memoData.profile?.displayName ?? 'memo'}`,
161
      reservedNameIDs
162
    );
163
    calloutFraming.memo = await this.memoService.createMemo(
×
164
      memoData,
165
      storageAggregator,
166
      userID
×
167
    );
×
168
    await this.profileService.addVisualsOnProfile(
169
      calloutFraming.memo.profile,
170
      memoData.profile?.visuals,
171
      [VisualType.BANNER]
×
172
    );
173
  }
174

175
  private async createNewLinkInCalloutFraming(
176
    calloutFraming: ICalloutFraming,
177
    linkData: CreateLinkInput,
178
    storageAggregator: IStorageAggregator
×
179
  ) {
×
180
    calloutFraming.link = await this.linkService.createLink(
×
181
      linkData,
182
      storageAggregator
183
    );
184
  }
185

186
  public async updateCalloutFraming(
×
187
    calloutFraming: ICalloutFraming,
×
188
    calloutFramingData: UpdateCalloutFramingInput,
189
    storageAggregator: IStorageAggregator,
190
    isParentCalloutTemplate: boolean,
191
    userID?: string
×
192
  ): Promise<ICalloutFraming> {
193
    if (calloutFramingData.profile) {
194
      calloutFraming.profile = await this.profileService.updateProfile(
195
        calloutFraming.profile,
196
        calloutFramingData.profile
197
      );
198
    }
×
199

200
    if (calloutFramingData.type) {
201
      const oldType = calloutFraming.type;
202
      const newType = calloutFramingData.type;
203

204
      // Validate framing type transitions for callout templates
×
205
      if (
×
206
        isParentCalloutTemplate &&
207
        newType !== oldType &&
208
        newType !== CalloutFramingType.NONE
209
      ) {
210
        throw new ValidationException(
×
211
          'Callout templates can only transition framing type to NONE.',
212
          LogContext.COLLABORATION
213
        );
214
      }
215
      calloutFraming.type = calloutFramingData.type;
216
    }
217

×
218
    switch (calloutFraming.type) {
219
      case CalloutFramingType.WHITEBOARD: {
220
        // If there was a memo before, we delete it
221
        if (calloutFraming.memo) {
222
          await this.memoService.deleteMemo(calloutFraming.memo.id);
223
          calloutFraming.memo = undefined;
×
224
        }
×
225

226
        // If there was a link before, we delete it
227
        if (calloutFraming.link) {
×
228
          await this.linkService.deleteLink(calloutFraming.link.id);
229
          calloutFraming.link = undefined;
230
        }
231

232
        // if there is no content coming with the mutation, we do nothing with the whiteboard
233
        if (!calloutFramingData.whiteboardContent) {
×
234
          return calloutFraming;
×
235
        }
236
        // if there is content and a whiteboard, we update it
237
        if (calloutFraming.whiteboard) {
238
          calloutFraming.whiteboard =
239
            await this.whiteboardService.updateWhiteboardContent(
240
              calloutFraming.whiteboard.id,
241
              calloutFramingData.whiteboardContent
242
            );
243
        } else {
×
244
          // if there is content and no whiteboard, we create a new one
245
          await this.createNewWhiteboardInCalloutFraming(
246
            calloutFraming,
247
            {
248
              profile: {
249
                displayName: 'Callout Framing Whiteboard',
250
              },
251
              content: calloutFramingData.whiteboardContent,
252
            },
253
            storageAggregator,
254
            userID
255
          );
256
        }
257
        break;
×
258
      }
×
259
      case CalloutFramingType.MEMO: {
×
260
        // If there was a whiteboard before, we delete it
261
        if (calloutFraming.whiteboard) {
262
          await this.whiteboardService.deleteWhiteboard(
263
            calloutFraming.whiteboard.id
×
264
          );
×
265
          calloutFraming.whiteboard = undefined;
266
        }
×
267

×
268
        // If there was a link before, we delete it
269
        if (calloutFraming.link) {
270
          await this.linkService.deleteLink(calloutFraming.link.id);
271
          calloutFraming.link = undefined;
272
        }
×
273

274
        // if there is no content coming with the mutation, we do nothing with the whiteboard
275
        if (!calloutFramingData.memoContent) {
276
          return calloutFraming;
277
        }
278

279
        // if there is content and a Memo AND the parent Callout is template, we update it
280
        if (
281
          calloutFraming.memo &&
282
          calloutFramingData.memoContent &&
283
          isParentCalloutTemplate
284
        ) {
285
          calloutFraming.memo = await this.memoService.updateMemoContent(
286
            calloutFraming.memo.id,
287
            calloutFramingData.memoContent
288
          );
289
        } else {
290
          // if there is content and no Memo, we create a new one
291
          await this.createNewMemoInCalloutFraming(
292
            calloutFraming,
293
            {
294
              profile: {
295
                displayName: 'Callout Framing Memo',
296
              },
297
              // content: calloutFramingData.memoContent,
298
            },
299
            storageAggregator,
300
            userID
301
          );
302
        }
303
        break;
304
      }
305
      case CalloutFramingType.LINK: {
306
        // If there was a whiteboard before, we delete it
307
        if (calloutFraming.whiteboard) {
308
          await this.whiteboardService.deleteWhiteboard(
309
            calloutFraming.whiteboard.id
310
          );
311
          calloutFraming.whiteboard = undefined;
312
        }
313

314
        // If there was a memo before, we delete it
315
        if (calloutFraming.memo) {
316
          await this.memoService.deleteMemo(calloutFraming.memo.id);
317
          calloutFraming.memo = undefined;
318
        }
319

320
        // Handle LINK type updates
321
        if (calloutFraming.link && calloutFramingData.link) {
322
          calloutFraming.link = await this.linkService.updateLink(
323
            calloutFramingData.link
324
          );
325
        } else if (calloutFramingData.link) {
326
          calloutFraming.link = await this.linkService.createLink(
327
            calloutFramingData.link as CreateLinkInput,
328
            storageAggregator
329
          );
330
        }
331
        break;
332
      }
333
      case CalloutFramingType.NONE:
334
      default: {
335
        // if the type is NONE we remove any existing framing content
336
        if (calloutFraming.whiteboard) {
337
          await this.whiteboardService.deleteWhiteboard(
338
            calloutFraming.whiteboard.id
339
          );
340
          calloutFraming.whiteboard = undefined;
341
        }
342
        if (calloutFraming.memo) {
343
          await this.memoService.deleteMemo(calloutFraming.memo.id);
344
          calloutFraming.memo = undefined;
345
        }
346
        if (calloutFraming.link) {
347
          await this.linkService.deleteLink(calloutFraming.link.id);
348
          calloutFraming.link = undefined;
349
        }
350
        break;
351
      }
352
    }
353

354
    return calloutFraming;
355
  }
356

357
  async delete(calloutFramingInput: ICalloutFraming): Promise<ICalloutFraming> {
358
    const calloutFramingID = calloutFramingInput.id;
359
    const calloutFraming = await this.getCalloutFramingOrFail(
360
      calloutFramingID,
361
      {
362
        relations: {
363
          profile: true,
364
          whiteboard: true,
365
          link: true,
366
          memo: true,
367
        },
368
      }
369
    );
370
    if (calloutFraming.profile) {
371
      await this.profileService.deleteProfile(calloutFraming.profile.id);
372
    }
373

374
    if (calloutFraming.whiteboard) {
375
      await this.whiteboardService.deleteWhiteboard(
376
        calloutFraming.whiteboard.id
377
      );
378
    }
379

380
    if (calloutFraming.link) {
381
      await this.linkService.deleteLink(calloutFraming.link.id);
382
    }
383

384
    if (calloutFraming.memo) {
385
      await this.memoService.deleteMemo(calloutFraming.memo.id);
386
    }
387

388
    if (calloutFraming.authorization) {
389
      await this.authorizationPolicyService.delete(
390
        calloutFraming.authorization
391
      );
392
    }
393

394
    const result = await this.calloutFramingRepository.remove(
395
      calloutFraming as CalloutFraming
396
    );
397
    result.id = calloutFramingID;
398
    return result;
399
  }
400

401
  async save(calloutFraming: ICalloutFraming): Promise<ICalloutFraming> {
402
    return await this.calloutFramingRepository.save(calloutFraming);
403
  }
404

405
  public async getCalloutFramingOrFail(
406
    calloutFramingID: string,
407
    options?: FindOneOptions<CalloutFraming>
408
  ): Promise<ICalloutFraming | never> {
409
    const calloutFraming = await this.calloutFramingRepository.findOne({
410
      where: { id: calloutFramingID },
411
      ...options,
412
    });
413

414
    if (!calloutFraming)
415
      throw new EntityNotFoundException(
416
        `No CalloutFraming found with the given id: ${calloutFramingID}`,
417
        LogContext.COLLABORATION
418
      );
419
    return calloutFraming;
420
  }
421

422
  public async getProfile(
423
    calloutFramingInput: ICalloutFraming,
424
    relations?: FindOptionsRelations<ICalloutFraming>
425
  ): Promise<IProfile> {
426
    const calloutFraming = await this.getCalloutFramingOrFail(
427
      calloutFramingInput.id,
428
      {
429
        relations: { profile: true, ...relations },
430
      }
431
    );
432
    if (!calloutFraming.profile)
433
      throw new EntityNotFoundException(
434
        `Callout profile not initialized: ${calloutFramingInput.id}`,
435
        LogContext.COLLABORATION
436
      );
437

438
    return calloutFraming.profile;
439
  }
440

441
  public async getWhiteboard(
442
    calloutFramingInput: ICalloutFraming,
443
    relations?: FindOptionsRelations<ICalloutFraming>
444
  ): Promise<IWhiteboard | null> {
445
    const calloutFraming = await this.getCalloutFramingOrFail(
446
      calloutFramingInput.id,
447
      {
448
        relations: { whiteboard: true, ...relations },
449
      }
450
    );
451
    if (!calloutFraming.whiteboard) {
452
      return null;
453
    }
454

455
    return calloutFraming.whiteboard;
456
  }
457

458
  public async getLink(
459
    calloutFramingInput: ICalloutFraming,
460
    relations?: FindOptionsRelations<ICalloutFraming>
461
  ): Promise<ILink | null> {
462
    const calloutFraming = await this.getCalloutFramingOrFail(
463
      calloutFramingInput.id,
464
      {
465
        relations: { link: true, ...relations },
466
      }
467
    );
468
    if (!calloutFraming.link) {
469
      return null;
470
    }
471

472
    return calloutFraming.link;
473
  }
474

475
  public async getMemo(
476
    calloutFramingInput: ICalloutFraming,
477
    relations?: FindOptionsRelations<ICalloutFraming>
478
  ): Promise<IMemo | null> {
479
    const calloutFraming = await this.getCalloutFramingOrFail(
480
      calloutFramingInput.id,
481
      {
482
        relations: { memo: true, ...relations },
483
      }
484
    );
485
    if (!calloutFraming.memo) {
486
      return null;
487
    }
488

489
    return calloutFraming.memo;
490
  }
491
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc