-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* add voting API endpoint WIP * polls API middleware * linting * specs for votes * restructure votes data * update TODO with new criteria * fix parenthesis * remove vote endpoint * fix specs * remove unuised voteRow variable * update varibles/queries for naming changes * use feature toggle for enabling postgres * fix: correct import of remove vote module * use params in /votes request * fix: split candidate query param * feat: hook feature ideas component up to API * feat: authorize voting via keycloak * fix: don't send empty auth header * feat: use keycloak error response handler in voting * refactor: use v-if (to prevent flickering change) * use identifier, not sys-id * refactor: make vote/unvote methods more RESTful * chore: return error code * test: update FeatureIdeas tests for refactor * refactor: move db logic to model file * refactor: export router for polls and events * refactor: use express next for error handling * test: update event/jira unit tests * test: spec polls API * fix: keycloak url and import * fix: shared error logging * do NOT respond with error when response is finished --------- Co-authored-by: Richard Doe <[email protected]>
- Loading branch information
1 parent
ca53b3c
commit 1c5b57d
Showing
33 changed files
with
1,141 additions
and
392 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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
// TODO: move to a standalone express micro-service, so the portal.js app does | ||
// not have a direct dependency on postgres, indeed need not know what | ||
// back-end storage is used. | ||
|
||
import express from 'express'; | ||
|
||
import logEvent from './log.js'; | ||
import eventTrending from './trending.js'; | ||
import eventViews from './views.js'; | ||
|
||
const router = express.Router(); | ||
|
||
router.post('/', logEvent); | ||
router.get('/trending', eventTrending); | ||
router.get('/views', eventViews); | ||
|
||
export default router; |
125 changes: 60 additions & 65 deletions
125
packages/portal/src/server-middleware/api/events/log.js
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,83 +1,78 @@ | ||
import isbot from 'isbot'; | ||
import pg from './pg.js'; | ||
import pg from '../pg.js'; | ||
|
||
// TODO: use `next` for error handling | ||
// TODO: accept multiple uris for the same action | ||
// TODO: log user agent? | ||
// TODO: validate action_types | ||
export default (config = {}) => { | ||
pg.config = config; | ||
export default async(req, res, next) => { | ||
try { | ||
// Respond early as clients don't need to wait for the results of this logging | ||
res.sendStatus(204); | ||
|
||
return async(req, res) => { | ||
try { | ||
// Respond early as clients don't need to wait for the results of this logging | ||
res.sendStatus(204); | ||
if (isbot(req.get('user-agent'))) { | ||
return; | ||
} | ||
|
||
if (!pg.enabled || isbot(req.get('user-agent'))) { | ||
return; | ||
} | ||
const { actionType, objectUri, sessionId } = req.body; | ||
const url = new URL(objectUri); | ||
// Ignore any search query or hash | ||
const uri = `${url.origin}${url.pathname}`; | ||
|
||
const { actionType, objectUri, sessionId } = req.body; | ||
const url = new URL(objectUri); | ||
// Ignore any search query or hash | ||
const uri = `${url.origin}${url.pathname}`; | ||
let objectRow; | ||
const selectObjectResult = await pg.query( | ||
'SELECT id FROM events.objects WHERE uri=$1', | ||
[uri] | ||
); | ||
|
||
let objectRow; | ||
const selectObjectResult = await pg.query( | ||
'SELECT id FROM events.objects WHERE uri=$1', | ||
if (selectObjectResult.rowCount > 0) { | ||
objectRow = selectObjectResult.rows[0]; | ||
} else { | ||
const insertObjectResult = await pg.query( | ||
'INSERT INTO events.objects (uri) VALUES($1) RETURNING id', | ||
[uri] | ||
); | ||
objectRow = insertObjectResult.rows[0]; | ||
} | ||
|
||
if (selectObjectResult.rowCount > 0) { | ||
objectRow = selectObjectResult.rows[0]; | ||
} else { | ||
const insertObjectResult = await pg.query( | ||
'INSERT INTO events.objects (uri) VALUES($1) RETURNING id', | ||
[uri] | ||
); | ||
objectRow = insertObjectResult.rows[0]; | ||
} | ||
let sessionRow; | ||
const selectSessionResult = await pg.query( | ||
'SELECT id FROM events.sessions WHERE uuid=$1', | ||
[sessionId] | ||
); | ||
|
||
let sessionRow; | ||
const selectSessionResult = await pg.query( | ||
'SELECT id FROM events.sessions WHERE uuid=$1', | ||
if (selectSessionResult.rowCount > 0) { | ||
sessionRow = selectSessionResult.rows[0]; | ||
} else { | ||
const insertSessionResult = await pg.query( | ||
'INSERT INTO events.sessions (uuid) VALUES($1) RETURNING id', | ||
[sessionId] | ||
); | ||
sessionRow = insertSessionResult.rows[0]; | ||
} | ||
|
||
if (selectSessionResult.rowCount > 0) { | ||
sessionRow = selectSessionResult.rows[0]; | ||
} else { | ||
const insertSessionResult = await pg.query( | ||
'INSERT INTO events.sessions (uuid) VALUES($1) RETURNING id', | ||
[sessionId] | ||
); | ||
sessionRow = insertSessionResult.rows[0]; | ||
} | ||
|
||
const selectActionResult = await pg.query(` | ||
SELECT a.id FROM events.actions a LEFT JOIN events.action_types at | ||
ON a.action_type_id=at.id | ||
WHERE a.object_id=$1 | ||
AND at.name=$2 | ||
AND a.session_id=$3 | ||
`, | ||
[objectRow.id, actionType, sessionRow.id] | ||
); | ||
if (selectActionResult.rowCount > 0) { | ||
// this session has already logged this action type for this object; don't log it again | ||
return; | ||
} | ||
|
||
await pg.query(` | ||
INSERT INTO events.actions (object_id, action_type_id, session_id, occurred_at) | ||
SELECT $1, at.id, $2, CURRENT_TIMESTAMP | ||
FROM events.action_types at | ||
WHERE at.name=$3 | ||
`, | ||
[objectRow.id, sessionRow.id, actionType] | ||
); | ||
} catch (err) { | ||
console.error(err); | ||
const selectActionResult = await pg.query(` | ||
SELECT a.id FROM events.actions a LEFT JOIN events.action_types at | ||
ON a.action_type_id=at.id | ||
WHERE a.object_id=$1 | ||
AND at.name=$2 | ||
AND a.session_id=$3 | ||
`, | ||
[objectRow.id, actionType, sessionRow.id] | ||
); | ||
if (selectActionResult.rowCount > 0) { | ||
// this session has already logged this action type for this object; don't log it again | ||
return; | ||
} | ||
}; | ||
|
||
await pg.query(` | ||
INSERT INTO events.actions (object_id, action_type_id, session_id, occurred_at) | ||
SELECT $1, at.id, $2, CURRENT_TIMESTAMP | ||
FROM events.action_types at | ||
WHERE at.name=$3 | ||
`, | ||
[objectRow.id, sessionRow.id, actionType] | ||
); | ||
} catch (err) { | ||
next(err); | ||
} | ||
}; |
Oops, something went wrong.