Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion api/.nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
11.4.0
18.20.4
14 changes: 6 additions & 8 deletions api/Dockerfile.development
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
FROM node:11.4.0
FROM node:18-bullseye

# force update sources.list (sketchy, but good for now until we update container)
RUN echo "deb http://archive.ubuntu.com/ubuntu xenial main universe" > /etc/apt/sources.list \
&& echo "deb http://archive.ubuntu.com/ubuntu xenial-updates main universe" >> /etc/apt/sources.list \
&& echo "deb http://security.ubuntu.com/ubuntu xenial-security main universe" >> /etc/apt/sources.list

# TODO: fix apt-get update (upgrade node image, lol)
RUN apt-get update -y && apt-get install --allow-unauthenticated -y gdal-bin
# Update the package list and install gdal-bin and its dependencies
RUN apt-get update -y && \
apt-get install -y --no-install-recommends \
gdal-bin \
&& apt-get clean && rm -rf /var/lib/apt/lists/*

WORKDIR /app
COPY package.json .
Expand Down
4 changes: 2 additions & 2 deletions api/Dockerfile.jobs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM node:11.4.0 as build-stage
FROM node:18-bullseye as build-stage

WORKDIR /app
COPY package.json .
Expand All @@ -7,7 +7,7 @@ RUN npm install
COPY . /app
RUN /app/node_modules/.bin/tsc --build /app/tsconfig.json

FROM keymetrics/pm2:12-alpine
FROM keymetrics/pm2:18-alpine
LABEL maintainer "Dan Melton <dan@civicsoftwarefoundation.org>"

WORKDIR /app
Expand Down
4 changes: 2 additions & 2 deletions api/Dockerfile.production
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM node:11.4.0 as build-stage
FROM node:18-bullseye as build-stage

WORKDIR /app
COPY package.json .
Expand All @@ -7,7 +7,7 @@ RUN npm install
COPY . /app
RUN /app/node_modules/.bin/tsc --build /app/tsconfig.json

FROM keymetrics/pm2:12-alpine
FROM keymetrics/pm2:18-alpine
LABEL maintainer "Dan Melton <dan@civicsoftwarefoundation.org>"

WORKDIR /app
Expand Down
2 changes: 1 addition & 1 deletion api/Dockerfile.qa
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM node:11.4.0
FROM node:18-bullseye
LABEL maintainer "Dan Melton <dan@civicsoftwarefoundation.org>"

RUN apt-get update -y && apt-get install -y gdal-bin
Expand Down
38 changes: 20 additions & 18 deletions api/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import * as cors from 'cors';
import * as fileUpload from 'express-fileupload';
import { setupRoutes } from './routes';
import db from './models/db';
import bugsnag from '@bugsnag/js';
import bugsnagExpress from '@bugsnag/plugin-express';
import Bugsnag from '@bugsnag/js';
import BugsnagPluginExpress from '@bugsnag/plugin-express';

/***
corsOptions set to return all Access-Control-Allow-Origin headers with requesting domain
Expand All @@ -14,36 +14,38 @@ import bugsnagExpress from '@bugsnag/plugin-express';
https://expressjs.com/en/resources/middleware/cors.html#configuring-cors-w-dynamic-origin
***/
const corsOptions = {
origin: function (origin, callback) {
callback(null, true);
},
credentials: true
origin: function (origin, callback) {
callback(null, true);
},
credentials: true,
};

(async () => {
await db();
const app: express.Express = express();

if (process.env.NODE_ENV === 'production') {
const bugsnagClient = bugsnag({
const bugsnagClient = Bugsnag.start({
apiKey: process.env.BUG_SNAG_API_KEY,
releaseStage: process.env.APP_ENV
releaseStage: process.env.APP_ENV,
plugins: [BugsnagPluginExpress],
});
bugsnagClient.use(bugsnagExpress);
const middleware = bugsnagClient.getPlugin('express');
app.use(middleware.requestHandler);
app.use(middleware.errorHandler);
}
app.use(logger('dev'));
app.use(fileUpload({
createParentPath: true,
limits: {
fileSize: 10 * 1024 * 1024 * 1024 // 10MB max file(s) size
},
useTempFiles : true,
tempFileDir : '/app/uploads',
debug: false // flip to true for local testing
}));
app.use(
fileUpload({
createParentPath: true,
limits: {
fileSize: 10 * 1024 * 1024 * 1024, // 10MB max file(s) size
},
useTempFiles: true,
tempFileDir: '/app/uploads',
debug: false, // flip to true for local testing
})
);
app.use(cors(corsOptions));
app.options('*', cors(corsOptions));

Expand Down
20 changes: 11 additions & 9 deletions api/controller/activities.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
import { Response } from 'express';
import { checkCurrentUser, IRequest } from '../routes/helpers';
import {
getActivityAttachmentAsync,
getAllActivityRecordsAsync,
} from '../services/activityService';
import { getActivityAttachmentAsync, getAllActivityRecordsAsync } from '../services/activityService';
import { bugsnagClient } from '../services/bugsnagService';
import { IsNumber } from 'class-validator';
import { checkDto } from './helpers';
Expand All @@ -13,14 +10,17 @@ export async function getActivities(request: IRequest, response: Response, next:
checkCurrentUser(request);

request.body.currentUserId = request.currentUser.id;
const hostName = (request.hostname || '').includes('openelectionsportland') ? 'openelectionsportland.org' : 'smalldonorelections.org';
const hostName = (request.hostname || '').includes('openelectionsportland')
? 'openelectionsportland.org'
: 'smalldonorelections.org';
const records = await getAllActivityRecordsAsync(request.body, hostName);
console.log({ records });
response.status(200).json(records);
} catch (err) {
if (process.env.NODE_ENV === 'production' && err.message !== 'No token set') {
bugsnagClient.notify(err);
}
return response.status(422).json({message: err.message});
return response.status(422).json({ message: err.message });
}
}

Expand All @@ -37,10 +37,12 @@ export async function getActivityAttachment(request: IRequest, response: Respons
checkCurrentUser(request);
const getActivityFileDto = Object.assign(new GetActivityAttachmentDto(), {
activityId: parseInt(request.params.id),
currentUserId: request.currentUser.id
currentUserId: request.currentUser.id,
});
await checkDto(getActivityFileDto);
const hostName = (request.hostname || '').includes('openelectionsportland') ? 'openelectionsportland.org' : 'smalldonorelections.org';
const hostName = (request.hostname || '').includes('openelectionsportland')
? 'openelectionsportland.org'
: 'smalldonorelections.org';
const data = await getActivityAttachmentAsync(getActivityFileDto);
response.set('Content-Type', data.contentType);
if (data.fileName) {
Expand All @@ -49,6 +51,6 @@ export async function getActivityAttachment(request: IRequest, response: Respons
response.setHeader('Content-Transfer-Encoding', 'binary');
return response.status(200).send(data.buffer);
} catch (err) {
return response.status(422).json({message: err.message});
return response.status(422).json({ message: err.message });
}
}
28 changes: 13 additions & 15 deletions api/controller/expenditures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ import {
IGetExpenditureAttrs,
getExpendituresAsync,
IUpdateExpenditureAttrs,
updateExpenditureAsync, createExpenditureCommentAsync, getExpenditureByIdAsync
updateExpenditureAsync,
createExpenditureCommentAsync,
getExpenditureByIdAsync,
} from '../services/expenditureService';
import { checkCurrentUser, IRequest } from '../routes/helpers';
import { checkDto } from './helpers';
Expand All @@ -15,7 +17,7 @@ import {
ExpenditureType,
ExpenditureSubType,
PayeeType,
PurposeType
PurposeType,
} from '../models/entity/Expenditure';
import { PaymentMethod } from '../models/entity/Expenditure';
import { bugsnagClient } from '../services/bugsnagService';
Expand Down Expand Up @@ -109,7 +111,7 @@ export async function addExpenditure(request: IRequest, response: Response, next
checkCurrentUser(request);
const addExpenditureDto = Object.assign(new AddExpenditureDto(), {
...request.body,
currentUserId: request.currentUser.id
currentUserId: request.currentUser.id,
});
await checkDto(addExpenditureDto);
const expenditure = await addExpenditureAsync(addExpenditureDto);
Expand All @@ -118,7 +120,7 @@ export async function addExpenditure(request: IRequest, response: Response, next
if (process.env.NODE_ENV === 'production' && err.message !== 'No token set') {
bugsnagClient.notify(err);
}
return response.status(422).json({message: err.message});
return response.status(422).json({ message: err.message });
}
}

Expand Down Expand Up @@ -163,7 +165,7 @@ export async function getExpenditures(request: IRequest, response: Response, nex
checkCurrentUser(request);
const getExpendituresDto = Object.assign(new GetExpendituresDto(), {
...request.body,
currentUserId: request.currentUser.id
currentUserId: request.currentUser.id,
});
await checkDto(getExpendituresDto);

Expand All @@ -181,7 +183,7 @@ export async function getExpenditures(request: IRequest, response: Response, nex
if (process.env.NODE_ENV === 'production' && err.message !== 'No token set') {
bugsnagClient.notify(err);
}
return response.status(422).json({message: err.message});
return response.status(422).json({ message: err.message });
}
}

Expand Down Expand Up @@ -270,7 +272,7 @@ export async function updateExpenditure(request: IRequest, response: Response, n
checkCurrentUser(request);
const updateExpenditureDto = Object.assign(new UpdateExpenditureDto(), {
...request.body,
currentUserId: request.currentUser.id
currentUserId: request.currentUser.id,
});
await checkDto(updateExpenditureDto);
const expenditure = await updateExpenditureAsync(updateExpenditureDto);
Expand All @@ -280,12 +282,11 @@ export async function updateExpenditure(request: IRequest, response: Response, n
if (process.env.NODE_ENV === 'production' && err.message !== 'No token set') {
bugsnagClient.notify(err);
}
return response.status(422).json({message: err.message});
return response.status(422).json({ message: err.message });
}
}

export class ExpenditureCommentDto {

currentUserId: number;

@IsNumber()
Expand All @@ -296,7 +297,6 @@ export class ExpenditureCommentDto {

@IsOptional()
attachmentPath: UploadedFile;

}

export async function createExpenditureComment(request: IRequest, response: Response, next: Function) {
Expand All @@ -305,7 +305,7 @@ export async function createExpenditureComment(request: IRequest, response: Resp
const attrs: any = {
expenditureId: parseInt(request.params.id),
currentUserId: request.currentUser.id,
comment: request.body.comment
comment: request.body.comment,
};
let file;
if (request.files && request.files.attachment) {
Expand All @@ -323,13 +323,11 @@ export async function createExpenditureComment(request: IRequest, response: Resp
if (process.env.NODE_ENV === 'production' && err.message !== 'No token set') {
bugsnagClient.notify(err);
}
return response.status(422).json({message: err.message});
return response.status(422).json({ message: err.message });
}
}


export class GetExpenditureByIdDto {

@IsNumber()
expenditureId: number;

Expand All @@ -342,7 +340,7 @@ export async function getExpenditureById(request: IRequest, response: Response,
checkCurrentUser(request);
const getExpenditureByIdDto = Object.assign(new GetExpenditureByIdDto(), {
expenditureId: parseInt(request.params.id),
currentUserId: request.currentUser.id
currentUserId: request.currentUser.id,
});
await checkDto(getExpenditureByIdDto);
const expenditure = await getExpenditureByIdAsync(getExpenditureByIdDto);
Expand Down
7 changes: 1 addition & 6 deletions api/controller/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { validate } from 'class-validator';
import { FileArray } from 'express-fileupload';
import { createReadStream } from 'fs';
import * as parse from 'csv-parse/lib';
import { parse } from 'csv-parse';
import { IAddContributionAttrs } from '../services/contributionService';
import { convertToTimeZone } from 'date-fns-timezone';
import {
Expand All @@ -14,7 +14,6 @@ import {
} from '../models/entity/Contribution';

export async function checkDto(dto): Promise<void> {
console.log('from checkDto');
const validationErrors = await validate(dto, { validationError: { target: false } });
if (validationErrors.length) {
throw new Error(
Expand All @@ -28,7 +27,6 @@ export async function checkDto(dto): Promise<void> {
}

export async function checkDtoWithEnums(dto): Promise<void> {
console.log('from checkDtoWithEnums');
const validationErrors = await validate(dto, { validationError: { target: false } });
if (validationErrors.length) {
throw new Error(
Expand Down Expand Up @@ -203,11 +201,9 @@ export async function parseBulkCsvData(body: IBulkUploadBody, file: FileArray):
.pipe(parse({ delimiter: ',', columns: true }))
.on('data', (row: IBulkUploadCSV) => {
if (runColumnCheck) {
console.log('header row start');
const rowTitleErrors = [];
Object.keys(row || {}).forEach((rowItem) => {
if (!acceptableColumnTitles.includes(rowItem)) {
console.log(`${rowItem} should not be here.`);
rowTitleErrors.push(rowItem);
}
});
Expand All @@ -218,7 +214,6 @@ export async function parseBulkCsvData(body: IBulkUploadBody, file: FileArray):
}
reject(new Error(`${rowErrorString}. Columns should be: ${acceptableColumnTitles.join(', ')}`));
}
console.log('header row end');
runColumnCheck = false;
}
const newRow: Partial<IAddContributionAttrs> = {};
Expand Down
15 changes: 15 additions & 0 deletions api/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
testMatch: ['**/test/**/*.spec.ts'],
moduleFileExtensions: ['ts', 'js'],
transform: {
'^.+\\.ts$': [
'ts-jest',
{
tsconfig: 'tsconfig.json',
},
],
},
maxWorkers: 1,
};
Loading