Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Rework examples, fix bugs, review i18n #339

Open
wants to merge 8 commits into
base: dev
Choose a base branch
from

Conversation

renoirb
Copy link
Collaborator

@renoirb renoirb commented May 31, 2018

Work in progress! (and so this PR message gets edited as work is still in progress)

PR Objectives

See Questions below, they might guide me in a way you'll be OK with this project's design goal and scope.

  • Ability to get a copy of the JWT token after authentication — Instead of just saying that one should not make available JWT token outside of HTTPOnly cookie, implement a way where at login time, to help us copy the JWT token we're just going to receive (so we don't need to base64decode from cookies). If we click the check box at login, if successful, the token is temporarily available in Vuex store, where we can copy it. But that it won't stay in VueX store if we reload the page.
  • Fix Issue when reloading page with data read from side-car bug, explained below
  • Review and Improve examples views so that Koa Fills VueX store, using dispatch and module
  • Read environment variables at run time for optional toggles (e.g. SHOW_EXAMPLES, LB_ADDR), that are separate from when we're building (e.g. yarn build with NODE_ENV="production")
  • Make Vue i18n to get translation from Koa (i.e. loaded from outside from server/locales/*.json, or client/locales/*.json. (See renoirb/experiments-nuxt-i18n-remote)
  • Fix The examples are not updated, but always appended with duplicates
  • Move addAll in client/components/Navbar.vue to be in client/store/menu.js as an hydrate action. Like the rest.

Bugs

The examples are not updated, but always appended with duplicates

Currently, there is no duplicate verification nor prevent loading if already there.

Issue when reloading page with data read from side-car

When we're on a view that is built based on data coming from Koa, when we reload, we get a NuxtError telling 127.0.0.1:80 is not available.

In other words, Axios/Nuxt would loose the proper port and get Axios' default setting that isn't port 3000 anymore

Reproduction:

  1. Sign In
  2. Go to Examples, then click on listing activitites
  3. Reload the page
  4. NuxtError

Questions

  1. How would you have runtime configuration environment variables set?
    For instance; After production build, when running, and want to tell to read data from something available to that node's internal network.
    Would you use nuxt-community/dotenv-module, or selectively declare which are applicable via something like rolodato/dotenv-safe ?

  2. How do you see localization and build time?
    Localization messages may be needed in both client/ and server/ code.
    What was your plan about this, or what do you prefer?
    And how about loading only what's needed, at the many places we'd have to inject it (e.g. Element UI' at Vue.use(Element, {...}), Nuxt, and so on.)

  3. Did you plan splitting data mocking from what Nuxt reads from Koa
    I see two use-cases in server/.
    1. "Mocking", where we statically serve data for demo purposes. (e.g. /hpi/platform/uaano/oauth/validate when LB_ADDR is the default)
    2. "Side-Car" (or BFF), where we get a simple GET /hpi/auth/whois and it abstracts out authentication and makes off-the-band HTTP calls to other data sources, and normalizes output for Nuxt to consume.
    Did you have plans, or lessons-learned you want to share here?

  4. How do you handle Ava tests
    Some rework needs to be done regarding session, maybe Vuex store calls too often at /hpi/auth/whois, tests times out. This has to be figured out.
    I've never used Ava so far (Only Mocha, Jest, etc). Looks good though.

Screenshots

1. Ability to get a copy of the JWT token after authentication

1.1. Check a box at login

screenshot 2018-06-01 20 01 04

1.2. After Sign-In Notification alert

screenshot 2018-05-31 20 51 07

1.3. Visualize in /session received data, including JWT token

screenshot 2018-05-31 20 51 02

1.4. Get a note that the Vuex store does no longer have the JWT token, and requires authentication

screenshot 2018-06-01 20 13 25

2. Review and Improve examples views

2.1. Side-Car XML data output from /hpi/examples/activity

screenshot 2018-06-01 20 11 16

2.2. Filled Vuex store, using data from Side-Car

Notice the link to /hpi/examples/activity
screenshot 2018-06-01 20 09 16

2.3. Creating an Activity

screenshot 2018-06-01 20 24 04

2.4. Looking the new Activity Detail

screenshot 2018-06-01 20 25 41

Renoir Boulanger added 2 commits May 30, 2018 17:55
* Remove /hpi/auth/token, make it optional via login
* Move @nuxt/axios to dependencies, otherwise it breaks
for (const activity of values) {
state.activities.push(Object.assign({}, activity))
state.activities.push({...activity})
Copy link
Collaborator Author

@renoirb renoirb Jun 1, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved to review comment.

Copy link
Collaborator Author

@renoirb renoirb Jun 2, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved to review comment.

<el-radio
label="activity.priority.low"
>
{{$t('activity.priority.low')}}
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Element UI uses label for both value and label.
At least it allows using default slot for i18n.

Also ensure that token Notification is shown not only at home.
Copy link
Collaborator Author

@renoirb renoirb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here are my commentary to explain the changes I've made.

@@ -14,7 +14,8 @@
"sourceMaps": true,
"env": {
"NODE_ENV": "development",
"DEBUG": "nuxt:*"
"DEBUG": "nuxt:*,koa:*",
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wished we could have more logging and be able to configure through this here.
So far, it seems only console.log works, but then it's hard to manage in code by (un|)commenting.

"NODE_ENV": "",
"DEBUG": "nuxt:*,koa:*"
}
},
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will be useful for debugging at build.

@@ -117,12 +156,12 @@ const ExampleGetter = namespace('examples/index', Getter)
default () {
return {
account: '',
region: 'activity.city.ly',
city: 'activity.city.ly',
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This wasn't consistent. Region or city, or area?

@@ -180,7 +220,7 @@ export default class NewActivity extends Vue {

this.$refs[formName].validate((valid) => {
if (valid) {
this.$store.dispatch('examples/activity/add', this.formData)
this.addActivity(this.formData)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Encapsulate it back to the Store

title,
onClick: function notifyOnClickHandler () {
this.$router.push('/session')
}.bind(this)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe there's a better way

let path = query.page || '/'
let authenticated = await store.getters['session/authenticated']
if (authenticated) {
redirect(path)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So we can redirect to / or elsewhere (e.g. bookmarked link as /login?page=%2Fabout => /about if already signed in and accessed /login.

// const API_HOST = process.env.API_HOST || '127.0.0.1'
// const API_PREFIX = process.env.API_PREFIX || '/'

const AXIOS_TIMEOUT = process.env.AXIOS_TIMEOUT || consts.AXIOS_DEFAULT_TIMEOUT
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe use different naming convention?

const AXIOS_DEFAULT_TIMEOUT = process.env.AXIOS_DEFAULT_TIMEOUT || consts.AXIOS_DEFAULT_TIMEOUT

for (const activity of values) {
state.activities.push(Object.assign({}, activity))
state.activities.push({...activity})
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will need refactoring.

I'm unsure the recommended way with Element-UI and table rows.

My gut feeling tells me it's better to have an object in the Vuex store, with an unique identifier as a key, so we can iterate.

Yet give an array when asked for the entries.

Because as this is, at any dispatch, the store will grow. With duplicates and all.

What's your feeling about that @clarkdo ?

This is refered to as The examples are not updated, but always appended with duplicates in BUGS in PR detail


if (consts.SHOW_EXAMPLES === true) {
if (process.env.SHOW_EXAMPLES === 'true') {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would that be OK like this, do you see a problem using this pattern?

ctx.status = 200
ctx.body = body
})

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't need this anymore.
The only time we should receive token is when we got a response from the authentication.

@clarkdo
Copy link
Owner

clarkdo commented Jun 6, 2018

@renoirb Thank you so much for the fantastic changes, I'll look into it asap.

BTW, could you have a look at the build failure of the pr?

@renoirb
Copy link
Collaborator Author

renoirb commented Jun 7, 2018

Hey @clarkdo,

Sure, I'll take a look!
I still haven't worked on i18n and vuex syncVuex looks like nuxt/i18n has a method where I could make an HTTP reqiest and merge messages, like what I need (i.e. translations managed outside of Nuxt).

But I'll make sure build passes too!

Copy link
Collaborator Author

@renoirb renoirb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here are some more comments from code I've backported from my project at my employer.

"DEBUG": "nuxt:*"
"DEBUG": "nuxt:*,axios:*,koa:*,koa-session:*,koa-router:*,follow-redirects:*",
"SHOW_EXAMPLES": "true",
"LOG_DIR": "${workspaceRoot}/logs"
Copy link
Collaborator Author

@renoirb renoirb Jun 8, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an unexpected Gem, @clarkdo!

I knew it was somehow redundant to do console.log with bunyan but I couldn't see how to use. With this, we could have VSCode handle logs. And make other users of Hare aware of this feature.

// An array of any paths to partially persist the state.
// If no paths are given, the complete state is persisted.
paths: [
'menu.hidden',
Copy link
Collaborator Author

@renoirb renoirb Jun 8, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Set in place persistedstate for UI preferences in localStorage.
We could have a persistedstate key for different use-case, e.g. a datatable filter preference, etc.
A in-app message translation workspace, storing locally edited translations prior to committing it to respective translation data store.

prefix: consts.BASE_API
})

router.get('/env', async ctx => {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That way, we could have a multi-level deployment and have Nuxt read from Side-Car, instead.

Note that I couldn't make @nuxtjs/dotenv work properly from Nuxt side.

Sure thing, when managing many deployments of the same app, and dynamic monitoring system, deployment specific toggles could be discovered from here.

What's your opinion, @clarkdo?

'M2NhIiwiY2xpZW50X2lkIjoidGF0LWNsaWVudCJ9.' +
'ovWxqcBptquNR5QUBz1it2Z3Fr0OxMvWsnXHIHTcliI'
'accept-language': 'zh',
Cookie: 'hare:sess=eyJjYXB0Y2hhIjoiR2hDTCIsImp3dCI6ImV5SmhiR2NpT2lKSVV6STFOaUlzSW5SNWNDSTZJa3BYVkNKOS5leUpoZFdRaU9sc2lZbUZ6SWwwc0luVnpaWEpmYm1GdFpTSTZJbUZrYldsdUlpd2ljMk52Y0dVaU9sc2ljbVZoWkNKZExDSmxlSEFpT2prNU9UazVPVGs1T1RrNU9Ua3NJblZ6WlhKSlpDSTZJalF3TWpnNFlqZGxOV0pqWkRjM016TXdNVFZpWTJRM1ptUTNNakl3TURBeElpd2lZWFYwYUc5eWFYUnBaWE1pT2xzaVlXUnRhVzRpWFN3aWFuUnBJam9pTnpKbFl6TmpORE10TURNd1lTMDBNV1ZrTFdGaVlqSXRZamRoTWpZNU5UQTJPVEl6SWl3aVkyeHBaVzUwWDJsa0lqb2lZbUZ6TFdOc2FXVnVkQ0o5LnV3eXd6aU5ldEh5ZlNkaXFjSnQ2WFVHeTRWX1dZSFI0SzZsN09QMlZCOUkiLCJfZXhwaXJlIjoxNTI4NTYyNDA0NDkwLCJfbWF4QWdlIjo4NjQwMDAwMH0=; hare:sess.sig=0gGUUVRpb3VRbsj8ibRHRXlie30'
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe the tes fails because of session HTTP header managed differently Koa side now.

Koa only looks at Cookie, and only the one it created.

$axios
}) => {
/*
if (!process.client) {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This still has to be figured out.

'ENDPOINT_BACKEND_VALIDATE',
// See also server/utils/environment-variables.js
'LB_ADDR',
'SHOW_EXAMPLES'
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm still unsure how to use this in Nuxt side. (incomplete)
An alternate would be to use for Nuxt and environment variable injection, either this and/or with an environment variable exposition Side-Car endpoint (see /hpi/env).
A sure thing would be that private data would NOT BE a good fit for unauthenticated /hpi/env, and for Nuxt side, this module here would be a good fit.

"dev:pm2": "pm2 start yarn --name=hare -- dev",
"test": "yarn lint && yarn build:client && ava --verbose --serial ",
"test": "yarn lint && yarn build:client",
"test:FIXME": "yarn lint && yarn build:client && ava --verbose --serial ",
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry about that.

# NOTE: Work needs to be done so hare logs could go there too.
# And we should review use of cross-env to use env-cmd
# https://github.com/kentcdodds/cross-env#other-solutions
#DEBUG=nuxt:*,axios:*,koa:*,koa-session:*,koa-router:*,follow-redirects:*
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is so cool!

Maybe its better to use Environment Variables instead of loading a long string in packages.json

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HOST is needed inside the Docker image, I should say.

HOST=0.0.0.0

# Might be renamed to ENDPOINT_BASEURL
LB_ADDR=http://example.private.local:8080
Copy link
Collaborator Author

@renoirb renoirb Jun 8, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What I mean is that LB_ADDR might be better renamed to ???_BASEURL.
In other projects, I see the convention of ADDR as an IPv4/IPv6 address, not a string that starts by http and has no trailing slash.

Also, maybe it should be mentioning about the fact that this endpoint SHOULD BE from a private network, only accessible from Nuxt (i.e. not publicly exposed).

Opinion @clarkdo ?

LB_ADDR=http://example.private.local:8080

ENDPOINT_BACKEND_AUTH=/your/own/namespace/oauth/token
ENDPOINT_BACKEND_VALIDATE=/your/own/namespace/user/validate
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My BACKEND endponts are likely not the same as yours :)

@renoirb renoirb force-pushed the renoirb-contribs branch 2 times, most recently from 4d86b41 to 8e2e97c Compare June 8, 2018 19:48
renoirb pushed a commit to renoirb/experiments-nuxt-koa-env-runtime-201806 that referenced this pull request Jun 19, 2018
@renoirb
Copy link
Collaborator Author

renoirb commented Jun 19, 2018

FYI, @clarkdo, I'm having issues running the service after a build when I tell which baseURL to use, it still does not use it.

To see what's missing, I've created an experiment repository, I have two orphaned branches and I thought you should know about them.

Branches:

  1. renoirb/experiments-nuxt-koa-env-runtime-201806, master branch; no Koa in use
  2. renoirb/experiments-nuxt-koa-env-runtime-201806, rework-hare branch; Attempt at using same Koa middleware patterns as Hare.

Maybe it is by design (or because we run Koa/Nuxt side by side), that its impossible to tell dynamically which process.env.HOST to take into account from within @nuxtjs/axios module.

Mind to take a look?


For the record, it seems to be the same issue at play from the current dev branch after;

yarn
yarn build
yarn --production
export HOST=foo.dev.local
export PORT=9098
yarn start

And we see any XHR failing when doing OPTIONS http://localhost:3000/ as if options aren't taken into account. Even though we try many other variable names (API_HOST, API_URL, etc...).

Notice the Network error and OPTIONS http://localhost:3000/ request in the screenshot below;

screenshot from 2018-06-19 15-28-59

@renoirb
Copy link
Collaborator Author

renoirb commented Jun 19, 2018

Related to last comment, I could at last make it work (see branch rework-hare-20180619).

Maybe that should be handled differently though.

Setup

yarn
yarn build
yarn --production

Intended use is from within a container (Dockerfile not provided here) where we'd run the steps above to build the service image.

The docker service would be started using a line similar to what's in start (see below)

// Adjust DEBUG=* to suit your logging preferences.
./node_modules/.bin/cross-env DEBUG=* \
                              NODE_ENV=production \
                              HOST=foo.dev.local \
                              PORT=9098 \
                              node build/main.js

You should be able to go back and forth to Index and About page, and click on an Element UI button with a "Click me!" caption and see an XHR request from http://foo.dev.local:9098/hpi/examples/activity properly formed and filling up the page with preformatted JSON string.

screenshot from 2018-06-19 18-13-01


Possibly useful links

@clarkdo
Copy link
Owner

clarkdo commented Apr 23, 2019

@renoirb I've made some changes for upgrading nuxt and refactoring, so the dir structure has been changed, sorry for the inconvenience, are you going to continue working on this pr ?

If anything I can help, feel free to contact me.

@renoirb
Copy link
Collaborator Author

renoirb commented Apr 25, 2019

You can close it.
I can make another fork off from your changes.

Also, I was wondering if you could document how you plan on using with Koa, is it planned to be used as an APi (hpi), AND a Nuxt serverMiddleware ?

Making changes to Koa logic takes time.
I've been now working with two projects now. (Both in TypeScript nowadays).

  1. Koa WebAPI ... serving i18n, menu data, session data, authentication
  2. Nuxt portal. A dumb client to the above. With @nuxt/axios preconfigured to talk to the above from both ssr/client

If you want. I can make a koa microservice. Only for the data. We could tell that service where to authenticate, where to fetch language definition, how to fill a user session data (based on outcome of authentication, exposed only if cookie is valid, ready for @nuxt/axios, and Vuex to setup vuex store for current user), etc.

@renoirb
Copy link
Collaborator Author

renoirb commented Apr 25, 2019

Related to loading i18n. I've started something (that I can now do better) in https://github.com/renoirb/experiments-nuxtjs-i18n-element-scss-201809 feel free to pick stuff from it.

@renoirb
Copy link
Collaborator Author

renoirb commented Jun 5, 2019

FYI, I've played a bit with Vuex and TypeScript.
I should be able to refactor this PR/hare with this.
I guess the ambition is to rewrite Hare with TypeScript?

nuxt-community/hackernews-nuxt-ts#16

@renoirb
Copy link
Collaborator Author

renoirb commented Nov 24, 2020

(It's a bit off-topic, but somewhat related to this PR that's not moving.)

Hi @clarkdo, I wanted to give you a small note that I have been thinking about how to expand on the architectural patterns and review how hare is made.

I've spend a few hours in the last days and I'd like to show you the progress.

Idea is that we could make hare be two separate packages ("apps") that can be published, and other co-dependencies can be either other npm packages, or handled from a mono-repo

Have a look at this private repo and README: https://github.com/renoirb/hare2/blob/master/apps/side-car/README.md and come back to me using your favourite communication medium :)

@renoirb renoirb requested a review from clarkdo November 26, 2020 23:41
@renoirb renoirb self-assigned this Nov 26, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants