diff --git a/src/api/app.js b/src/api/app.js
index 9ba2d29..4e18b89 100644
--- a/src/api/app.js
+++ b/src/api/app.js
@@ -6,17 +6,30 @@ const env = process.env.NODE_ENV || 'dev'
if ('dev' === env) require('babel-register')
+const app = require('./index').default
+const db = require('./db').default
+const json = require('./json.mjs').default
+
const fullEnv = ('dev' === env) ? 'Development' : 'Production'
-/** Configuration for the application */
-const appConfig = require('./appsettings.json')
+const { port } = json('./appsettings.json')
-/** Express app */
-const app = require('./index').default
+/**
+ * Log a start-up message for the app
+ * @param {string} status The status to display
+ */
+const startupMsg = (status) => {
+ console.log(chalk`{reset myPrayerJournal ${status} | Port: {bold ${port}} | Mode: {bold ${fullEnv}}}`)
+}
-// Ensure the database exists...
-require('./db').default.verify().then(() =>
- // ...and start it up!
- app.listen(appConfig.port, () => {
- console.log(chalk`{reset myPrayerJournal | Port: {bold ${appConfig.port}} | Mode: {bold ${fullEnv}}}`)
- }))
+// Ensure the database exists before starting up
+db.verify()
+.then(() => app.listen(port, () => startupMsg('ready')))
+.catch(err => {
+ console.log(chalk`\n{reset {bgRed.white.bold || Error connecting to PostgreSQL }}`)
+ for (let key of Object.keys(err)) {
+ console.log(chalk`${key}: {reset {bold ${err[key]}}}`)
+ }
+ console.log('')
+ startupMsg('failed')
+})
diff --git a/src/api/db/index.js b/src/api/db/index.js
index 0b5b349..5e20290 100644
--- a/src/api/db/index.js
+++ b/src/api/db/index.js
@@ -18,6 +18,6 @@ const query = (text, params) => pool.query(text, params)
export default {
query: query,
- request: request(query),
+ request: request(pool),
verify: ddl(query).ensureDatabase
}
diff --git a/src/api/db/request.js b/src/api/db/request.js
index c25f2f0..4a96a85 100644
--- a/src/api/db/request.js
+++ b/src/api/db/request.js
@@ -3,7 +3,7 @@
import { Pool } from 'pg'
import cuid from 'cuid'
-export default function (query) {
+export default function (pool) {
return {
/**
* Get the current requests for a user (i.e., their complete current journal)
@@ -11,24 +11,55 @@ export default function (query) {
* @return The requests that make up the current journal
*/
journal: async userId =>
- (await query('SELECT "requestId" FROM request WHERE "userId" = $1', [userId])).rows,
+ (await pool.query({
+ name: 'journal',
+ text: `
+ SELECT
+ request."requestId",
+ (SELECT "text"
+ FROM mpj.history
+ WHERE history."requestId" = request."requestId"
+ AND "text" IS NOT NULL
+ ORDER BY "asOf" DESC
+ LIMIT 1) AS "text",
+ (SELECT "asOf"
+ FROM mpj.history
+ WHERE history."requestId" = request."requestId"
+ ORDER BY "asOf" DESC
+ LIMIT 1) AS "asOf"
+ FROM mpj.request
+ WHERE "userId" = $1
+ GROUP BY request."requestId"`
+ }, [userId])).rows,
/**
* Add a new prayer request
* @param {string} userId The Id of the user
* @param {string} requestText The text of the request
- * @return {string} The Id of the created request
+ * @return The created request
*/
addNew: async (userId, requestText) => {
const id = cuid()
const enteredOn = Date.now()
- await query(`
- BEGIN;
- INSERT INTO request ("requestId", "enteredOn", "userId") VALUES ($1, $2, $3);
- INSERT INTO history ("requestId", "asOf", "status", "text") VALUES ($1, $2, 'Created', $4);
- COMMIT;`,
- [ id, enteredOn, userId, requestText ])
- return id
+ ;(async () => {
+ const client = await pool.connect()
+ try {
+ await client.query('BEGIN')
+ await client.query(
+ 'INSERT INTO mpj.request ("requestId", "enteredOn", "userId") VALUES ($1, $2, $3)',
+ [ id, enteredOn, userId ])
+ await client.query(
+ `INSERT INTO mpj.history ("requestId", "asOf", "status", "text") VALUES ($1, $2, 'Created', $3)`,
+ [ id, enteredOn, requestText ])
+ await client.query('COMMIT')
+ } catch (e) {
+ await client.query('ROLLBACK')
+ throw e
+ } finally {
+ client.release()
+ }
+ return { requestId: id, text: requestText, asOf: enteredOn }
+ })().catch(e => console.error(e.stack))
}
}
}
diff --git a/src/api/index.js b/src/api/index.js
index a467651..f516249 100644
--- a/src/api/index.js
+++ b/src/api/index.js
@@ -1,6 +1,7 @@
'use strict'
import Koa from 'koa'
+import bodyParser from 'koa-bodyparser'
import morgan from 'koa-morgan'
import send from 'koa-send'
import serveFrom from 'koa-static'
@@ -16,6 +17,8 @@ export default app
.use(morgan('dev'))
// Serve the Vue files from /public
.use(serveFrom('public'))
+ // Parse the body into ctx.request.body, if present
+ .use(bodyParser())
// Tie in all the routes
.use(router.routes())
.use(router.allowedMethods())
diff --git a/src/api/json.mjs b/src/api/json.mjs
new file mode 100644
index 0000000..c5eb575
--- /dev/null
+++ b/src/api/json.mjs
@@ -0,0 +1,12 @@
+'use strict'
+
+import fs from 'fs'
+
+/**
+ * Read and parse a JSON file
+ * @param {string} path The path to the file
+ * @param {string} encoding The encoding of the file (defaults to UTF-8)
+ * @return {*} The parsed contents of the file
+ */
+export default (path, encoding = 'utf-8') =>
+ JSON.parse(fs.readFileSync(path, encoding))
diff --git a/src/api/routes/request.js b/src/api/routes/request.js
index b08edf4..06f3191 100644
--- a/src/api/routes/request.js
+++ b/src/api/routes/request.js
@@ -8,8 +8,7 @@ const router = new Router()
export default function (checkJwt) {
router.post('/', checkJwt, async (ctx, next) => {
- const newId = await db.request.addNew(ctx.state.user.sub, ctx.body.requestText)
- ctx.body = { id: newId }
+ ctx.body = await db.request.addNew(ctx.state.user.sub, ctx.request.body.requestText)
await next()
})
diff --git a/src/app/src/api/index.js b/src/app/src/api/index.js
index 91b7732..c0a1a83 100644
--- a/src/app/src/api/index.js
+++ b/src/app/src/api/index.js
@@ -23,6 +23,12 @@ export default {
/**
* Get all prayer requests and their most recent updates
*/
- journal: () => http.get('journal/')
+ journal: () => http.get('journal/'),
+
+ /**
+ * Add a new prayer request
+ * @param {string} requestText The text of the request to be added
+ */
+ addRequest: requestText => http.post('request/', { requestText })
}
diff --git a/src/app/src/auth/AuthService.js b/src/app/src/auth/AuthService.js
index c71717f..e840abf 100644
--- a/src/app/src/auth/AuthService.js
+++ b/src/app/src/auth/AuthService.js
@@ -1,7 +1,9 @@
-import auth0 from 'auth0-js'
-import { AUTH_CONFIG } from './auth0-variables'
+'use strict'
-import * as types from '@/store/mutation-types'
+import auth0 from 'auth0-js'
+
+import AUTH_CONFIG from './auth0-variables'
+import mutations from '@/store/mutation-types'
export default class AuthService {
@@ -64,7 +66,7 @@ export default class AuthService {
this.setSession(authResult)
this.userInfo(authResult.accessToken)
.then(user => {
- store.commit(types.USER_LOGGED_ON, user)
+ store.commit(mutations.USER_LOGGED_ON, user)
router.replace('/dashboard')
})
}
@@ -93,7 +95,7 @@ export default class AuthService {
localStorage.removeItem('expires_at')
localStorage.setItem('user_profile', JSON.stringify({}))
// navigate to the home route
- store.commit(types.USER_LOGGED_OFF)
+ store.commit(mutations.USER_LOGGED_OFF)
router.replace('/')
}
diff --git a/src/app/src/components/Dashboard.vue b/src/app/src/components/Dashboard.vue
index b86c3a7..71c273d 100644
--- a/src/app/src/components/Dashboard.vue
+++ b/src/app/src/components/Dashboard.vue
@@ -5,14 +5,19 @@
template(v-if="!isLoadingJournal")
new-request
p journal has {{ journal.length }} entries
+ request-list-item(v-for="request in journal" v-bind:request="request" v-bind:key="request.requestId")
\ No newline at end of file
diff --git a/src/app/src/components/request/RequestListItem.vue b/src/app/src/components/request/RequestListItem.vue
new file mode 100644
index 0000000..d2b2445
--- /dev/null
+++ b/src/app/src/components/request/RequestListItem.vue
@@ -0,0 +1,15 @@
+
+ div
+ p Id {{ request.requestId }} as of {{ request.asOf }}
+ p {{ request.text }}
+
+
+
diff --git a/src/app/src/store/action-types.js b/src/app/src/store/action-types.js
index dc4fcf0..6d24c31 100644
--- a/src/app/src/store/action-types.js
+++ b/src/app/src/store/action-types.js
@@ -1 +1,8 @@
-export const LOAD_JOURNAL = 'load-journal'
+'use strict'
+
+export default {
+ /** Action to add a prayer request (pass request text) */
+ ADD_REQUEST: 'add-request',
+ /** Action to load the user's prayer journal */
+ LOAD_JOURNAL: 'load-journal'
+}
diff --git a/src/app/src/store/index.js b/src/app/src/store/index.js
index a9cb43c..556f9ce 100644
--- a/src/app/src/store/index.js
+++ b/src/app/src/store/index.js
@@ -4,8 +4,8 @@ import Vuex from 'vuex'
import api from '@/api'
import AuthService from '@/auth/AuthService'
-import * as types from './mutation-types'
-import * as actions from './action-types'
+import mutations from './mutation-types'
+import actions from './action-types'
Vue.use(Vuex)
@@ -38,38 +38,48 @@ export default new Vuex.Store({
isLoadingJournal: false
},
mutations: {
- [types.USER_LOGGED_ON] (state, user) {
+ [mutations.USER_LOGGED_ON] (state, user) {
localStorage.setItem('user_profile', JSON.stringify(user))
state.user = user
api.setBearer(localStorage.getItem('id_token'))
state.isAuthenticated = true
},
- [types.USER_LOGGED_OFF] (state) {
+ [mutations.USER_LOGGED_OFF] (state) {
state.user = {}
api.removeBearer()
state.isAuthenticated = false
},
- [types.LOADING_JOURNAL] (state, flag) {
+ [mutations.LOADING_JOURNAL] (state, flag) {
state.isLoadingJournal = flag
},
- [types.LOADED_JOURNAL] (state, journal) {
+ [mutations.LOADED_JOURNAL] (state, journal) {
state.journal = journal
+ },
+ [mutations.REQUEST_ADDED] (state, newRequest) {
+ state.journal.unshift(newRequest)
}
},
actions: {
- [actions.LOAD_JOURNAL] ({ commit }) {
- commit(types.LOADED_JOURNAL, {})
- commit(types.LOADING_JOURNAL, true)
+ async [actions.LOAD_JOURNAL] ({ commit }) {
+ commit(mutations.LOADED_JOURNAL, {})
+ commit(mutations.LOADING_JOURNAL, true)
api.setBearer(localStorage.getItem('id_token'))
- api.journal()
- .then(jrnl => {
- commit(types.LOADING_JOURNAL, false)
- commit(types.LOADED_JOURNAL, jrnl.data)
- })
- .catch(err => {
- commit(types.LOADING_JOURNAL, false)
- logError(err)
- })
+ try {
+ const jrnl = await api.journal()
+ commit(mutations.LOADED_JOURNAL, jrnl.data)
+ } catch (err) {
+ logError(err)
+ } finally {
+ commit(mutations.LOADING_JOURNAL, false)
+ }
+ },
+ async [actions.ADD_REQUEST] ({ commit }, requestText) {
+ try {
+ const newRequest = await api.addRequest(requestText)
+ commit(mutations.REQUEST_ADDED, newRequest)
+ } catch (err) {
+ logError(err)
+ }
}
},
getters: {},
diff --git a/src/app/src/store/mutation-types.js b/src/app/src/store/mutation-types.js
index d306356..e396b63 100644
--- a/src/app/src/store/mutation-types.js
+++ b/src/app/src/store/mutation-types.js
@@ -1,4 +1,14 @@
-export const USER_LOGGED_OFF = 'user-logged-out'
-export const USER_LOGGED_ON = 'user-logged-on'
-export const LOADING_JOURNAL = 'loading-journal'
-export const LOADED_JOURNAL = 'journal-loaded'
+'use strict'
+
+export default {
+ /** Mutation for when the user's prayer journal is being loaded */
+ LOADING_JOURNAL: 'loading-journal',
+ /** Mutation for when the user's prayer journal has been loaded */
+ LOADED_JOURNAL: 'journal-loaded',
+ /** Mutation for adding a new prayer request (pass text) */
+ REQUEST_ADDED: 'request-added',
+ /** Mutation for logging a user off */
+ USER_LOGGED_OFF: 'user-logged-off',
+ /** Mutation for logging a user on (pass user) */
+ USER_LOGGED_ON: 'user-logged-on'
+}