Skip to content

Commit a04211e

Browse files
authored
Merge pull request #44 from icefoganalytics/caleb/dev
ytgov#168 - Ability to delete place (site)
2 parents 8f48231 + 5933610 commit a04211e

File tree

12 files changed

+225
-68
lines changed

12 files changed

+225
-68
lines changed

.tool-versions

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
nodejs 20.18.0
2+
ruby 3.3.5

api/.eslintrc.cjs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/* eslint-env node */
2+
3+
// https://github.com/typescript-eslint/typescript-eslint/issues/251
4+
module.exports = {
5+
root: true,
6+
env: {
7+
es2020: true,
8+
node: true,
9+
},
10+
extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended", "prettier"],
11+
overrides: [],
12+
parser: "@typescript-eslint/parser",
13+
plugins: ["@typescript-eslint"],
14+
rules: {
15+
"@typescript-eslint/no-unused-vars": [
16+
"error",
17+
{
18+
args: "all",
19+
argsIgnorePattern: "^_",
20+
caughtErrors: "all",
21+
caughtErrorsIgnorePattern: "^_",
22+
destructuredArrayIgnorePattern: "^_",
23+
varsIgnorePattern: "^_",
24+
ignoreRestSiblings: true,
25+
},
26+
],
27+
},
28+
}

api/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ CreateMigrationRoutes(app);
119119

120120
configureAuthentication(app);
121121

122-
app.get('/api/healthCheck', (req: Request, res: Response) => {
122+
app.get('/api/healthCheck', (_req: Request, res: Response) => {
123123
doHealthCheck(res);
124124
});
125125

@@ -195,7 +195,7 @@ app.use('/api', RequiresAuthentication, staticRouter);
195195
app.use(express.static(path.join(__dirname, 'web')));
196196

197197
// if no other routes match, just send the front-end
198-
app.use((req: Request, res: Response) => {
198+
app.use((_req: Request, res: Response) => {
199199
res.sendFile(path.join(__dirname, 'web') + '/index.html');
200200
});
201201

api/middleware/index.ts

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,21 +20,11 @@ export function ReturnValidationErrors(req: Request, res: Response, next: NextFu
2020
next();
2121
}
2222

23-
export function RequiresRoleAdmin(req: Request, res: Response, next: NextFunction) {
24-
if (req.user && req.user.roles.indexOf('Admin') == -1) {
25-
return res.status(401).send('You are not an Administrator');
26-
}
27-
28-
next();
29-
}
30-
31-
export async function doHealthCheck(req: Request, res: Response) {
23+
export async function doHealthCheck(_req: Request, res: Response) {
3224
//let dbConnected = await data.isConnected();
3325

3426
//if (!dbConnected)
3527
// return res.status(500).send(`Not able to connect to <strong>MONGODB</strong> database on <strong>${MONGO_HOST}</strong>.`);
3628

37-
res.send(
38-
`Connection to database on '<strong>${DB_HOST}</strong>' is connected and functioning.`
39-
);
29+
res.send(`Connection to database on '<strong>${DB_HOST}</strong>' is connected and functioning.`);
4030
}

api/routes/place-router.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import PlacesController from '../controllers/places-controller';
1313
import { PlacePolicy } from '../policies';
1414
import { generatePDF } from '../utils/pdf-generator';
1515
import { createThumbnail } from '../utils/image';
16+
import { DestroyService } from '../services/place';
1617

1718
const placeService = new PlaceService(DB_CONFIG);
1819
const PAGE_SIZE = 10;
@@ -357,3 +358,18 @@ placeRouter.patch(
357358
});
358359
}
359360
);
361+
362+
placeRouter.delete(
363+
'/:id',
364+
authorize([UserRoles.SITE_ADMIN, UserRoles.ADMINISTRATOR]),
365+
async (req: Request, res: Response) => {
366+
try {
367+
const id = parseInt(req.params.id);
368+
await DestroyService.perform(id);
369+
return res.status(204).json({ messages: 'Place was deleted' });
370+
} catch (error) {
371+
console.error(`Place deletion failed: ${error}`, { error });
372+
return res.status(422).json({ messages: `Place deletion failed: ${error}` });
373+
}
374+
}
375+
);

api/routes/user-router.ts

Lines changed: 26 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,7 @@ import axios from 'axios';
33
import moment from 'moment';
44
import { body, param } from 'express-validator';
55

6-
import {
7-
DB_CONFIG,
8-
ISSUER_BASE_URL,
9-
CLIENT_ID,
10-
AUTH_DB_CONNECTION,
11-
} from '../config';
6+
import { DB_CONFIG, ISSUER_BASE_URL, CLIENT_ID, AUTH_DB_CONNECTION } from '../config';
127
import { UserService } from '../services';
138
import { ReturnValidationErrors } from '../middleware';
149
import { UserRoles } from '../models/user-roles';
@@ -17,41 +12,33 @@ import { authorize, UserRoleOptions } from '../middleware/authorization';
1712
export const userRouter = express.Router();
1813
const db = new UserService(DB_CONFIG);
1914

20-
userRouter.get(
21-
'/',
22-
authorize([UserRoles.ADMINISTRATOR]),
23-
async (req: Request, res: Response) => {
24-
const users = await db.getAll();
25-
26-
for (const user of users) {
27-
if (user.last_login_date)
28-
user.last_login_date_display = moment(user.last_login_date)
29-
.utc(true)
30-
.format('YYYY-MM-DD @ h:mmA');
31-
32-
if (user.expire_date) {
33-
const isExpired = moment().isAfter(moment(user.expire_date));
34-
if (user.status == 'Active' && isExpired) user.status = 'Expired';
35-
}
36-
}
15+
userRouter.get('/', authorize([UserRoles.ADMINISTRATOR]), async (req: Request, res: Response) => {
16+
const users = await db.getAll();
3717

38-
res.json({ data: users });
18+
for (const user of users) {
19+
if (user.last_login_date)
20+
user.last_login_date_display = moment(user.last_login_date)
21+
.utc(true)
22+
.format('YYYY-MM-DD @ h:mmA');
23+
24+
if (user.expire_date) {
25+
const isExpired = moment().isAfter(moment(user.expire_date));
26+
if (user.status == 'Active' && isExpired) user.status = 'Expired';
27+
}
3928
}
40-
);
29+
30+
res.json({ data: users });
31+
});
4132

4233
userRouter.get('/roles', authorize(), async (req: Request, res: Response) => {
4334
return res.json({ data: UserRoleOptions });
4435
});
4536

46-
userRouter.get(
47-
'/me',
48-
authorize([], true),
49-
async (req: Request, res: Response) => {
50-
const person = req.user;
37+
userRouter.get('/me', authorize([], true), async (req: Request, res: Response) => {
38+
const person = req.user;
5139

52-
if (person) return res.json({ data: await makeDTO(person) });
53-
}
54-
);
40+
if (person) return res.json({ data: await makeDTO(person) });
41+
});
5542

5643
userRouter.get(
5744
'/:id',
@@ -66,9 +53,7 @@ userRouter.get(
6653
const isExpired = moment().isAfter(moment(user.expire_date));
6754
if (isExpired) user.status = 'Expired';
6855

69-
user.expire_date_display = moment(user.expire_date)
70-
.utc(false)
71-
.format('YYYY-MM-DD');
56+
user.expire_date_display = moment(user.expire_date).utc(false).format('YYYY-MM-DD');
7257
}
7358

7459
res.json({ data: user });
@@ -87,8 +72,7 @@ userRouter.put(
8772
ReturnValidationErrors,
8873
async (req: Request, res: Response) => {
8974
const { id } = req.params;
90-
const { first_name, last_name, expire_date_display, role_list, status } =
91-
req.body;
75+
const { first_name, last_name, expire_date_display, role_list, status } = req.body;
9276
const item = {
9377
first_name,
9478
last_name,
@@ -106,10 +90,7 @@ userRouter.put(
10690
userRouter.post(
10791
'/sign-up-external',
10892
[
109-
body('email')
110-
.isEmail()
111-
.normalizeEmail()
112-
.withMessage('This email appears invalid'),
93+
body('email').isEmail().normalizeEmail().withMessage('This email appears invalid'),
11394
body('first_name').notEmpty(),
11495
body('last_name').notEmpty(),
11596
body('password')
@@ -124,9 +105,7 @@ userRouter.post(
124105

125106
if (existingEmail) {
126107
return res.status(400).json({
127-
errors: [
128-
{ msg: 'A user with that email already exists, try to sign in' },
129-
],
108+
errors: [{ msg: 'A user with that email already exists, try to sign in' }],
130109
});
131110
}
132111

@@ -150,9 +129,7 @@ userRouter.post(
150129
});
151130
})
152131
.catch((err: any) => {
153-
return res
154-
.status(400)
155-
.json({ errors: [{ msg: err.response.data.description }] });
132+
return res.status(400).json({ errors: [{ msg: err.response.data.description }] });
156133
});
157134
}
158135
);
@@ -208,9 +185,7 @@ async function makeDTO(userRaw: any) {
208185
dto.display_name = `${userRaw.first_name} ${userRaw.last_name}`;
209186

210187
if (userRaw.expire_date)
211-
dto.expire_date_display = moment(userRaw.expire_date)
212-
.utc(false)
213-
.format('YYYY-MM-DD');
188+
dto.expire_date_display = moment(userRaw.expire_date).utc(false).format('YYYY-MM-DD');
214189

215190
//dto.roles = _.split(userRaw.roles, ",").filter(r => r.length > 0);
216191
//dto.access = await db.getAccessFor(userRaw.email);
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import BaseService from '../base-service';
2+
3+
import { DB_CONFIG } from '../../config';
4+
import knex, { Knex } from 'knex';
5+
6+
const db = knex(DB_CONFIG);
7+
8+
export class DestroyService extends BaseService {
9+
constructor(private placeId: number) {
10+
super();
11+
}
12+
13+
async perform(): Promise<void> {
14+
return db.transaction(async (trx) => {
15+
await this.deleteAssociations(trx);
16+
await this.deleteConstructionPeriods(trx);
17+
await this.deleteContacts(trx);
18+
await this.deleteDates(trx);
19+
await this.deleteDescriptions(trx);
20+
await this.deleteFirstNationAssociations(trx);
21+
await this.deleteFunctionalUses(trx);
22+
await this.deleteHistoricalPatterns(trx);
23+
await this.deleteNames(trx);
24+
await this.deleteOwnerships(trx);
25+
// await this.deletePhotos(trx);
26+
await this.deletePlaceEdits(trx);
27+
await this.deletePreviousOwnerships(trx);
28+
await this.deleteRevisionLogs(trx);
29+
await this.deleteThemes(trx);
30+
await this.deleteWebLinks(trx);
31+
32+
await trx('place').where({ id: this.placeId }).delete();
33+
});
34+
}
35+
36+
private async deleteAssociations(trx: Knex.Transaction) {
37+
await trx('Association').where({ placeId: this.placeId }).delete();
38+
}
39+
40+
private async deleteConstructionPeriods(trx: Knex.Transaction) {
41+
await trx('ConstructionPeriod').where({ placeId: this.placeId }).delete();
42+
}
43+
44+
private async deleteContacts(trx: Knex.Transaction) {
45+
await trx('Contact').where({ placeId: this.placeId }).delete();
46+
}
47+
48+
private async deleteDates(trx: Knex.Transaction) {
49+
await trx('Dates').where({ placeId: this.placeId }).delete();
50+
}
51+
52+
private async deleteDescriptions(trx: Knex.Transaction) {
53+
await trx('Description').where({ placeId: this.placeId }).delete();
54+
}
55+
56+
private async deleteFirstNationAssociations(trx: Knex.Transaction) {
57+
await trx('FirstNationAssociation').where({ placeId: this.placeId }).delete();
58+
}
59+
60+
private async deleteFunctionalUses(trx: Knex.Transaction) {
61+
await trx('FunctionalUse').where({ placeId: this.placeId }).delete();
62+
}
63+
64+
private async deleteHistoricalPatterns(trx: Knex.Transaction) {
65+
await trx('historicalpattern').where({ placeId: this.placeId }).delete();
66+
}
67+
68+
private async deleteNames(trx: Knex.Transaction) {
69+
await trx('name').where({ placeId: this.placeId }).delete();
70+
}
71+
72+
private async deleteOwnerships(trx: Knex.Transaction) {
73+
await trx('Ownership').where({ placeId: this.placeId }).delete();
74+
}
75+
76+
// private async deletePhotos(trx: Knex.Transaction) {
77+
// return;
78+
// }
79+
80+
private async deletePlaceEdits(trx: Knex.Transaction) {
81+
// TODO Investigate if a seperate delete service is needed
82+
await trx('PlaceEdit').where({ placeId: this.placeId }).delete();
83+
}
84+
85+
private async deletePreviousOwnerships(trx: Knex.Transaction) {
86+
await trx('PreviousOwnership').where({ placeId: this.placeId }).delete();
87+
}
88+
89+
private async deleteRevisionLogs(trx: Knex.Transaction) {
90+
await trx('RevisionLog').where({ placeId: this.placeId }).delete();
91+
}
92+
93+
private async deleteThemes(trx: Knex.Transaction) {
94+
await trx('Theme').where({ placeId: this.placeId }).delete();
95+
}
96+
97+
private async deleteWebLinks(trx: Knex.Transaction) {
98+
await trx('WebLink').where({ placeId: this.placeId }).delete();
99+
}
100+
}
101+
102+
export default DestroyService;

api/services/place/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1+
export { DestroyService } from './destroy-service';
12
export { PrintSiteService } from './print-site-service';

api/services/place/print-site-service.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -268,8 +268,6 @@ export class PrintSiteService extends BaseService {
268268
descriptions: descriptionsHandlebarData,
269269
};
270270

271-
console.log(handlebarsData);
272-
273271
return template(handlebarsData);
274272
}
275273

web/src/apis/places-api.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,4 +61,13 @@ export default {
6161
return Promise.reject(error);
6262
});
6363
},
64+
delete(id) {
65+
return http
66+
.delete(`${placeUrl}/${id}`)
67+
.then((response) => response.data)
68+
.catch((error) => {
69+
console.error(error);
70+
return Promise.reject(error);
71+
});
72+
}
6473
};

0 commit comments

Comments
 (0)