Skip to content

Commit 128d4f3

Browse files
Release 20250906 (#1209)
--------- Co-authored-by: semantic-release-bot <semantic-release-bot@martynus.net>
2 parents f254005 + d8dceb8 commit 128d4f3

File tree

6 files changed

+118
-4
lines changed

6 files changed

+118
-4
lines changed

CHANGELOG/CHANGELOG_next.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
# [2.15.0-rc.1](https://github.com/ReliefApplications/ems-backend/compare/v2.14.0...v2.15.0-rc.1) (2025-08-29)
2+
3+
4+
### Features
5+
6+
* file explorer widget ([ed7b3fa](https://github.com/ReliefApplications/ems-backend/commit/ed7b3fa899199fcd5035265ae63a27eb734e3f12)), closes [Ab#113709](https://github.com/Ab/issues/113709)
7+
18
# [2.14.0-rc.1](https://github.com/ReliefApplications/ems-backend/compare/v2.13.0...v2.14.0-rc.1) (2025-03-17)
29

310

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "ems-backend",
3-
"version": "2.14.0",
3+
"version": "2.15.0-rc.1",
44
"description": "",
55
"main": "index.js",
66
"scripts": {

src/routes/file/file.controller.ts

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import { NextFunction, Request, Response } from 'express';
2+
import { RouteDefinition } from 'types/route-definition';
3+
import BaseController from '../../abstractions/base.controller';
4+
import { Record, Resource } from '@models';
5+
import { Types } from 'mongoose';
6+
7+
/**
8+
* File controller
9+
*/
10+
export default class FileController extends BaseController {
11+
public basePath = 'file';
12+
13+
/** @returns List of available routes */
14+
public routes(): RouteDefinition[] {
15+
return [
16+
{
17+
path: '/item/:itemId/associated-record',
18+
method: 'get',
19+
handler: this.findAssociatedRecordId.bind(this),
20+
},
21+
];
22+
}
23+
24+
/**
25+
* Find the associated record ID for a given drive and item.
26+
*
27+
* @param req Express request
28+
* @param res Express response
29+
* @param next Express next function
30+
* @returns Promise<void> - Sends the associated record ID in the response or an error if not found.
31+
*/
32+
public async findAssociatedRecordId(
33+
req: Request,
34+
res: Response,
35+
next: NextFunction
36+
): Promise<void> {
37+
try {
38+
const { itemId } = req.params;
39+
const { resourceId } = req.query;
40+
if (!resourceId && typeof resourceId !== 'string') {
41+
res.status(400).send({ error: 'Resource ID is required' });
42+
return;
43+
}
44+
if (!itemId) {
45+
res.status(400).send({ error: 'Item ID is required' });
46+
return;
47+
}
48+
49+
const [resource] = await Resource.aggregate([
50+
{ $match: { _id: new Types.ObjectId(String(resourceId)) } },
51+
{
52+
$project: {
53+
fileFields: {
54+
$filter: {
55+
input: '$fields',
56+
cond: { $eq: ['$$this.type', 'file'] },
57+
},
58+
},
59+
},
60+
},
61+
]);
62+
63+
if (!resource) {
64+
res.status(404).send({ error: 'Resource not found' });
65+
return;
66+
}
67+
68+
// Now use the file fields to build the query for Record
69+
const fileFieldQueries = resource.fileFields.map((field) => ({
70+
[`data.${field.name}`]: {
71+
$elemMatch: {
72+
'content.itemId': { $regex: new RegExp(`^${itemId}$`, 'i') },
73+
// 'content.driveId': { $regex: new RegExp(`^${driveId}$`, 'i') },
74+
},
75+
},
76+
}));
77+
78+
const associatedRecord = await Record.findOne({
79+
resource: new Types.ObjectId(String(resourceId)),
80+
archived: { $ne: true },
81+
$or: fileFieldQueries,
82+
}).select('_id');
83+
84+
if (!associatedRecord) {
85+
res.status(404).send({ error: 'Record not found' });
86+
return;
87+
}
88+
89+
res.locals.data = associatedRecord._id;
90+
this.send(res);
91+
} catch (err) {
92+
next(err);
93+
}
94+
}
95+
}

src/routes/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import config from 'config';
1515
import { RouteDefinition } from 'types/route-definition';
1616
import { logger } from '@services/logger.service';
1717
import ActivityController from './activity/activity.controller';
18+
import FileController from './file/file.controller';
1819

1920
/**
2021
*
@@ -82,7 +83,7 @@ export default function registerRoutes(): Router | undefined {
8283
router.use('/notification', notification);
8384

8485
// Define an array of controller objects
85-
const controllers = [new ActivityController()];
86+
const controllers = [new ActivityController(), new FileController()];
8687

8788
// Dynamically register routes for each controller
8889
controllers.forEach((controller) => {

src/schema/types/record.type.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,17 @@ export const RecordType = new GraphQLObjectType({
141141
})
142142
),
143143
},
144+
canUpdate: {
145+
type: GraphQLBoolean,
146+
async resolve(parent, args, context) {
147+
const parentForm: Form = await Form.findById(
148+
parent.form,
149+
'fields permissions resource structure'
150+
);
151+
const ability = await extendAbilityForRecords(context.user, parentForm);
152+
return ability.can('update', parent);
153+
},
154+
},
144155
}),
145156
});
146157

0 commit comments

Comments
 (0)