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

GEWIS / sudosos-backend / 25753937432

12 May 2026 09:17AM UTC coverage: 88.117% (-1.0%) from 89.089%
25753937432

push

github

web-flow
chore(deps): fix missing dependencies for running docs:dev (#911)

3925 of 4574 branches covered (85.81%)

Branch coverage included in aggregate %.

20093 of 22683 relevant lines covered (88.58%)

1125.83 hits per line

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

27.66
/src/controller/simple-file-controller.ts
1
/**
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
 */
1✔
20

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

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

37
/**
1✔
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
 */
1✔
44
export default class SimpleFileController extends BaseController {
1✔
45
  private logger: Logger = log4js.getLogger('SimpleFileController');
1✔
46

47
  private fileService: FileService;
48

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

59
  /**
1✔
60
   * @inheritDoc
61
   */
1✔
62
  getPolicy(): Policy {
1✔
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
  /**
1✔
86
   * @typedef {object} FileUpload
87
   * @property {string} name.required - The name of the file
88
   * @property {string} file - file - binary
89
   */
1✔
90

91
  /**
1✔
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
   */
1✔
103
  public async uploadFile(req: RequestWithToken, res: Response): Promise<void> {
1✔
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
  /**
1✔
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
   */
1✔
138
  public async downloadFile(req: RequestWithToken, res: Response): Promise<void> {
1✔
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
  /**
1✔
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
   */
1✔
167
  public async deleteFile(req: RequestWithToken, res: Response): Promise<void> {
1✔
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
}
1✔
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