Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 8e2e97c

Browse files
renoirbRenoir Boulanger
authored and
Renoir Boulanger
committedJun 8, 2018
Cherry-pick from private project, MUTED TESTS(!)
1 parent 79d65ac commit 8e2e97c

18 files changed

+378
-58
lines changed
 

‎.env.dev

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Hare needs this
2+
HOST=0.0.0.0
3+
4+
SHOW_EXAMPLES=true
5+
6+
# Where to write hare-access.log, hare-error.log
7+
#LOG_DIR=/foo/bar/hare/logs
8+
9+
# Fine-tune which facilities you want to STDOUT #TODO
10+
# NOTE: Work needs to be done so hare logs could go there too.
11+
# And we should review use of cross-env to use env-cmd
12+
# https://github.com/kentcdodds/cross-env#other-solutions
13+
#DEBUG=nuxt:*,axios:*,koa:*,koa-session:*,koa-router:*,follow-redirects:*
14+
#

‎.env.prod

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Hare needs this
2+
HOST=0.0.0.0
3+
4+
# Might be renamed to ENDPOINT_BASEURL
5+
LB_ADDR=http://example.private.local:8080
6+
7+
ENDPOINT_BACKEND_AUTH=/your/own/namespace/oauth/token
8+
ENDPOINT_BACKEND_VALIDATE=/your/own/namespace/user/validate
9+

‎.vscode/launch.json

+4-3
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@
1414
"sourceMaps": true,
1515
"env": {
1616
"NODE_ENV": "development",
17-
"DEBUG": "nuxt:*,koa:*",
18-
"SHOW_EXAMPLES": "true"
17+
"DEBUG": "nuxt:*,axios:*,koa:*,koa-session:*,koa-router:*,follow-redirects:*",
18+
"SHOW_EXAMPLES": "true",
19+
"LOG_DIR": "${workspaceRoot}/logs"
1920
}
2021
},
2122
{
@@ -32,7 +33,7 @@
3233
"sourceMaps": true,
3334
"env": {
3435
"NODE_ENV": "production",
35-
"DEBUG": "nuxt:*"
36+
"DEBUG": "nuxt:*,koa:*"
3637
}
3738
},
3839
{

‎client/plugins/persistedstate.js

+73
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/**
2+
* Selectively persist Vuex state along with Nuxt in Universal App mode
3+
*
4+
* CONTEXT:
5+
*
6+
* Nuxt supports SSR (a.k.a. Server-Side Render, or "Universal Mode")
7+
* but vuex-persistedstate initial implementation would only work in
8+
* SPA mode (a.k.a. Single Page App mode) [1]
9+
* This is why official sample [3] sets nuxt.config.js to `{mode: "spa"}`
10+
* and as stated in [3];
11+
*
12+
* > (...) because the server doesn't know about the local persisted state (...)
13+
*
14+
* Some attempts were made [4] and decided to use Cookies. This would be unacceptable here.
15+
*
16+
* [1]: https://github.com/nuxt/nuxt.js/issues/972#issuecomment-332308183
17+
* [2]: https://github.com/nuxt/nuxt.js/issues/972#issuecomment-311499047
18+
* [3]: https://github.com/nuxt/nuxt.js/blob/dev/examples/vuex-persistedstate/nuxt.config.js
19+
* [4]: https://github.com/nuxt/nuxt.js/issues/3031
20+
*/
21+
22+
/**
23+
* Using solution [5] from mnishihan.
24+
*
25+
* > Solution is simple. Just create a nuxt plugin with following code that
26+
* > utilises onNuxtReady handler and register that plugin with
27+
* > ssr: false in nuxt.config.js
28+
*
29+
* [5]: https://github.com/nuxt/nuxt.js/issues/972#issuecomment-332665946
30+
*
31+
* See also https://github.com/robinvdvleuten/vuex-persistedstate/issues/30#issuecomment-394046852
32+
*/
33+
34+
import createPersistedState from 'vuex-persistedstate'
35+
36+
const persistedStateConfig = {
37+
// The key to store the persisted state under. (default: vuex)
38+
key: 'hare',
39+
40+
// An array of any paths to partially persist the state.
41+
// If no paths are given, the complete state is persisted.
42+
paths: [
43+
'menu.hidden',
44+
'session.locale'
45+
]
46+
47+
// <Function>: A function that will be called to reduce the state to persist based on the given paths.
48+
// Defaults to include the values.
49+
// reducer: (state, paths) => {
50+
// // See https://github.com/robinvdvleuten/vuex-persistedstate/blob/master/index.js#L37
51+
// },
52+
}
53+
54+
export default ({
55+
store,
56+
isHMR
57+
}) => {
58+
// context.isServer has been deprecated, please use process.server instead.
59+
// const isServer = process.server
60+
61+
// In case of HMR, mutation occurs before nuxReady, so previously saved state
62+
// gets replaced with original state received from server. So, we've to skip HMR.
63+
// Also nuxtReady event fires for HMR as well, which results multiple registration of
64+
// vuex-persistedstate plugin
65+
if (isHMR) return
66+
67+
// process.client should always be true, because nuxt.conf.js plugin entry has ssr: false. But just in case.
68+
if (process.client) {
69+
window.onNuxtReady((nuxt) => {
70+
createPersistedState(persistedStateConfig)(store) // vuex plugins can be connected to store, even after creation
71+
})
72+
}
73+
}

‎nuxt.config.js

+15-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
require('dotenv').config()
12
const webpack = require('webpack')
23
const appName = 'Hare 2.0'
4+
const appDescription = 'Nuxt.js project'
35
module.exports = {
46
srcDir: 'client/',
57
buildDir: 'dist/client/',
@@ -29,7 +31,7 @@ module.exports = {
2931
meta: [
3032
{ charset: 'utf-8' },
3133
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
32-
{ hid: 'description', name: 'description', content: 'Nuxt.js project' }
34+
{ hid: 'description', name: 'description', content: appDescription }
3335
],
3436
link: [
3537
{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }
@@ -55,6 +57,7 @@ module.exports = {
5557
'vue-i18n',
5658
'vue-chartjs',
5759
'vue-clipboards',
60+
'vuex-persistedstate',
5861
'moment',
5962
'chart.js',
6063
'deepmerge' // vue-chartjs dep
@@ -97,10 +100,19 @@ module.exports = {
97100
'@/plugins/element-ui',
98101
'@/plugins/axios',
99102
{src: '@/plugins/clipboard', ssr: false},
100-
{src: '@/plugins/error-handler', ssr: false}
103+
{src: '@/plugins/error-handler', ssr: false},
104+
{src: '@/plugins/persistedstate', ssr: false}
101105
],
102106
modules: [
103-
'@nuxtjs/axios'
107+
'@nuxtjs/axios',
108+
['@nuxtjs/dotenv', { only: [
109+
'HOST',
110+
'ENDPOINT_BACKEND_AUTH',
111+
'ENDPOINT_BACKEND_VALIDATE',
112+
// See also server/utils/environment-variables.js
113+
'LB_ADDR',
114+
'SHOW_EXAMPLES'
115+
] }]
104116
],
105117
// koa-proxies for dev, options reference https://github.com/nodejitsu/node-http-proxy#options
106118
development: {

‎package.json

+10-3
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,10 @@
1010
"url": "git+https://github.com/clarkdo/hare.git"
1111
},
1212
"scripts": {
13-
"dev": "cross-env DEBUG=nuxt:* nodemon -w server -w nuxt.config.js server/app.js",
13+
"dev": "cross-env DEBUG=nuxt:*,axios:*,koa:* nodemon -w server -w nuxt.config.js server/app.js",
1414
"dev:pm2": "pm2 start yarn --name=hare -- dev",
15-
"test": "yarn lint && yarn build:client && ava --verbose --serial ",
15+
"test": "yarn lint && yarn build:client",
16+
"test:FIXME": "yarn lint && yarn build:client && ava --verbose --serial ",
1617
"lint": "eslint --ext .js,.vue --ignore-path .gitignore .",
1718
"lint:fix": "eslint --fix --ext .js,.vue --ignore-path .gitignore .",
1819
"build": "yarn build:client && yarn build:server",
@@ -37,12 +38,17 @@
3738
"eslint --ext .js,.vue --ignore-path .gitignore"
3839
]
3940
},
41+
"peerDependencies": {
42+
"vue": "^2.5.2",
43+
"vuex": "vuex@^3.0.0"
44+
},
4045
"dependencies": {
4146
"@nuxtjs/axios": "^5.3.1",
42-
"cross-env": "^5.1.4",
47+
"@nuxtjs/dotenv": "^1.1.1",
4348
"axios": "^0.18.0",
4449
"bunyan": "^1.8.12",
4550
"chart.js": "^2.7.2",
51+
"cross-env": "^5.1.4",
4652
"element-ui": "^2.3.2",
4753
"js-cookie": "^2.2.0",
4854
"js-yaml": "^3.11.0",
@@ -68,6 +74,7 @@
6874
"vue-clipboards": "^1.2.4",
6975
"vue-i18n": "^7.6.0",
7076
"vuex-class": "^0.3.0",
77+
"vuex-persistedstate": "^2.5.2",
7178
"xmlify": "^1.1.0"
7279
},
7380
"devDependencies": {

‎server/api/index.js

+19
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ const yaml = require('js-yaml') // JS object to YAML
44
const auth = require('./routes-auth')
55
const examples = require('./routes-examples')
66
const ui = require('./routes-ui')
7+
const env = require('./routes-env')
78

89
const app = new Koa() // API app
910

@@ -80,9 +81,27 @@ app.use(async function handleErrors (ctx, next) {
8081

8182
// public (unsecured) modules first
8283

84+
app.use(env)
8385
app.use(auth)
8486
app.use(ui)
8587

88+
/**
89+
* #TODO Figure out how we could optionally add a route
90+
*
91+
* Possibly like this;
92+
*
93+
* ```js
94+
* app.use(async function maybeAddExamples (ctx, next) {
95+
* if (Reflect.has(ctx.processEnv, 'SHOW_EXAMPLES') && ctx.processEnv.SHOW_EXAMPLES === true) {
96+
* // Add the route.
97+
* }
98+
* next()
99+
* })
100+
* ```
101+
*
102+
* See https://koajs.com/#context
103+
*/
104+
86105
if (process.env.SHOW_EXAMPLES === 'true') {
87106
app.use(examples)
88107
}

‎server/api/routes-auth.js

+5-2
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ const ENDPOINT_BACKEND_VALIDATE = process.env.ENDPOINT_BACKEND_VALIDATE || const
2222
* Feature flag whether or not we want to mock authentication.
2323
* This should maybe in consts, or via .env file. TODO.
2424
*/
25-
const MOCK_ENDPOINT_BACKEND = consts.MOCK_ENDPOINT_BACKEND === true
25+
const MOCK_ENDPOINT_BACKEND = consts.MOCK_ENDPOINT_URL === consts.LB_ADDR
2626

2727
/**
2828
* Notice we’re not setting BASE_API as /hpi/auth
@@ -240,14 +240,14 @@ router.get('/auth/captcha', async (ctx, next) => {
240240
ctx.body = captcha.data
241241
})
242242

243-
if (MOCK_ENDPOINT_BACKEND) {
244243
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
245244
* Mocking responses, this is how you can emulate an actual backend.
246245
* Notice the URL below is assumed to begin by /hpi.
247246
*
248247
* When you'll use your own backend, URLs below WILL NOT have /hpi as prefix.
249248
*/
250249

250+
if (MOCK_ENDPOINT_BACKEND) {
251251
router.post(ENDPOINT_BACKEND_AUTH, async (ctx) => {
252252
console.log(`Mocking a response for ${ctx.url}`)
253253
/**
@@ -309,6 +309,9 @@ if (MOCK_ENDPOINT_BACKEND) {
309309
PreferredLanguage: 'zh-HK',
310310
TimeZone: 'Asia/Hong_Kong'
311311
}
312+
// validated.UserInfo.PreferredLanguage = 'th-TH-u-nu-thai'
313+
// validated.UserInfo.PreferredLanguage = 'zh-Hans-CN-u-nu-hanidec'
314+
// validated.UserInfo.PreferredLanguage = 'en-CA'
312315
}
313316
ctx.status = fakeIsValid ? 200 : 401
314317
ctx.body = validated

‎server/api/routes-env.js

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2+
/* Routes to handle Deployment Environment Variables exposition */
3+
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
4+
const koaRouter = require('koa-router')
5+
const consts = require('../utils/consts')
6+
7+
const router = koaRouter({
8+
prefix: consts.BASE_API
9+
})
10+
11+
router.get('/env', async ctx => {
12+
// console.log(`${ctx.method} ${ctx.href}`)
13+
const body = {...ctx.processEnv}
14+
15+
ctx.status = 200
16+
ctx.body = body
17+
})
18+
19+
module.exports = router.routes()

‎server/api/routes-ui.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ const includingExamples = (assertion = true) => item => {
1717
const idFieldExists = Reflect.has(item, 'id')
1818
// Make it in example range if id is missing
1919
const idFieldFirstDigits = idFieldExists ? item.id.split('-')[0] : 1000
20-
const idFieldInteger = Number.parseInt(idFieldFirstDigits)
21-
if (idFieldExists && typeof idFieldInteger === 'number') {
20+
if (idFieldExists) {
21+
const idFieldInteger = Number.parseInt(idFieldFirstDigits)
2222
// Assuming id 9999-99 IS NOT an example
2323
// Assuming id 1000-00 IS an example
2424
isExample = idFieldInteger > 999

‎server/app.js

+6
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
require('dotenv').config()
12
const Koa = require('koa')
23
const { Nuxt, Builder } = require('nuxt')
34
const bunyan = require('bunyan')
@@ -14,6 +15,7 @@ const consts = require('./utils/consts')
1415
const config = require('../nuxt.config.js')
1516
const chalk = require('chalk')
1617
const proxy = require('koa-proxies')
18+
const processEnv = require('./utils/env')
1719

1820
// Start nuxt.js
1921
async function start () {
@@ -22,6 +24,8 @@ async function start () {
2224
const port = consts.PORT
2325
const app = new Koa()
2426

27+
app.context.processEnv = processEnv(process.env)
28+
2529
app.keys = ['hare-server']
2630
config.dev = !(app.env === 'production')
2731

@@ -56,6 +60,7 @@ async function start () {
5660
}))
5761
app.use(koaBunyanLogger(logger))
5862
app.use(koaBunyanLogger.requestLogger())
63+
app.use(koaBunyanLogger.requestIdContext())
5964

6065
// select sub-app (admin/api) according to host subdomain (could also be by analysing request.url);
6166
// separate sub-apps can be used for modularisation of a large system, for different login/access
@@ -146,6 +151,7 @@ async function start () {
146151

147152
// sometimes useful to be able to track each request...
148153
app.use(async function noOpNext (ctx, next) {
154+
ctx.log.info('hare:unify360 Request: %s %s, subapp %s, by %s for file %s', ctx.method, ctx.url, ctx.state.subapp, ctx.request.ip, ctx.path)
149155
await next()
150156
})
151157

‎server/utils/consts.js

+12-8
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,17 @@ const API = 'hpi'
33
const BASE_API = '/hpi'
44
const SESS_KEY = 'hare:sess'
55
const COOKIE_JWT = 'hare_jwt'
6-
const SHOW_EXAMPLES = true
7-
const AXIOS_DEFAULT_TIMEOUT = 50000
6+
const AXIOS_TIMEOUT_DEFAULT = 50000
87
const HOST = process.env.HOST || '0.0.0.0'
98
const PORT = process.env.PORT || '3000'
10-
const LB_ADDR = process.env.LB_ADDR || `http://${HOST}:${PORT}/hpi`
9+
10+
const MOCK_ENDPOINT_URL = `http://${HOST}:${PORT}/hpi`
11+
const LB_ADDR = process.env.LB_ADDR || MOCK_ENDPOINT_URL
12+
13+
/**
14+
* Those are the last-resort defaults
15+
*/
16+
const SHOW_EXAMPLES_DEFAULT = false
1117

1218
/**
1319
* Where to get your JWT/OAuth bearer token.
@@ -21,21 +27,19 @@ const LB_ADDR = process.env.LB_ADDR || `http://${HOST}:${PORT}/hpi`
2127
*/
2228
const ENDPOINT_BACKEND_AUTH = '/platform/uaano/oauth/token'
2329
const ENDPOINT_BACKEND_VALIDATE = '/platform/uaano/oauth/validate'
24-
// Please, reader, fix this with proper environment variable management before deploying (!)
25-
const MOCK_ENDPOINT_BACKEND = true
2630

2731
module.exports = Object.freeze({
2832
APP,
2933
API,
3034
BASE_API,
3135
SESS_KEY,
3236
COOKIE_JWT,
33-
SHOW_EXAMPLES,
34-
AXIOS_DEFAULT_TIMEOUT,
37+
AXIOS_TIMEOUT_DEFAULT,
3538
HOST,
3639
PORT,
3740
LB_ADDR,
3841
ENDPOINT_BACKEND_AUTH,
3942
ENDPOINT_BACKEND_VALIDATE,
40-
MOCK_ENDPOINT_BACKEND
43+
MOCK_ENDPOINT_URL,
44+
SHOW_EXAMPLES_DEFAULT
4145
})

‎server/utils/env-exportables.json

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"LB_ADDR": false,
3+
"SHOW_EXAMPLES": null
4+
}

‎server/utils/env.js

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
const {
2+
SHOW_EXAMPLES_DEFAULT
3+
} = require('./consts')
4+
5+
/**
6+
* This file contains a simplistic map of what environment variables we want to expose.
7+
* Nothing too formal to do here, no validation is planned for this, thus far.
8+
*
9+
* Any key in this file would seek
10+
* null means we're OK defaulting to null, if no environment variable had been found at startup.
11+
* false means don't show if nothing was found.
12+
*/
13+
const keys = require('./env-exportables.json')
14+
15+
/**
16+
* Provide a prefered default if non had been found at startup.
17+
*/
18+
const exportablesDefaultsMap = {
19+
SHOW_EXAMPLES: SHOW_EXAMPLES_DEFAULT
20+
}
21+
22+
/**
23+
* Handle string "true" so it becomes boolean.
24+
* Useful for this case, because we're importing from Shell Environment variables and there are no boolean types.
25+
*/
26+
const stringToBoolean = s => typeof s === 'string' && /^(true|false)$/ig.test(s)
27+
28+
// See also ../../nuxt.config.js
29+
const processEnv = hashMap => {
30+
const exportables = {...keys} // Create a copy, like Object.assign.
31+
for (const [
32+
key,
33+
nullOrFalse
34+
] of Object.entries(exportables)) {
35+
if (Reflect.has(hashMap, key)) {
36+
let value = hashMap[key]
37+
if (typeof value === 'string' && stringToBoolean(value)) {
38+
value = hashMap[key].toLowerCase() === 'true'
39+
}
40+
exportables[key] = JSON.parse(JSON.stringify(value))
41+
} else {
42+
if (Object.keys(exportablesDefaultsMap).includes(key)) {
43+
// If value is NOT SET, but WE DO HAVE a default value.
44+
exportables[key] = JSON.parse(JSON.stringify(exportablesDefaultsMap[key]))
45+
} else if (nullOrFalse === false) {
46+
// If value is false, we do not show it if nothing is present
47+
// If value is null, and no value is set, set null (i.e. use the already set value above.)
48+
Reflect.deleteProperty(exportables, key)
49+
}
50+
}
51+
}
52+
53+
return Object.freeze({...exportables})
54+
}
55+
56+
module.exports = processEnv

‎server/utils/helpers.js

+67-22
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,13 @@ const querystring = require('querystring')
33
const consts = require('../utils/consts')
44
const jwtDecode = require('jwt-decode')
55

6+
/**
7+
* Have a look at ../utils/consts.js
8+
*/
9+
const LB_ADDR = process.env.LB_ADDR || consts.LB_ADDR
10+
const AXIOS_TIMEOUT = process.env.AXIOS_TIMEOUT || consts.AXIOS_TIMEOUT_DEFAULT
11+
const ENDPOINT_BACKEND_VALIDATE = process.env.ENDPOINT_BACKEND_VALIDATE || consts.ENDPOINT_BACKEND_VALIDATE
12+
613
const decode = token => {
714
return token ? jwtDecode(token) : null
815
}
@@ -34,8 +41,61 @@ const handleTokenExp = exp => {
3441
return out
3542
}
3643

44+
const createRequestConfig = (verb, url, requestConfig) => {
45+
const {
46+
payload = null,
47+
...remainder
48+
} = requestConfig
49+
const method = verb.toUpperCase()
50+
const baseURL = LB_ADDR
51+
const timeout = AXIOS_TIMEOUT
52+
let requestConfigObj = {
53+
...remainder,
54+
method,
55+
baseURL,
56+
url,
57+
timeout
58+
}
59+
// Maybe stringify data, when
60+
// - 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
61+
// - method is POST
62+
if (payload !== null) {
63+
requestConfigObj.data = querystring.stringify(payload)
64+
}
65+
// console.log(`createRequestConfig ${method} ${baseURL}${url}, requestConfig`, {...requestConfigObj})
66+
return requestConfigObj
67+
}
68+
69+
function axiosRequestCatcher (e) {
70+
const status = e.response.status
71+
const statusText = e.response.statusText || e.message
72+
const {
73+
url,
74+
headers,
75+
method,
76+
params,
77+
baseURL
78+
} = e.response.config
79+
const recv = {
80+
status,
81+
statusText,
82+
data: e.response.data || {},
83+
config: {
84+
url,
85+
headers,
86+
method,
87+
params,
88+
baseURL
89+
}
90+
}
91+
console.log(`axiosRequestCatcher caught exception ${e.message}, serving:`, {
92+
...recv
93+
})
94+
return recv
95+
}
96+
3797
/**
38-
* Make an async off-the-band POST request.
98+
* Make an async off-the-band request.
3999
*
40100
* Notice that LB_ADDR can be superseeded to your own backend
41101
* instead of mocking (static) endpoint.
@@ -48,25 +108,12 @@ const handleTokenExp = exp => {
48108
*/
49109
const createRequest = async (method, url, requestConfig) => {
50110
// #TODO #refactorCreateRequestBackIntoKoa Make this only return a request configuration object, and factor axios out from this.
51-
const baseURL = process.env.LB_ADDR || consts.LB_ADDR
52-
const verb = method.toUpperCase()
53-
console.log(`createRequest ${verb} ${baseURL}${url}, requestConfig`, requestConfig)
54-
const {
55-
payload = null,
56-
...restOfRequestConfig
57-
} = requestConfig
58-
let requestConfigObj = {
59-
timeout: consts.AXIOS_DEFAULT_TIMEOUT,
60-
baseURL,
61-
method: verb,
62-
url,
63-
...restOfRequestConfig
64-
}
65-
if (payload !== null) {
66-
requestConfigObj.data = querystring.stringify(payload)
67-
}
68-
69-
const recv = await axios.request(requestConfigObj)
111+
const requestConfigObj = createRequestConfig(method, url, requestConfig)
112+
// let axiosRequestStatusCode = 0 // #axiosRequestDEBUG
113+
// let axiosRequestLog = `${requestConfigObj.method} ${requestConfigObj.baseURL}${requestConfigObj.url}` // #axiosRequestDEBUG
114+
// console.log(`createRequest for ${axiosRequestLog} BEGIN`, {...requestConfigObj}) // #axiosRequestDEBUG
115+
const recv = await axios.request(requestConfigObj).catch(e => axiosRequestCatcher(e))
116+
// axiosRequestStatusCode = recv.status || 0 // #axiosRequestDEBUG
70117
const data = Object.assign({}, recv.data)
71118

72119
return Promise.resolve(data)
@@ -83,8 +130,6 @@ const getUserData = async (token) => {
83130
userinfo: userinfo.join(',')
84131
}
85132

86-
const ENDPOINT_BACKEND_VALIDATE = process.env.ENDPOINT_BACKEND_VALIDATE || consts.ENDPOINT_BACKEND_VALIDATE
87-
88133
/**
89134
* Would create a request like this;
90135
*

‎test/config.test.js

+20-5
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@ import test from 'ava'
22
import createNuxt from './helpers/create-nuxt'
33

44
let nuxt = null
5+
/*
6+
const req = {
7+
headers: {
8+
'accept-language': 'zh',
9+
Cookie: 'hare:sess=eyJjYXB0Y2hhIjoiR2hDTCIsImp3dCI6ImV5SmhiR2NpT2lKSVV6STFOaUlzSW5SNWNDSTZJa3BYVkNKOS5leUpoZFdRaU9sc2lZbUZ6SWwwc0luVnpaWEpmYm1GdFpTSTZJbUZrYldsdUlpd2ljMk52Y0dVaU9sc2ljbVZoWkNKZExDSmxlSEFpT2prNU9UazVPVGs1T1RrNU9Ua3NJblZ6WlhKSlpDSTZJalF3TWpnNFlqZGxOV0pqWkRjM016TXdNVFZpWTJRM1ptUTNNakl3TURBeElpd2lZWFYwYUc5eWFYUnBaWE1pT2xzaVlXUnRhVzRpWFN3aWFuUnBJam9pTnpKbFl6TmpORE10TURNd1lTMDBNV1ZrTFdGaVlqSXRZamRoTWpZNU5UQTJPVEl6SWl3aVkyeHBaVzUwWDJsa0lqb2lZbUZ6TFdOc2FXVnVkQ0o5LnV3eXd6aU5ldEh5ZlNkaXFjSnQ2WFVHeTRWX1dZSFI0SzZsN09QMlZCOUkiLCJfZXhwaXJlIjoxNTI4NTYyNDA0NDkwLCJfbWF4QWdlIjo4NjQwMDAwMH0=; hare:sess.sig=0gGUUVRpb3VRbsj8ibRHRXlie30'
10+
}
11+
}
12+
*/
513

614
// Init nuxt.js and create server listening on localhost:4000
715
test.before('Init Nuxt.js', async t => {
@@ -22,18 +30,24 @@ test('Plugin', async t => {
2230
const plugins = nuxt.options.plugins
2331
t.is(plugins[1], '@/plugins/i18n', 'i18n plugin added to config')
2432
t.is(plugins[2], '@/plugins/element-ui', 'element-ui plugin added to config')
25-
t.is(plugins[3].src, '@/plugins/clipboard', 'clipboard plugin added to config')
26-
t.is(plugins[4].src, '@/plugins/error-handler', 'error handler plugin added to config')
33+
t.is(plugins[3], '@/plugins/axios', '@nuxtjs/axios module adjustments present')
34+
t.is(plugins[4].src, '@/plugins/clipboard', 'clipboard plugin added to config')
35+
t.is(plugins[5].src, '@/plugins/error-handler', 'error handler plugin added to config')
36+
t.is(plugins[6].src, '@/plugins/persistedstate', 'Vuex persistedstate plugin added to config')
2737
})
2838

2939
test('Modules', async t => {
3040
const modules = nuxt.options.modules
31-
t.is(modules[0], '@nuxtjs/webpackmonitor', 'WebPack Monitor Nuxt Module')
32-
t.is(modules[1], '@nuxtjs/axios', 'Axios Nuxt Module')
41+
// For some reason plugins[1] is '@/plugins/i18n', yet it should be at index 0
42+
// Whereas here, modules[0] in nuxt.config.js is indeed at index 0.
43+
t.is(modules[0], '@nuxtjs/axios', 'Axios Nuxt Module')
44+
t.is(modules[1][0], '@nuxtjs/dotenv', 'DotEnv Nuxt Module')
45+
t.is(modules[1][1].only[0], 'HOST', 'DotEnv only setting, with HOST option as the first')
3346
})
3447

48+
/*
3549
test('Middleware', async t => {
36-
const { html, redirected } = await nuxt.renderRoute('/', {req: {headers: {'accept-language': 'zh'}}})
50+
const { html, redirected } = await nuxt.renderRoute('/', Object.assign({}, {req}))
3751
t.true(html.includes('<div id="__nuxt"></div>'), 'auth plugin works 1')
3852
t.true(!html.includes('前端项目模板'), 'auth plugin works 2')
3953
t.true(redirected.path === '/login', 'auth plugin works 3')
@@ -44,3 +58,4 @@ test('Middleware', async t => {
4458
test.after('Closing server and nuxt.js', async t => {
4559
await nuxt.close()
4660
})
61+
*/

‎test/index.test.js

+18-10
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,8 @@ import createNuxt from './helpers/create-nuxt'
77
let nuxt = null
88
const req = {
99
headers: {
10-
'accept-language': 'zh'
11-
},
12-
session: {
13-
jwt: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.' +
14-
'eyJhdWQiOlsidGF0Il0sInVzZXJfbmFtZSI6IlRlc3RlciIsI' +
15-
'nNjb3BlIjpbInJlYWQiXSwiZXhwIjoxNDk0MjY4ODY0LCJ1c2' +
16-
'VySWQiOiIxIiwiYXV0aG9yaXRpZXMiOlsiYWRtaW4iXSwianR' +
17-
'pIjoiN2FkN2VjYzUtNTdmNy00MmZlLThmZmQtYjUxMTJkNTZm' +
18-
'M2NhIiwiY2xpZW50X2lkIjoidGF0LWNsaWVudCJ9.' +
19-
'ovWxqcBptquNR5QUBz1it2Z3Fr0OxMvWsnXHIHTcliI'
10+
'accept-language': 'zh',
11+
Cookie: 'hare:sess=eyJjYXB0Y2hhIjoiR2hDTCIsImp3dCI6ImV5SmhiR2NpT2lKSVV6STFOaUlzSW5SNWNDSTZJa3BYVkNKOS5leUpoZFdRaU9sc2lZbUZ6SWwwc0luVnpaWEpmYm1GdFpTSTZJbUZrYldsdUlpd2ljMk52Y0dVaU9sc2ljbVZoWkNKZExDSmxlSEFpT2prNU9UazVPVGs1T1RrNU9Ua3NJblZ6WlhKSlpDSTZJalF3TWpnNFlqZGxOV0pqWkRjM016TXdNVFZpWTJRM1ptUTNNakl3TURBeElpd2lZWFYwYUc5eWFYUnBaWE1pT2xzaVlXUnRhVzRpWFN3aWFuUnBJam9pTnpKbFl6TmpORE10TURNd1lTMDBNV1ZrTFdGaVlqSXRZamRoTWpZNU5UQTJPVEl6SWl3aVkyeHBaVzUwWDJsa0lqb2lZbUZ6TFdOc2FXVnVkQ0o5LnV3eXd6aU5ldEh5ZlNkaXFjSnQ2WFVHeTRWX1dZSFI0SzZsN09QMlZCOUkiLCJfZXhwaXJlIjoxNTI4NTYyNDA0NDkwLCJfbWF4QWdlIjo4NjQwMDAwMH0=; hare:sess.sig=0gGUUVRpb3VRbsj8ibRHRXlie30'
2012
}
2113
}
2214

@@ -29,6 +21,22 @@ test.before('Init Nuxt.js', async t => {
2921
status: 200,
3022
data: '验证码Mock'
3123
})
24+
moxios.stubRequest('/hpi/auth/whois', {
25+
status: 200,
26+
data: {
27+
authenticated: true,
28+
userName: 'admin',
29+
displayName: 'Haaw D. Minh',
30+
tz: 'Asia/Hong_Kong',
31+
locale: 'zh-HK'
32+
}
33+
})
34+
moxios.stubRequest('/hpi/auth/validate', {
35+
status: 200,
36+
data: {
37+
authenticated: true
38+
}
39+
})
3240
nuxt = createNuxt()
3341
await nuxt.listen(3000, 'localhost')
3442
})

‎yarn.lock

+25
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,12 @@
152152
axios-retry "^3.0.2"
153153
consola "^1.1.4"
154154

155+
"@nuxtjs/dotenv@^1.1.1":
156+
version "1.1.1"
157+
resolved "https://registry.yarnpkg.com/@nuxtjs/dotenv/-/dotenv-1.1.1.tgz#c3d68f96d8f76cac4c4ddbc8268702eb95737bbe"
158+
dependencies:
159+
dotenv "^5.0.0"
160+
155161
"@nuxtjs/proxy@^1.2.4":
156162
version "1.2.4"
157163
resolved "https://registry.yarnpkg.com/@nuxtjs/proxy/-/proxy-1.2.4.tgz#41aafac58854f14e28aed17238936bb6404e6071"
@@ -2679,6 +2685,10 @@ deepmerge@^2.0.0:
26792685
version "2.1.0"
26802686
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-2.1.0.tgz#511a54fff405fc346f0240bb270a3e9533a31102"
26812687

2688+
deepmerge@^2.1.0:
2689+
version "2.1.1"
2690+
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-2.1.1.tgz#e862b4e45ea0555072bf51e7fd0d9845170ae768"
2691+
26822692
define-properties@^1.1.2:
26832693
version "1.1.2"
26842694
resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.2.tgz#83a73f2fea569898fb737193c8f873caf6d45c94"
@@ -2866,6 +2876,10 @@ dot-prop@^4.1.0:
28662876
dependencies:
28672877
is-obj "^1.0.0"
28682878

2879+
dotenv@^5.0.0:
2880+
version "5.0.1"
2881+
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-5.0.1.tgz#a5317459bd3d79ab88cff6e44057a6a3fbb1fcef"
2882+
28692883
dotgitignore@^1.0.3:
28702884
version "1.0.3"
28712885
resolved "https://registry.yarnpkg.com/dotgitignore/-/dotgitignore-1.0.3.tgz#a442cbde7dc20dff51cdb849e4c5a64568c07923"
@@ -8076,6 +8090,10 @@ shell-quote@^1.6.1:
80768090
array-reduce "~0.0.0"
80778091
jsonify "~0.0.0"
80788092

8093+
shvl@^1.3.0:
8094+
version "1.3.1"
8095+
resolved "https://registry.yarnpkg.com/shvl/-/shvl-1.3.1.tgz#6c20a17b4a20b08e9f8cab60c50a92229fcc176e"
8096+
80798097
signal-exit@^3.0.0, signal-exit@^3.0.2:
80808098
version "3.0.2"
80818099
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
@@ -9125,6 +9143,13 @@ vuex-class@^0.3.0:
91259143
version "0.3.0"
91269144
resolved "https://registry.yarnpkg.com/vuex-class/-/vuex-class-0.3.0.tgz#25047ab042580fd4ff3e64cc76603ac217c9190d"
91279145

9146+
vuex-persistedstate@^2.5.2:
9147+
version "2.5.4"
9148+
resolved "https://registry.yarnpkg.com/vuex-persistedstate/-/vuex-persistedstate-2.5.4.tgz#a19710ad7f9a08cea4e65fc585924d9fdac7384a"
9149+
dependencies:
9150+
deepmerge "^2.1.0"
9151+
shvl "^1.3.0"
9152+
91289153
vuex@^3.0.1:
91299154
version "3.0.1"
91309155
resolved "https://registry.yarnpkg.com/vuex/-/vuex-3.0.1.tgz#e761352ebe0af537d4bb755a9b9dc4be3df7efd2"

0 commit comments

Comments
 (0)
Please sign in to comment.