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

GEWIS / sudosos-backend / 23944264587

03 Apr 2026 11:12AM UTC coverage: 89.102% (+0.04%) from 89.064%
23944264587

push

github

web-flow
refactor: centralize env config setup (#835)

1828 of 2248 branches covered (81.32%)

Branch coverage included in aggregate %.

242 of 252 new or added lines in 73 files covered. (96.03%)

1 existing line in 1 file now uncovered.

9414 of 10369 relevant lines covered (90.79%)

1014.39 hits per line

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

10.42
/src/controller/simple-file-controller.ts
1
/**
2
 *  SudoSOS back-end API service.
3
 *  Copyright (C) 2026 Study association GEWIS
4
 *
5
 *  This program is free software: you can redistribute it and/or modify
6
 *  it under the terms of the GNU Affero General Public License as published
7
 *  by the Free Software Foundation, either version 3 of the License, or
8
 *  (at your option) any later version.
9
 *
10
 *  This program is distributed in the hope that it will be useful,
11
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 *  GNU Affero General Public License for more details.
14
 *
15
 *  You should have received a copy of the GNU Affero General Public License
16
 *  along with this program.  If not, see <https://www.gnu.org/licenses/>.
17
 *
18
 *  @license
19
 */
20

21
/**
22
 * This is the module page of the simple-file-controller.
23
 *
24
 * @module internal/files
25
 */
26

27
import log4js, { Logger } from 'log4js';
2✔
28
import { Response } from 'express';
29
import { UploadedFile } from 'express-fileupload';
30
import BaseController, { BaseControllerOptions } from './base-controller';
2✔
31
import Policy from './policy';
32
import { RequestWithToken } from '../middleware/token-middleware';
33
import FileService from '../service/file-service';
2✔
34
import SimpleFileRequest from './request/simple-file-request';
35
import putFileInResponse from '../files/response';
2✔
36

37
/**
38
 * This is a mock-controller since there is no actual use for this controller in sudoSOS.
39
 * This controller allows you to upload files to the server and retrieve them, however
40
 * in actual production environment we only want to upload files with associations.
41
 *
42
 * For example, the product controller would use the file-service to upload files.
43
 */
44
export default class SimpleFileController extends BaseController {
2✔
45
  private logger: Logger = log4js.getLogger('SimpleFileController');
×
46

47
  private fileService: FileService;
48

49
  /**
50
   * Creates a new product controller instance.
51
   * @param options - The options passed to the base controller.
52
   */
53
  public constructor(options: BaseControllerOptions) {
54
    super(options);
×
NEW
55
    this.configureLogger(this.logger);
×
56
    this.fileService = new FileService();
×
57
  }
58

59
  /**
60
   * @inheritDoc
61
   */
62
  getPolicy(): Policy {
63
    return {
×
64
      '/': {
65
        POST: {
66
          body: { modelName: 'SimpleFileRequest' },
67
          policy: async (req) => this.roleManager.can(req.token.roles, 'create', 'all', 'SimpleFile', ['*']),
×
68
          handler: this.uploadFile.bind(this),
69
        },
70
      },
71
      '/:id(\\d+)': {
72
        GET: {
73
          policy: async (req) => this.roleManager.can(req.token.roles, 'get', 'all', 'SimpleFile', ['*']),
×
74
          handler: this.downloadFile.bind(this),
75
        },
76
        DELETE: {
77
          policy: async (req) => this.roleManager.can(req.token.roles, 'delete', 'all', 'SimpleFile', ['*']),
×
78
          handler: this.deleteFile.bind(this),
79
        },
80
      },
81
    };
82
  }
83

84

85
  /**
86
   * @typedef {object} FileUpload
87
   * @property {string} name.required - The name of the file
88
   * @property {string} file - file - binary
89
   */
90

91
  /**
92
   * POST /files
93
   * @summary Upload a file with the given name.
94
   * @operationId createFile
95
   * @tags files - Operations of the simple files controller
96
   * @consumes multipart/form-data
97
   * @param {FileUpload} request.body.required - simple file - multipart/form-data
98
   * @security JWT
99
   * @return {SimpleFileResponse} 200 - The uploaded file entity
100
   * @return {string} 400 - Validation error
101
   * @return {string} 500 - Internal server error
102
   */
103
  public async uploadFile(req: RequestWithToken, res: Response): Promise<void> {
104
    this.logger.trace('Upload simple file by user', req.token.user);
×
105
    const { body, files } = req;
×
106

107
    if (!req.files || Object.keys(files).length !== 1) {
×
108
      res.status(400).send('No file or too many files were uploaded');
×
109
      return;
×
110
    }
111
    if (files.file === undefined) {
×
112
      res.status(400).send("No file is uploaded in the 'file' field");
×
113
      return;
×
114
    }
115

116
    // handle request
117
    try {
×
118
      res.json(await this.fileService.uploadSimpleFile(
×
119
        req.token.user, files.file as UploadedFile, body as SimpleFileRequest,
120
      ));
121
    } catch (error) {
122
      this.logger.error('Could not upload file:', error);
×
123
      res.status(500).json('Internal server error');
×
124
    }
125
  }
126

127
  /**
128
   * GET /files/{id}
129
   * @summary Download a file with the given id.
130
   * @operationId getFile
131
   * @tags files - Operations of the simple files controller
132
   * @param {integer} id.path.required - The id of the file which should be downloaded
133
   * @security JWT
134
   * @return {string} 200 - The requested file
135
   * @return {string} 404 - File not found
136
   * @return {string} 500 - Internal server error
137
   */
138
  public async downloadFile(req: RequestWithToken, res: Response): Promise<void> {
139
    const { id } = req.params;
×
140
    this.logger.trace('Download simple file', id, ' by user', req.token.user);
×
141

142
    try {
×
143
      const fileInfo = await this.fileService.getSimpleFile(Number.parseInt(id, 10));
×
144
      if (fileInfo === undefined) {
×
145
        res.status(404);
×
146
      }
147

148
      const { file, data } = fileInfo;
×
149
      putFileInResponse(res, file, data);
×
150
    } catch (error) {
151
      this.logger.error('Could not download file:', error);
×
152
      res.status(500).json('Internal server error');
×
153
    }
154
  }
155

156
  /**
157
   * DELETE /files/{id}
158
   * @summary Delete the file with the given id.
159
   * @operationId deleteFile
160
   * @tags files - Operations of the simple files controller
161
   * @param {integer} id.path.required - The id of the file which should be deleted
162
   * @security JWT
163
   * @return 204 - Success
164
   * @return {string} 404 - File not found
165
   * @return {string} 500 - Internal server error
166
   */
167
  public async deleteFile(req: RequestWithToken, res: Response): Promise<void> {
168
    const { id } = req.params;
×
169
    this.logger.trace('Download simple file', id, 'by user', req.token.user);
×
170

171
    try {
×
172
      await this.fileService.deleteSimpleFile(Number.parseInt(id, 10));
×
173
      res.status(204);
×
174
      res.send();
×
175
    } catch (error) {
176
      this.logger.error('Could not delete file:', error);
×
177
      res.status(500).json('Internal server error');
×
178
    }
179
  }
180
}
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

© 2026 Coveralls, Inc