• 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.85
/src/domain/common/visual/visual.service.ts
1
import { Injectable } from '@nestjs/common';
42✔
2
import { InjectRepository } from '@nestjs/typeorm';
42✔
3
import { FindOneOptions, Repository } from 'typeorm';
42✔
4
import {
42✔
5
  EntityNotFoundException,
6
  ValidationException,
7
} from '@common/exceptions';
8
import { LogContext } from '@common/enums';
42✔
9
import { UpdateVisualInput } from '@domain/common/visual/dto/visual.dto.update';
10
import { CreateVisualInput } from '@domain/common/visual/dto/visual.dto.create';
11
import { AuthorizationPolicy } from '@domain/common/authorization-policy';
42✔
12
import { AuthorizationPolicyService } from '../authorization-policy/authorization.policy.service';
42✔
13
import { getImageDimensions, streamToBuffer } from '@common/utils';
42✔
14
import { Visual } from './visual.entity';
42✔
15
import { IVisual } from './visual.interface';
16
import { DeleteVisualInput } from './dto/visual.dto.delete';
17
import { IStorageBucket } from '@domain/storage/storage-bucket/storage.bucket.interface';
42✔
18
import { Readable } from 'stream';
19
import { IDocument } from '@domain/storage/document/document.interface';
20
import { DocumentService } from '@domain/storage/document/document.service';
21
import { StorageBucketService } from '@domain/storage/storage-bucket/storage.bucket.service';
42✔
22
import { StorageUploadFailedException } from '@common/exceptions/storage/storage.upload.failed.exception';
42✔
23
import { AuthorizationPolicyType } from '@common/enums/authorization.policy.type';
42✔
24
import { VisualType } from '@common/enums/visual.type';
42✔
25
import { DEFAULT_VISUAL_CONSTRAINTS } from './visual.constraints';
26

27
@Injectable()
42✔
28
export class VisualService {
29
  constructor(
×
30
    private authorizationPolicyService: AuthorizationPolicyService,
×
31
    private documentService: DocumentService,
×
32
    private storageBucketService: StorageBucketService,
33
    @InjectRepository(Visual)
×
34
    private visualRepository: Repository<Visual>
35
  ) {}
36

37
  public createVisual(
38
    visualInput: CreateVisualInput,
39
    initialUri?: string
40
  ): IVisual {
×
41
    const visual: IVisual = Visual.create({
42
      ...visualInput,
×
43
      uri: initialUri ?? '',
44
    });
NEW
45

×
46
    visual.authorization = new AuthorizationPolicy(
47
      AuthorizationPolicyType.VISUAL
48
    );
49

×
50
    if (initialUri) {
×
51
      visual.uri = initialUri;
52
    }
53

×
54
    return visual;
55
  }
56

57
  async updateVisual(visualData: UpdateVisualInput): Promise<IVisual> {
×
58
    const visual = await this.getVisualOrFail(visualData.visualID);
×
59
    visual.uri = visualData.uri;
×
60
    if (visualData.alternativeText !== undefined) {
×
61
      visual.alternativeText = visualData.alternativeText;
62
    }
63

×
64
    return await this.visualRepository.save(visual);
65
  }
66

67
  async deleteVisual(deleteData: DeleteVisualInput): Promise<IVisual> {
×
68
    const visualID = deleteData.ID;
×
69
    const visual = await this.getVisualOrFail(visualID);
70

×
71
    if (visual.authorization)
×
72
      await this.authorizationPolicyService.delete(visual.authorization);
73

×
74
    const { id } = visual;
×
75
    const result = await this.visualRepository.remove(visual as Visual);
×
76
    return {
77
      ...result,
78
      id,
79
    };
80
  }
81

82
  async uploadImageOnVisual(
83
    visual: IVisual,
84
    storageBucket: IStorageBucket,
85
    readStream: Readable,
86
    fileName: string,
87
    mimetype: string,
88
    userID: string
89
  ): Promise<IDocument | never> {
×
90
    this.validateMimeType(visual, mimetype);
91

×
92
    if (!readStream)
×
93
      throw new ValidationException(
94
        'Readstream should be defined!',
95
        LogContext.DOCUMENT
96
      );
97

×
98
    const buffer = await streamToBuffer(readStream);
99

×
100
    const { imageHeight, imageWidth } = await this.getImageDimensions(buffer);
×
101
    this.validateImageWidth(visual, imageWidth);
×
102
    this.validateImageHeight(visual, imageHeight);
×
103
    const documentForVisual = await this.documentService.getDocumentFromURL(
104
      visual.uri
105
    );
106

×
107
    try {
108
      const newDocument =
×
109
        await this.storageBucketService.uploadFileAsDocumentFromBuffer(
110
          storageBucket.id,
111
          buffer,
112
          fileName,
113
          mimetype,
114
          userID
115
        );
116
      // Delete the old document
×
117
      if (
×
118
        documentForVisual &&
×
119
        newDocument.externalID != documentForVisual?.externalID
120
      ) {
×
121
        await this.documentService.deleteDocument({
122
          ID: documentForVisual.id,
123
        });
124
      }
×
125
      return newDocument;
126
    } catch (error: any) {
×
127
      throw new StorageUploadFailedException(
128
        'Upload on visual failed!',
129
        LogContext.STORAGE_BUCKET,
130
        {
131
          message: error.message,
132
          fileName,
133
          visualID: visual.id,
134
          originalException: error,
135
        }
136
      );
137
    }
138
  }
139

140
  async getVisualOrFail(
141
    visualID: string,
142
    options?: FindOneOptions<Visual>
143
  ): Promise<IVisual> {
×
144
    const visual = await this.visualRepository.findOne({
145
      where: { id: visualID },
146
      ...options,
147
    });
×
148
    if (!visual)
×
149
      throw new EntityNotFoundException(
150
        `Not able to locate visual with the specified ID: ${visualID}`,
151
        LogContext.SPACES
152
      );
×
153
    return visual;
154
  }
155

156
  async saveVisual(visual: IVisual): Promise<IVisual> {
×
157
    return await this.visualRepository.save(visual);
158
  }
159

160
  public async getImageDimensions(buffer: Buffer) {
×
161
    return getImageDimensions(buffer);
162
  }
163

164
  public validateMimeType(visual: IVisual, mimeType: string) {
×
165
    if (!visual.allowedTypes.includes(mimeType)) {
×
166
      throw new ValidationException(
167
        `Image upload type (${mimeType}) not in allowed mime types: ${visual.allowedTypes}`,
168
        LogContext.COMMUNITY
169
      );
170
    }
171
  }
172

173
  public validateImageWidth(visual: IVisual, imageWidth: number) {
×
174
    if (imageWidth < visual.minWidth || imageWidth > visual.maxWidth)
×
175
      throw new ValidationException(
176
        `Upload image has a width resolution of '${imageWidth}' which is not in the allowed range of ${visual.minWidth} - ${visual.maxWidth} pixels!`,
177
        LogContext.COMMUNITY
178
      );
179
  }
180

181
  public validateImageHeight(visual: IVisual, imageHeight: number) {
×
182
    if (imageHeight < visual.minHeight || imageHeight > visual.maxHeight)
×
183
      throw new ValidationException(
184
        `Upload image has a height resolution of '${imageHeight}' which is not in the allowed range of ${visual.minHeight} - ${visual.maxHeight} pixels!`,
185
        LogContext.COMMUNITY
186
      );
187
  }
188

189
  public createVisualBanner(uri?: string): IVisual {
×
190
    return this.createVisual(
191
      {
192
        name: VisualType.BANNER,
193
        ...DEFAULT_VISUAL_CONSTRAINTS[VisualType.BANNER],
194
      },
195
      uri
196
    );
197
  }
198

199
  public createVisualWhiteboardPreview(uri?: string): IVisual {
200
    return this.createVisual(
201
      {
202
        name: VisualType.WHITEBOARD_PREVIEW,
203
        ...DEFAULT_VISUAL_CONSTRAINTS[VisualType.WHITEBOARD_PREVIEW],
×
204
      },
205
      uri
206
    );
207
  }
208

209
  public createVisualCard(uri?: string): IVisual {
210
    return this.createVisual(
211
      {
212
        name: VisualType.CARD,
213
        ...DEFAULT_VISUAL_CONSTRAINTS[VisualType.CARD],
214
      },
215
      uri
216
    );
217
  }
×
218

219
  public createVisualBannerWide(uri?: string): IVisual {
220
    return this.createVisual(
221
      {
222
        name: VisualType.BANNER_WIDE,
223
        ...DEFAULT_VISUAL_CONSTRAINTS[VisualType.BANNER_WIDE],
224
      },
225
      uri
226
    );
227
  }
228

229
  public createVisualAvatar(uri?: string): IVisual {
230
    return this.createVisual(
231
      {
×
232
        name: VisualType.AVATAR,
233
        ...DEFAULT_VISUAL_CONSTRAINTS[VisualType.AVATAR],
234
      },
235
      uri
236
    );
237
  }
238
}
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