Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adiciona paginação ao endpoint GET /users #1676

Merged
merged 2 commits into from
May 8, 2024
Merged
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
32 changes: 8 additions & 24 deletions models/content.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { v4 as uuidV4 } from 'uuid';
import { ForbiddenError, ValidationError } from 'errors';
import database from 'infra/database.js';
import balance from 'models/balance.js';
import pagination from 'models/pagination.js';
import prestige from 'models/prestige';
import user from 'models/user.js';
import validator from 'models/validator.js';
Expand All @@ -12,13 +13,13 @@ import queries from 'queries/rankingQueries';
async function findAll(values = {}, options = {}) {
values = validateValues(values);
await replaceOwnerUsernameWithOwnerId(values);
const offset = (values.page - 1) * values.per_page;

const query = {
values: [],
};

if (!values.count) {
const offset = (values.page - 1) * values.per_page;
query.values = [values.limit || values.per_page, offset];
}

Expand Down Expand Up @@ -218,7 +219,7 @@ async function findWithStrategy(options = {}) {

options.order = 'published_at DESC';
results.rows = await findAll(options);
options.totalRows = results.rows[0]?.total_rows;
options.total_rows = results.rows[0]?.total_rows;
results.pagination = await getPagination(options);

return results;
Expand All @@ -229,7 +230,7 @@ async function findWithStrategy(options = {}) {

options.order = 'published_at ASC';
results.rows = await findAll(options);
options.totalRows = results.rows[0]?.total_rows;
options.total_rows = results.rows[0]?.total_rows;
results.pagination = await getPagination(options);

return results;
Expand All @@ -252,34 +253,17 @@ async function findWithStrategy(options = {}) {
results.rows = rankContentListByRelevance(contentList);
}

values.totalRows = results.rows[0]?.total_rows;
values.total_rows = results.rows[0]?.total_rows;
results.pagination = await getPagination(values, options);

return results;
}
}

async function getPagination(values, options = {}) {
async function getPagination(values, options) {
values.count = true;

const totalRows = values.totalRows ?? (await findAll(values, options))[0]?.total_rows ?? 0;
const perPage = values.per_page;
const firstPage = 1;
const lastPage = Math.ceil(totalRows / values.per_page);
const nextPage = values.page >= lastPage ? null : values.page + 1;
const previousPage = values.page <= 1 ? null : values.page > lastPage ? lastPage : values.page - 1;
const strategy = values.strategy;

return {
currentPage: values.page,
totalRows: totalRows,
perPage: perPage,
firstPage: firstPage,
nextPage: nextPage,
previousPage: previousPage,
lastPage: lastPage,
strategy: strategy,
};
values.total_rows = values.total_rows ?? (await findAll(values, options))[0]?.total_rows ?? 0;
return pagination.get(values);
}

async function create(postedContent, options = {}) {
Expand Down
29 changes: 17 additions & 12 deletions models/controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -125,22 +125,27 @@ function logRequest(request, response, next) {

function injectPaginationHeaders(pagination, endpoint, response) {
const links = [];
const baseUrl = `${webserver.host}${endpoint}?strategy=${pagination.strategy}`;
const baseUrl = `${webserver.host}${endpoint}`;

if (pagination.firstPage) {
links.push(`<${baseUrl}&page=${pagination.firstPage}&per_page=${pagination.perPage}>; rel="first"`);
}

if (pagination.previousPage) {
links.push(`<${baseUrl}&page=${pagination.previousPage}&per_page=${pagination.perPage}>; rel="prev"`);
}
const searchParams = new URLSearchParams();

if (pagination.nextPage) {
links.push(`<${baseUrl}&page=${pagination.nextPage}&per_page=${pagination.perPage}>; rel="next"`);
if (pagination.strategy) {
searchParams.set('strategy', pagination.strategy);
}

if (pagination.lastPage) {
links.push(`<${baseUrl}&page=${pagination.lastPage}&per_page=${pagination.perPage}>; rel="last"`);
const pages = [
{ page: pagination.firstPage, rel: 'first' },
{ page: pagination.previousPage, rel: 'prev' },
{ page: pagination.nextPage, rel: 'next' },
{ page: pagination.lastPage, rel: 'last' },
];

for (const { page, rel } of pages) {
if (page) {
searchParams.set('page', page);
searchParams.set('per_page', pagination.perPage);
links.push(`<${baseUrl}?${searchParams.toString()}>; rel="${rel}"`);
}
}

const linkHeaderString = links.join(', ');
Expand Down
26 changes: 26 additions & 0 deletions models/pagination.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
async function get({ total_rows, page, per_page, strategy }) {
const firstPage = 1;
const lastPage = Math.ceil(total_rows / per_page);
const nextPage = page >= lastPage ? null : page + 1;
const previousPage = page <= 1 ? null : page > lastPage ? lastPage : page - 1;

const pagination = {
currentPage: page,
totalRows: total_rows,
perPage: per_page,
firstPage: firstPage,
nextPage: nextPage,
previousPage: previousPage,
lastPage: lastPage,
};

if (strategy) {
pagination.strategy = strategy;
}

return pagination;
}

export default Object.freeze({
get,
});
51 changes: 51 additions & 0 deletions models/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import database from 'infra/database.js';
import authentication from 'models/authentication.js';
import balance from 'models/balance.js';
import emailConfirmation from 'models/email-confirmation.js';
import pagination from 'models/pagination.js';
import validator from 'models/validator.js';

async function findAll() {
Expand All @@ -25,6 +26,55 @@ async function findAll() {
return results.rows;
}

async function findAllWithPagination(values) {
const offset = (values.page - 1) * values.per_page;

const query = {
text: `
WITH user_window AS (
SELECT
COUNT(*) OVER()::INTEGER as total_rows,
id
FROM users
ORDER BY updated_at DESC
LIMIT $1 OFFSET $2
)

SELECT
*
FROM
users
INNER JOIN
user_window ON users.id = user_window.id
CROSS JOIN LATERAL (
SELECT
get_user_current_tabcoins(users.id) as tabcoins,
get_user_current_tabcash(users.id) as tabcash
) as balance
ORDER BY updated_at DESC
`,
values: [values.limit || values.per_page, offset],
};

const queryResults = await database.query(query);

const results = {
rows: queryResults.rows,
};

values.total_rows = results.rows[0]?.total_rows ?? (await countTotalRows());

results.pagination = await pagination.get(values);

return results;
}

async function countTotalRows() {
aprendendofelipe marked this conversation as resolved.
Show resolved Hide resolved
const countQuery = `SELECT COUNT(*) OVER()::INTEGER as total_rows FROM users`;
const countResult = await database.query(countQuery);
return countResult.rows[0].total_rows;
}

async function findOneByUsername(username, options = {}) {
const baseQuery = `
WITH user_found AS (
Expand Down Expand Up @@ -446,6 +496,7 @@ async function updateRewardedAt(userId, options) {
export default Object.freeze({
create,
findAll,
findAllWithPagination,
findOneByUsername,
findOneByEmail,
findOneById,
Expand Down
22 changes: 20 additions & 2 deletions pages/api/v1/users/index.public.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,39 @@ export default nextConnect({
.use(authentication.injectAnonymousOrUser)
.use(controller.logRequest)
.use(cacheControl.noCache)
.get(authorization.canRequest('read:user:list'), getHandler)
.get(getValidationHandler, authorization.canRequest('read:user:list'), getHandler)
.post(
postValidationHandler,
authorization.canRequest('create:user'),
firewall.canRequest('create:user'),
postHandler,
);

function getValidationHandler(request, response, next) {
const cleanValues = validator(request.query, {
page: 'optional',
per_page: 'optional',
});

request.query = cleanValues;

next();
}

async function getHandler(request, response) {
const userTryingToList = request.context.user;

const userList = await user.findAll();
const results = await user.findAllWithPagination({
page: request.query.page,
per_page: request.query.per_page,
});

const userList = results.rows;

const secureOutputValues = authorization.filterOutput(userTryingToList, 'read:user:list', userList);

controller.injectPaginationHeaders(results.pagination, '/api/v1/users', response);

return response.status(200).json(secureOutputValues);
}

Expand Down
24 changes: 12 additions & 12 deletions tests/integration/api/v1/contents/[username]/get.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -558,21 +558,21 @@ describe('GET /api/v1/contents/[username]', () => {
per_page: '30',
rel: 'first',
strategy: 'new',
url: `http://localhost:3000/api/v1/contents/${defaultUser.username}?strategy=new&page=1&per_page=30`,
url: `${orchestrator.webserverUrl}/api/v1/contents/${defaultUser.username}?strategy=new&page=1&per_page=30`,
},
next: {
page: '2',
per_page: '30',
rel: 'next',
strategy: 'new',
url: `http://localhost:3000/api/v1/contents/${defaultUser.username}?strategy=new&page=2&per_page=30`,
url: `${orchestrator.webserverUrl}/api/v1/contents/${defaultUser.username}?strategy=new&page=2&per_page=30`,
},
last: {
page: '2',
per_page: '30',
rel: 'last',
strategy: 'new',
url: `http://localhost:3000/api/v1/contents/${defaultUser.username}?strategy=new&page=2&per_page=30`,
url: `${orchestrator.webserverUrl}/api/v1/contents/${defaultUser.username}?strategy=new&page=2&per_page=30`,
},
});

Expand All @@ -599,21 +599,21 @@ describe('GET /api/v1/contents/[username]', () => {
per_page: '30',
rel: 'first',
strategy: 'new',
url: `http://localhost:3000/api/v1/contents/${defaultUser.username}?strategy=new&page=1&per_page=30`,
url: `${orchestrator.webserverUrl}/api/v1/contents/${defaultUser.username}?strategy=new&page=1&per_page=30`,
},
prev: {
page: '1',
per_page: '30',
rel: 'prev',
strategy: 'new',
url: `http://localhost:3000/api/v1/contents/${defaultUser.username}?strategy=new&page=1&per_page=30`,
url: `${orchestrator.webserverUrl}/api/v1/contents/${defaultUser.username}?strategy=new&page=1&per_page=30`,
},
last: {
page: '2',
per_page: '30',
rel: 'last',
strategy: 'new',
url: `http://localhost:3000/api/v1/contents/${defaultUser.username}?strategy=new&page=2&per_page=30`,
url: `${orchestrator.webserverUrl}/api/v1/contents/${defaultUser.username}?strategy=new&page=2&per_page=30`,
},
});

Expand Down Expand Up @@ -700,21 +700,21 @@ describe('GET /api/v1/contents/[username]', () => {
per_page: '30',
rel: 'first',
strategy: 'relevant',
url: `http://localhost:3000/api/v1/contents/${defaultUser.username}?strategy=relevant&page=1&per_page=30`,
url: `${orchestrator.webserverUrl}/api/v1/contents/${defaultUser.username}?strategy=relevant&page=1&per_page=30`,
},
next: {
page: '2',
per_page: '30',
rel: 'next',
strategy: 'relevant',
url: `http://localhost:3000/api/v1/contents/${defaultUser.username}?strategy=relevant&page=2&per_page=30`,
url: `${orchestrator.webserverUrl}/api/v1/contents/${defaultUser.username}?strategy=relevant&page=2&per_page=30`,
},
last: {
page: '2',
per_page: '30',
rel: 'last',
strategy: 'relevant',
url: `http://localhost:3000/api/v1/contents/${defaultUser.username}?strategy=relevant&page=2&per_page=30`,
url: `${orchestrator.webserverUrl}/api/v1/contents/${defaultUser.username}?strategy=relevant&page=2&per_page=30`,
},
});

Expand Down Expand Up @@ -743,21 +743,21 @@ describe('GET /api/v1/contents/[username]', () => {
per_page: '30',
rel: 'first',
strategy: 'relevant',
url: `http://localhost:3000/api/v1/contents/${defaultUser.username}?strategy=relevant&page=1&per_page=30`,
url: `${orchestrator.webserverUrl}/api/v1/contents/${defaultUser.username}?strategy=relevant&page=1&per_page=30`,
},
prev: {
page: '1',
per_page: '30',
rel: 'prev',
strategy: 'relevant',
url: `http://localhost:3000/api/v1/contents/${defaultUser.username}?strategy=relevant&page=1&per_page=30`,
url: `${orchestrator.webserverUrl}/api/v1/contents/${defaultUser.username}?strategy=relevant&page=1&per_page=30`,
},
last: {
page: '2',
per_page: '30',
rel: 'last',
strategy: 'relevant',
url: `http://localhost:3000/api/v1/contents/${defaultUser.username}?strategy=relevant&page=2&per_page=30`,
url: `${orchestrator.webserverUrl}/api/v1/contents/${defaultUser.username}?strategy=relevant&page=2&per_page=30`,
},
});

Expand Down
Loading
Loading