-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(agora): migrate Teams component (AG-1541) (#2809)
- Loading branch information
Showing
119 changed files
with
10,286 additions
and
712 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export * from './dataversion'; | ||
export * from './teams'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
// -------------------------------------------------------------------------- // | ||
// External | ||
// -------------------------------------------------------------------------- // | ||
import { mongo, connection } from 'mongoose'; | ||
import { Request, Response, NextFunction } from 'express'; | ||
|
||
// -------------------------------------------------------------------------- // | ||
// Internal | ||
// -------------------------------------------------------------------------- // | ||
import { setHeaders, cache } from '../helpers'; | ||
import { TeamCollection } from '../models'; | ||
import { Team } from '@sagebionetworks/agora/api-client-angular'; | ||
|
||
// -------------------------------------------------------------------------- // | ||
// GridFs | ||
// -------------------------------------------------------------------------- // | ||
|
||
let fsBucket: any; | ||
|
||
connection.once('open', function () { | ||
fsBucket = new mongo.GridFSBucket(connection.db); | ||
}); | ||
|
||
// -------------------------------------------------------------------------- // | ||
// Teams | ||
// -------------------------------------------------------------------------- // | ||
|
||
export async function getTeams() { | ||
let teams: Team[] | undefined = cache.get('teams'); | ||
|
||
if (teams) { | ||
return teams; | ||
} | ||
|
||
teams = await TeamCollection.find().lean().exec(); | ||
// sort null programs last | ||
sortNullProgramsForTeamsLast(teams); | ||
|
||
cache.set('teams', teams); | ||
return teams; | ||
} | ||
|
||
export function sortNullProgramsForTeamsLast(teams: Team[]): void { | ||
// default sort has null entries first so we will | ||
// sort nulls last since Sage is null and should be last | ||
// as per business requirements | ||
if (!teams) return teams; | ||
teams.sort((a, b) => { | ||
if (!a.program) { | ||
return 1; | ||
} else if (!b.program) { | ||
return -1; | ||
} else { | ||
return (a.program + a.team_full).localeCompare(b.program + b.team_full); | ||
} | ||
}); | ||
} | ||
|
||
export async function teamsRoute( | ||
req: Request, | ||
res: Response, | ||
next: NextFunction, | ||
) { | ||
try { | ||
const teams = await getTeams(); | ||
setHeaders(res); | ||
res.json({ items: teams }); | ||
} catch (err) { | ||
next(err); | ||
} | ||
} | ||
|
||
// -------------------------------------------------------------------------- // | ||
// Team member images | ||
// -------------------------------------------------------------------------- // | ||
|
||
export async function getTeamMemberImage(name: string) { | ||
name = name.toLowerCase().replace(/[- ]/g, '_'); | ||
|
||
let files = await fsBucket.find({ filename: name + '.jpg' }).toArray(); | ||
if (!files.length) { | ||
files = await fsBucket.find({ filename: name + '.jpeg' }).toArray(); | ||
if (!files.length) { | ||
files = await fsBucket.find({ filename: name + '.png' }).toArray(); | ||
} | ||
} | ||
|
||
return files[0] || undefined; | ||
} | ||
|
||
export async function teamMemberImageRoute( | ||
req: Request, | ||
res: Response, | ||
next: NextFunction, | ||
) { | ||
if (!req.params || !req.params.name) { | ||
res.status(404).send('Not found'); | ||
return; | ||
} | ||
|
||
try { | ||
const name = req.params.name.trim(); | ||
const file = await getTeamMemberImage(name); | ||
|
||
if (file?._id) { | ||
const stream = fsBucket.openDownloadStream(file._id); | ||
|
||
stream.on('data', (chunk: Buffer) => { | ||
res.write(chunk); | ||
}); | ||
|
||
stream.on('error', () => { | ||
res.sendStatus(404); | ||
}); | ||
|
||
stream.on('end', () => { | ||
res.end(); | ||
}); | ||
} else { | ||
res.end(); | ||
} | ||
} catch (err) { | ||
next(err); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,76 @@ | ||
import { Response } from 'express'; | ||
import debug from 'debug'; | ||
import NodeCache from 'node-cache'; | ||
|
||
export function setHeaders(res: Response) { | ||
res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate'); | ||
res.setHeader('Pragma', 'no-cache'); | ||
res.setHeader('Expires', 0); | ||
} | ||
|
||
// Normalize a port into a number, string, or false. | ||
export function normalizePort(val: any) { | ||
const tPort = parseInt(val, 10); | ||
|
||
if (isNaN(tPort)) { | ||
// named pipe | ||
return val; | ||
} | ||
|
||
if (tPort >= 0) { | ||
// port number | ||
return tPort; | ||
} | ||
|
||
return false; | ||
} | ||
|
||
// Event listener for HTTP server "error" event | ||
export function onError(error: any, port: any) { | ||
if (error.syscall !== 'listen') { | ||
throw error; | ||
} | ||
|
||
const bind = typeof port === 'string' ? 'Pipe ' + port : 'Port ' + port; | ||
|
||
// handle specific listen errors with friendly messages | ||
switch (error.code) { | ||
case 'EACCES': | ||
console.error(bind + ' requires elevated privileges'); | ||
process.exit(1); | ||
break; | ||
case 'EADDRINUSE': | ||
console.error(bind + ' is already in use'); | ||
process.exit(1); | ||
break; | ||
default: | ||
throw error; | ||
} | ||
} | ||
|
||
// Event listener for HTTP server "listening" event | ||
export function onListening(address: any) { | ||
debug('Listening on ' + typeof address === 'string' ? 'pipe ' + address : 'port ' + address.port); | ||
} | ||
|
||
// -------------------------------------------------------------------------- // | ||
// Cache | ||
// -------------------------------------------------------------------------- // | ||
export const cache = new NodeCache(); | ||
|
||
// TODO: Performance issues with node-cache on large object, this should be revisited when possible. | ||
// For now used AlternativeCache (local variables) to store large set of data. | ||
|
||
class AlternativeCache { | ||
data: { [key: string]: any } = {}; | ||
|
||
set(key: string, data: any) { | ||
this.data[key] = data; | ||
} | ||
|
||
get(key: string) { | ||
return this.data[key] || undefined; | ||
} | ||
} | ||
|
||
export const altCache = new AlternativeCache(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export * from './dataversion'; | ||
export * from './teams'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
// -------------------------------------------------------------------------- // | ||
// External | ||
// -------------------------------------------------------------------------- // | ||
import { Schema, model } from 'mongoose'; | ||
|
||
// -------------------------------------------------------------------------- // | ||
// Internal | ||
// -------------------------------------------------------------------------- // | ||
import { Team, TeamMember } from '@sagebionetworks/agora/api-client-angular'; | ||
|
||
// -------------------------------------------------------------------------- // | ||
// Schemas | ||
// -------------------------------------------------------------------------- // | ||
const TeamMemberSchema = new Schema<TeamMember>({ | ||
name: { type: String, required: true }, | ||
isPrimaryInvestigator: { type: Boolean, required: true }, | ||
url: String, | ||
}); | ||
|
||
const TeamSchema = new Schema<Team>( | ||
{ | ||
team: { type: String, required: true }, | ||
team_full: { type: String, required: true }, | ||
program: { type: String, required: true }, | ||
description: { type: String, required: true }, | ||
members: { type: [TeamMemberSchema], required: true }, | ||
}, | ||
{ collection: 'teaminfo' }, | ||
); | ||
|
||
// -------------------------------------------------------------------------- // | ||
// Models | ||
// -------------------------------------------------------------------------- // | ||
export const TeamCollection = model<Team>('TeamCollection', TeamSchema); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
API_DOCS_URL="http://localhost:8000/api-docs" | ||
APP_VERSION="0.0.1" | ||
APP_VERSION="4.0.0" | ||
CSR_API_URL="http://localhost:8000/api/v1" | ||
SSR_API_URL="http://agora-api:3333/v1" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.