Articles routing progress

All articles, and querys for limit and offset working.
This commit is contained in:
Josh Black 2017-09-19 17:26:05 -06:00
parent f1cb95d88a
commit 0c3c909ae6
11 changed files with 197 additions and 46 deletions

View File

@ -1,4 +1,7 @@
import { IUserModel } from '../models/user-model';
export interface IArticle {
slug: string;
title: string;
@ -9,4 +12,5 @@ export interface IArticle {
updatedAt: Date;
favorited: boolean;
favoritesCount: number;
author: IUserModel;
}

View File

@ -5,3 +5,11 @@ export interface IUser {
bio?: string;
image?: string;
}
export interface IProfile {
username: string;
bio: string;
image: string;
following: boolean;
}

View File

@ -1,13 +1,11 @@
import { model, Model, Schema, Document } from 'mongoose';
import { IArticle } from '../interfaces/article-interface';
import { IUserModel } from './user-model';
// import * as mongoose from 'mongoose';
// const User = mongoose.model('User');
import { IUserModel, User } from './user-model';
export interface IArticleModel extends IArticle, Document {
toArticleJSON(user);
formatAsArticleJSON(user);
}
@ -22,21 +20,22 @@ const ArticleSchema = new Schema({
}, {timestamps: true});
ArticleSchema.methods.toArticleJSON = function(user: IUserModel) {
ArticleSchema.methods.formatAsArticleJSON = function(user: IUserModel) {
return {
slug: this.slug,
title: this.title,
description: this.description,
body: this.body,
tagList: this.tagList,
createdAt: this.createdAt,
updatedAt: this.updatedAt,
tagList: this.tagList,
favorited: user ? user.isFavorite(this._id) : false,
favoritesCount: this.favoritesCount,
author: this.author
author: this.author.formatAsProfileJSON(user)
};
};
// TODO: taglist
// TODO fix author
export const Article: Model<IArticleModel> = model<IArticleModel>('Article', ArticleSchema);

View File

@ -2,12 +2,12 @@
import { Document, Schema, Model, model } from 'mongoose';
import { IUser } from '../interfaces/user-interface';
import * as jwt from 'jsonwebtoken';
import { jwtSecret } from './authentication';
import { jwtSecret } from '../utilities/authentication';
import * as crypto from 'crypto';
export interface IUserModel extends IUser, Document {
token: string;
token?: string;
generateJWT();
formatAsUserJSON();
setPassword(password);

View File

@ -1,46 +1,182 @@
import { Router, Response, NextFunction } from 'express';
import { authentication } from '../models/authentication';
import { JWTRequest } from '../interfaces/requests-interface';
import { Router, NextFunction, Response } from 'express';
import { authentication } from '../utilities/authentication';
import { ProfileRequest } from '../interfaces/requests-interface';
import { Article, IArticleModel } from '../models/article-model';
import { IUserModel, User } from '../models/user-model';
// import * as Promise from 'bluebird';
const router: Router = Router();
const Promise = require('bluebird');
/**
* // GET /api/articles
* GET /api/articles
*/
router.get('/', authentication.optional, (req: JWTRequest, res: Response, next: NextFunction) => {
router.get('/', authentication.optional, (req: ProfileRequest, res: Response, next: NextFunction) => {
Article
.findOne()
.then( (article: IArticleModel) => {
// res.status(200).json({article: article.toArticleJSON()});
res.json({message: 'working on this...'});
}
)
.catch(next);
// Handle all URL query parameters
const limit: number = req.query.limit ? Number(req.query.limit) : 20;
const offset: number = req.query.offset ? Number(req.query.offset) : 0;
// .findById(req.payload.id)
// .then((user: IUserModel) => {
// res.status(200).json({user: user.formatAsUserJSON()});
// }
// )
// .catch(next);
//
// Try to determine the user making the request
let thisUser: IUserModel;
// If authentication was performed was successful look up the profile relative to authenticated user
if (req.payload) {
User
.findById(req.payload.id)
.then( (user: IUserModel) => {
return thisUser = req.profile.formatAsProfileJSON(user);
})
.catch();
// If authentication was NOT performed or successful look up profile relative to that same user (following = false)
} else {
thisUser = req.profile;
}
);
// TODO: Remining routes
// Define promises
const p1 = thisUser;
const p2 = Article.count( {});
const p3 = Article.find().limit(limit).skip(offset).populate('author').catch();
// Resolve and use promise results
Promise
.all([p1, p2, p3])
.then(results => {
const user: IUserModel = results[0];
const articlesCount: number = results[1];
const articles = results[2];
res.json(
{articles: articles.map((article: IArticleModel) => {
return article.formatAsArticleJSON(user);
}),
articlesCount});
})
.catch(next);
});
// PREVIOUS ATTEMPT:
// router.get('/', authentication.optional, (req: ProfileRequest, res: Response, next: NextFunction) => {
//
// let articlesCount = 0;
// let thisUser: IUserModel;
//
// // If authentication was performed was successful look up the profile relative to authenticated user
// if (req.payload) {
// User
// .findById(req.payload.id)
// .then( (user: IUserModel) => {
// return thisUser = req.profile.formatAsProfileJSON(user);
// })
// .catch();
//
// // If authentication was NOT performed or successful look up profile relative to that same user (following = false)
// } else {
// thisUser = req.profile;
// }
//
// Article
// .count( (err, count) => {
// articlesCount = count;
// })
// .find()
// .sort({updatedAt: 'desc'})
// .then( (articles: IArticleModel[]) => {
// res.json({articles: articles.map(article => {
// console.log(article);
// return article.formatAsArticleJSON(thisUser);
// }), articlesCount
// });
// })
// .catch(next);
// });
// WORKING:
// interface IQuery {
// tagList: {$in: any[]};
// author: string;
// _id: {$in: any[]};
// limit: number;
// offset: number;
// }
// let query: IQuery;
// let limit = 20;
// let offset = 0;
//
// if (typeof req.query.limit !== 'undefined') {
// limit = req.query.limit;
// }
//
// if (typeof req.query.offset !== 'undefined') {
// offset = req.query.offset;
// }
//
// if ( typeof req.query.tag !== 'undefined' ) {
// query.tagList = {$in : [req.query.tag]};
// }
//
// Promise.all([
// req.query.author ? User.findOne({username: req.query.author}) : null,
// req.query.favorited ? User.findOne({username: req.query.favorited}) : null
// ]).then(function(results){
// const author = results[0];
// const favoriter = results[1];
//
// if (author) {
// query.author = author._id;
// }
//
// if (favoriter) {
// query._id = {$in: favoriter.favorites};
// } else if (req.query.favorited) {
// query._id = {$in: []};
// }
//
// Promise.all([
// Article.find(query)
// .limit(Number(limit))
// .skip(Number(offset))
// .sort({createdAt: 'desc'})
// .populate('author')
// .exec(),
// Article.count(query).exec(),
// req.payload ? User.findById(req.payload.id) : null,
// ]).then(function(results){
// const articles = results[0];
// const articlesCount = results[1];
// const user = results[2];
//
// return res.json({
// articles: articles.map(function(article) {
// return article.formatAsArticleJSON(user);
// }),
// articlesCount
// });
// });
// }).catch(next);
// TODO: Remaining routes
// GET /api/articles/feed
// GET /api/articles/:slug
// POST /api/articles
// PUT /api/articles/:slug
// DELETE /api/articles/:slug
// POST /api/articles/:slug/comments
// GET /api/articles/:slug/comments
// DELETE /api/articles/:slug/comments/:id
// POST /api/articles/:slug/favorite
// DELETE /api/articles/:slug/favorite

View File

@ -1,7 +1,7 @@
import { NextFunction, Request, Response, Router } from 'express';
import { NextFunction, Response, Router } from 'express';
import { IUserModel, User } from '../models/user-model';
import { authentication } from '../models/authentication';
import { authentication } from '../utilities/authentication';
import { ProfileRequest } from '../interfaces/requests-interface';
const router: Router = Router();
@ -28,7 +28,7 @@ router.param('username', (req: ProfileRequest, res: Response, next: NextFunction
*/
router.get('/:username', authentication.optional, (req: ProfileRequest, res: Response, next: NextFunction) => {
// If authentication was performed and successful look up the profile relative to authenticated user
// If authentication was performed and was successful look up the profile relative to authenticated user
if (req.payload) {
User
.findById(req.payload.id)
@ -39,15 +39,9 @@ router.get('/:username', authentication.optional, (req: ProfileRequest, res: Res
// If authentication was NOT performed or successful look up profile relative to that same user (following = false)
} else {
// User
// .findOne({username: req.params.username})
// .then( (user: IUserModel) => {
// res.status(200).json({profile: user.formatAsProfileJSON(user)});
// })
// .catch(next);
res.status(200).json({profile: req.profile.formatAsProfileJSON(req.profile)});
}
// ISSUE: why have to repeat query? Why route-level variable not accessible here?
});
@ -87,7 +81,6 @@ router.delete('/:username/follow', authentication.required, (req: ProfileRequest
})
.catch(next);
});
// TODO: DELETE route working on first try
export const ProfilesRoutes: Router = router;

View File

@ -1,6 +1,6 @@
import { IUserModel, User } from '../models/user-model';
import { authentication } from '../models/authentication';
import { authentication } from '../utilities/authentication';
import { NextFunction, Response, Router } from 'express';
import { JWTRequest } from '../interfaces/requests-interface';

View File

@ -3,6 +3,7 @@ import * as passport from 'passport';
import { User } from '../models/user-model';
import * as passportLocal from 'passport-local';
const LocalStrategy = passportLocal.Strategy;

9
package-lock.json generated
View File

@ -10,6 +10,15 @@
"integrity": "sha512-rBfrD56OxaqVjghtVqp2EEX0ieHkRk6IefDVrQXIVGvlhDOEBTvZff4Q02uo84ukVkH4k5eB1cPKGDM2NlFL8A==",
"dev": true
},
"@types/bluebird-global": {
"version": "3.5.3",
"resolved": "https://registry.npmjs.org/@types/bluebird-global/-/bluebird-global-3.5.3.tgz",
"integrity": "sha512-EP8wk7BRKtb9MhdHSc/xeUSfBByHQpm6pFwvQV1dO0d2o0wShYB9xEOx+OGP+2VNtADaBnhvXJ+Z5sgseR+f/w==",
"dev": true,
"requires": {
"@types/bluebird": "3.5.8"
}
},
"@types/bson": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/@types/bson/-/bson-1.0.4.tgz",

View File

@ -19,6 +19,7 @@
"homepage": "https://",
"devDependencies": {
"@types/bluebird": "^3.5.8",
"@types/bluebird-global": "^3.5.3",
"@types/express": "^4.0.37",
"@types/express-jwt": "0.0.37",
"@types/express-session": "^1.15.3",