Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 19 additions & 3 deletions config/functions.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ module.exports = {
if (authType === 'Basic') {
const base64Credentials = authHeader.split(' ')[1];
const decodedCredentials = Buffer.from(base64Credentials, 'base64').toString('ascii');
const [_username, password] = decodedCredentials.split(':');
const [, password] = decodedCredentials.split(':');

if (!password) {
return 'Password is required';
Expand Down Expand Up @@ -199,5 +199,21 @@ module.exports = {
}, {}));

return terms;
}
};
},

paginate(results, { page = 1, pageSize = 25 }) {
const paginatedResults = results.slice(
(page - 1) * pageSize,
page * pageSize
);
const meta = {
pagination: {
page: page,
total: results.length,
pageCount: Math.ceil(results.length / pageSize),
pageSize: pageSize,
}
};
return { results: paginatedResults, meta };
Comment on lines +204 to +217
Copy link

Copilot AI Dec 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Edge case issue: When pageSize is 0 or negative, the paginate function will produce unexpected results. Math.ceil(results.length / 0) will return Infinity, and the slice operation with negative pageSize could produce unexpected pagination behavior. Consider adding validation to ensure pageSize is a positive number.

Copilot uses AI. Check for mistakes.
},
};
34 changes: 30 additions & 4 deletions src/api/study/controllers/study.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,8 @@ const { NotFoundError } = require('@strapi/utils').errors;

module.exports = createCoreController('api::study.study', ({ strapi }) => ({
async find(ctx) {

// If not providing a dataset id, return only studies that are listed
if (!ctx.query.filters?.datasets?.id?.$eq){
if (!ctx.query.filters?.datasets?.id?.$eq) {
ctx.query.filters = {
...ctx.query.filters,
is_listed: true,
Expand Down Expand Up @@ -79,7 +78,7 @@ module.exports = createCoreController('api::study.study', ({ strapi }) => ({
fields: ['name', 'description', 'type', 'category'],
},
cover_dataset: {
fields: [false],
fields: ['id'],
populate: ['media'],
},
},
Expand Down Expand Up @@ -114,6 +113,33 @@ module.exports = createCoreController('api::study.study', ({ strapi }) => ({
};
}

// If sorting by publication date, get studies and manually sort
// as nested sorting returns duplicates (https://github.com/strapi/strapi/issues/11892)
const [sort, order = 'asc'] = ctx.query.sort?.split(':') || [];
if (sort === 'publications.date') {
const studies = await strapi.entityService.findMany(
'api::study.study',
ctx.query
Comment on lines +120 to +122
Copy link

Copilot AI Dec 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The pagination parameters are not removed from ctx.query before fetching all studies. When strapi.entityService.findMany is called with ctx.query, it may include pagination settings (like page and pageSize) that could limit the results returned from the database. This would mean the manual sort is only applied to a subset of studies, not all of them, leading to incorrect sorting across pages.

You should explicitly remove pagination parameters from the query before fetching: const { pagination, sort, ...queryWithoutPagination } = ctx.query; and then pass queryWithoutPagination to findMany.

Copilot uses AI. Check for mistakes.
);

studies.sort((a, b) => {
const aLatest = Math.max(
...(a.publications || []).map((p) => new Date(p.date)),
0
);
const bLatest = Math.max(
...(b.publications || []).map((p) => new Date(p.date)),
Comment on lines +127 to +131
Copy link

Copilot AI Dec 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Potential bug when a study has publications with empty or invalid dates. If a publication has an invalid date string, new Date(p.date) will return Invalid Date, which when spread into Math.max() will cause the entire expression to evaluate to NaN. This could lead to incorrect sorting behavior.

Consider adding a filter to handle invalid dates before mapping, or add a fallback when the date is invalid. For example, you could use new Date(p.date).getTime() || 0 to ensure invalid dates are treated as 0.

Suggested change
...(a.publications || []).map((p) => new Date(p.date)),
0
);
const bLatest = Math.max(
...(b.publications || []).map((p) => new Date(p.date)),
...((a.publications || [])
.map((p) => new Date(p.date).getTime())
.filter((t) => !isNaN(t))),
0
);
const bLatest = Math.max(
...((b.publications || [])
.map((p) => new Date(p.date).getTime())
.filter((t) => !isNaN(t))),

Copilot uses AI. Check for mistakes.
0
);
return order === 'asc' ? aLatest - bLatest : bLatest - aLatest;
});

const { results: paginatedStudies, meta } =
strapi.config.functions.paginate(studies, ctx.query.pagination || {});

return this.transformResponse(paginatedStudies, meta);
}

return await super.find(ctx);
},
async findOne(ctx) {
Expand Down Expand Up @@ -200,7 +226,7 @@ module.exports = createCoreController('api::study.study', ({ strapi }) => ({
populate: ['media', 'data', 'resources'],
},
resources: true,
media: { fields: ['title', 'type'], populate: ['file']},
media: { fields: ['title', 'type'], populate: ['file'] },
cover_dataset: {
fields: [],
populate: ['media'],
Expand Down