From 151afcf3a9388a17484549dcc35843b8d7fc864a Mon Sep 17 00:00:00 2001 From: Matthias Monschein Date: Thu, 9 May 2024 18:35:51 +0200 Subject: [PATCH] :sparkles: organization memberships are now displayed --- core/src/controllers/organization.ts | 12 +++ core/src/models/Membership.ts | 7 +- core/src/routes/organization.ts | 2 + glass-pane.postman_collection.json | 85 ++++++++++++---------- web/src/api/organ.ts | 19 +++++ web/src/api/organization.ts | 25 ++++++- web/src/components/OrganizationNetwork.vue | 1 + web/src/models/Membership.ts | 32 +++++++- 8 files changed, 142 insertions(+), 41 deletions(-) diff --git a/core/src/controllers/organization.ts b/core/src/controllers/organization.ts index 6a61b93..7b1afcd 100644 --- a/core/src/controllers/organization.ts +++ b/core/src/controllers/organization.ts @@ -1,6 +1,7 @@ import { Request, Response } from 'express'; import Organization from '../models/Organization'; +import Membership from '../models/Membership'; /** * Parses the organization ID from the request parameters to @@ -85,3 +86,14 @@ export const remove = async (req: Request, res: Response): Promise => { await organization.remove(); res.send({ 'success': true, }); } + +/** + * Gets the members of an organization. + * @param req The request object. + * @param res The response object (with `res.locals.organization`). + */ +export const getMembers = async (req: Request, res: Response): Promise => { + const organization = res.locals.organization as Organization; + const members = await Membership.get(organization); + res.send({ 'success': true, 'members': members.map(member => ({ ...member.json(), organization: undefined, })), }); +} diff --git a/core/src/models/Membership.ts b/core/src/models/Membership.ts index eb5ed1d..51aa20c 100644 --- a/core/src/models/Membership.ts +++ b/core/src/models/Membership.ts @@ -5,6 +5,7 @@ import OrganSource from './OrganSource'; import Role from './Role'; import Organization from './Organization'; import MembershipSource from './MembershipSource'; +import Person from './Person'; /** * Represents a membership in an organization. @@ -177,7 +178,11 @@ class Membership { client.release(); const memberships = []; for (const row of res.rows) { - const organ = await Organ.get(row.organ); + let organ: Person|Organization|null = await Person.get(row.organ); + if (!organ) + organ = await Organization.get(row.organ); + if (!organ) + continue; const role = await Role.get(row.role); if (!organ || !role) continue; memberships.push(new Membership(organ, v, role, row.since, row.until)); diff --git a/core/src/routes/organization.ts b/core/src/routes/organization.ts index f6e4c4b..207838a 100644 --- a/core/src/routes/organization.ts +++ b/core/src/routes/organization.ts @@ -10,4 +10,6 @@ router.get('/:oid', controller.parseOid, controller.get); router.patch('/:oid', controller.parseOid, controller.update); router.delete('/:oid', controller.parseOid, controller.remove); +router.get('/:oid/members', controller.parseOid, controller.getMembers); + export default router; diff --git a/glass-pane.postman_collection.json b/glass-pane.postman_collection.json index f614c82..73b4c71 100644 --- a/glass-pane.postman_collection.json +++ b/glass-pane.postman_collection.json @@ -80,13 +80,13 @@ "method": "GET", "header": [], "url": { - "raw": "{{baseURL}}/organ/{{new_organ}}/memberships", + "raw": "{{baseURL}}/organ/{{new_person}}/memberships", "host": [ "{{baseURL}}" ], "path": [ "organ", - "{{new_organ}}", + "{{new_person}}", "memberships" ] } @@ -120,13 +120,13 @@ } }, "url": { - "raw": "{{baseURL}}/organ/{{new_organ}}/memberships", + "raw": "{{baseURL}}/organ/{{new_person}}/memberships", "host": [ "{{baseURL}}" ], "path": [ "organ", - "{{new_organ}}", + "{{new_person}}", "memberships" ] } @@ -148,13 +148,13 @@ } }, "url": { - "raw": "{{baseURL}}/organ/{{new_organ}}/memberships", + "raw": "{{baseURL}}/organ/{{new_person}}/memberships", "host": [ "{{baseURL}}" ], "path": [ "organ", - "{{new_organ}}", + "{{new_person}}", "memberships" ] } @@ -176,13 +176,13 @@ } }, "url": { - "raw": "{{baseURL}}/organ/{{new_organ}}/memberships", + "raw": "{{baseURL}}/organ/{{new_person}}/memberships", "host": [ "{{baseURL}}" ], "path": [ "organ", - "{{new_organ}}", + "{{new_person}}", "memberships" ] } @@ -269,16 +269,13 @@ } }, "url": { - "raw": "http://localhost:8888/api/organ//sources", - "protocol": "http", + "raw": "{{baseURL}}/organ/{{new_organ}}/sources", "host": [ - "localhost" + "{{baseURL}}" ], - "port": "8888", "path": [ - "api", "organ", - "", + "{{new_organ}}", "sources" ] } @@ -413,14 +410,11 @@ "method": "POST", "header": [], "url": { - "raw": "http://localhost:8888/api/organ/new", - "protocol": "http", + "raw": "{{baseURL}}/organ/new", "host": [ - "localhost" + "{{baseURL}}" ], - "port": "8888", "path": [ - "api", "organ", "new" ] @@ -514,16 +508,13 @@ "method": "GET", "header": [], "url": { - "raw": "http://localhost:8888/api/organ/364", - "protocol": "http", + "raw": "{{baseURL}}/organ/{{$randomInt}}", "host": [ - "localhost" + "{{baseURL}}" ], - "port": "8888", "path": [ - "api", "organ", - "364" + "{{$randomInt}}" ] } }, @@ -756,7 +747,7 @@ "header": [], "body": { "mode": "raw", - "raw": "{\r\n \"to\": {{relative}},\r\n \"since\": \"{{relation_since}}\",\r\n \"url\": \"new source here\"\r\n}", + "raw": "{\r\n \"other\": {{relative}},\r\n \"since\": \"{{relation_since}}\",\r\n \"url\": \"new source here\"\r\n}", "options": { "raw": { "language": "json" @@ -1048,14 +1039,11 @@ } }, "url": { - "raw": "http://localhost:8888/api/person/new", - "protocol": "http", + "raw": "{{baseURL}}/person/new", "host": [ - "localhost" + "{{baseURL}}" ], - "port": "8888", "path": [ - "api", "person", "new" ] @@ -1104,7 +1092,7 @@ ] }, { - "name": "search persons", + "name": "search people", "request": { "method": "GET", "header": [], @@ -1149,16 +1137,13 @@ "method": "GET", "header": [], "url": { - "raw": "http://localhost:8888/api/person/", - "protocol": "http", + "raw": "{{baseURL}}/person/{{new_person}}", "host": [ - "localhost" + "{{baseURL}}" ], - "port": "8888", "path": [ - "api", "person", - "" + "{{new_person}}" ] } }, @@ -1377,6 +1362,30 @@ { "name": "organizations", "item": [ + { + "name": "members", + "item": [ + { + "name": "get members", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseURL}}/organization/{{new_organization}}/members", + "host": [ + "{{baseURL}}" + ], + "path": [ + "organization", + "{{new_organization}}", + "members" + ] + } + }, + "response": [] + } + ] + }, { "name": "create organization", "event": [ diff --git a/web/src/api/organ.ts b/web/src/api/organ.ts index 0987bd7..c0a5b31 100644 --- a/web/src/api/organ.ts +++ b/web/src/api/organ.ts @@ -54,6 +54,25 @@ export interface OrganMembership { until?: Date; } +export interface OrganizationMember { + /** + * The organ that is a member of the organization. + */ + organ: Organ; + /** + * The role that the person has in the organization. + */ + role: Role; + /** + * The date that the person became a member of the organization. + */ + since: Date; + /** + * The date that the person stopped being a member of the organization. + */ + until?: Date; +} + /** * Represents a membership of an organ in an organization. * A membership is identified by its `organ`, `organization`, diff --git a/web/src/api/organization.ts b/web/src/api/organization.ts index 5bf9e2c..d5bbc96 100644 --- a/web/src/api/organization.ts +++ b/web/src/api/organization.ts @@ -1,6 +1,7 @@ import { API, jreq } from './index'; import type { APIResponse } from './index'; -import type { Organ } from './organ'; +import type { Organ, OrganizationMember } from './organ'; +import type { Person } from './person'; /** * Represents an organization. @@ -38,6 +39,13 @@ export interface OrganizationsResponse extends APIResponse { organizations: Organization[]; } +/** + * Represents a response from the API for the members of an organization. + */ +export interface OrganizationMembersResponse extends APIResponse { + members: OrganizationMember[]; +} + /** * Creates a new organization. * @param name The name of the organization. @@ -118,3 +126,18 @@ export const update = async ( export const remove = async (oid: number): Promise => { return await jreq(`${API}/organization/${oid}`, { method: 'DELETE' }) as APIResponse; }; + +export const members = async (oid: number): Promise => { + const res = await jreq(`${API}/organization/${oid}/members`) as OrganizationMembersResponse; + res.members.forEach(m => { + const person = m.organ as Person; + if (person.birthdate !== undefined) person.birthdate = new Date(person.birthdate); + if (person.deathdate !== undefined) person.deathdate = new Date(person.deathdate); + const organization = m.organ as Organization; + if (organization.established !== undefined) organization.established = new Date(organization.established); + if (organization.dissolved !== undefined) organization.dissolved = new Date(organization.dissolved); + m.since = new Date(m.since); + m.until = m.until ? new Date(m.until) : undefined; + }); + return res; +}; diff --git a/web/src/components/OrganizationNetwork.vue b/web/src/components/OrganizationNetwork.vue index 911057e..043677f 100644 --- a/web/src/components/OrganizationNetwork.vue +++ b/web/src/components/OrganizationNetwork.vue @@ -23,6 +23,7 @@ const refreshNetwork = async () => { if (!props.organization) return; memberships.value = await Membership.get(new Organ(props.organization.id, props.organization.bio)); members.value = await Membership.get(props.organization); + console.log(members.value); }; watch(() => props.organization, refreshNetwork, { immediate: true }); diff --git a/web/src/models/Membership.ts b/web/src/models/Membership.ts index 9ec477d..1b74d35 100644 --- a/web/src/models/Membership.ts +++ b/web/src/models/Membership.ts @@ -1,6 +1,9 @@ import * as organ from '@/api/organ'; +import * as organization from '@/api/organization'; +import * as person from '@/api/person'; import Organ from './Organ'; import Organization from './Organization'; +import Person from './Person'; import Role from './Role'; /** @@ -82,7 +85,34 @@ class Membership { */ public static async get (organ: Organ, organization: Organization, role: Role, since: Date): Promise; public static async get (v: Organ|Organization, v2?: Organization, v3?: Role, v4?: Date): Promise { - if (v instanceof Organization) return []; + if (v instanceof Organization) { + const res = await organization.members(v.id); + return res.members.map((m: any) => new Membership( + (m.organ as person.Person).firstname !== undefined + ? new Person( + m.organ.id, + m.organ.bio, + m.organ.firstname, + m.organ.lastname, + m.organ.birthdate, + m.organ.deathdate + ) + : new Organization( + m.organ.id, + m.organ.bio, + m.organ.name, + m.organ.established, + m.organ.dissolved + ), + v, + new Role( + m.role.id, + m.role.name + ), + new Date(m.since), + m.until ? new Date(m.until) : undefined + )); + } else if (v instanceof Organ) { const res = await organ.memberships(v.id); return res.memberships.map((m: any) => new Membership(