Skip to content

Commit

Permalink
Merge branch 'dev' into CHE-113/story/Profiles-Improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
brok3turtl3 committed Aug 9, 2024
2 parents 0d4343e + 4224d74 commit 13a88e1
Show file tree
Hide file tree
Showing 60 changed files with 1,438 additions and 835 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
- name: Set up Node.js
uses: actions/setup-node@v2
with:
node-version: '18.17.1'
node-version: '20.14.0'
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Install Docker Compose
Expand Down
20 changes: 17 additions & 3 deletions docker-compose-test-solo.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,32 @@ services:
- client_node_modules:/usr/src/app/client/node_modules
depends_on:
- ch-mongo-test
- ch-pg-test
environment:
- JWT_SECRET=${JWT_SECRET}
- NODE_ENV=test
- JWT_SECRET=testJwtSecret
- MONGO_URI=mongodb://ch-mongo-test:27017/ch-testdb
- POSTGRES_USER=postgres
- POSTGRES_DB=ch-dev-database
- POSTGRES_PASSWORD=ch-dev
- POSTGRES_DB=ch-test-database
- POSTGRES_PASSWORD=ch-test
# suppress aws sdk v2 deprecation warning
- AWS_SDK_JS_SUPPRESS_MAINTENANCE_MODE_MESSAGE=1;
- TEST_CMD=${TEST_CMD}
- TEST_FILE=${TEST_FILE}
command: npm run ${TEST_CMD} ${TEST_FILE}

ch-pg-test:
image: postgres:16.3
container_name: ch-pg-test
environment:
- POSTGRES_USER=postgres
- POSTGRES_DB=ch-test-database
- POSTGRES_PASSWORD=ch-test
volumes:
- ./scripts/db/postgres/sql_db_init_test.sql:/docker-entrypoint-initdb.d/sql_db_init_test.sql
ports:
- '5432:5432'

ch-mongo-test:
image: mongo
container_name: ch-mongo-test
Expand Down
20 changes: 17 additions & 3 deletions docker-compose-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,30 @@ services:
- client_node_modules:/usr/src/app/client/node_modules
depends_on:
- ch-mongo-test
- ch-pg-test
environment:
- JWT_SECRET=${JWT_SECRET}
- NODE_ENV=test
- JWT_SECRET=testJwtSecret
- MONGO_URI=mongodb://ch-mongo-test:27017/ch-testdb
- POSTGRES_USER=postgres
- POSTGRES_DB=ch-dev-database
- POSTGRES_PASSWORD=ch-dev
- POSTGRES_DB=ch-test-database
- POSTGRES_PASSWORD=ch-test
# suppress aws sdk v2 deprecation warning
- AWS_SDK_JS_SUPPRESS_MAINTENANCE_MODE_MESSAGE=1;
command: npm run test:all

ch-pg-test:
image: postgres:16.3
container_name: ch-pg-test
environment:
- POSTGRES_USER=postgres
- POSTGRES_DB=ch-test-database
- POSTGRES_PASSWORD=ch-test
volumes:
- ./scripts/db/postgres/sql_db_init_test.sql:/docker-entrypoint-initdb.d/sql_db_init_test.sql
ports:
- '5432:5432'

ch-mongo-test:
image: mongo
container_name: ch-mongo-test
Expand Down
75 changes: 75 additions & 0 deletions scripts/db/postgres/sql_db_init_test.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
-- DROP EXISTING TABLES
DROP TABLE IF EXISTS follow_ups;
DROP TABLE IF EXISTS applications;
DROP TABLE IF EXISTS jobs;
DROP TABLE IF EXISTS statuses;
DROP TABLE IF EXISTS follow_up_types;

-- CREATE JOBS TABLE
CREATE TABLE jobs (
id SERIAL PRIMARY KEY,
title VARCHAR(255) NOT NULL,
company VARCHAR(255) NOT NULL,
location VARCHAR(255),
description TEXT,
url VARCHAR(255),
created_at TIMESTAMPTZ DEFAULT NOW()
);

-- CREATE STATUSES TABLE
CREATE TABLE statuses (
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL
);

-- INSERT DEFAULT STATUSES
INSERT INTO statuses (name) VALUES
('Applied'),
('Phone Screen'),
('Interviewing'),
('Offer Received'),
('Rejected'),
('Withdrawn');

-- CREATE FOLLOW-UP TYPES TABLE
CREATE TABLE follow_up_types (
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL
);

-- INSERT DEFAULT FOLLOW-UP TYPES
INSERT INTO follow_up_types (name) VALUES
('After Apply'),
('After Phone Screen'),
('After Interview'),
('After Technical Interview'),
('After Offer Received'),
('After Rejection'),
('After Withdrawal');

-- CREATE APPLICATIONS TABLE
CREATE TABLE applications (
id SERIAL PRIMARY KEY,
job_id INT NOT NULL,
status_id INT NOT NULL,
user_id VARCHAR(255) NOT NULL,
quick_apply BOOLEAN NOT NULL,
date_applied TIMESTAMPTZ DEFAULT NOW(),
general_notes TEXT,
last_updated TIMESTAMPTZ DEFAULT NOW(),
notification_period INT DEFAULT 3,
notifications_paused BOOLEAN DEFAULT FALSE,
FOREIGN KEY (job_id) REFERENCES jobs(id),
FOREIGN KEY (status_id) REFERENCES statuses(id)
);

-- CREATE FOLLOW-UPS TABLE
CREATE TABLE follow_ups (
id SERIAL PRIMARY KEY,
application_id INT NOT NULL,
follow_up_date TIMESTAMPTZ DEFAULT NOW(),
follow_up_type_id INT NOT NULL,
notes TEXT,
FOREIGN KEY (application_id) REFERENCES applications(id),
FOREIGN KEY (follow_up_type_id) REFERENCES follow_up_types(id)
);
5 changes: 1 addition & 4 deletions server/config/sql-db.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
import { Pool } from 'pg';
import dotenv from 'dotenv';

dotenv.config();

const pool = new Pool({
user: process.env.POSTGRES_USER,
host: 'postgres',
host: process.env.NODE_ENV === 'test' ? 'ch-pg-test' : 'postgres',
database: process.env.POSTGRES_DB,
password: process.env.POSTGRES_PASSWORD,
port: 5432,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import Alumni from '../models/alumniModel';
import Alumni from '../../../models/alumniModel';
import { Request, Response } from 'express';
import { IAlumni } from '../types/alumni';
import { IAlumni } from '../../../types/alumni';

interface SearchQuery {
name?: { $regex: string; $options: string };
company?: { $regex: string; $options: string };
}

// ENDPOINT GET api/users/login
// PURPOSE Retrieve all alumni data
// ACCESS Private
const getAllAlumniData = async (req: Request, res: Response) => {
const page = parseInt(req.query.page as string) || 1;
const limit = parseInt(req.query.limit as string) || 10;
Expand Down Expand Up @@ -38,4 +41,4 @@ const getAllAlumniData = async (req: Request, res: Response) => {
}
};

export { getAllAlumniData };
export default getAllAlumniData;
3 changes: 3 additions & 0 deletions server/controllers/alumniController/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import getAllAlumniData from './getAllAlumniData/getAllAumniData';

export { getAllAlumniData };
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { Request, Response } from 'express';
import { pool } from '../../../config/sql-db';

const createApplication = async (req: Request, res: Response) => {
try {
const {
title,
company,
location,
description,
url,
status_id,
quick_apply,
date_applied,
general_notes,
user_id,
} = req.body;

const appliedDate = new Date(date_applied).toISOString();

const jobQuery = `
INSERT INTO jobs (title, company, location, description, url)
VALUES ($1, $2, $3, $4, $5)
RETURNING id
`;
const jobValues = [title, company, location, description, url];
const jobResult = await pool.query(jobQuery, jobValues);
const job_id = jobResult.rows[0].id;

const applicationQuery = `
INSERT INTO applications (job_id, status_id, user_id, quick_apply, date_applied, general_notes, last_updated)
VALUES ($1, $2, $3, $4, $5, $6, NOW())
RETURNING id
`;
const applicationValues = [job_id, status_id, user_id, quick_apply, appliedDate, general_notes];
const applicationResult = await pool.query(applicationQuery, applicationValues);

res.status(201).json({ id: applicationResult.rows[0].id });
} catch (error) {
console.error('Error creating application:', error);
res.status(500).json({ message: 'Internal server error' });
}
};

export default createApplication;
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { Request, Response } from 'express';
import { pool } from '../../../config/sql-db';

interface StatusCount {
status: string;
count: string;
}

const getAggregatedUserStats = async (req: Request, res: Response) => {
const { userId } = req.params;
if (!req.user || req.user.id !== userId)
return res.status(401).json({ message: 'You are not authorized to retrieve those records' });
try {
const applicationsByStatusQuery = `
SELECT statuses.name AS status, COUNT(*) AS count
FROM applications
JOIN statuses ON applications.status_id = statuses.id
WHERE applications.user_id = $1
GROUP BY statuses.name
`;
const applicationsByStatusResult = await pool.query<StatusCount>(applicationsByStatusQuery, [
userId,
]);

const totalApplications = applicationsByStatusResult.rows.reduce(
(sum: number, row: StatusCount) => sum + parseInt(row.count, 10),
0,
);

res.json({
totalApplications,
applicationsByStatus: applicationsByStatusResult.rows,
});
} catch (error) {
console.error('Error fetching aggregated data:', error);
res.status(500).send('Internal server error');
}
};

export default getAggregatedUserStats;
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { Request, Response } from 'express';
import { pool } from '../../../config/sql-db';

const getAllApplications = async (req: Request, res: Response) => {
try {
const { userId, status, date } = req.query;

let query = `
SELECT
applications.id,
jobs.company,
jobs.title,
statuses.name AS status,
applications.general_notes,
applications.date_applied,
applications.last_updated,
applications.notification_period,
applications.notifications_paused
FROM
applications
INNER JOIN jobs ON applications.job_id = jobs.id
INNER JOIN statuses ON applications.status_id = statuses.id
WHERE
applications.user_id = $1
`;

const queryParams = [userId];
let paramIndex = 2;

if (status) {
query += ` AND statuses.name != $${paramIndex}`;
queryParams.push(status);
paramIndex += 1;
}

if (date) {
query += ` AND applications.date_applied >= $${paramIndex}`;
queryParams.push(date);
}

const { rows } = await pool.query(query, queryParams);

res.json(rows);
} catch (error) {
console.error('Error fetching job applications:', error);
res.status(500).json({ message: 'Internal server error' });
}
};

export default getAllApplications;
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { Request, Response } from 'express';
import { pool } from '../../../config/sql-db';

const getApplicationById = async (req: Request, res: Response) => {
const { id } = req.params;
try {
const query = `
SELECT
applications.id,
jobs.title,
jobs.company,
jobs.location,
jobs.description,
jobs.url,
statuses.id AS status_id,
statuses.name AS status,
applications.quick_apply,
applications.date_applied,
applications.general_notes,
applications.job_id,
applications.user_id
FROM
applications
INNER JOIN jobs ON applications.job_id = jobs.id
INNER JOIN statuses ON applications.status_id = statuses.id
WHERE
applications.id = $1
`;
const { rows } = await pool.query(query, [id]);

if (rows.length === 0) {
return res.status(404).json({ message: 'Application not found' });
}

if (!req.user || req.user.id !== rows[0].user_id)
return res.status(401).json({ message: 'You are not authorized to retrieve those records' });

res.json(rows[0]);
} catch (error) {
console.error('Error fetching application by id:', error);
res.status(500).json({ message: 'Internal server error' });
}
};

export default getApplicationById;
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { Request, Response } from 'express';
import { pool } from '../../../config/sql-db';

const getStatuses = async (req: Request, res: Response) => {
try {
const { rows } = await pool.query('SELECT * FROM statuses');
res.json(rows);
} catch (error) {
console.error('Error fetching statuses:', error);
res.status(500).json({ message: 'Internal server error' });
}
};

export default getStatuses;
Loading

0 comments on commit 13a88e1

Please sign in to comment.