• 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

10.39
/src/domain/innovation-hub/innovation.hub.service.ts
1
import { FindManyOptions, FindOneOptions, Repository } from 'typeorm';
2✔
2
import { Injectable } from '@nestjs/common';
2✔
3
import { InjectRepository } from '@nestjs/typeorm';
2✔
4
import {
2✔
5
  EntityNotFoundException,
6
  RelationshipNotFoundException,
7
  ValidationException,
8
} from '@common/exceptions';
2✔
9
import { LogContext, ProfileType } from '@common/enums';
2✔
10
import { ProfileService } from '@domain/common/profile/profile.service';
2✔
11
import { VisualType } from '@common/enums/visual.type';
2✔
12
import { AuthorizationPolicy } from '@domain/common/authorization-policy';
2✔
13
import { IInnovationHub, InnovationHub, InnovationHubType } from './types';
14
import { CreateInnovationHubInput, UpdateInnovationHubInput } from './dto';
2✔
15
import { AuthorizationPolicyService } from '@domain/common/authorization-policy/authorization.policy.service';
2✔
16
import { NamingService } from '@services/infrastructure/naming/naming.service';
2✔
17
import { TagsetReservedName } from '@common/enums/tagset.reserved.name';
2✔
18
import { SearchVisibility } from '@common/enums/search.visibility';
2✔
19
import { IAccount } from '@domain/space/account/account.interface';
20
import { IContributor } from '@domain/community/contributor/contributor.interface';
2✔
21
import { AuthorizationPolicyType } from '@common/enums/authorization.policy.type';
22
import { AccountLookupService } from '@domain/space/account.lookup/account.lookup.service';
23
import { SpaceLookupService } from '@domain/space/space.lookup/space.lookup.service';
2✔
24

25
@Injectable()
26
export class InnovationHubService {
×
27
  constructor(
×
28
    @InjectRepository(InnovationHub)
×
29
    private readonly innovationHubRepository: Repository<InnovationHub>,
×
30
    private readonly profileService: ProfileService,
×
31
    private readonly authorizationPolicyService: AuthorizationPolicyService,
32
    private readonly spaceLookupService: SpaceLookupService,
33
    private namingService: NamingService,
34
    private accountLookupService: AccountLookupService
35
  ) {}
36

37
  public async createInnovationHub(
×
38
    createData: CreateInnovationHubInput,
×
39
    account: IAccount
40
  ): Promise<IInnovationHub | never> {
×
41
    try {
×
42
      await this.validateCreateInput(createData);
43
    } catch (e) {
44
      const err = e as Error;
45
      throw new ValidationException(
46
        `Incorrect input provided: ${err.message}`,
47
        LogContext.INNOVATION_HUB
×
48
      );
×
49
    }
50

51
    if (!account.storageAggregator) {
52
      throw new EntityNotFoundException(
53
        `Unable to load storage aggregator on account for creating innovation Hub: ${account.id}`,
54
        LogContext.ACCOUNT
55
      );
×
56
    }
57

58
    const subdomainAvailable =
×
59
      await this.namingService.isInnovationHubSubdomainAvailable(
×
60
        createData.subdomain
61
      );
62
    if (!subdomainAvailable)
63
      throw new ValidationException(
64
        `Unable to create Innovation Hub: the provided subdomain is already taken: ${createData.subdomain}`,
×
65
        LogContext.INNOVATION_HUB
×
66
      );
×
67

×
68
    const reservedNameIDs = await this.namingService.getReservedNameIDsInHubs();
×
69
    if (createData.nameID && createData.nameID.length > 0) {
70
      const nameTaken = reservedNameIDs.includes(createData.nameID);
71
      if (nameTaken)
72
        throw new ValidationException(
73
          `Unable to create Innovation Hub: the provided nameID is already taken: ${createData.nameID}`,
×
74
          LogContext.INNOVATION_HUB
75
        );
76
    } else {
77
      createData.nameID =
78
        this.namingService.createNameIdAvoidingReservedNameIDs(
79
          `${createData.profileData.displayName}`,
80
          reservedNameIDs
×
NEW
81
        );
×
82
    }
83

84
    const hub: IInnovationHub = InnovationHub.create(createData);
×
85
    hub.authorization = new AuthorizationPolicy(
×
86
      AuthorizationPolicyType.INNOVATION_HUB
×
87
    );
88
    hub.listedInStore = true;
×
89
    hub.searchVisibility = SearchVisibility.ACCOUNT;
90
    hub.account = account;
91

92
    hub.profile = await this.profileService.createProfile(
93
      createData.profileData,
94
      ProfileType.INNOVATION_HUB,
×
95
      account.storageAggregator
96
    );
97

98
    await this.profileService.addOrUpdateTagsetOnProfile(hub.profile, {
99
      name: TagsetReservedName.DEFAULT,
×
100
      tags: [],
101
    });
102

103
    await this.profileService.addVisualsOnProfile(
104
      hub.profile,
×
105
      createData.profileData.visuals,
106
      [VisualType.BANNER_WIDE]
107
    );
108

×
109
    return await this.save(hub);
110
  }
111

112
  public save(hub: IInnovationHub): Promise<IInnovationHub> {
113
    return this.innovationHubRepository.save(hub);
114
  }
×
115

116
  public async updateOrFail(
117
    input: UpdateInnovationHubInput
118
  ): Promise<IInnovationHub> {
119
    const innovationHub: IInnovationHub = await this.getInnovationHubOrFail(
×
120
      input.ID,
×
121
      { relations: { profile: true } }
122
    );
×
123

×
124
    if (input.nameID) {
×
125
      if (input.nameID !== innovationHub.nameID) {
×
126
        const reservedNameIDs =
127
          await this.namingService.getReservedNameIDsInHubs();
128
        const nameTaken = reservedNameIDs.includes(input.nameID);
129
        if (nameTaken) {
130
          throw new ValidationException(
×
131
            `Unable to update Innovation Hub nameID: the provided nameID '${input.nameID}' is already taken`,
132
            LogContext.INNOVATION_HUB
133
          );
×
134
        }
×
135
        innovationHub.nameID = input.nameID;
136
      }
137
    }
×
138
    if (
×
139
      innovationHub.type === InnovationHubType.LIST &&
140
      input.spaceListFilter
141
    ) {
142
      if (!input.spaceListFilter.length) {
143
        throw new Error(
144
          `At least one Space needs to be provided for Innovation Hub of type '${InnovationHubType.LIST}'`
×
145
        );
146
      }
147

148
      // validate spaces
×
149
      const trueOrList = await this.spaceLookupService.spacesExist(
×
150
        input.spaceListFilter
151
      );
152

153
      if (Array.isArray(trueOrList)) {
154
        throw new Error(
155
          `Spaces with the following identifiers not found: '${trueOrList.join(
×
156
            ','
157
          )}'`
×
158
        );
×
159
      }
160
      innovationHub.spaceListFilter = input.spaceListFilter;
161
    }
×
162
    if (
×
163
      innovationHub.type === InnovationHubType.VISIBILITY &&
×
164
      input.spaceVisibilityFilter
165
    )
166
      innovationHub.spaceVisibilityFilter = input.spaceVisibilityFilter;
167
    if (input.profileData) {
168
      innovationHub.profile = await this.profileService.updateProfile(
×
169
        innovationHub.profile,
×
170
        input.profileData
171
      );
172
    }
×
173
    if (typeof input.listedInStore === 'boolean') {
×
174
      innovationHub.listedInStore = !!input.listedInStore;
175
    }
176

×
177
    if (input.searchVisibility) {
178
      innovationHub.searchVisibility = input.searchVisibility;
179
    }
180

×
181
    return await this.save(innovationHub);
182
  }
183

184
  public async delete(innovationHubID: string): Promise<IInnovationHub> {
×
185
    const hub = await this.getInnovationHubOrFail(innovationHubID, {
×
186
      relations: { profile: true },
187
    });
188

×
189
    if (hub.profile) {
×
190
      await this.profileService.deleteProfile(hub.profile.id);
191
    }
×
192

193
    if (hub.authorization)
194
      await this.authorizationPolicyService.delete(hub.authorization);
×
195

196
    const result = await this.innovationHubRepository.remove(
×
197
      hub as InnovationHub
198
    );
199
    result.id = innovationHubID;
200

×
201
    return result;
202
  }
203

204
  public getInnovationHubs(options?: FindManyOptions<InnovationHub>) {
205
    return this.innovationHubRepository.find(options);
206
  }
207

×
208
  public async getInnovationHubOrFail(
209
    innovationHubID: string,
210
    options?: FindOneOptions<InnovationHub>
211
  ): Promise<IInnovationHub> {
212
    const innovationHub = await this.innovationHubRepository.findOne({
×
213
      where: { id: innovationHubID },
×
214
      ...options,
215
    });
216

217
    if (!innovationHub)
×
218
      throw new EntityNotFoundException(
219
        `Unable to find InnovationHub with ID: ${innovationHubID}`,
220
        LogContext.SPACES
221
      );
222
    return innovationHub;
223
  }
224

×
225
  public async getInnovationHubByNameIdOrFail(
×
226
    innovationHubNameID: string,
227
    options?: FindOneOptions<InnovationHub>
228
  ): Promise<IInnovationHub> {
×
229
    const innovationHub = await this.innovationHubRepository.findOne({
230
      where: { nameID: innovationHubNameID },
×
231
      ...options,
232
    });
233

234
    if (!innovationHub)
235
      throw new EntityNotFoundException(
236
        `Unable to find InnovationHub with NameID: ${innovationHubNameID}`,
×
237
        LogContext.SPACES
×
238
      );
×
239
    return innovationHub;
240
  }
241

242
  public async getInnovationHubFlexOrFail(
243
    args: { subdomain?: string; idOrNameId?: string },
244
    options?: FindOneOptions<InnovationHub>
245
  ): Promise<InnovationHub | never> {
×
246
    if (!Object.keys(args).length) {
×
247
      throw new Error('No criteria provided for fetching the Innovation Hub');
248
    }
249

250
    const { idOrNameId, subdomain } = args;
251

252
    const whereArgs = [
×
253
      { id: idOrNameId },
254
      { nameID: idOrNameId },
255
      { subdomain },
256
    ];
257

258
    const innovationHub = await this.innovationHubRepository.findOne({
×
259
      where: options?.where
260
        ? Array.isArray(options.where)
261
          ? [...whereArgs, ...options.where]
262
          : [...whereArgs, options.where]
×
263
        : [{ id: idOrNameId }, { subdomain }, { nameID: idOrNameId }],
×
264
      ...options,
265
    });
266

267
    if (!innovationHub) {
268
      throw new EntityNotFoundException(
269
        `Innovation hub '${idOrNameId}' not found`,
×
270
        LogContext.INNOVATION_HUB
271
      );
272
    }
273

274
    return innovationHub;
275
  }
276

277
  public async getSpaceListFilterOrFail(
×
278
    hubId: string
×
279
  ): Promise<string[] | undefined | never> {
×
280
    const hub = await this.innovationHubRepository.findOneBy({
281
      id: hubId,
282
    });
283

×
284
    if (!hub) {
285
      throw new EntityNotFoundException(
×
286
        `Innovation Hub with id: '${hubId}' not found!`,
287
        LogContext.INNOVATION_HUB
×
288
      );
×
289
    }
290

291
    return hub.spaceListFilter;
292
  }
293

294
  private async validateCreateInput({
295
    type,
296
    spaceListFilter,
297
    spaceVisibilityFilter,
×
298
  }: CreateInnovationHubInput): Promise<true | never> {
×
299
    if (type === InnovationHubType.LIST) {
×
300
      if (spaceVisibilityFilter) {
301
        throw new Error(
302
          `Visibility filter not applicable for Innovation Hub of type '${InnovationHubType.LIST}'`
303
        );
304
      }
×
305
      if (spaceListFilter && spaceListFilter.length) {
×
306
        // If specified on create, validate spaces
307
        const trueOrList =
308
          await this.spaceLookupService.spacesExist(spaceListFilter);
309

310
        if (Array.isArray(trueOrList)) {
311
          throw new Error(
×
312
            `Spaces with the following identifiers not found: '${trueOrList.join(
313
              ','
314
            )}'`
315
          );
316
        }
317
      }
318
    }
319

320
    if (type === InnovationHubType.VISIBILITY) {
321
      if (!spaceVisibilityFilter) {
322
        throw new Error(
323
          `A visibility needs to be provided for Innovation Hub of type '${InnovationHubType.VISIBILITY}'`
324
        );
325
      }
326

327
      if (spaceListFilter && spaceListFilter.length) {
328
        throw new Error(
329
          `List of Spaces not applicable for Innovation Hub of type '${InnovationHubType.VISIBILITY}'`
330
        );
331
      }
332
    }
333

334
    return true;
335
  }
336

337
  public async getProvider(innovationHubID: string): Promise<IContributor> {
338
    const innovationHub = await this.innovationHubRepository.findOne({
339
      where: { id: innovationHubID },
340
      relations: {
341
        account: true,
342
      },
343
    });
344
    if (!innovationHub || !innovationHub.account) {
345
      throw new RelationshipNotFoundException(
346
        `Unable to load innovation Hub with account to get Provider ${innovationHubID} `,
347
        LogContext.LIBRARY
348
      );
349
    }
350
    const provider = await this.accountLookupService.getHost(
351
      innovationHub.account
352
    );
353
    if (!provider) {
354
      throw new RelationshipNotFoundException(
355
        `Unable to load provider for InnovationHub ${innovationHubID} `,
356
        LogContext.LIBRARY
357
      );
358
    }
359
    return provider;
360
  }
361
}
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