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

Integrate with frontend web app #79

Merged
merged 32 commits into from
Oct 27, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
9a762a7
Added POST users/check API endpoint for check on username availability
JonathanLeeWH Nov 23, 2020
6462563
Removed username minlength of 6 constraint from user model
JonathanLeeWH Nov 23, 2020
b033ba5
Added corresponding test cases for users/check API endpoints in users…
JonathanLeeWH Nov 24, 2020
8e7881b
Aded additional check involving username in database for verifyAuthTo…
JonathanLeeWH Nov 24, 2020
7aefd3b
Added .gcloudignore and app.yaml to .gitignore
JonathanLeeWH Nov 25, 2020
98664ff
Merge branch 'master' into integrate-with-frontend-web-app
JonathanLeeWH Nov 26, 2020
dded707
Merge branch 'master' into integrate-with-frontend-web-app
JonathanLeeWH Dec 10, 2020
5050afe
Merge branch 'master' into integrate-with-frontend-web-app
JonathanLeeWH Dec 10, 2020
6788910
Merge branch 'master' into integrate-with-frontend-web-app
JonathanLeeWH Jul 3, 2021
6a9a120
Added `/api/search/software` API endpoint to allow for searching of s…
JonathanLeeWH Jul 27, 2021
0ac7ec2
Merge pull request #208 from learnsoftwaredevelopment/add-search-api-…
JonathanLeeWH Jul 27, 2021
230c084
Added support for `page` and `per_page` query params in `api/search/s…
JonathanLeeWH Jul 28, 2021
c3a6442
Amended `software` model by changing `developedBy` and `maintainedBy`…
JonathanLeeWH Jul 29, 2021
dec51f8
Merge pull request #210 from learnsoftwaredevelopment/amend-software-…
JonathanLeeWH Jul 29, 2021
f9330d6
Merge pull request #211 from learnsoftwaredevelopment/improved-search…
JonathanLeeWH Jul 29, 2021
c83bc19
Merge branch 'master' into integrate-with-frontend-web-app
JonathanLeeWH Jul 30, 2021
967a532
Improved `software` model with `shortDescription`, `pricing`, `videoL…
JonathanLeeWH Jul 31, 2021
e0eba38
Merge pull request #212 from learnsoftwaredevelopment/improve-softwar…
JonathanLeeWH Jul 31, 2021
686a229
Added missing `default field` in `software` model `twitterUsername` f…
JonathanLeeWH Jul 31, 2021
6f71655
Merge pull request #213 from learnsoftwaredevelopment/add-missing-def…
JonathanLeeWH Jul 31, 2021
086f0d4
Resolved a bug in `POST` `software` API endpoint where the `software`…
JonathanLeeWH Aug 1, 2021
1e47f48
Merge pull request #214 from learnsoftwaredevelopment/resolved-bug-in…
JonathanLeeWH Aug 1, 2021
2d71cf3
Populate `user` information in `api/search/software` response
JonathanLeeWH Aug 3, 2021
8be5d1b
Added `api/software/added/recent` and `api/software/updates/recent` A…
JonathanLeeWH Aug 3, 2021
e1aa8a0
Merge pull request #217 from learnsoftwaredevelopment/add-new-recent-…
JonathanLeeWH Aug 3, 2021
5023ac7
Resolved a bug where previously video host with subdomain `www.` was …
JonathanLeeWH Aug 10, 2021
259eb8f
Merge pull request #223 from learnsoftwaredevelopment/resolved-video-…
JonathanLeeWH Aug 10, 2021
304d419
Merge branch 'master' into integrate-with-frontend-web-app
JonathanLeeWH Oct 6, 2021
5c5065c
Removed testing nodejs 16 in GitHub Actions as the focus is on Nodejs…
JonathanLeeWH Oct 6, 2021
dfead43
Merge branch 'master' into integrate-with-frontend-web-app
JonathanLeeWH Oct 27, 2021
61961ac
Amended GitHub Actions to new nodejs LTS Version `16`
JonathanLeeWH Oct 27, 2021
8ce4e0b
Merge pull request #269 from learnsoftwaredevelopment/amended-node-js…
JonathanLeeWH Oct 27, 2021
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
3 changes: 2 additions & 1 deletion .github/workflows/node.js-with-docker-db.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ jobs:

strategy:
matrix:
node-version: [14]
node-version: ['lts/*']
check-latest: true

# If the Pull Request is coming from a fork (pull_request_target), ensure it's opened by "dependabot[bot]". Otherwise, clone it normally.
# References:
Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/node.js.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ jobs:

strategy:
matrix:
node-version: [14.x]
node-version: ['lts/*']
check-latest: true

steps:
- uses: actions/[email protected]
Expand Down
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -104,4 +104,6 @@ dist
.tern-port

# custom
*firebase-adminsdk*.json
*firebase-adminsdk*.json
.gcloudignore
app.yaml
8 changes: 4 additions & 4 deletions app.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,13 @@ const morgan = require('morgan');
const cors = require('cors');
const mongoose = require('mongoose');
const config = require('./utils/config');

const rootRouter = require('./routes/root');
const usersRouter = require('./routes/api/users');

const middleware = require('./utils/middleware');
const logger = require('./utils/logger');
const rootRouter = require('./routes/root');
const usersRouter = require('./routes/api/users');
const authRouter = require('./routes/api/auth');
const softwareRouter = require('./routes/api/software');
const searchRouter = require('./routes/api/search');

const app = express();

Expand Down Expand Up @@ -56,6 +55,7 @@ app.use(rootRouter);
app.use('/api/users', usersRouter);
app.use('/api/auth', authRouter);
app.use('/api/software', softwareRouter);
app.use('/api/search', searchRouter);

app.use(middleware.unknownEndPoint);
app.use(middleware.errorHandler);
Expand Down
30 changes: 30 additions & 0 deletions controllers/api/searchController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
const Software = require('../../models/software');

const searchSoftware = async (req, res) => {
const page = parseInt(req.query.page, 10) || 0;
const perPage = parseInt(req.query.per_page, 10) || 30;
const query = {
name: {
$regex: req.query.q,
$options: 'i',
},
};

const queryResponse = await Software.find(query)
.sort({ name: 'asc' })
.skip(page * perPage)
.limit(perPage)
.populate('meta.addedByUser', { username: 1, name: 1 })
.populate('meta.updatedByUser', { username: 1, name: 1 });

const totalQueryResultCount = await Software.find(query).countDocuments();

return res.status(200).json({
totalQueryResultCount,
queryResponse,
});
};

module.exports = {
searchSoftware,
};
27 changes: 27 additions & 0 deletions controllers/api/softwareController.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const postSoftware = async (req, res) => {
const softwareObject = {
...body,
meta: {
...body.meta,
addedByUser: userId,
updatedByUser: userId,
},
Expand Down Expand Up @@ -146,10 +147,36 @@ const deleteSoftwareById = async (req, res) => {
return res.status(204).end();
};

const getRecentAddedSoftware = async (req, res) => {
const count = parseInt(req.query.count, 10) || 5;

const response = await Software.find({})
.sort({ createdAt: 'desc' })
.limit(count)
.populate('meta.addedByUser', { username: 1, name: 1 })
.populate('meta.updatedByUser', { username: 1, name: 1 });

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

const getRecentUpdatedSoftware = async (req, res) => {
const count = parseInt(req.query.count, 10) || 5;

const response = await Software.find({})
.sort({ updatedAt: 'desc' })
.limit(count)
.populate('meta.addedByUser', { username: 1, name: 1 })
.populate('meta.updatedByUser', { username: 1, name: 1 });

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

module.exports = {
getSoftware,
postSoftware,
patchSoftwareById,
getSoftwareById,
deleteSoftwareById,
getRecentAddedSoftware,
getRecentUpdatedSoftware,
};
46 changes: 41 additions & 5 deletions controllers/api/usersController.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const User = require('../../models/user');
const firebaseAdmin = require('../../utils/firebaseConfig');
const jwtUtils = require('../../utils/jwtUtils');
const { checkUsernameValidity } = require('../../utils/api/usersUtils');

const getUsers = async (req, res) => {
const users = await User.find({}).populate([
Expand All @@ -17,17 +18,24 @@ const getUsers = async (req, res) => {
};

const postUsers = async (req, res) => {
const { body } = req;
const { username } = req.body;

const authToken = jwtUtils.getReqAuthToken(req);

const decodedToken = await jwtUtils.verifyAuthToken(authToken, false);

if (!decodedToken) {
return res.status(401).json({
error: 'Missing/Invalid Token',
});
}

const response = await firebaseAdmin.auth().getUser(decodedToken.uid);

const userRecord = response.toJSON();

const user = new User({
username: body.username,
username: !username ? null : username,
name: userRecord.displayName,
email: userRecord.email,
firebaseUid: userRecord.uid,
Expand All @@ -36,14 +44,42 @@ const postUsers = async (req, res) => {
const savedUser = await user.save();

// Set custom claim with backend user id (different from firebase user id)
await firebaseAdmin
.auth()
.setCustomUserClaims(decodedToken.uid, { backendId: savedUser.id });
await firebaseAdmin.auth().setCustomUserClaims(decodedToken.uid, {
backendId: savedUser.id,
username: savedUser.username,
});

return res.status(201).json(savedUser);
};

const postUserAvailability = async (req, res) => {
const { username } = req.body;

if (!username) {
return res.status(400).json({
error: 'Missing username in request.',
});
}

const isValidUsername = checkUsernameValidity(username);

if (!isValidUsername) {
return res.status(400).json({
error: 'Invalid username.',
});
}

const response = await User.findOne({
username,
}).lean();

return res.json({
usernameStatus: response === null ? 'Available' : 'Not Available',
});
};

module.exports = {
getUsers,
postUsers,
postUserAvailability,
};
66 changes: 53 additions & 13 deletions models/software.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const mongoose = require('mongoose');
const { isURL } = require('validator');
const uniqueValidator = require('mongoose-unique-validator');
const { ALLOWED_VIDEO_HOST_WHITELIST } = require('../utils/config');

const softwareSchema = new mongoose.Schema(
{
Expand All @@ -26,6 +27,12 @@ const softwareSchema = new mongoose.Schema(
trim: true,
default: '0.0.0',
},
shortDescription: {
type: String,
trim: true,
maxlength: 100,
required: [true, 'Software short description is required'],
},
description: {
type: String,
trim: true,
Expand All @@ -34,9 +41,10 @@ const softwareSchema = new mongoose.Schema(
homePage: {
type: String,
validate: [
(value) => isURL(value, {
protocols: ['http', 'https'],
}),
(value) =>
isURL(value, {
protocols: ['http', 'https'],
}),
'A valid url is required',
],
required: [true, 'Software homepage url is required'],
Expand All @@ -51,6 +59,12 @@ const softwareSchema = new mongoose.Schema(
type: Boolean,
required: true,
},
pricing: {
type: String,
trim: true,
lowercase: true,
required: [true, 'Software pricing is required'],
},
buildOn: {
type: [
{
Expand All @@ -64,21 +78,45 @@ const softwareSchema = new mongoose.Schema(
developedBy: {
type: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
type: String,
trim: true,
lowercase: true,
},
],
default: [],
},
maintainedBy: {
type: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
type: String,
trim: true,
lowercase: true,
},
],
default: [],
},
videoLink: {
type: String,
validate: [
(value) => {
if (value) {
return isURL(value, {
protocols: ['http', 'https'],
host_whitelist: ALLOWED_VIDEO_HOST_WHITELIST,
});
}
return true;
},
'A valid video url is required (Only youtube.com or vimeo.com are supported)',
],
default: '',
},
twitterUsername: {
type: String,
trim: true,
lowercase: true,
default: '',
},
query: {
isEnabled: {
type: Boolean,
Expand All @@ -87,19 +125,21 @@ const softwareSchema = new mongoose.Schema(
updateUrl: {
type: String,
validate: [
(value) => isURL(value, {
protocols: ['http', 'https'],
}) || value === '',
(value) =>
isURL(value, {
protocols: ['http', 'https'],
}) || value === '',
'A valid update url is required',
],
default: '',
},
downloadUrl: {
type: String,
validate: [
(value) => isURL(value, {
protocols: ['http', 'https', 'ftp'],
}) || value === '',
(value) =>
isURL(value, {
protocols: ['http', 'https', 'ftp'],
}) || value === '',
'A valid download url is required',
],
default: '',
Expand Down
4 changes: 2 additions & 2 deletions models/user.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
const mongoose = require('mongoose');
const uniqueValidator = require('mongoose-unique-validator');
const { isEmail, normalizeEmail } = require('validator');
const { ALLOWED_USERNAME_REGEX } = require('../utils/config');

const usernameRegex = RegExp('^[a-z0-9_.-]+$');
const usernameRegex = RegExp(ALLOWED_USERNAME_REGEX);

const userSchema = new mongoose.Schema(
{
username: {
type: String,
required: [true, 'Missing username'],
minlength: [6, 'The username should be at least 6 characters long'],
unique: true,
lowercase: true,
trim: true,
Expand Down
Loading