Compare commits

..

3 Commits

32 changed files with 511 additions and 73 deletions

View File

@ -15,6 +15,8 @@ require('./routes/info.routes')(app);
require('./routes/auth.routes')(app);
require('./routes/user.routes')(app);
require('./routes/player.routes')(app);
require('./routes/team.routes')(app);
require('./routes/position.routes')(app);
require('./routes/pitchType.routes')(app);
require('./routes/bullpenSession.routes')(app);

View File

@ -3,8 +3,23 @@ const {registerUser} = require("../helper/user.helper");
const Player = db.Player;
const User = db.User;
const Auth = db.Auth;
const Team = db.Team;
const Op = db.Sequelize.Op;
const includeParams = [{
model: User,
as: 'user',
include: {
model: Auth,
as: 'auth',
attributes: ['email']
}
}, {
model: Team,
as: 'teams',
attributes: ['name']
}];
exports.insert = (req, res) => {
registerUser(req.body.user).then(user => {
return Player.create({
@ -20,7 +35,9 @@ exports.insert = (req, res) => {
jerseyNumber: req.body.jerseyNumber,
userId: user.id
}).then(player => {
return player.reload({ include: { model: User }});
return player.reload({
include: includeParams
});
});
}).then(player => {
res.status(201).send(player);
@ -35,7 +52,7 @@ exports.findAll = (req, res) => {
Player.findAll({
where: condition,
include: ['user']
include: includeParams
}).then(data => {
res.send(data);
}).catch(err => {
@ -47,15 +64,7 @@ exports.findOne = (req, res) => {
const id = req.params.id;
Player.findByPk(id, {
include: {
model: User,
as: 'user',
include: {
model: Auth,
as: 'auth',
attributes: ['email']
}
}
include: includeParams
}).then(data => {
if (data) {
res.send(data);
@ -72,15 +81,7 @@ exports.findOneByUserId = (req, res) => {
Player.findOne({
where: { userId: userId },
include: {
model: User,
as: 'user',
include: {
model: Auth,
as: 'auth',
attributes: ['email']
}
}
include: includeParams
}).then(data => {
if (data) {
res.send(data);

View File

@ -0,0 +1,35 @@
const db = require("../models/index");
const Position = db.Position;
exports.findAll = (req, res) => {
Position.findAll()
.then(data => {
res.send(data);
})
.catch(err => {
res.status(500).send({
message:
err.message || "Some error occurred while retrieving pitch types."
});
});
};
exports.findOne = (req, res) => {
const id = req.params.id;
Position.findByPk(id)
.then(data => {
if (data) {
res.send(data);
} else {
res.status(404).send({
message: `Cannot find pitch type with id=${id}.`
});
}
})
.catch(err => {
res.status(500).send({
message: `Error retrieving pitch type with id=${id}: ${err.message}`
});
});
};

View File

@ -1,8 +1,17 @@
const db = require("../models/index");
const Team = db.Team;
const Player = db.Player;
const Op = db.Sequelize.Op;
exports.insert = (req, res) => {
Team.create(req.body)
.then(team => {
return res.status(201).send(team);
})
.catch(err => {
res.status(500).send({ message: err.message });
});
}
exports.findAll = (req, res) => {
const title = req.query.title;
const condition = title ? {title: {[Op.iLike]: `%${title}%`}} : null;
@ -34,8 +43,48 @@ exports.findOne = (req, res) => {
});
};
exports.addPlayer = (req, res) => {
const id = req.params.id;
const playerId = req.params.playerId;
exports.addPlayer = async (req, res) => {
const teamId = req.params.id;
const { playerIds } = req.body;
try {
// Fetch the team by ID
const team = await Team.findByPk(teamId);
if (!team) {
return res.status(404).send({ message: `Team with id=${teamId} not found.` });
}
// Add multiple players to the team
await team.addPlayers(playerIds);
res.status(200).send({ message: `Players added to team with id=${teamId} successfully.` });
} catch (err) {
res.status(500).send({
message: `Error adding players to team with id=${teamId}: ${err.message}`
});
}
}
exports.removePlayer = async (req, res) => {
const teamId = req.params.id;
const { playerIds } = req.body;
try {
// Fetch the team by ID
const team = await Team.findByPk(teamId);
if (!team) {
return res.status(404).send({ message: `Team with id=${teamId} not found.` });
}
// Add multiple players to the team
await team.removePlayers(playerIds);
res.status(200).send({ message: `Players added to team with id=${teamId} successfully.` });
} catch (err) {
res.status(500).send({
message: `Error adding players to team with id=${teamId}: ${err.message}`
});
}
}

View File

@ -6,8 +6,8 @@ const { Auth: Auth, User: User, Role: Role } = db;
const registerUser = (user) => {
return Auth.create({
email: user.email,
password: user.password
email: user.auth.email,
password: user.auth.password
}).then((auth) => {
return User.create({
firstName: user.firstName,

View File

@ -2,7 +2,15 @@ const db = require("./models/index");
const { beforeAll, afterAll } = require('@jest/globals');
const { Auth: Auth, User: User, Player: Player, Role: Role, PitchType: PitchType } = db;
const {
Auth: Auth,
User: User,
Player: Player,
Role: Role,
PitchType: PitchType,
Team: Team,
Position: Position
} = db;
const Op = db.Sequelize.Op;
beforeAll(async () => {
@ -31,6 +39,7 @@ beforeAll(async () => {
await Auth.destroy({ where: {} });
await User.destroy({ where: {} });
await Player.destroy({ where: {} });
await Team.destroy({ where: {} });
await Auth.create({
email: 'player@example.com', password: 'hash1234'
}).then(auth => {
@ -76,6 +85,19 @@ beforeAll(async () => {
{ name: 'Sweeper', abbreviation: 'SW' },
{ name: 'Slurve', abbreviation: 'SLV' }
]);
await Position.destroy({ where: {} });
await Position.bulkCreate([
{ name: 'Pitcher', abbreviation: 'P', description: '' },
{ name: 'Catcher', abbreviation: 'C', description: '' },
{ name: 'First Baseman', abbreviation: '1B', description: '' },
{ name: 'Second Baseman', abbreviation: '2B', description: '' },
{ name: 'Third Baseman', abbreviation: '3B', description: '' },
{ name: 'Short Stop', abbreviation: 'SS', description: '' },
{ name: 'Left Fielder', abbreviation: 'LF', description: '' },
{ name: 'Center Fielder', abbreviation: 'CF', description: '' },
{ name: 'Right Fielder', abbreviation: 'RF', description: '' },
]);
});
afterAll(async () => {

View File

@ -5,7 +5,7 @@ checkDuplicateUsernameOrEmail = (req, res, next) => {
// Username
Auth.findOne({
where: {
email: req.body.email
email: req.body.auth.email
}
}).then(auth => {
if (auth) {
@ -16,6 +16,8 @@ checkDuplicateUsernameOrEmail = (req, res, next) => {
}
next();
}).catch(err => {
return res.status(500).send({ message: err });
});
};

View File

@ -1,4 +1,3 @@
'use strict';
/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up(queryInterface, Sequelize) {
@ -22,7 +21,7 @@ module.exports = {
}
});
},
async down(queryInterface, Sequelize) {
async down(queryInterface, _Sequelize) {
await queryInterface.dropTable('Roles');
}
};

View File

@ -1,5 +1,3 @@
'use strict';
/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up (queryInterface, Sequelize) {
@ -39,7 +37,7 @@ module.exports = {
});
},
async down (queryInterface, Sequelize) {
async down (queryInterface, _Sequelize) {
await queryInterface.dropTable('UserRoles');
}
};

View File

@ -1,4 +1,3 @@
'use strict';
/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up(queryInterface, Sequelize) {

View File

@ -1,4 +1,3 @@
'use strict';
/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up(queryInterface, Sequelize) {
@ -25,7 +24,7 @@ module.exports = {
}
});
},
async down(queryInterface, Sequelize) {
async down(queryInterface, _Sequelize) {
await queryInterface.dropTable('PitchTypes');
}
};

View File

@ -1,4 +1,3 @@
'use strict';
/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up(queryInterface, Sequelize) {
@ -28,7 +27,7 @@ module.exports = {
}
});
},
async down(queryInterface, Sequelize) {
async down(queryInterface, _Sequelize) {
await queryInterface.dropTable('BullpenSessions');
}
};

View File

@ -1,4 +1,3 @@
'use strict';
/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up(queryInterface, Sequelize) {
@ -46,7 +45,7 @@ module.exports = {
}
});
},
async down(queryInterface, Sequelize) {
async down(queryInterface, _Sequelize) {
await queryInterface.dropTable('Pitches');
}
};

View File

@ -0,0 +1,37 @@
/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up(queryInterface, Sequelize) {
await queryInterface.createTable('Positions', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
name: {
type: Sequelize.STRING,
allowNull: false,
unique: true
},
abbreviation: {
type: Sequelize.STRING,
allowNull: false
},
description: {
type: Sequelize.STRING,
allowNull: false
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
async down(queryInterface, _Sequelize) {
await queryInterface.dropTable('Positions');
}
};

View File

@ -48,22 +48,6 @@ module.exports = {
type: Sequelize.DATE
}
});
// 2. Kopiere Daten von Users nach Players
await queryInterface.sequelize.query(`
INSERT INTO "Players" ("userId", "height", "weight", "gender", "bats", "throws", "createdAt", "updatedAt")
SELECT
"id" as "userId",
"height",
"weight",
"gender"::text::"enum_Players_gender",
"handedness"::text::"enum_Players_bats",
"handedness"::text::"enum_Players_throws",
"createdAt",
"updatedAt"
FROM "Users"
`);
},
async down(queryInterface, /*Sequelize*/) {
await queryInterface.dropTable('Players');

View File

@ -0,0 +1,39 @@
/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up (queryInterface, Sequelize) {
await queryInterface.createTable('PlayerPositions', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
playerId: {
type: Sequelize.INTEGER,
references: {
model: 'Players',
key: 'id'
}
},
positionId: {
type: Sequelize.INTEGER,
references: {
model: 'Positions',
key: 'id'
}
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
async down (queryInterface, _Sequelize) {
await queryInterface.dropTable('PlayerPositions');
}
};

View File

@ -0,0 +1,33 @@
/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up(queryInterface, Sequelize) {
await queryInterface.createTable('Teams', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
name: {
type: Sequelize.STRING,
allowNull: false,
unique: true
},
description: {
type: Sequelize.STRING,
allowNull: false
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
async down(queryInterface, _Sequelize) {
await queryInterface.dropTable('Teams');
}
};

View File

@ -0,0 +1,39 @@
/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up (queryInterface, Sequelize) {
await queryInterface.createTable('PlayerTeams', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
playerId: {
type: Sequelize.INTEGER,
references: {
model: 'Players',
key: 'id'
}
},
teamId: {
type: Sequelize.INTEGER,
references: {
model: 'Teams',
key: 'id'
}
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
async down (queryInterface, _Sequelize) {
await queryInterface.dropTable('PlayerTeams');
}
};

View File

@ -0,0 +1,22 @@
/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up(queryInterface, _Sequelize) {
await queryInterface.sequelize.query(`
INSERT INTO "Players" ("userId", "height", "weight", "gender", "bats", "throws", "createdAt", "updatedAt")
SELECT
"id" as "userId",
"height",
"weight",
"gender"::text::"enum_Players_gender",
"handedness"::text::"enum_Players_bats",
"handedness"::text::"enum_Players_throws",
"createdAt",
"updatedAt"
FROM "Users"
`);
},
async down(queryInterface, /*Sequelize*/) {
await queryInterface.dropTable('Players');
}
};

View File

@ -18,8 +18,10 @@ module.exports = (sequelize, DataTypes) => {
otherKey: 'teamId',
as: 'teams',
});
Player.hasMany(models.Position, {
Player.belongsToMany(models.Position, {
through: "PlayerPositions",
foreignKey: 'playerId',
otherKey: 'positionId',
as: 'positions',
})
}

View File

@ -7,7 +7,12 @@ module.exports = (sequelize, DataTypes) => {
* The `models/index` file will call this method automatically.
*/
static associate(models) {
// define association here
Position.belongsToMany(models.Player, {
through: "PlayerPositions",
foreignKey: 'positionId',
otherKey: 'playerId',
as: 'players',
});
}
}
Position.init({

View File

@ -0,0 +1,21 @@
const { authJwt } = require("../middleware");
const controller = require("../controllers/position.controller");
module.exports = function(app) {
app.use(function(req, res, next) {
res.header(
"Access-Control-Allow-Headers",
"x-access-token, Origin, Content-Type, Accept"
);
next();
});
app.get(
"/api/positions",
[authJwt.verifyToken],
controller.findAll);
app.get(
"/api/positions/:id",
[authJwt.verifyToken],
controller.findOne);
};

View File

@ -0,0 +1,33 @@
const { authJwt } = require("../middleware");
const controller = require("../controllers/team.controller");
module.exports = function(app) {
app.use(function(req, res, next) {
res.header(
"Access-Control-Allow-Headers",
"x-access-token, Origin, Content-Type, Accept"
);
next();
});
app.post(
"/api/teams",
[authJwt.verifyToken, authJwt.isCoachOrAdmin],
controller.insert);
app.get(
"/api/teams",
[authJwt.verifyToken, authJwt.isCoachOrAdmin],
controller.findAll);
app.get(
"/api/teams/:id",
[authJwt.verifyToken, authJwt.isCoachOrAdmin],
controller.findOne);
app.put(
'/api/teams/:id/add_players',
[authJwt.verifyToken, authJwt.isCoachOrAdmin],
controller.addPlayer);
app.put(
'/api/teams/:id/remove_players',
[authJwt.verifyToken, authJwt.isCoachOrAdmin],
controller.removePlayer);
};

View File

@ -2,9 +2,11 @@ const signupUser = {
firstName: "Hans",
lastName: "Zimmer",
dateOfBirth: "1956-11-23",
roles: ['admin'],
auth: {
email: "hans.zimmer@email.com",
password: "secret123",
roles: ['admin']
password: "secret123"
}
}
module.exports = {

View File

@ -46,6 +46,6 @@ describe("Test player creation, authentication and retrieval", () => {
.set('x-access-token', user.accessToken);
expect(response.header['content-type']).toBe('application/json; charset=utf-8');
expect(response.statusCode).toBe(200);
expect(response.body.filter(p => p.User.id === player.User.id).shift()).toEqual(player);
expect(response.body.filter(p => p.user.id === player.user.id).shift()).toEqual(player);
});
});

View File

@ -0,0 +1,26 @@
const request = require("supertest")
const {
expect,
describe,
test,
} = require('@jest/globals');
const app = require("../app")
describe("Test retrieving playing positions", () => {
test("should retrieve all playing positions", async () => {
let response = await request(app)
.post("/api/auth/login")
.send({
email: 'player@example.com',
password: 'hash1234'
});
const user = response.body;
response = await request(app)
.get('/api/positions')
.set('x-access-token', user.accessToken);
expect(response.statusCode).toBe(200);
expect(response.body.length).toEqual(9);
});
});

91
backend/test/team.test.js Normal file
View File

@ -0,0 +1,91 @@
const request = require("supertest")
const {
expect,
describe,
test,
} = require('@jest/globals');
const app = require("../app")
describe("Test team creation, adding and removing player", () => {
test("should create a team", async () => {
let response = await request(app)
.post("/api/auth/login")
.send({
email: 'coach@example.com',
password: 'hash1234'
});
const user = response.body;
response = await request(app)
.post("/api/teams")
.set('x-access-token', user.accessToken)
.send({
name: "Team 1",
description: "Team 1 description"
});
expect(response.statusCode).toBe(201);
const teamId = response.body.id;
const teamName = response.body.name;
response = await request(app)
.get('/api/teams')
.set('x-access-token', user.accessToken);
expect(response.statusCode).toBe(200);
expect(response.body.length).toBeGreaterThan(0);
expect(response.body.filter(t => t.id === teamId).shift()).toBeDefined();
response = await request(app)
.get('/api/players')
.set('x-access-token', user.accessToken);
expect(response.statusCode).toBe(200);
expect(response.body.length).toBe(1);
const playerId = response.body[0].id;
// Add player to team
response = await request(app)
.put(`/api/teams/${teamId}/add_players`)
.set('x-access-token', user.accessToken)
.send({
playerIds: [playerId]
});
expect(response.statusCode).toBe(200);
response = await request(app)
.get('/api/teams')
.set('x-access-token', user.accessToken);
expect(response.statusCode).toBe(200);
expect(response.body.length).toBeGreaterThan(0);
expect(response.body.filter(t => t.id === teamId).shift()).toBeDefined();
response = await request(app)
.get(`/api/players/${playerId}`)
.set('x-access-token', user.accessToken);
expect(response.statusCode).toBe(200);
expect(response.body.teams.length).toBe(1);
expect(response.body.teams[0].name).toBe(teamName);
// Remove player from team
response = await request(app)
.put(`/api/teams/${teamId}/remove_players`)
.set('x-access-token', user.accessToken)
.send({
playerIds: [playerId]
});
expect(response.statusCode).toBe(200);
response = await request(app)
.get(`/api/teams/${teamId}`)
.set('x-access-token', user.accessToken);
expect(response.statusCode).toBe(200);
expect(response.body.players.length).toBe(0);
response = await request(app)
.get(`/api/players/${playerId}`)
.set('x-access-token', user.accessToken);
expect(response.statusCode).toBe(200);
expect(response.body.teams.length).toBe(0);
});
});

View File

@ -22,8 +22,8 @@ describe("Test user authentication", () => {
let response = await request(app)
.post("/api/auth/login")
.send({
email: signupUser.email,
password: signupUser.password,
email: signupUser.auth.email,
password: signupUser.auth.password,
});
expect(response.statusCode).toBe(200);
expect(response.body.accessToken).not.toBeNull();