Skip to content

Commit

Permalink
🚧 rudimentary organization operations in backend
Browse files Browse the repository at this point in the history
  • Loading branch information
MattMoony committed May 6, 2024
1 parent 19b705e commit fc59804
Show file tree
Hide file tree
Showing 5 changed files with 237 additions and 7 deletions.
73 changes: 73 additions & 0 deletions core/src/controllers/organization.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { Request, Response } from 'express';
import Organization from '../models/Organization';

/**
* Parses the organization ID from the request parameters to
* an `Organization` object and stores it in `res.locals.organization`.
* @param req The request object.
* @param res The response object.
*/
export const parseOid = async (req: Request, res: Response, next: Function): Promise<void> => {
if (isNaN(parseInt(req.params.orgId))) {
res.send({ 'success': false, 'msg': 'bad organizationid', });
return;
}
const organization = await Organization.get(parseInt(req.params.orgId));
if (!organization) {
res.send({ 'success': false, 'msg': 'organization not found', });
return;
}
res.locals.organization = organization;
next();
};

/**
* Creates a new organization.
* @param req The request object.
* @param res The response object.
*/
export const create = async (req: Request, res: Response): Promise<void> => {
if (!req.body.name) {
res.send({ 'success': false, 'msg': 'missing name', });
return;
}
const organization = await Organization.create(req.body.name, req.body.established, req.body.dissolved);
res.send({ 'success': true, 'organization': organization });
};

/**
* Gets an organization.
* @param req The request object.
* @param res The response object (with `res.locals.organization`).
*/
export const get = async (req: Request, res: Response): Promise<void> => {
const organization = res.locals.organization as Organization;
res.send({ 'success': true, 'organization': organization.json(), });
};

/**
* Updates an organization.
* @param req The request object.
* @param res The response object (with `res.locals.organization`).
*/
export const update = async (req: Request, res: Response): Promise<void> => {
const organization = res.locals.organization as Organization;

if (req.body.name) organization.name = req.body.name;
if (req.body.established) organization.established = req.body.established;
if (req.body.dissolved) organization.dissolved = req.body.dissolved;

await organization.update();
res.send({ 'success': true, 'organization': organization.json(), });
}

/**
* Removes an organization.
* @param req The request object.
* @param res The response object (with `res.locals.organization`).
*/
export const remove = async (req: Request, res: Response): Promise<void> => {
const organization = res.locals.organization as Organization;
await organization.remove();
res.send({ 'success': true, });
}
11 changes: 9 additions & 2 deletions core/src/models/Organ.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,15 +128,22 @@ class Organ {
return fsPromises.writeFile(`${process.env.DATA_DIR}/${this.id}.md`, this.bio);
}

/**
* Removes the organ from the database.
* @returns A promise that resolves when the organ has been removed.
*/
public async remove (): Promise<void>;
/**
* Removes a source from the organ.
* @param source The source to remove.
* @returns A promise that resolves when the source has been removed.
*/
public async remove (source: OrganSource): Promise<void>;
public remove (v: OrganSource): Promise<void> {
public async remove (v?: OrganSource): Promise<void> {
if (v instanceof OrganSource) return v.remove();
throw new Error('Invalid removal type');
const client = await pool.connect();
await client.query('DELETE FROM organ WHERE oid = $1', [this.id]);
client.release();
}

/**
Expand Down
133 changes: 132 additions & 1 deletion core/src/models/Organization.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import Organ from './Organ';
import { pool } from '../db';

import Organ, { OrganSource } from './Organ';

/**
* Represents an organization - i.e. a grouping of several
Expand All @@ -17,6 +19,10 @@ class Organization extends Organ {
this.dissolved = dissolved;
}

/**
* Returns a JSON representation of the organization.
* @returns The JSON representation of the organization.
*/
public json (): Object {
return {
...super.json(),
Expand All @@ -26,9 +32,134 @@ class Organization extends Organ {
};
}

/**
* Returns a string representation of the organization.
* @returns The string representation of the organization.
*/
public toString (): string {
return `"${this.name}" (Organization#${this.id})`;
}

/**
* Adds a source to the organization.
* @param source The source to add.
* @returns A promise that resolves with the new source.
*/
public async add (source: string): Promise<OrganSource>;
public async add (v: string): Promise<OrganSource> {
if (typeof v === 'string') return await super.add(v);
throw new Error('Invalid argument type');
}

/**
* Updates the organization in the database.
* @returns A promise that resolves when the organization has been updated.
*/
public async update (): Promise<void>;
public async update (): Promise<void> {
await super.update();
const client = await pool.connect();
await client.query(
'UPDATE organizations SET name = $1, established = $2, dissolved = $3 WHERE id = $4',
[this.name, this.established, this.dissolved, this.id]
);
client.release();
}

/**
* Removes the organization from the database.
* @returns A promise that resolves when the organization has been removed.
*/
public async remove (): Promise<void>;
/**
* Removes a source from the organization.
* @param source The source to remove.
* @returns A promise that resolves when the source has been removed.
*/
public async remove (source: OrganSource): Promise<void>;
public async remove (v?: OrganSource): Promise<void> {
if (v instanceof OrganSource) return super.remove(v);
const client = await pool.connect();
await client.query(
'DELETE FROM organizations WHERE id = $1',
[this.id]
);
client.release();
super.remove();
}

/**
* Creates a new organ.
* @returns A promise that resolves with the new organ.
*/
public static async create (): Promise<Organ>;
/**
* Creates a new organ.
* @deprecated Use `Organ.create` with the bio string instead.
* @param bio The bio of the organ.
* @returns A promise that resolves with the new organ.
*/
public static async create (bio: string): Promise<Organ>;
/**
* Creates a new organization.
* @param name The name of the organization.
* @param established The date the organization was established.
* @param dissolved The date the organization was dissolved.
* @returns A promise that resolves with the new organization.
*/
public static async create (name: string, established?: Date, dissolved?: Date): Promise<Organization>;
public static async create (v?: string, v2?: Date, v3?: Date): Promise<Organ> {
if (typeof v === 'undefined') return Organ.create();
if (typeof v === 'string') return Organization.create(v, v2, v3);
throw new Error('Invalid creation type');
}

/**
* Gets an organization by its ID.
* @param id The ID of the organization.
* @returns A promise that resolves with the organization, or null if it doesn't exist.
*/
public static async get (id: number): Promise<Organization|null> {
const organ = await super.get(id);
if (!organ) return null;
const client = await pool.connect();
const res = await client.query(
'SELECT name, established, dissolved FROM organizations WHERE id = $1',
[id]
);
client.release();
if (res.rows.length === 0) return null;
return new Organization(
organ.id,
organ.bio,
res.rows[0].name,
res.rows[0].established,
res.rows[0].dissolved
);
}

/**
* Finds organizations by their name.
* @param query The name to search for.
* @returns A promise that resolves with the organizations found.
*/
public static async find (query: string): Promise<Organization[]> {
const client = await pool.connect();
const res = await client.query(
`SELECT *
FROM organizations
WHERE name ILIKE $1`,
[`%${query}%`]
);
client.release();
return Promise.all(res.rows.map(async row => new Organization(
+row.oid,
(await Organ.get(+row.oid))!.bio,
row.name,
row.established,
row.dissolved
)));
}
}

export default Organization;
16 changes: 12 additions & 4 deletions core/src/models/Person.ts
Original file line number Diff line number Diff line change
Expand Up @@ -364,20 +364,28 @@ class Person extends Organ {
* @returns {Promise<void>} A promise that resolves when the person is removed.
*/
public async remove (): Promise<void>;
/**
* Remove a source for this person.
* @param {OrganSource} source The source to remove.
* @returns {Promise<void>} A promise that resolves when the source is removed.
*/
public async remove (source: OrganSource): Promise<void>;
/**
* Remove a relation from this person.
* @param {Relation} relation The relation to remove.
* @returns {Promise<void>} A promise that resolves when the relation is removed.
*/
public async remove (relation: Relation): Promise<void>;
public async remove (v?: Relation): Promise<void> {
public async remove (v?: Relation|OrganSource): Promise<void> {
if (v instanceof OrganSource) return await super.remove(v);
if (v instanceof Relation) return await v.remove();
const client = await pool.connect();
await client.query(
'DELETE FROM person WHERE pid = $1',
[this.id],
);
client.release();
super.remove();
}

/**
Expand Down Expand Up @@ -588,14 +596,14 @@ class Person extends Organ {
const res = await client.query(
`SELECT *
FROM person
WHERE LOWER(firstname)||' '||LOWER(lastname) LIKE $1
OR LOWER(lastname)||' '||LOWER(firstname) LIKE $1`,
WHERE firstname||' '||lastname ILIKE $1
OR lastname||' '||firstname ILIKE $1`,
[`%${query}%`],
);
client.release();
return Promise.all(res.rows.map(async (row) => new Person(
+row.pid,
(await Organ.get(row.pid))!.bio,
(await Organ.get(+row.pid))!.bio,
row.firstname,
row.lastname,
row.birthdate,
Expand Down
11 changes: 11 additions & 0 deletions core/src/routes/organization.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Router } from 'express';
import * as controller from '../controllers/organization';

const router: Router = Router();

router.post('/new', controller.create);
router.get('/:orgId', controller.parseOid, controller.get);
router.patch('/:orgId', controller.parseOid, controller.update);
router.delete('/:orgId', controller.parseOid, controller.remove);

export default router;

0 comments on commit fc59804

Please sign in to comment.