From 23ea65ac1e3f8f4b6826dc383b000cceadee4873 Mon Sep 17 00:00:00 2001 From: llunaCreixent Date: Mon, 6 Feb 2023 12:50:19 +0100 Subject: [PATCH 01/22] Add sequelize model of News --- src/models/news.js | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 src/models/news.js diff --git a/src/models/news.js b/src/models/news.js new file mode 100644 index 00000000..0910503e --- /dev/null +++ b/src/models/news.js @@ -0,0 +1,27 @@ +import Sequelize from 'sequelize'; + +import db from '../database'; + +const News = db.define('news', { + id: { + type: Sequelize.INTEGER, + primaryKey: true, + autoIncrement: true, + }, + message_en: { + type: Sequelize.TEXT, + }, + date: { + type: Sequelize.STRING, + allowNull: false, + validate: { + notEmpty: true, + }, + }, + icon_id: { + type: Sequelize.INTEGER, + allowNull: false, + }, +}); + +export default News; From 243749213118a511033ccab49e431672c70f61af Mon Sep 17 00:00:00 2001 From: llunaCreixent Date: Mon, 6 Feb 2023 12:56:04 +0100 Subject: [PATCH 02/22] Add default values and active field in News model --- src/models/news.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/models/news.js b/src/models/news.js index 0910503e..9f7b5664 100644 --- a/src/models/news.js +++ b/src/models/news.js @@ -17,11 +17,17 @@ const News = db.define('news', { validate: { notEmpty: true, }, + defaultValue: Sequelize.NOW, }, icon_id: { type: Sequelize.INTEGER, allowNull: false, }, + active: { + type: Sequelize.BOOLEAN, + allowNull: false, + defaultValue: true, + }, }); export default News; From ae516aa85e0666dbb2d19205983540779b568316 Mon Sep 17 00:00:00 2001 From: llunaCreixent Date: Tue, 7 Feb 2023 08:22:20 +0100 Subject: [PATCH 03/22] Update dependencies: sequelize-cli --- package-lock.json | 18 +++++++++--------- package.json | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/package-lock.json b/package-lock.json index e6b8fae8..d0a096bf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12497,9 +12497,9 @@ } }, "glob": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz", - "integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -12509,9 +12509,9 @@ } }, "minimatch": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.2.tgz", - "integrity": "sha512-bNH9mmM9qsJ2X4r2Nat1B//1dJVcn3+iBLa3IgqJ7EbGaDNepL9QSHOxN4ng33s52VMMhhIfgCYDk3C4ZmlDAg==", + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "requires": { "brace-expansion": "^2.0.1" } @@ -16763,9 +16763,9 @@ } }, "sequelize-cli": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/sequelize-cli/-/sequelize-cli-6.5.2.tgz", - "integrity": "sha512-ltXvQOxFFb9pVuDqPJYOS3/X32OVni9zK1S0d0JfloSjmCKZmMDFBWndZbcbJH0QBfyxTt7FLt/kSmKwTRlbsQ==", + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/sequelize-cli/-/sequelize-cli-6.6.0.tgz", + "integrity": "sha512-FwTClhGRvXKanFRHMZbgfXOBV8UC2B3VkE0WOdW1n39/36PF4lWyurF95f246une/V4eaO3a7/Ywvy++3r+Jmg==", "requires": { "cli-color": "^2.0.3", "fs-extra": "^9.1.0", diff --git a/package.json b/package.json index 15df082c..86b7db20 100644 --- a/package.json +++ b/package.json @@ -80,7 +80,7 @@ "pg": "^8.8.0", "pg-copy-streams": "^6.0.4", "sequelize": "^6.28.0", - "sequelize-cli": "^6.5.2", + "sequelize-cli": "^6.6.0", "sharp": "^0.31.3", "web3": "^1.8.1", "winston": "^3.8.2" From b7a22a328ab94f7f7c5ca7e9c774037ffd7390cb Mon Sep 17 00:00:00 2001 From: llunaCreixent Date: Tue, 7 Feb 2023 11:04:51 +0100 Subject: [PATCH 04/22] Create migration file --- .../migrations/20230207095851-create-news.js | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 src/database/migrations/20230207095851-create-news.js diff --git a/src/database/migrations/20230207095851-create-news.js b/src/database/migrations/20230207095851-create-news.js new file mode 100644 index 00000000..365d0ca2 --- /dev/null +++ b/src/database/migrations/20230207095851-create-news.js @@ -0,0 +1,44 @@ +'use strict'; + +/** @type {import('sequelize-cli').Migration} */ +module.exports = { + async up (queryInterface, Sequelize) { + await queryInterface.createTable('news', { + id: { + type: Sequelize.INTEGER, + primaryKey: true, + autoIncrement: true, + }, + createdAt: { + type: Sequelize.DATE, + }, + updatedAt: { + type: Sequelize.DATE, + }, + message_en: { + type: Sequelize.TEXT, + }, + date: { + type: Sequelize.STRING, + allowNull: false, + validate: { + notEmpty: true, + }, + defaultValue: Sequelize.NOW, + }, + icon_id: { + type: Sequelize.INTEGER, + allowNull: false, + }, + active: { + type: Sequelize.BOOLEAN, + allowNull: false, + defaultValue: true, + }, + }); + }, + + async down (queryInterface) { + await queryInterface.dropTable('news'); + } +}; From db48f6537a6fd4f94934c2f5f8f8628f2737705e Mon Sep 17 00:00:00 2001 From: llunaCreixent Date: Tue, 7 Feb 2023 11:11:20 +0100 Subject: [PATCH 05/22] Set date as DATE type --- src/database/migrations/20230207095851-create-news.js | 2 +- src/models/news.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/database/migrations/20230207095851-create-news.js b/src/database/migrations/20230207095851-create-news.js index 365d0ca2..211a1008 100644 --- a/src/database/migrations/20230207095851-create-news.js +++ b/src/database/migrations/20230207095851-create-news.js @@ -19,7 +19,7 @@ module.exports = { type: Sequelize.TEXT, }, date: { - type: Sequelize.STRING, + type: Sequelize.DATE, allowNull: false, validate: { notEmpty: true, diff --git a/src/models/news.js b/src/models/news.js index 9f7b5664..2797dde9 100644 --- a/src/models/news.js +++ b/src/models/news.js @@ -12,7 +12,7 @@ const News = db.define('news', { type: Sequelize.TEXT, }, date: { - type: Sequelize.STRING, + type: Sequelize.DATE, allowNull: false, validate: { notEmpty: true, From 4ed1e4f64b7385d940a3f8a62010f40e9e96e195 Mon Sep 17 00:00:00 2001 From: llunaCreixent Date: Tue, 7 Feb 2023 11:37:32 +0100 Subject: [PATCH 06/22] Make lint happy --- src/database/migrations/20230207095851-create-news.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/database/migrations/20230207095851-create-news.js b/src/database/migrations/20230207095851-create-news.js index 211a1008..88e9994b 100644 --- a/src/database/migrations/20230207095851-create-news.js +++ b/src/database/migrations/20230207095851-create-news.js @@ -2,7 +2,7 @@ /** @type {import('sequelize-cli').Migration} */ module.exports = { - async up (queryInterface, Sequelize) { + async up(queryInterface, Sequelize) { await queryInterface.createTable('news', { id: { type: Sequelize.INTEGER, @@ -38,7 +38,7 @@ module.exports = { }); }, - async down (queryInterface) { + async down(queryInterface) { await queryInterface.dropTable('news'); - } + }, }; From f7bfb57e7cd84025b5f2b3baeacb8dde44c24f2f Mon Sep 17 00:00:00 2001 From: llunaCreixent Date: Tue, 7 Feb 2023 11:38:18 +0100 Subject: [PATCH 07/22] Add news routes and validation --- src/routes/index.js | 2 ++ src/routes/news.js | 11 +++++++++++ src/validations/news.js | 10 ++++++++++ 3 files changed, 23 insertions(+) create mode 100644 src/routes/news.js create mode 100644 src/validations/news.js diff --git a/src/routes/index.js b/src/routes/index.js index 58875cf3..c0ce7974 100644 --- a/src/routes/index.js +++ b/src/routes/index.js @@ -2,6 +2,7 @@ import express from 'express'; import httpStatus from 'http-status'; import APIError from '../helpers/errors'; +import newsRouter from './news'; import transfersRouter from './transfers'; import uploadsRouter from './uploads'; import usersRouter from './users'; @@ -13,6 +14,7 @@ router.get('/', (req, res) => { respondWithSuccess(res); }); +router.use('/news', newsRouter); router.use('/transfers', transfersRouter); router.use('/uploads', uploadsRouter); router.use('/users', usersRouter); diff --git a/src/routes/news.js b/src/routes/news.js new file mode 100644 index 00000000..746ef281 --- /dev/null +++ b/src/routes/news.js @@ -0,0 +1,11 @@ +import express from 'express'; + +import newsController from '../controllers/news'; +import newsValidation from '../validations/news'; +import validate from '../helpers/validate'; + +const router = express.Router(); + +router.get('/', validate(newsValidation.findNews), newsController.findNews); + +export default router; \ No newline at end of file diff --git a/src/validations/news.js b/src/validations/news.js new file mode 100644 index 00000000..45730d4c --- /dev/null +++ b/src/validations/news.js @@ -0,0 +1,10 @@ +import { Joi } from 'celebrate'; + +export default { + findNews: { + query: Joi.object({ + active: Joi.boolean(), + afterDate: Joi.date(), + }).or('active', 'date'), + }, +}; From 8589f56e5134e5dbaa43f69754c22025f2d9c379 Mon Sep 17 00:00:00 2001 From: llunaCreixent Date: Tue, 7 Feb 2023 11:47:14 +0100 Subject: [PATCH 08/22] Add news controler --- src/controllers/news.js | 48 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 src/controllers/news.js diff --git a/src/controllers/news.js b/src/controllers/news.js new file mode 100644 index 00000000..0876b84e --- /dev/null +++ b/src/controllers/news.js @@ -0,0 +1,48 @@ +import News from '../models/news'; +import { respondWithSuccess } from '../helpers/responses'; + + +async function resolveBatch(req, res, next) { + const { username, address } = req.query; + + User.findAll({ + where: { + active: true, + }, + }) + .then((response) => { + respondWithSuccess(res, response); + }) + .catch((err) => { + next(err); + }); +} + +async function findByDate(req, res, next) { + const { query } = req.query; + + News.findAll({ + where: { + date: ..., // TODO (and active==true) + }, + order: [['date', 'ASC']], // Review + limit: 10, // Review + }) + .then((response) => { + respondWithSuccess(res, response); + }) + .catch((err) => { + next(err); + }); +} + +export default { + + findNews: async (req, res, next) => { + if (req.query.date) { + return await findByDate(req, res, next); + } + + return await resolveBatch(req, res, next); + }, +}; From 40fcd32dce5d6ee218724c97371eb5dd75c0ed0d Mon Sep 17 00:00:00 2001 From: llunaCreixent Date: Mon, 13 Feb 2023 10:51:58 +0100 Subject: [PATCH 09/22] Implement news controller and validation --- src/controllers/news.js | 35 ++++++++++++++++++++++++----------- src/validations/news.js | 4 +++- 2 files changed, 27 insertions(+), 12 deletions(-) diff --git a/src/controllers/news.js b/src/controllers/news.js index 0876b84e..44b47173 100644 --- a/src/controllers/news.js +++ b/src/controllers/news.js @@ -1,17 +1,29 @@ import News from '../models/news'; import { respondWithSuccess } from '../helpers/responses'; +function prepareNewsResult(response) { + return { + iconId: response.iconId, + message: { + en: response.message_en, + }, + date: response.date, + }; +} async function resolveBatch(req, res, next) { - const { username, address } = req.query; + const { active, limit, offset } = req.query; - User.findAll({ + News.findAll({ where: { - active: true, + active: active || true , }, + order: [['date', 'DESC']], + limit: limit || 10, + offset: offset || 0, }) .then((response) => { - respondWithSuccess(res, response); + respondWithSuccess(res, response.map(prepareNewsResult)); }) .catch((err) => { next(err); @@ -19,17 +31,19 @@ async function resolveBatch(req, res, next) { } async function findByDate(req, res, next) { - const { query } = req.query; + const { active, afterDate, limit, offset } = req.query; News.findAll({ where: { - date: ..., // TODO (and active==true) + active: active || true , + date: { [Op.gte]: afterDate }, }, - order: [['date', 'ASC']], // Review - limit: 10, // Review + order: [['date', 'DESC']], + limit: limit || 10, + offset: offset || 0, }) .then((response) => { - respondWithSuccess(res, response); + respondWithSuccess(res, response.map(prepareNewsResult)); }) .catch((err) => { next(err); @@ -39,10 +53,9 @@ async function findByDate(req, res, next) { export default { findNews: async (req, res, next) => { - if (req.query.date) { + if (req.query.query) { return await findByDate(req, res, next); } - return await resolveBatch(req, res, next); }, }; diff --git a/src/validations/news.js b/src/validations/news.js index 45730d4c..5643d2c1 100644 --- a/src/validations/news.js +++ b/src/validations/news.js @@ -5,6 +5,8 @@ export default { query: Joi.object({ active: Joi.boolean(), afterDate: Joi.date(), - }).or('active', 'date'), + limit: Joi.number().integer(), + offset: Joi.number().integer(), + }).or('active', 'afterDate', 'limit', 'offset'), }, }; From 739918889433eb739474acf5047dfd11cb1e29d6 Mon Sep 17 00:00:00 2001 From: Elena San Miguel Date: Mon, 13 Feb 2023 10:57:58 +0100 Subject: [PATCH 10/22] Update news.js --- src/routes/news.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/news.js b/src/routes/news.js index 746ef281..477cc49a 100644 --- a/src/routes/news.js +++ b/src/routes/news.js @@ -8,4 +8,4 @@ const router = express.Router(); router.get('/', validate(newsValidation.findNews), newsController.findNews); -export default router; \ No newline at end of file +export default router; From 5e01cfc28e3d4e4892e0dc4519362a096e605248 Mon Sep 17 00:00:00 2001 From: llunaCreixent Date: Mon, 13 Feb 2023 12:20:32 +0100 Subject: [PATCH 11/22] Update API.md with users endpoints description --- API.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/API.md b/API.md index 7f37ac22..56472079 100644 --- a/API.md +++ b/API.md @@ -148,7 +148,7 @@ Returns stored transfer meta data including the payment note. This data is only - `403` Verification failed or not allowed to read data - `404` Transaction hash not found -### Get entry by username +### Get user entry by username Get the users entry including its `safeAddress`. @@ -174,7 +174,7 @@ Get the users entry including its `safeAddress`. - `404` Not found -### Search database by usernames +### Search users database by usernames Find a user in the database. @@ -206,7 +206,7 @@ Find a user in the database. When no user was found an empty response will be returned. -### Get multiple entries by username / address +### Get multiple user entries by username / address Resolve multiple usernames (via `username[]`) and/or Safe addresses (via `address[]`) in a batch. @@ -263,7 +263,7 @@ Do a dry-run to check if `email` and `username` fields are valid before creating - `400` Parameters missing or malformed - `409` Entry already exists -### Create new entry +### Create new user entry **Request:** @@ -307,7 +307,7 @@ Create a new entry in the database, connecting a `username` with a `safeAddress` - `403` Verification failed - `409` Entry already exists -### Update entry +### Update user entry **Request:** @@ -349,7 +349,7 @@ Update (or create) an entry in the database, connecting a `username` with a `saf - `403` Verification failed - `409` Entry already exists -### Get email +### Get user email **Request:** From c201670fb45ef0a1a84771356a102cde5cfca26a Mon Sep 17 00:00:00 2001 From: llunaCreixent Date: Mon, 13 Feb 2023 13:34:54 +0100 Subject: [PATCH 12/22] Add documentation of GET news endpoint --- API.md | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/API.md b/API.md index 56472079..498ed9d7 100644 --- a/API.md +++ b/API.md @@ -379,3 +379,36 @@ Get the email from the entry of the `safeAddress` in the database. - `400` Parameters missing or malformed - `403` Verification failed - `404` User entry not found + +### Search news database by date + +Find news in the database. + +**Request:** + +`GET /api/news?active=&afterDate=&limit=&offset=` + +**Response:** + +``` +{ + status: 'ok', + data: [ + { + iconId: , + message: { + en: , + }, + date: , + }, + { + [...] + }, + [...] + ] +} +``` + +**Errors:** + +When no news was found an empty response will be returned. \ No newline at end of file From 4eff0d7a4cc1ff259316f2af259c0ce4475828be Mon Sep 17 00:00:00 2001 From: llunaCreixent Date: Tue, 14 Feb 2023 19:48:33 +0100 Subject: [PATCH 13/22] Fix import --- src/controllers/news.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/controllers/news.js b/src/controllers/news.js index 44b47173..f17198b4 100644 --- a/src/controllers/news.js +++ b/src/controllers/news.js @@ -1,3 +1,5 @@ +import { Op } from 'sequelize'; + import News from '../models/news'; import { respondWithSuccess } from '../helpers/responses'; From 4bdb5c7e071492fd0398c53200258d05ddd3dadb Mon Sep 17 00:00:00 2001 From: llunaCreixent Date: Tue, 14 Feb 2023 19:49:30 +0100 Subject: [PATCH 14/22] Rename isActive --- src/controllers/news.js | 19 ++++++++++++++----- .../migrations/20230207095851-create-news.js | 4 ++-- src/models/news.js | 4 ++-- src/validations/news.js | 4 ++-- 4 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/controllers/news.js b/src/controllers/news.js index f17198b4..f9a0da21 100644 --- a/src/controllers/news.js +++ b/src/controllers/news.js @@ -14,11 +14,16 @@ function prepareNewsResult(response) { } async function resolveBatch(req, res, next) { - const { active, limit, offset } = req.query; + const { isActive, limit, offset } = req.query; + + let activeBool = true; + if (isActive === false){ + activeBool = false; + } News.findAll({ where: { - active: active || true , + isActive: activeBool, }, order: [['date', 'DESC']], limit: limit || 10, @@ -33,12 +38,16 @@ async function resolveBatch(req, res, next) { } async function findByDate(req, res, next) { - const { active, afterDate, limit, offset } = req.query; + const { isActive, afterDate, limit, offset } = req.query; + const activeBool = true; + if (isActive && isActive === 'false'){ + activeBool = false; + } News.findAll({ where: { - active: active || true , - date: { [Op.gte]: afterDate }, + isActive: activeBool, + date: { [Op.gte]: new Date(afterDate) }, }, order: [['date', 'DESC']], limit: limit || 10, diff --git a/src/database/migrations/20230207095851-create-news.js b/src/database/migrations/20230207095851-create-news.js index 88e9994b..a723215e 100644 --- a/src/database/migrations/20230207095851-create-news.js +++ b/src/database/migrations/20230207095851-create-news.js @@ -26,11 +26,11 @@ module.exports = { }, defaultValue: Sequelize.NOW, }, - icon_id: { + iconId: { type: Sequelize.INTEGER, allowNull: false, }, - active: { + isActive: { type: Sequelize.BOOLEAN, allowNull: false, defaultValue: true, diff --git a/src/models/news.js b/src/models/news.js index 2797dde9..d2165443 100644 --- a/src/models/news.js +++ b/src/models/news.js @@ -19,11 +19,11 @@ const News = db.define('news', { }, defaultValue: Sequelize.NOW, }, - icon_id: { + iconId: { type: Sequelize.INTEGER, allowNull: false, }, - active: { + isActive: { type: Sequelize.BOOLEAN, allowNull: false, defaultValue: true, diff --git a/src/validations/news.js b/src/validations/news.js index 5643d2c1..05da9967 100644 --- a/src/validations/news.js +++ b/src/validations/news.js @@ -3,10 +3,10 @@ import { Joi } from 'celebrate'; export default { findNews: { query: Joi.object({ - active: Joi.boolean(), + isActive: Joi.boolean(), afterDate: Joi.date(), limit: Joi.number().integer(), offset: Joi.number().integer(), - }).or('active', 'afterDate', 'limit', 'offset'), + }), }, }; From a4da4909ff4a20030e5dee714e377d712f66cdb8 Mon Sep 17 00:00:00 2001 From: llunaCreixent Date: Tue, 14 Feb 2023 19:49:48 +0100 Subject: [PATCH 15/22] Fix typo, wrong attribute --- src/controllers/news.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/news.js b/src/controllers/news.js index f9a0da21..f2e60bdb 100644 --- a/src/controllers/news.js +++ b/src/controllers/news.js @@ -64,7 +64,7 @@ async function findByDate(req, res, next) { export default { findNews: async (req, res, next) => { - if (req.query.query) { + if (req.query.afterDate) { return await findByDate(req, res, next); } return await resolveBatch(req, res, next); From ed7f56ca5c68ccc1bfb0f28f539668fedf3cf11b Mon Sep 17 00:00:00 2001 From: llunaCreixent Date: Tue, 14 Feb 2023 19:50:30 +0100 Subject: [PATCH 16/22] Add news tests --- test/news-find.test.js | 143 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 143 insertions(+) create mode 100644 test/news-find.test.js diff --git a/test/news-find.test.js b/test/news-find.test.js new file mode 100644 index 00000000..ce925dc9 --- /dev/null +++ b/test/news-find.test.js @@ -0,0 +1,143 @@ +import httpStatus from 'http-status'; +import request from 'supertest'; + +import News from '~/models/news'; +import app from '~'; + +const NUM_TEST_NEWS = 5; + +const news = []; + +beforeAll(async () => { + const items = new Array(NUM_TEST_NEWS).fill(0); + + await Promise.all( + items.map(async (item, index) => { + const message_en = `Message ${index + 1}`; + const date = new Date(`2015-03-${10 + index}T12:00:00Z`); + const iconId = index + 1; + + const newsInstance = await News.create({ + message_en, + date: date.toISOString(), + iconId, + }) + + news.push({ + id: newsInstance.dataValues.id, + message_en, + iconId, + date: date.toISOString(), + }); + }), + ); +}); + +afterAll(async () => { + await Promise.all( + news.map(async (newsItem) => { + return await News.destroy({ + where: { + message_en: newsItem.message_en, + }, + }); + }), + ); +}); + +describe('GET /news/?afterDate=... - Search via date', () => { + it('should return all news', async () => { + await request(app) + .get('/api/news') + .set('Accept', 'application/json') + .expect(httpStatus.OK) + .expect(({ body }) => { + if (body.data.length !== NUM_TEST_NEWS) { + throw new Error('Did not return all expected entries'); + } + }); + }); + + it('should return all matching news ordered by the most recent first', async () => { + const resp = await request(app) + .get(`/api/news/?afterDate=${news[3].date}`) + .set('Accept', 'application/json') + .expect(httpStatus.OK) + .expect(({ body }) => { + if ( + body.data.length !== 2 || + body.data[0].message.en !== news[4].message_en || + body.data[0].iconId !== news[4].iconId + ) { + throw new Error('Did not return expected entries'); + } + }); + }); + + it('should return all matching news with pagination', async () => { + const resp = await request(app) + .get(`/api/news/?afterDate=${news[1].date}&limit=2&offset=1`) + .set('Accept', 'application/json') + .expect(httpStatus.OK) + .expect(({ body }) => { + if ( + body.data.length !== 2 || + body.data[0].message.en !== news[3].message_en || + body.data[0].iconId !== news[3].iconId + ) { + throw new Error('Did not return expected entries'); + } + }); + }); + + it('should fail silently when no items were found', async () => { + await request(app) + .get(`/api/news/?afterDate=${new Date()}`) + .set('Accept', 'application/json') + .expect(httpStatus.OK) + .expect(({ body }) => { + if (body.data.length !== 0) { + throw new Error('Invalid entries found'); + } + }); + }); + + it('should fail silently when no items were found (because no inactive users)', async () => { + await request(app) + .get('/api/news/?isActive=false') + .set('Accept', 'application/json') + .expect(httpStatus.OK) + .expect(({ body }) => { + console.log({body}); + if (body.data.length !== 0) { + throw new Error('Invalid entries found'); + } + }); + }); + + it('should return all news when asking for active news', async () => { + await request(app) + .get('/api/news/?isActive=true') + .set('Accept', 'application/json') + .expect(httpStatus.OK) + .expect(({ body }) => { + if (body.data.length !== NUM_TEST_NEWS) { + throw new Error('Did not return all expected entries'); + } + }); + }); + + it('should fail when afterDate contains invalid date format', async () => { + await request(app) + .get('/api/news/?afterDate=lala') + .set('Accept', 'application/json') + .expect(httpStatus.BAD_REQUEST); + }); + + it('should fail when afterDate is empty', async () => { + await request(app) + .get('/api/news/?afterDate=') + .set('Accept', 'application/json') + .expect(httpStatus.BAD_REQUEST); + }); +}); From b8b0714abbdbc0f7d53a969684f75058be8afaa9 Mon Sep 17 00:00:00 2001 From: llunaCreixent Date: Tue, 14 Feb 2023 19:51:46 +0100 Subject: [PATCH 17/22] Fix API.md documentation --- API.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/API.md b/API.md index 498ed9d7..7d84ea88 100644 --- a/API.md +++ b/API.md @@ -386,7 +386,7 @@ Find news in the database. **Request:** -`GET /api/news?active=&afterDate=&limit=&offset=` +`GET /api/news?isActive=&afterDate=&limit=&offset=` **Response:** @@ -411,4 +411,4 @@ Find news in the database. **Errors:** -When no news was found an empty response will be returned. \ No newline at end of file +When no news were found an empty response will be returned. \ No newline at end of file From a0029dfd43707f827997b601c6b580353f8905b4 Mon Sep 17 00:00:00 2001 From: llunaCreixent Date: Tue, 14 Feb 2023 19:55:05 +0100 Subject: [PATCH 18/22] Fix activeBool --- src/controllers/news.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/controllers/news.js b/src/controllers/news.js index f2e60bdb..860e6e86 100644 --- a/src/controllers/news.js +++ b/src/controllers/news.js @@ -17,7 +17,7 @@ async function resolveBatch(req, res, next) { const { isActive, limit, offset } = req.query; let activeBool = true; - if (isActive === false){ + if (isActive === false) { activeBool = false; } @@ -39,8 +39,9 @@ async function resolveBatch(req, res, next) { async function findByDate(req, res, next) { const { isActive, afterDate, limit, offset } = req.query; - const activeBool = true; - if (isActive && isActive === 'false'){ + + let activeBool = true; + if (isActive === false) { activeBool = false; } @@ -62,7 +63,6 @@ async function findByDate(req, res, next) { } export default { - findNews: async (req, res, next) => { if (req.query.afterDate) { return await findByDate(req, res, next); From 5b4faa91e24fe7a9a01567452933fc2701f00045 Mon Sep 17 00:00:00 2001 From: llunaCreixent Date: Tue, 14 Feb 2023 19:56:35 +0100 Subject: [PATCH 19/22] Make lint happy --- test/news-find.test.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/test/news-find.test.js b/test/news-find.test.js index ce925dc9..da21d041 100644 --- a/test/news-find.test.js +++ b/test/news-find.test.js @@ -21,7 +21,7 @@ beforeAll(async () => { message_en, date: date.toISOString(), iconId, - }) + }); news.push({ id: newsInstance.dataValues.id, @@ -59,7 +59,7 @@ describe('GET /news/?afterDate=... - Search via date', () => { }); it('should return all matching news ordered by the most recent first', async () => { - const resp = await request(app) + await request(app) .get(`/api/news/?afterDate=${news[3].date}`) .set('Accept', 'application/json') .expect(httpStatus.OK) @@ -75,7 +75,7 @@ describe('GET /news/?afterDate=... - Search via date', () => { }); it('should return all matching news with pagination', async () => { - const resp = await request(app) + await request(app) .get(`/api/news/?afterDate=${news[1].date}&limit=2&offset=1`) .set('Accept', 'application/json') .expect(httpStatus.OK) @@ -108,7 +108,6 @@ describe('GET /news/?afterDate=... - Search via date', () => { .set('Accept', 'application/json') .expect(httpStatus.OK) .expect(({ body }) => { - console.log({body}); if (body.data.length !== 0) { throw new Error('Invalid entries found'); } From 96ab9681d13f0ae1bc0accb56356493e2980c8c5 Mon Sep 17 00:00:00 2001 From: llunaCreixent Date: Wed, 15 Feb 2023 08:49:35 +0100 Subject: [PATCH 20/22] Set defaults in Joi schema --- package-lock.json | 3 ++- src/controllers/news.js | 22 ++++++---------------- src/validations/news.js | 6 +++--- 3 files changed, 11 insertions(+), 20 deletions(-) diff --git a/package-lock.json b/package-lock.json index d0a096bf..c1950e8a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18291,7 +18291,8 @@ "app-module-path": "^2.2.0", "ganache": "7.6.0", "mocha": "10.1.0", - "original-require": "^1.0.1" + "original-require": "^1.0.1", + "solc": "^0.5.14" } }, "tslib": { diff --git a/src/controllers/news.js b/src/controllers/news.js index 860e6e86..e55e3407 100644 --- a/src/controllers/news.js +++ b/src/controllers/news.js @@ -16,18 +16,13 @@ function prepareNewsResult(response) { async function resolveBatch(req, res, next) { const { isActive, limit, offset } = req.query; - let activeBool = true; - if (isActive === false) { - activeBool = false; - } - News.findAll({ where: { - isActive: activeBool, + isActive: isActive, }, order: [['date', 'DESC']], - limit: limit || 10, - offset: offset || 0, + limit: limit, + offset: offset, }) .then((response) => { respondWithSuccess(res, response.map(prepareNewsResult)); @@ -40,19 +35,14 @@ async function resolveBatch(req, res, next) { async function findByDate(req, res, next) { const { isActive, afterDate, limit, offset } = req.query; - let activeBool = true; - if (isActive === false) { - activeBool = false; - } - News.findAll({ where: { - isActive: activeBool, + isActive: isActive, date: { [Op.gte]: new Date(afterDate) }, }, order: [['date', 'DESC']], - limit: limit || 10, - offset: offset || 0, + limit: limit, + offset: offset, }) .then((response) => { respondWithSuccess(res, response.map(prepareNewsResult)); diff --git a/src/validations/news.js b/src/validations/news.js index 05da9967..19b3c13c 100644 --- a/src/validations/news.js +++ b/src/validations/news.js @@ -3,10 +3,10 @@ import { Joi } from 'celebrate'; export default { findNews: { query: Joi.object({ - isActive: Joi.boolean(), + isActive: Joi.boolean().default(true), afterDate: Joi.date(), - limit: Joi.number().integer(), - offset: Joi.number().integer(), + limit: Joi.number().integer().default(10), + offset: Joi.number().integer().default(0), }), }, }; From 85a3b233dabca72ecd03f3b91d7b7d821f3933dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Louise=20Linn=C3=A9?= Date: Thu, 23 Mar 2023 14:46:08 +0100 Subject: [PATCH 21/22] Update comments/docs of news --- API.md | 6 +++++- test/news-find.test.js | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/API.md b/API.md index 7d84ea88..7ecc3da5 100644 --- a/API.md +++ b/API.md @@ -382,7 +382,11 @@ Get the email from the entry of the `safeAddress` in the database. ### Search news database by date -Find news in the database. +Resolves multiple news items starting from newest item in the database, via: +- `isActive` specifies whether only active or only inactive items should be returned (default true) +- `afterDate` sets a past limit in time before which no news items are included. The response will include news on the exact "afterDate"-date. (default no limit) +- `limit` the maximum number of items returned +- `offset` skips that number of the filtered latest items **Request:** diff --git a/test/news-find.test.js b/test/news-find.test.js index da21d041..22b599c7 100644 --- a/test/news-find.test.js +++ b/test/news-find.test.js @@ -102,7 +102,7 @@ describe('GET /news/?afterDate=... - Search via date', () => { }); }); - it('should fail silently when no items were found (because no inactive users)', async () => { + it('should fail silently when no items were found (because no inactive news items)', async () => { await request(app) .get('/api/news/?isActive=false') .set('Accept', 'application/json') From 24183650318b5d69761a900617ef6526b5f71bda Mon Sep 17 00:00:00 2001 From: llunaCreixent Date: Mon, 27 Mar 2023 14:38:13 +0200 Subject: [PATCH 22/22] Add isAvtive in findNews response, and add tests --- src/controllers/news.js | 1 + test/news-find.test.js | 16 ++++++++++------ 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/controllers/news.js b/src/controllers/news.js index e55e3407..f9cd2fb8 100644 --- a/src/controllers/news.js +++ b/src/controllers/news.js @@ -10,6 +10,7 @@ function prepareNewsResult(response) { en: response.message_en, }, date: response.date, + isActive: response.isActive, }; } diff --git a/test/news-find.test.js b/test/news-find.test.js index 22b599c7..09a47b00 100644 --- a/test/news-find.test.js +++ b/test/news-find.test.js @@ -21,6 +21,7 @@ beforeAll(async () => { message_en, date: date.toISOString(), iconId, + isActive: index !== 2, // Only one news in inactive }); news.push({ @@ -46,13 +47,13 @@ afterAll(async () => { }); describe('GET /news/?afterDate=... - Search via date', () => { - it('should return all news', async () => { + it('should return active news by default', async () => { await request(app) .get('/api/news') .set('Accept', 'application/json') .expect(httpStatus.OK) .expect(({ body }) => { - if (body.data.length !== NUM_TEST_NEWS) { + if (body.data.length !== NUM_TEST_NEWS - 1) { throw new Error('Did not return all expected entries'); } }); @@ -102,25 +103,28 @@ describe('GET /news/?afterDate=... - Search via date', () => { }); }); - it('should fail silently when no items were found (because no inactive news items)', async () => { + it('should return inactive news when asking for inactive news', async () => { await request(app) .get('/api/news/?isActive=false') .set('Accept', 'application/json') .expect(httpStatus.OK) .expect(({ body }) => { - if (body.data.length !== 0) { + if (body.data.length !== 1 || body.data[0].isActive !== false) { throw new Error('Invalid entries found'); } }); }); - it('should return all news when asking for active news', async () => { + it('should return active news when asking for active news', async () => { await request(app) .get('/api/news/?isActive=true') .set('Accept', 'application/json') .expect(httpStatus.OK) .expect(({ body }) => { - if (body.data.length !== NUM_TEST_NEWS) { + if ( + body.data.length !== NUM_TEST_NEWS - 1 || + body.data[0].isActive !== true + ) { throw new Error('Did not return all expected entries'); } });