• 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

8.41
/src/domain/collaboration/collaboration/collaboration.service.ts
1
import { Injectable } from '@nestjs/common';
17✔
2
import { InjectEntityManager, InjectRepository } from '@nestjs/typeorm';
17✔
3
import { EntityManager, FindOneOptions, Repository } from 'typeorm';
17✔
4
import {
5
  EntityNotFoundException,
6
  EntityNotInitializedException,
7
  RelationshipNotFoundException,
8
} from '@common/exceptions';
9
import { LogContext } from '@common/enums';
10
import { AuthorizationPolicy } from '@domain/common/authorization-policy/authorization.policy.entity';
17✔
11
import { AuthorizationPolicyService } from '@domain/common/authorization-policy/authorization.policy.service';
12
import { Collaboration } from '@domain/collaboration/collaboration/collaboration.entity';
13
import { ICollaboration } from '@domain/collaboration/collaboration/collaboration.interface';
14
import { AgentInfo } from '@core/authentication.agent.info/agent.info';
15
import { CreateTagsetTemplateInput } from '@domain/common/tagset-template';
16
import { TagsetReservedName } from '@common/enums/tagset.reserved.name';
17
import { TimelineService } from '@domain/timeline/timeline/timeline.service';
17✔
18
import { ITimeline } from '@domain/timeline/timeline/timeline.interface';
17✔
19
import { IStorageAggregator } from '@domain/storage/storage-aggregator/storage.aggregator.interface';
17✔
20
import { InnovationFlowService } from '../innovation-flow/innovation.flow.service';
21
import { TagsetType } from '@common/enums/tagset.type';
17✔
22
import { IInnovationFlow } from '../innovation-flow/innovation.flow.interface';
17✔
23
import { CreateCollaborationInput } from './dto/collaboration.dto.create';
24
import { Space } from '@domain/space/space/space.entity';
17✔
25
import { SpaceLevel } from '@common/enums/space.level';
26
import { AuthorizationPolicyType } from '@common/enums/authorization.policy.type';
17✔
27
import { CreateInnovationFlowInput } from '../innovation-flow/dto/innovation.flow.dto.create';
17✔
28
import { LicenseService } from '@domain/common/license/license.service';
17✔
29
import { LicenseType } from '@common/enums/license.type';
30
import { LicenseEntitlementType } from '@common/enums/license.entitlement.type';
31
import { LicenseEntitlementDataType } from '@common/enums/license.entitlement.data.type';
32
import { CalloutsSetService } from '../callouts-set/callouts.set.service';
17✔
33
import { CalloutVisibility } from '@common/enums/callout.visibility';
34
import { ICalloutsSet } from '../callouts-set/callouts.set.interface';
17✔
35
import { CalloutsSetType } from '@common/enums/callouts.set.type';
36
import { sortBySortOrder } from '../innovation-flow-state/utils/sortBySortOrder';
37

38
@Injectable()
39
export class CollaborationService {
40
  constructor(
41
    private authorizationPolicyService: AuthorizationPolicyService,
17✔
42
    private calloutsSetService: CalloutsSetService,
17✔
43
    private innovationFlowService: InnovationFlowService,
44
    @InjectRepository(Collaboration)
17✔
45
    private collaborationRepository: Repository<Collaboration>,
46
    @InjectEntityManager('default')
17✔
47
    private entityManager: EntityManager,
17✔
48
    private timelineService: TimelineService,
17✔
49
    private licenseService: LicenseService
17✔
50
  ) {}
17✔
51

52
  async createCollaboration(
53
    collaborationData: CreateCollaborationInput,
17✔
54
    storageAggregator: IStorageAggregator,
55
    agentInfo?: AgentInfo
17✔
56
  ): Promise<ICollaboration> {
57
    if (
58
      !collaborationData.calloutsSetData ||
59
      !collaborationData.innovationFlowData
17✔
60
    ) {
61
      throw new RelationshipNotFoundException(
17✔
62
        'Unable to create Collaboration: missing required data',
63
        LogContext.COLLABORATION
64
      );
17✔
65
    }
66
    const collaboration: ICollaboration = Collaboration.create();
×
67
    collaboration.authorization = new AuthorizationPolicy(
×
68
      AuthorizationPolicyType.COLLABORATION
×
69
    );
×
70

×
71
    collaboration.calloutsSet = this.calloutsSetService.createCalloutsSet(
×
72
      collaborationData.calloutsSetData,
×
73
      CalloutsSetType.COLLABORATION
74
    );
×
75

76
    const innovationFlowStatesTagsetInput =
×
77
      this.createInnovationFlowStatesTagsetTemplateInput(
×
78
        collaborationData.innovationFlowData
×
79
      );
×
80
    collaboration.calloutsSet.tagsetTemplateSet =
81
      this.calloutsSetService.addTagsetTemplate(
82
        collaboration.calloutsSet,
83
        innovationFlowStatesTagsetInput
84
      );
85

86
    // save the calloutsSet with the tagsetTemplates so can use it in the innovation flow as a template for it's tags
87
    await this.calloutsSetService.save(collaboration.calloutsSet);
88

×
NEW
89
    collaboration.isTemplate = collaborationData.isTemplate || false;
×
90

91
    if (!collaboration.isTemplate) {
92
      collaboration.timeline = this.timelineService.createTimeline();
×
93
    }
×
94

×
95
    collaboration.license = this.licenseService.createLicense({
×
96
      type: LicenseType.COLLABORATION,
97
      entitlements: [
98
        {
×
99
          type: LicenseEntitlementType.SPACE_FLAG_SAVE_AS_TEMPLATE,
100
          dataType: LicenseEntitlementDataType.FLAG,
101
          limit: 0,
102
          enabled: false,
103
        },
×
104
        {
105
          type: LicenseEntitlementType.SPACE_FLAG_WHITEBOARD_MULTI_USER,
106
          dataType: LicenseEntitlementDataType.FLAG,
107
          limit: 0,
108
          enabled: false,
×
109
        },
×
110
        {
111
          type: LicenseEntitlementType.SPACE_FLAG_MEMO_MULTI_USER,
112
          dataType: LicenseEntitlementDataType.FLAG,
×
113
          limit: 0,
114
          enabled: false,
115
        },
116
      ],
117
    });
×
118

119
    const flowStatesTagsetTemplate = this.calloutsSetService.getTagsetTemplate(
120
      collaboration.calloutsSet.tagsetTemplateSet,
×
121
      TagsetReservedName.FLOW_STATE
122
    );
123

124
    if (!flowStatesTagsetTemplate) {
125
      throw new RelationshipNotFoundException(
126
        'Unable to create tagset template for flow states',
127
        LogContext.COLLABORATION
×
128
      );
129
    }
130
    // Note: need to create the innovation flow after creation of
131
    // tagsetTemplates on Collaboration so can pass it in to the InnovationFlow
132
    collaboration.innovationFlow =
133
      await this.innovationFlowService.createInnovationFlow(
134
        collaborationData.innovationFlowData,
×
135
        storageAggregator,
136
        flowStatesTagsetTemplate
137
      );
138

×
139
    if (collaborationData.calloutsSetData.calloutsData) {
140
      collaborationData.calloutsSetData.calloutsData.forEach(
141
        callout => (callout.isTemplate = collaboration.isTemplate)
142
      );
143
      collaboration.calloutsSet.callouts =
144
        await this.calloutsSetService.addCallouts(
145
          collaboration.calloutsSet,
×
146
          collaborationData.calloutsSetData.calloutsData,
147
          storageAggregator,
148
          agentInfo?.userID
149
        );
150
    }
151

×
152
    this.calloutsSetService.moveCalloutsToDefaultFlowState(
153
      flowStatesTagsetTemplate.allowedValues,
154
      collaboration.calloutsSet.callouts
155
    );
156

157
    return collaboration;
158
  }
×
159

×
160
  private createInnovationFlowStatesTagsetTemplateInput(
161
    innovationFlowData: CreateInnovationFlowInput
162
  ): CreateTagsetTemplateInput {
163
    this.innovationFlowService.validateInnovationFlowDefinition(
164
      innovationFlowData.states
×
165
    );
166
    const allowedValues = innovationFlowData.states
167
      .sort(sortBySortOrder)
168
      .map(state => state.displayName);
×
169
    let defaultSelectedValue = innovationFlowData.currentStateDisplayName;
×
170
    if (
171
      !defaultSelectedValue ||
172
      allowedValues.indexOf(defaultSelectedValue) === -1
173
    ) {
174
      defaultSelectedValue = allowedValues[0];
175
    }
176

177
    const tagsetTemplateDataStates: CreateTagsetTemplateInput = {
178
      name: TagsetReservedName.FLOW_STATE,
×
179
      type: TagsetType.SELECT_ONE,
180
      allowedValues,
181
      defaultSelectedValue,
×
182
    };
183
    return tagsetTemplateDataStates;
184
  }
×
185

×
186
  async save(collaboration: ICollaboration): Promise<ICollaboration> {
187
    return await this.collaborationRepository.save(collaboration);
188
  }
189

190
  async getCollaborationOrFail(
191
    collaborationID: string,
192
    options?: FindOneOptions<Collaboration>
×
193
  ): Promise<ICollaboration | never> {
×
194
    const { where, ...rest } = options ?? {};
195
    const collaboration = await this.collaborationRepository.findOne({
×
196
      where: {
197
        ...where,
198
        id: collaborationID,
199
      },
200
      ...rest,
201
    });
×
202
    if (!collaboration)
203
      throw new EntityNotFoundException(
204
        `No Collaboration found with the given id: ${collaborationID}`,
205
        LogContext.COLLABORATION
206
      );
207
    return collaboration;
208
  }
209

210
  public async getChildCollaborationsOrFail(
211
    collaborationID: string
212
  ): Promise<ICollaboration[] | never> {
213
    const space = await this.entityManager.findOne(Space, {
214
      where: { collaboration: { id: collaborationID } },
215
      relations: {
216
        subspaces: {
217
          collaboration: true,
218
        },
219
      },
220
    });
221
    if (!space) {
×
222
      throw new EntityNotFoundException(
223
        `Unable to find Space for provided collaborationID: ${collaborationID}`,
224
        LogContext.COLLABORATION
225
      );
226
    }
227

×
228
    switch (space.level) {
229
      case SpaceLevel.L0:
230
        const spacesInAccount = await this.entityManager.find(Space, {
231
          where: {
232
            levelZeroSpaceID: space.id,
233
          },
234
          relations: {
×
235
            collaboration: true,
×
236
          },
237
          select: {
238
            collaboration: {
239
              id: true,
240
            },
241
          },
242
        });
×
243
        return [...spacesInAccount].map(x => {
×
244
          if (!x.collaboration) {
245
            throw new EntityNotInitializedException(
246
              `Collaboration not found in space level ${x.level} '${x.id}'`,
247
              LogContext.COLLABORATION
×
248
            );
249
          }
250
          return x.collaboration;
251
        });
252
      case SpaceLevel.L1:
253
        const subsubspaces = space.subspaces;
×
254
        if (!subsubspaces) {
×
255
          throw new EntityNotInitializedException(
256
            `Subsubspaces not found on subspace with level ${space.level}`,
257
            LogContext.COLLABORATION
×
258
          );
259
        }
×
260

261
        return subsubspaces?.map(subsubspace => {
262
          if (!subsubspace.collaboration) {
263
            throw new EntityNotInitializedException(
264
              `Collaboration not found on subsubspace ${subsubspace.id}`,
265
              LogContext.COLLABORATION
×
266
            );
267
          }
268
          return subsubspace.collaboration;
269
        });
270
    }
271

272
    return [];
273
  }
×
274

×
275
  public async deleteCollaborationOrFail(
276
    collaborationID: string
277
  ): Promise<ICollaboration | never> {
278
    // Note need to load it in with all contained entities so can remove fully
279
    const collaboration = await this.getCollaborationOrFail(collaborationID, {
280
      relations: {
×
281
        calloutsSet: true,
282
        timeline: true,
×
283
        innovationFlow: true,
284
        authorization: true,
285
        license: true,
286
      },
287
    });
288

289
    if (
290
      !collaboration.calloutsSet ||
291
      !collaboration.innovationFlow ||
292
      !collaboration.authorization ||
293
      !collaboration.license
294
    )
295
      throw new RelationshipNotFoundException(
×
296
        `Unable to remove Collaboration: missing child entities ${collaboration.id} `,
×
297
        LogContext.SPACE_ABOUT
×
298
      );
299

300
    await this.calloutsSetService.deleteCalloutsSet(
301
      collaboration.calloutsSet.id
302
    );
×
303

304
    if (collaboration.timeline) {
305
      // There's no timeline for collaboration templates
×
306
      await this.timelineService.deleteTimeline(collaboration.timeline.id);
×
307
    }
×
308

309
    await this.authorizationPolicyService.delete(collaboration.authorization);
310

311
    await this.innovationFlowService.deleteInnovationFlow(
312
      collaboration.innovationFlow.id
313
    );
×
314
    await this.licenseService.removeLicenseOrFail(collaboration.license.id);
×
315

×
316
    return await this.collaborationRepository.remove(
317
      collaboration as Collaboration
318
    );
319
  }
320

×
321
  async getTimelineOrFail(collaborationID: string): Promise<ITimeline> {
322
    const collaboration = await this.getCollaborationOrFail(collaborationID, {
323
      relations: { timeline: true },
324
    });
×
325
    const timeline = collaboration.timeline;
326

327
    if (!timeline) {
328
      throw new EntityNotFoundException(
329
        `Unable to find timeline for collaboration: ${collaboration.id}`,
330
        LogContext.COLLABORATION
331
      );
×
332
    }
333

334
    return timeline;
335
  }
336

337
  async getInnovationFlow(collaborationID: string): Promise<IInnovationFlow> {
338
    const collaboration = await this.getCollaborationOrFail(collaborationID, {
339
      relations: {
340
        innovationFlow: true,
×
341
      },
×
342
    });
343

344
    const innovationFlow = collaboration.innovationFlow;
345
    if (!innovationFlow)
346
      throw new RelationshipNotFoundException(
×
347
        `Unable to load InnovationFlow for Collaboration ${collaborationID} `,
348
        LogContext.SPACES
349
      );
350

351
    return innovationFlow;
×
352
  }
×
353

354
  public async getPostsCount(calloutsSet: ICalloutsSet): Promise<number> {
355
    const [result]: {
×
356
      postsCount: number;
357
    }[] = await this.entityManager.connection.query(
×
358
      `
359
      SELECT COUNT(*) as postsCount FROM \`callouts_set\`
×
360
      RIGHT JOIN \`callout\` ON \`callout\`.\`calloutsSetId\` = \`callouts_set\`.\`id\`
361
      RIGHT JOIN \`callout_contribution\` ON \`callout_contribution\`.\`calloutId\` = \`callout\`.\`id\`
362
      WHERE \`callouts_set\`.\`id\` = '${calloutsSet.id}'
363
        AND \`callout\`.\`visibility\` = '${CalloutVisibility.PUBLISHED}'
×
364
        AND NOT(ISNULL(\`callout_contribution\`.\`postId\`));
365
      `
366
    );
367

368
    return result.postsCount;
369
  }
×
370

371
  public async getWhiteboardsCount(calloutsSet: ICalloutsSet): Promise<number> {
372
    const [result]: {
373
      whiteboardsCount: number;
374
    }[] = await this.entityManager.connection.query(
375
      `
376
      SELECT COUNT(*) as whiteboardsCount
×
377
      FROM \`callouts_set\` RIGHT JOIN \`callout\` ON \`callout\`.\`calloutsSetId\` = \`callouts_set\`.\`id\`
×
378
      RIGHT JOIN \`callout_contribution\` ON \`callout_contribution\`.\`calloutId\` = \`callout\`.\`id\`
379
      WHERE \`callouts_set\`.\`id\` = '${calloutsSet.id}'
380
        AND \`callout\`.\`visibility\` = '${CalloutVisibility.PUBLISHED}'
×
381
        AND NOT(ISNULL(\`callout_contribution\`.\`whiteboardId\`));
×
382
      `
383
    );
384

385
    return result.whiteboardsCount;
×
386
  }
×
387
}
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