restructured backend project, added migration, seeders and tests
This commit is contained in:
parent
e171e01123
commit
f7f80ac690
|
|
@ -15,17 +15,17 @@ app.use(express.json());
|
|||
app.use(express.urlencoded({ extended: true }));
|
||||
|
||||
// database
|
||||
const db = require("./models");
|
||||
const Role = db.role;
|
||||
const User = db.user;
|
||||
const db = require("./models/index");
|
||||
const Role = db.Role;
|
||||
const User = db.User;
|
||||
const PitchType = db.pitchType;
|
||||
|
||||
// db.sequelize.sync();
|
||||
db.sequelize.sync();
|
||||
// force: true will drop the table if it already exists
|
||||
db.sequelize.sync({force: true}).then(() => {
|
||||
console.log('Drop and Re-sync Database with { force: true }');
|
||||
initial();
|
||||
});
|
||||
// db.sequelize.sync({force: true}).then(() => {
|
||||
// console.log('Drop and Re-sync Database with { force: true }');
|
||||
// initial();
|
||||
// });
|
||||
|
||||
// simple route
|
||||
app.get("/", (req, res) => {
|
||||
|
|
@ -12,11 +12,11 @@ module.exports = {
|
|||
},
|
||||
test: {
|
||||
dialect: 'postgres',
|
||||
username: 'tester',
|
||||
password: 'test123',
|
||||
username: 'test',
|
||||
password: 'test123!',
|
||||
database: 'bullpen',
|
||||
host: 'localhost',
|
||||
port: 15432,
|
||||
port: 5432,
|
||||
logging: false,
|
||||
},
|
||||
staging: {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
const db = require("../models");
|
||||
const db = require("../models/index");
|
||||
const config = require("../config/auth.config");
|
||||
const { user: User, role: Role, refreshToken: RefreshToken } = db;
|
||||
const { User: User, Role: Role, RefreshToken: RefreshToken } = db;
|
||||
|
||||
const Op = db.Sequelize.Op;
|
||||
|
||||
|
|
@ -32,6 +32,8 @@ exports.signup = (req, res) => {
|
|||
// user role = 1
|
||||
user.setRoles([1]).then(() => {
|
||||
res.send({ message: "User registered successfully!" });
|
||||
}, (error) => {
|
||||
console.log(JSON.stringify(error, null, 2));
|
||||
});
|
||||
}
|
||||
})
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
const db = require("../models");
|
||||
const db = require("../models/index");
|
||||
const PitchType = db.pitchType;
|
||||
const Op = db.Sequelize.Op;
|
||||
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
const db = require("../models");
|
||||
const db = require("../models/index");
|
||||
const User = db.user;
|
||||
const Op = db.Sequelize.Op;
|
||||
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
const jwt = require("jsonwebtoken");
|
||||
const config = require("../config/auth.config.js");
|
||||
const db = require("../models");
|
||||
const db = require("../models/index");
|
||||
const User = db.user;
|
||||
|
||||
const { TokenExpiredError } = jwt;
|
||||
|
|
@ -1,6 +1,5 @@
|
|||
const db = require("../models");
|
||||
const ROLES = db.ROLES;
|
||||
const User = db.user;
|
||||
const db = require("../models/index");
|
||||
const User = db.User;
|
||||
|
||||
checkDuplicateUsernameOrEmail = (req, res, next) => {
|
||||
// Username
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
'use strict';
|
||||
/** @type {import('sequelize-cli').Migration} */
|
||||
module.exports = {
|
||||
async up(queryInterface, Sequelize) {
|
||||
await queryInterface.createTable('Users', {
|
||||
id: {
|
||||
allowNull: false,
|
||||
autoIncrement: true,
|
||||
primaryKey: true,
|
||||
type: Sequelize.INTEGER
|
||||
},
|
||||
firstName: {
|
||||
allowNull: false,
|
||||
type: Sequelize.STRING
|
||||
},
|
||||
lastName: {
|
||||
allowNull: false,
|
||||
type: Sequelize.STRING
|
||||
},
|
||||
email: {
|
||||
allowNull: false,
|
||||
type: Sequelize.STRING
|
||||
},
|
||||
dateOfBirth: {
|
||||
allowNull: false,
|
||||
type: Sequelize.DATE
|
||||
},
|
||||
password: {
|
||||
allowNull: false,
|
||||
type: Sequelize.STRING
|
||||
},
|
||||
createdAt: {
|
||||
allowNull: false,
|
||||
type: Sequelize.DATE
|
||||
},
|
||||
updatedAt: {
|
||||
allowNull: false,
|
||||
type: Sequelize.DATE
|
||||
}
|
||||
});
|
||||
},
|
||||
async down(queryInterface, Sequelize) {
|
||||
await queryInterface.dropTable('Users');
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
'use strict';
|
||||
/** @type {import('sequelize-cli').Migration} */
|
||||
module.exports = {
|
||||
async up(queryInterface, Sequelize) {
|
||||
await queryInterface.createTable('Roles', {
|
||||
id: {
|
||||
allowNull: false,
|
||||
autoIncrement: true,
|
||||
primaryKey: true,
|
||||
type: Sequelize.INTEGER
|
||||
},
|
||||
name: {
|
||||
type: Sequelize.STRING
|
||||
},
|
||||
createdAt: {
|
||||
allowNull: false,
|
||||
type: Sequelize.DATE
|
||||
},
|
||||
updatedAt: {
|
||||
allowNull: false,
|
||||
type: Sequelize.DATE
|
||||
}
|
||||
});
|
||||
},
|
||||
async down(queryInterface, Sequelize) {
|
||||
await queryInterface.dropTable('Roles');
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
'use strict';
|
||||
|
||||
/** @type {import('sequelize-cli').Migration} */
|
||||
module.exports = {
|
||||
async up (queryInterface, Sequelize) {
|
||||
await queryInterface.createTable('UserRoles', {
|
||||
id: {
|
||||
allowNull: false,
|
||||
autoIncrement: true,
|
||||
primaryKey: true,
|
||||
type: Sequelize.INTEGER
|
||||
},
|
||||
userId: {
|
||||
type: Sequelize.INTEGER,
|
||||
references: {
|
||||
model: 'Users',
|
||||
key: 'id'
|
||||
},
|
||||
onUpdate: 'CASCADE',
|
||||
onDelete: 'CASCADE'
|
||||
},
|
||||
roleId: {
|
||||
type: Sequelize.INTEGER,
|
||||
references: {
|
||||
model: 'Roles',
|
||||
key: 'id'
|
||||
},
|
||||
onUpdate: 'CASCADE',
|
||||
onDelete: 'CASCADE'
|
||||
},
|
||||
createdAt: {
|
||||
allowNull: false,
|
||||
type: Sequelize.DATE
|
||||
},
|
||||
updatedAt: {
|
||||
allowNull: false,
|
||||
type: Sequelize.DATE
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
async down (queryInterface, Sequelize) {
|
||||
await queryInterface.dropTable('UserRoles');
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
'use strict';
|
||||
/** @type {import('sequelize-cli').Migration} */
|
||||
module.exports = {
|
||||
async up(queryInterface, Sequelize) {
|
||||
await queryInterface.createTable('RefreshTokens', {
|
||||
id: {
|
||||
allowNull: false,
|
||||
autoIncrement: true,
|
||||
primaryKey: true,
|
||||
type: Sequelize.INTEGER
|
||||
},
|
||||
token: {
|
||||
type: Sequelize.STRING
|
||||
},
|
||||
userId: {
|
||||
type: Sequelize.INTEGER,
|
||||
references: {
|
||||
model: 'Users',
|
||||
key: 'id'
|
||||
},
|
||||
onUpdate: 'CASCADE',
|
||||
onDelete: 'CASCADE'
|
||||
},
|
||||
expiryDate: {
|
||||
type: Sequelize.DATE
|
||||
},
|
||||
createdAt: {
|
||||
allowNull: false,
|
||||
type: Sequelize.DATE
|
||||
},
|
||||
updatedAt: {
|
||||
allowNull: false,
|
||||
type: Sequelize.DATE
|
||||
}
|
||||
});
|
||||
},
|
||||
async down(queryInterface, Sequelize) {
|
||||
await queryInterface.dropTable('RefreshTokens');
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
'use strict';
|
||||
/** @type {import('sequelize-cli').Migration} */
|
||||
module.exports = {
|
||||
async up(queryInterface, Sequelize) {
|
||||
await queryInterface.createTable('PitchTypes', {
|
||||
id: {
|
||||
allowNull: false,
|
||||
autoIncrement: true,
|
||||
primaryKey: true,
|
||||
type: Sequelize.INTEGER
|
||||
},
|
||||
name: {
|
||||
type: Sequelize.STRING
|
||||
},
|
||||
abbreviation: {
|
||||
type: Sequelize.STRING
|
||||
},
|
||||
createdAt: {
|
||||
allowNull: false,
|
||||
type: Sequelize.DATE
|
||||
},
|
||||
updatedAt: {
|
||||
allowNull: false,
|
||||
type: Sequelize.DATE
|
||||
}
|
||||
});
|
||||
},
|
||||
async down(queryInterface, Sequelize) {
|
||||
await queryInterface.dropTable('PitchTypes');
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
'use strict';
|
||||
/** @type {import('sequelize-cli').Migration} */
|
||||
module.exports = {
|
||||
async up(queryInterface, Sequelize) {
|
||||
await queryInterface.createTable('BullpenSessions', {
|
||||
id: {
|
||||
allowNull: false,
|
||||
autoIncrement: true,
|
||||
primaryKey: true,
|
||||
type: Sequelize.INTEGER
|
||||
},
|
||||
pitcherId: {
|
||||
type: Sequelize.INTEGER
|
||||
},
|
||||
startedAt: {
|
||||
type: Sequelize.DATE
|
||||
},
|
||||
finishedAt: {
|
||||
type: Sequelize.DATE
|
||||
},
|
||||
createdAt: {
|
||||
allowNull: false,
|
||||
type: Sequelize.DATE
|
||||
},
|
||||
updatedAt: {
|
||||
allowNull: false,
|
||||
type: Sequelize.DATE
|
||||
}
|
||||
});
|
||||
},
|
||||
async down(queryInterface, Sequelize) {
|
||||
await queryInterface.dropTable('BullpenSessions');
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
'use strict';
|
||||
/** @type {import('sequelize-cli').Migration} */
|
||||
module.exports = {
|
||||
async up(queryInterface, Sequelize) {
|
||||
await queryInterface.createTable('Pitches', {
|
||||
id: {
|
||||
allowNull: false,
|
||||
autoIncrement: true,
|
||||
primaryKey: true,
|
||||
type: Sequelize.INTEGER
|
||||
},
|
||||
pitchTypeId: {
|
||||
type: Sequelize.INTEGER,
|
||||
references: {
|
||||
model: 'PitchTypes',
|
||||
key: 'id'
|
||||
},
|
||||
onUpdate: 'CASCADE',
|
||||
onDelete: 'CASCADE'
|
||||
},
|
||||
bullpenSessionId: {
|
||||
type: Sequelize.INTEGER,
|
||||
references: {
|
||||
model: 'BullpenSessions',
|
||||
key: 'id'
|
||||
},
|
||||
onUpdate: 'CASCADE',
|
||||
onDelete: 'CASCADE'
|
||||
},
|
||||
aimedArea: {
|
||||
type: Sequelize.INTEGER
|
||||
},
|
||||
hitArea: {
|
||||
type: Sequelize.INTEGER
|
||||
},
|
||||
createdAt: {
|
||||
allowNull: false,
|
||||
type: Sequelize.DATE
|
||||
},
|
||||
updatedAt: {
|
||||
allowNull: false,
|
||||
type: Sequelize.DATE
|
||||
}
|
||||
});
|
||||
},
|
||||
async down(queryInterface, Sequelize) {
|
||||
await queryInterface.dropTable('Pitches');
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
'use strict';
|
||||
const {
|
||||
Model
|
||||
} = require('sequelize');
|
||||
module.exports = (sequelize, DataTypes) => {
|
||||
class BullpenSession extends Model {
|
||||
/**
|
||||
* Helper method for defining associations.
|
||||
* This method is not a part of Sequelize lifecycle.
|
||||
* The `models/index` file will call this method automatically.
|
||||
*/
|
||||
static associate(models) {
|
||||
BullpenSession.hasMany(models.Pitch, {
|
||||
foreignKey: 'bullpenSessionId',
|
||||
as: 'pitches',
|
||||
});
|
||||
BullpenSession.belongsTo(models.User, {
|
||||
as: "pitcher"
|
||||
});
|
||||
}
|
||||
}
|
||||
BullpenSession.init({
|
||||
pitcherId: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
references: {
|
||||
model: 'User',
|
||||
key: 'id'
|
||||
}
|
||||
},
|
||||
startedAt: {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: false,
|
||||
},
|
||||
finishedAt: {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: false,
|
||||
}
|
||||
}, {
|
||||
sequelize,
|
||||
modelName: 'BullpenSession',
|
||||
tableName: 'BullpenSessions'
|
||||
});
|
||||
return BullpenSession;
|
||||
};
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const Sequelize = require('sequelize');
|
||||
const process = require('process');
|
||||
const basename = path.basename(__filename);
|
||||
const env = process.env.NODE_ENV || 'development';
|
||||
const config = require(__dirname + '/../config/config.js')[env];
|
||||
const db = {};
|
||||
|
||||
let sequelize;
|
||||
if (config.use_env_variable) {
|
||||
sequelize = new Sequelize(process.env[config.use_env_variable], config);
|
||||
} else {
|
||||
sequelize = new Sequelize(config.database, config.username, config.password, config);
|
||||
}
|
||||
|
||||
fs
|
||||
.readdirSync(__dirname)
|
||||
.filter(file => {
|
||||
return (
|
||||
file.indexOf('.') !== 0 &&
|
||||
file !== basename &&
|
||||
file.slice(-3) === '.js' &&
|
||||
file.indexOf('.test.js') === -1
|
||||
);
|
||||
})
|
||||
.forEach(file => {
|
||||
const model = require(path.join(__dirname, file))(sequelize, Sequelize.DataTypes);
|
||||
db[model.name] = model;
|
||||
});
|
||||
|
||||
Object.keys(db).forEach(modelName => {
|
||||
if (db[modelName].associate) {
|
||||
db[modelName].associate(db);
|
||||
}
|
||||
});
|
||||
|
||||
db.sequelize = sequelize;
|
||||
db.Sequelize = Sequelize;
|
||||
|
||||
module.exports = db;
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
'use strict';
|
||||
const {
|
||||
Model, DataTypes
|
||||
} = require('sequelize');
|
||||
module.exports = (sequelize, DataTypes) => {
|
||||
class Pitch extends Model {
|
||||
/**
|
||||
* Helper method for defining associations.
|
||||
* This method is not a part of Sequelize lifecycle.
|
||||
* The `models/index` file will call this method automatically.
|
||||
*/
|
||||
static associate(models) {
|
||||
Pitch.belongsTo(models.BullpenSession, {
|
||||
foreignKey: 'bullpenSessionId',
|
||||
as: 'bullpenSession'
|
||||
});
|
||||
Pitch.belongsTo(models.PitchType);
|
||||
}
|
||||
}
|
||||
Pitch.init({
|
||||
pitchTypeId: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
references: { // User belongsTo PitchType 1:1
|
||||
model: 'PitchTypes',
|
||||
key: 'id'
|
||||
}
|
||||
},
|
||||
bullpenSessionId: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
references: {
|
||||
model: 'BullpenSessions',
|
||||
key: 'id'
|
||||
}
|
||||
},
|
||||
aimedArea: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false
|
||||
},
|
||||
hitArea: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false
|
||||
}
|
||||
}, {
|
||||
sequelize,
|
||||
modelName: 'Pitch',
|
||||
tableName: 'Pitches'
|
||||
});
|
||||
return Pitch;
|
||||
};
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
'use strict';
|
||||
const { Model } = require('sequelize');
|
||||
module.exports = (sequelize, DataTypes) => {
|
||||
class PitchType extends Model {
|
||||
/**
|
||||
* Helper method for defining associations.
|
||||
* This method is not a part of Sequelize lifecycle.
|
||||
* The `models/index` file will call this method automatically.
|
||||
*/
|
||||
static associate(models) {
|
||||
// define association here
|
||||
}
|
||||
}
|
||||
PitchType.init({
|
||||
name: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
unique: true
|
||||
},
|
||||
abbreviation: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false
|
||||
}
|
||||
}, {
|
||||
sequelize,
|
||||
modelName: 'PitchType',
|
||||
tableName: 'PitchTypes'
|
||||
});
|
||||
return PitchType;
|
||||
};
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
const { Model } = require('sequelize');
|
||||
const config = require("../config/auth.config");
|
||||
const {v4: uuidv4} = require("uuid");
|
||||
|
||||
module.exports = (sequelize, DataTypes) => {
|
||||
class RefreshToken extends Model {
|
||||
/**
|
||||
* Helper method for defining associations.
|
||||
* This method is not a part of Sequelize lifecycle.
|
||||
* The `models/index` file will call this method automatically.
|
||||
*/
|
||||
static associate(models) {
|
||||
RefreshToken.belongsTo(models.User, {
|
||||
foreignKey: 'userId',
|
||||
targetKey: 'id'
|
||||
});
|
||||
}
|
||||
}
|
||||
RefreshToken.init({
|
||||
token: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
expiryDate: {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: false,
|
||||
},
|
||||
}, {
|
||||
sequelize,
|
||||
modelName: 'RefreshToken',
|
||||
tableName: 'RefreshTokens'
|
||||
});
|
||||
|
||||
RefreshToken.createToken = async function (user) {
|
||||
let expiredAt = new Date();
|
||||
|
||||
expiredAt.setSeconds(expiredAt.getSeconds() + config.jwtRefreshExpiration);
|
||||
|
||||
let _token = uuidv4();
|
||||
|
||||
let refreshToken = await this.create({
|
||||
token: _token,
|
||||
userId: user.id,
|
||||
expiryDate: expiredAt.getTime(),
|
||||
});
|
||||
|
||||
return refreshToken.token;
|
||||
};
|
||||
|
||||
RefreshToken.verifyExpiration = (token) => {
|
||||
return token.expiryDate.getTime() < new Date().getTime();
|
||||
};
|
||||
|
||||
return RefreshToken;
|
||||
};
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
const { Model } = require('sequelize');
|
||||
module.exports = (sequelize, DataTypes) => {
|
||||
class Role extends Model {
|
||||
/**
|
||||
* Helper method for defining associations.
|
||||
* This method is not a part of Sequelize lifecycle.
|
||||
* The `models/index` file will call this method automatically.
|
||||
*/
|
||||
static associate(models) {
|
||||
Role.belongsToMany(models.User, {
|
||||
through: "UserRoles",
|
||||
foreignKey: 'roleId',
|
||||
as: 'users',
|
||||
});
|
||||
}
|
||||
}
|
||||
Role.init({
|
||||
name: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
}
|
||||
}, {
|
||||
sequelize,
|
||||
modelName: 'Role',
|
||||
tableName: "Roles",
|
||||
});
|
||||
return Role;
|
||||
};
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
const { Model } = require('sequelize');
|
||||
const bcrypt = require("bcryptjs");
|
||||
|
||||
module.exports = (sequelize, DataTypes) => {
|
||||
class User extends Model {
|
||||
/**
|
||||
* Helper method for defining associations.
|
||||
* This method is not a part of Sequelize lifecycle.
|
||||
* The `models/index` file will call this method automatically.
|
||||
*/
|
||||
static associate(models) {
|
||||
User.belongsToMany(models.Role, {
|
||||
through: "UserRoles",
|
||||
foreignKey: 'userId',
|
||||
as: 'roles',
|
||||
});
|
||||
User.hasOne(models.RefreshToken, {
|
||||
foreignKey: 'userId',
|
||||
targetKey: 'id'
|
||||
});
|
||||
}
|
||||
}
|
||||
User.init({
|
||||
firstName: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false
|
||||
},
|
||||
lastName: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false
|
||||
},
|
||||
dateOfBirth: {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: false
|
||||
},
|
||||
email: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
unique: true
|
||||
},
|
||||
password: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false
|
||||
}
|
||||
}, {
|
||||
sequelize,
|
||||
modelName: 'User',
|
||||
tableName: "Users",
|
||||
hooks: {
|
||||
beforeCreate: async (user) => {
|
||||
const salt = await bcrypt.genSalt(10);
|
||||
user.password = await bcrypt.hash(user.password, salt);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
User.prototype.validPassword = function (password) {
|
||||
return bcrypt.compareSync(password, this.password);
|
||||
};
|
||||
|
||||
return User;
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -8,8 +8,7 @@
|
|||
"db:create:test": "cross-env NODE_ENV=test npx sequelize-cli db:create",
|
||||
"db:reset": "npx sequelize-cli db:drop && npx sequelize-cli db:create && npx sequelize-cli db:migrate && npx sequelize-cli db:seed:all --debug",
|
||||
"setup-db": "npx sequelize-cli db:drop && npx sequelize-cli db:create && npx sequelize-cli db:migrate && npx sequelize-cli db:seed:all --debug",
|
||||
"start": "cross-env NODE_ENV=test node server.js",
|
||||
"setup-example-db": "node database/setup.js"
|
||||
"start": "cross-env NODE_ENV=test node server.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
|
|
@ -26,6 +25,7 @@
|
|||
"jsonwebtoken": "^9.0.2",
|
||||
"pg": "^8.13.3",
|
||||
"pg-hstore": "^2.3.4",
|
||||
"process": "^0.11.10",
|
||||
"sequelize": "^6.37.5",
|
||||
"sqlite3": "^5.1.7",
|
||||
"uuid": "^11.1.0"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
'use strict';
|
||||
|
||||
/** @type {import('sequelize-cli').Migration} */
|
||||
module.exports = {
|
||||
async up (queryInterface, Sequelize) {
|
||||
return queryInterface.bulkInsert('Roles', [{
|
||||
id: 1,
|
||||
name: 'user',
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
}, {
|
||||
id: 2,
|
||||
name: 'administrator',
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date()
|
||||
}]);
|
||||
},
|
||||
|
||||
async down (queryInterface, Sequelize) {
|
||||
/**
|
||||
* Add commands to revert seed here.
|
||||
*
|
||||
* Example:
|
||||
* await queryInterface.bulkDelete('People', null, {});
|
||||
*/
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
const bcrypt = require("bcryptjs");
|
||||
|
||||
/** @type {import('sequelize-cli').Migration} */
|
||||
module.exports = {
|
||||
async up (queryInterface, Sequelize) {
|
||||
return queryInterface.bulkInsert('Users', [{
|
||||
firstName: 'Nolan',
|
||||
lastName: 'Ryan',
|
||||
dateOfBirth: new Date(1947, 1, 31),
|
||||
email: 'ryan.nolan@bullpen.com',
|
||||
password: bcrypt.hashSync('nolan', 8),
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
}]);
|
||||
},
|
||||
|
||||
async down (queryInterface, Sequelize) {
|
||||
/**
|
||||
* Add commands to revert seed here.
|
||||
*
|
||||
* Example:
|
||||
* await queryInterface.bulkDelete('People', null, {});
|
||||
*/
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
'use strict';
|
||||
|
||||
/** @type {import('sequelize-cli').Migration} */
|
||||
module.exports = {
|
||||
async up (queryInterface, Sequelize) {
|
||||
/**
|
||||
* Add seed commands here.
|
||||
*
|
||||
* Example:
|
||||
* await queryInterface.bulkInsert('People', [{
|
||||
* name: 'John Doe',
|
||||
* isBetaMember: false
|
||||
* }], {});
|
||||
*/
|
||||
},
|
||||
|
||||
async down (queryInterface, Sequelize) {
|
||||
/**
|
||||
* Add commands to revert seed here.
|
||||
*
|
||||
* Example:
|
||||
* await queryInterface.bulkDelete('People', null, {});
|
||||
*/
|
||||
}
|
||||
};
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
const app = require('./src/index');
|
||||
const app = require('./app');
|
||||
|
||||
// set port, listen for requests
|
||||
const PORT = process.env.PORT || 8080;
|
||||
|
|
|
|||
|
|
@ -1,13 +0,0 @@
|
|||
module.exports = {
|
||||
HOST: "localhost",
|
||||
USER: "postgres",
|
||||
PASSWORD: "123",
|
||||
DB: "testdb",
|
||||
dialect: "postgres",
|
||||
pool: {
|
||||
max: 5,
|
||||
min: 0,
|
||||
acquire: 30000,
|
||||
idle: 10000
|
||||
}
|
||||
};
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
module.exports = {
|
||||
dialect: 'sqlite',
|
||||
storage: 'database/example-db.sqlite',
|
||||
logQueryParameters: false,
|
||||
benchmark: false
|
||||
}
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
const { DataTypes } = require('sequelize');
|
||||
|
||||
module.exports = (sequelize) => {
|
||||
const BullpenSession = sequelize.define('bullpenSession', {
|
||||
id: {
|
||||
allowNull: false,
|
||||
autoIncrement: true,
|
||||
primaryKey: true,
|
||||
type: DataTypes.INTEGER
|
||||
},
|
||||
// pitcher: {
|
||||
// type: DataTypes.INTEGER,
|
||||
// allowNull: false,
|
||||
// references: {
|
||||
// model: 'User',
|
||||
// key: 'id'
|
||||
// }
|
||||
// },
|
||||
aimedArea: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false
|
||||
},
|
||||
hitArea: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false
|
||||
}
|
||||
}, {
|
||||
sequelize,
|
||||
modelName: 'BullpenSession',
|
||||
tableName: 'BullpenSessions',
|
||||
});
|
||||
|
||||
return BullpenSession;
|
||||
};
|
||||
|
|
@ -1,67 +0,0 @@
|
|||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const env = process.env.NODE_ENV || 'development';
|
||||
let config = require(path.join(__dirname, '../../config/config.js'))[env];
|
||||
if (!config) config = require(path.join(__dirname, '../../config/config.js')).development;
|
||||
|
||||
const Sequelize = require("sequelize");
|
||||
// const sequelize = new Sequelize({
|
||||
// dialect: config.dialect,
|
||||
// storage: config.storage,
|
||||
// logQueryParameters: config.logQueryParameters,
|
||||
// benchmark: config.benchmark
|
||||
// });
|
||||
|
||||
const sequelize = new Sequelize(
|
||||
config.database,
|
||||
config.username,
|
||||
config.password,
|
||||
config
|
||||
// {
|
||||
// host: config.HOST,
|
||||
// dialect: config.dialect,
|
||||
// pool: {
|
||||
// max: config.pool.max,
|
||||
// min: config.pool.min,
|
||||
// acquire: config.pool.acquire,
|
||||
// idle: config.pool.idle
|
||||
// }
|
||||
// }
|
||||
);
|
||||
|
||||
const db = {};
|
||||
|
||||
db.Sequelize = Sequelize;
|
||||
db.sequelize = sequelize;
|
||||
|
||||
db.user = require("./user.model.js")(sequelize);
|
||||
db.role = require("./role.model.js")(sequelize);
|
||||
db.pitchType = require("./pitchType.model.js")(sequelize);
|
||||
db.pitch = require("./pitch.model.js")(sequelize);
|
||||
db.bullpenSession = require("./bullpenSession.model.js")(sequelize);
|
||||
db.refreshToken = require("./refreshToken.model.js")(sequelize);
|
||||
|
||||
db.role.belongsToMany(db.user, {
|
||||
through: "UserRoles"
|
||||
});
|
||||
db.user.belongsToMany(db.role, {
|
||||
through: "UserRoles"
|
||||
});
|
||||
db.refreshToken.belongsTo(db.user, {
|
||||
foreignKey: 'userId', targetKey: 'id'
|
||||
});
|
||||
db.user.hasOne(db.refreshToken, {
|
||||
foreignKey: 'userId', targetKey: 'id'
|
||||
});
|
||||
|
||||
db.ROLES = ["user", "admin", "moderator"];
|
||||
|
||||
db.bullpenSession.hasMany(db.pitch);
|
||||
db.bullpenSession.belongsTo(db.user, {
|
||||
as: "pitcher"
|
||||
});
|
||||
db.pitch.belongsTo(db.bullpenSession);
|
||||
db.pitch.belongsTo(db.pitchType);
|
||||
|
||||
module.exports = db;
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
const { DataTypes } = require('sequelize');
|
||||
|
||||
module.exports = (sequelize) => {
|
||||
const Pitch = sequelize.define('pitch', {
|
||||
id: {
|
||||
allowNull: false,
|
||||
autoIncrement: true,
|
||||
primaryKey: true,
|
||||
type: DataTypes.INTEGER
|
||||
},
|
||||
type: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
references: { // User belongsTo PitchType 1:1
|
||||
model: 'PitchTypes',
|
||||
key: 'id'
|
||||
}
|
||||
},
|
||||
bullpenSessionId: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
references: { // User belongsTo PitchType 1:1
|
||||
model: 'BullpenSessions',
|
||||
key: 'id'
|
||||
}
|
||||
},
|
||||
aimedArea: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false
|
||||
},
|
||||
hitArea: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false
|
||||
}
|
||||
}, {
|
||||
sequelize,
|
||||
modelName: 'Pitch',
|
||||
tableName: 'Pitches',
|
||||
});
|
||||
|
||||
return Pitch;
|
||||
};
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
const { DataTypes } = require('sequelize');
|
||||
|
||||
module.exports = (sequelize) => {
|
||||
const PitchType = sequelize.define('pitchType', {
|
||||
id: {
|
||||
allowNull: false,
|
||||
autoIncrement: true,
|
||||
primaryKey: true,
|
||||
type: DataTypes.INTEGER
|
||||
},
|
||||
name: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
unique: true
|
||||
},
|
||||
abbreviation: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false
|
||||
}
|
||||
}, {
|
||||
sequelize,
|
||||
modelName: 'PitchType',
|
||||
tableName: 'PitchTypes'
|
||||
});
|
||||
|
||||
return PitchType;
|
||||
};
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
const { DataTypes } = require('sequelize');
|
||||
const config = require("../config/auth.config");
|
||||
const { v4: uuidv4 } = require("uuid");
|
||||
|
||||
module.exports = (sequelize) => {
|
||||
const RefreshToken = sequelize.define("refreshToken", {
|
||||
token: {
|
||||
type: DataTypes.STRING,
|
||||
},
|
||||
expiryDate: {
|
||||
type: DataTypes.DATE,
|
||||
},
|
||||
}, {
|
||||
sequelize,
|
||||
modelName: 'RefreshToken',
|
||||
tableName: 'RefreshTokens',
|
||||
});
|
||||
|
||||
RefreshToken.createToken = async function (user) {
|
||||
let expiredAt = new Date();
|
||||
|
||||
expiredAt.setSeconds(expiredAt.getSeconds() + config.jwtRefreshExpiration);
|
||||
|
||||
let _token = uuidv4();
|
||||
|
||||
let refreshToken = await this.create({
|
||||
token: _token,
|
||||
userId: user.id,
|
||||
expiryDate: expiredAt.getTime(),
|
||||
});
|
||||
|
||||
return refreshToken.token;
|
||||
};
|
||||
|
||||
RefreshToken.verifyExpiration = (token) => {
|
||||
return token.expiryDate.getTime() < new Date().getTime();
|
||||
};
|
||||
|
||||
return RefreshToken;
|
||||
};
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
const { DataTypes } = require('sequelize');
|
||||
|
||||
module.exports = (sequelize) => {
|
||||
const Role = sequelize.define("role", {
|
||||
id: {
|
||||
allowNull: false,
|
||||
autoIncrement: true,
|
||||
type: DataTypes.INTEGER,
|
||||
primaryKey: true
|
||||
},
|
||||
name: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
}
|
||||
}, {
|
||||
sequelize,
|
||||
modelName: 'Role',
|
||||
tableName: "Roles",
|
||||
});
|
||||
|
||||
return Role;
|
||||
};
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
const bcrypt = require('bcryptjs');
|
||||
const { DataTypes } = require('sequelize');
|
||||
|
||||
module.exports = (sequelize) => {
|
||||
const User = sequelize.define("user", {
|
||||
id: {
|
||||
allowNull: false,
|
||||
autoIncrement: true,
|
||||
primaryKey: true,
|
||||
type: DataTypes.INTEGER
|
||||
},
|
||||
firstName: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false
|
||||
},
|
||||
lastName: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false
|
||||
},
|
||||
dateOfBirth: {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: false
|
||||
},
|
||||
email: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
unique: true
|
||||
},
|
||||
password: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false
|
||||
}
|
||||
}, {
|
||||
sequelize,
|
||||
modelName: 'User',
|
||||
tableName: "Users",
|
||||
hooks: {
|
||||
beforeCreate: async (user) => {
|
||||
const salt = await bcrypt.genSalt(10);
|
||||
user.password = await bcrypt.hash(user.password, salt);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
User.prototype.validPassword = function (password) {
|
||||
return bcrypt.compareSync(password, this.password);
|
||||
};
|
||||
|
||||
return User;
|
||||
};
|
||||
|
|
@ -6,4 +6,6 @@ const signupUser = {
|
|||
password: "secret123"
|
||||
}
|
||||
|
||||
exports.signupUser = signupUser;
|
||||
module.exports = {
|
||||
signupUser
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,64 +7,44 @@ const {
|
|||
afterAll,
|
||||
} = require('@jest/globals');
|
||||
|
||||
const app = require("../src/index")
|
||||
|
||||
const db = require("../src/models");
|
||||
const app = require("../app")
|
||||
|
||||
const { signupUser } = require("./data/user.test.data")
|
||||
const res = require("express/lib/response");
|
||||
|
||||
describe("Test the root path", () => {
|
||||
beforeAll(() => {
|
||||
db.sequelize.sync({force: true}).then(() => {
|
||||
db.role.bulkCreate([
|
||||
{ name: 'user' },
|
||||
{ name: 'moderator' },
|
||||
{ name: 'administrator' },
|
||||
]);
|
||||
})
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
try {
|
||||
await db.sequelize.close();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
|
||||
test("It should response the GET method", done => {
|
||||
request(app)
|
||||
.get("/")
|
||||
.then(response => {
|
||||
expect(response.statusCode).toBe(200);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe("Test user authentication", () => {
|
||||
test("should signup a user", done => {
|
||||
request(app)
|
||||
.post("/api/auth/signup")
|
||||
.send(signupUser)
|
||||
.then( res => {
|
||||
console.log(JSON.stringify(res, null, 2));
|
||||
expect(res.header['content-type']).toBe('application/json; charset=utf-8');
|
||||
expect(res.statusCode).toBe(400);
|
||||
expect(res.statusCode).toBe(200);
|
||||
done();
|
||||
}, error => { done(); })
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
// describe("GET /api/auth/signup", () => {
|
||||
// it("should signup a user", async () => {
|
||||
// return request(app)
|
||||
// .post("/api/auth/signup")
|
||||
// .send(signupUser)
|
||||
// .expect('Content-Type', /json/)
|
||||
// .expect(200)
|
||||
// .then((res) => {
|
||||
// expect(res.statusCode).toBe(200);
|
||||
test("Test user login", done => {
|
||||
let user = {};
|
||||
request(app)
|
||||
.post("/api/auth/signin")
|
||||
.send({
|
||||
email: 'ryan.nolan@bullpen.com',
|
||||
password: 'nolan'
|
||||
})
|
||||
.then( res => {
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(res.body.accessToken).not.toBeNull();
|
||||
console.log(res.body);
|
||||
user = res.body;
|
||||
done();
|
||||
// }).then(() => {
|
||||
// request(app)
|
||||
// .get(`/api/users/${user.id}`)
|
||||
// .then( res2 => {
|
||||
// expect(res2.statusCode).toBe(200);
|
||||
// })
|
||||
// });
|
||||
// });
|
||||
//
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in New Issue