finished tutorial

This commit is contained in:
2020-03-18 03:25:33 +00:00
parent b7251fabfe
commit db39e2212b
15 changed files with 552 additions and 31 deletions

View File

@@ -429,6 +429,8 @@ https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/imp
https://vueschool.io/articles/vuejs-tutorials/lazy-loading-and-code-splitting-in-vue-js/ https://vueschool.io/articles/vuejs-tutorials/lazy-loading-and-code-splitting-in-vue-js/
### Importing js
In javascript you can use several ways to import modules. In javascript you can use several ways to import modules.
`import { function } from "module";` is used if you want to import a specific function/class etc from a `.js` file. You can chain the imports with a comma between them. These have to be javascript objects. `import { function } from "module";` is used if you want to import a specific function/class etc from a `.js` file. You can chain the imports with a comma between them. These have to be javascript objects.
@@ -443,3 +445,12 @@ component: () =>
``` ```
The difference between `require` and `import`: `import` is better to use as it's a function, whereas `require` is a js library. `require` is synchronous but `import` can be asynchronous, and supports lazy loading. The difference between `require` and `import`: `import` is better to use as it's a function, whereas `require` is a js library. `require` is synchronous but `import` can be asynchronous, and supports lazy loading.
### Importing vue components
When importing other vue components (e.g vue-lottie) any package that says use `npm install --save` can be substituted with `yarn add`. The `--save` just says to add to the `package.json`.
If importing vue components that have been downloaded into `node_modules`, you can simply import them like any other component. The `.vue` is optional. e.g.
Installing is done with `yarn add vue-lottie` and importing in another vue component is done with `import Lottie from "vue-lottie"`.

View File

@@ -13,6 +13,7 @@
"aws-amplify-vue": "^1.1.4", "aws-amplify-vue": "^1.1.4",
"core-js": "^3.6.4", "core-js": "^3.6.4",
"vue": "^2.6.11", "vue": "^2.6.11",
"vue-lottie": "^0.2.1",
"vue-router": "^3.1.5", "vue-router": "^3.1.5",
"vuetify": "^2.2.11", "vuetify": "^2.2.11",
"vuex": "^3.1.2" "vuex": "^3.1.2"

View File

@@ -27,7 +27,10 @@
<router-view></router-view> <router-view></router-view>
<br /> <br />
<!-- <h1 v-if="this.email != null"> --> <!-- <h1 v-if="this.email != null"> -->
<h1 v-if="this.userEmail != null">Hello {{ this.userEmail }}</h1> <h1 v-if="this.$store.getters.authorized != false">
<!-- Hello {{ this.userEmail }} -->
Hello {{ this.$store.getters.userEmail }}
</h1>
<h1 v-else>Welcome!</h1> <h1 v-else>Welcome!</h1>
<v-col cols="2"> <v-col cols="2">
<v-row justify-center> <v-row justify-center>
@@ -47,33 +50,27 @@ import { signOut } from "./utils/auth";
// import { mapGetters } from "vuex"; // import { mapGetters } from "vuex";
export default { export default {
data() { data() {
return { return {};
email: null
};
}, },
computed: { computed: {},
userEmail() {
return this.$store.state.userEmail
}
},
async created() { async created() {
try { try {
await this.$store.dispatch("fetchUser"); await this.$store.dispatch("fetchUser");
} catch (error) { } catch (error) {
console.log(error); console.log(error);
} finally { } finally {
console.log("finally") console.log("finally");
console.log(this.email) console.log(this.email);
console.log(this.$store.state.userEmail) console.log(this.$store.state.userEmail);
// this.email = this.$store.state.userEmail;
} }
}, },
methods: { methods: {
async dosignOut() { async dosignOut() {
signOut(); try {
this.$store.commit("user", null); await signOut();
this.$store.commit("userEmail", null); } finally {
this.email = this.$store.userEmail; this.$store.dispatch("fetchUser");
}
} }
} }
}; };

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,23 @@
<template>
<lottie :options="defaultOptions" :height="400" :width="400"> </lottie>
</template>
<script>
import Lottie from "vue-lottie";
import * as delivery from "../assets/lottie/delivery.json";
// console.log(delivery)
export default {
name: "Animation",
components: {
lottie: Lottie
},
data() {
return {
defaultOptions: { animationData: delivery.default },
animationSpeed: 1
};
}
};
</script>
<style lang="scss" scoped></style>

View File

@@ -5,10 +5,14 @@ import store from "./store";
import vuetify from "./plugins/vuetify"; import vuetify from "./plugins/vuetify";
import Amplify from "aws-amplify"; import Amplify from "aws-amplify";
import awsconfig from "./aws-exports"; import awsconfig from "./aws-exports";
import Lottie from "vue-lottie";
Amplify.configure(awsconfig); Amplify.configure(awsconfig);
Vue.use(Amplify); Vue.use(Amplify);
Vue.component("lottie", Lottie);
console.log(Lottie);
Vue.config.productionTip = false; Vue.config.productionTip = false;
new Vue({ new Vue({

View File

@@ -20,7 +20,8 @@ const routes = [
// this generates a separate chunk (about.[hash].js) for this route // this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited. // which is lazy-loaded when the route is visited.
component: () => component: () =>
import(/* webpackChunkName: "about" */ "../views/About.vue") import(/* webpackChunkName: "about" */ "../views/About.vue"),
// meta: { requiresAuth: false }
}, },
{ {
path: "/signUp", path: "/signUp",
@@ -52,7 +53,7 @@ const router = new VueRouter({
}); });
getUser().then(user => { getUser().then(user => {
console.log("router getting user.") console.log("router getting user.");
if (user) { if (user) {
router.push({ path: "/" }); router.push({ path: "/" });
} }
@@ -82,7 +83,7 @@ AmplifyEventBus.$on("authState", async state => {
}); });
router.beforeResolve(async (to, _from, next) => { router.beforeResolve(async (to, _from, next) => {
console.log("router before resolve getting current user.") console.log("router before resolve getting current user.");
const user = await getUser(); const user = await getUser();
if (!user) { if (!user) {
if (to.matched.some(record => record.meta.requiresAuth)) { if (to.matched.some(record => record.meta.requiresAuth)) {

View File

@@ -37,11 +37,14 @@ const store = new Vuex.Store({
} }
}, },
modules: {}, modules: {},
// getters: { getters: {
// userEmail: state => { userEmail: state => {
// return state.userEmail; return state.userEmail;
// } },
// } authorized: state => {
return state.authorized;
}
}
}); });
// store.dispatch.fetchUser() // store.dispatch.fetchUser()

View File

@@ -66,7 +66,6 @@ async function signIn(username, password) {
try { try {
console.log("Attempting sign in"); console.log("Attempting sign in");
const user = await Auth.signIn(username, password); const user = await Auth.signIn(username, password);
AmplifyEventBus.$emit("authState", "signedIn");
console.log("Signed in in fn"); console.log("Signed in in fn");
console.log(user); console.log(user);
if (user) { if (user) {

View File

@@ -1,5 +1,49 @@
<template> <template>
<div class="about"> <div class="about">
<h1>This is an about page</h1> <h1>This is an about page</h1>
<!-- <br /> -->
<v-card class="d-inline-flex">
<animation></animation>
</v-card>
</div> </div>
</template> </template>
<script>
import Animation from "../components/Animation";
// import Lottie from "vue-lottie";
import * as delivery from "@/assets/lottie/halloween.json";
export default {
components: {
animation: Animation
},
methods: {
handleAnimation: function(anim) {
this.anim = anim;
},
stop: function() {
this.anim.stop();
},
play: function() {
this.anim.play();
},
pause: function() {
this.anim.pause();
},
onSpeedChange: function() {
this.anim.setSpeed(this.animationSpeed);
}
},
data() {
return {
defaultOptions: { animationData: delivery },
animationSpeed: 1
};
}
};
</script>
<style lang="scss" scoped></style>

View File

@@ -1,11 +1,49 @@
<template> <template>
<div> <div>
<!-- <h1>Hello {{ this.$store.getters.userEmail }}</h1> --> <v-btn rounded color="primary" class="padme" @click="showJWT"
>Show JWT</v-btn
>
<h1 style=""></h1>
</div> </div>
</template> </template>
<script> <script>
import { Auth } from "aws-amplify";
import Lottie from "vue-lottie";
export default {
data() {
return {
jwt: "",
tempHeaders: {}
};
},
methods: {
async showJWT() {
try {
this.jwt = await Auth.currentSession();
this.tempHeaders = {
Authorization: `Bearer ${(await Auth.currentSession())
.getIdToken()
.getJwtToken()}`
};
} finally {
console.log(this.jwt);
let id = this.jwt.getIdToken();
console.log(id);
let jwt = id.getJwtToken();
console.log(jwt);
console.log(this.tempHeaders)
alert(this.tempHeaders.Authorization)
console.log(Lottie)
// alert(this.jwt);
}
}
}
};
</script> </script>
<style lang="scss" scoped></style> <style lang="scss" scoped>
.padme {
margin-left: 10px;
}
</style>

134
src/views/Untitled-5.md Normal file
View File

@@ -0,0 +1,134 @@
# Managing sign in/out + displaying name
## Create the Vuex store
### state
Your state should contain 3 items:
- authorized:bool = flag if logged in or not.
- user: str = user object.
- userEmail: str = username or email.
### mutations
You should define two mutations (to update this state):
```javascript
mutations: {
user(state, user) {
state.authorized =
!!user && user.attributes && user.attributes.email_verified;
state.user = user;
},
userEmail(state, userEmail) {
state.userEmail = userEmail;
}
},
```
- When you set the user state you should check to make sure the user object you pass in exists and set the authorized flag at the same time.
### actions
You should define an action that fetches the user. This should call the `getUser` method of whatever auth system you're using and pass this in to the method.
This `getUser` dispatch should either get the user and set the store state, or set it to null if no user exists. This way, any interaction with `getUser` will make sure the store is up to date.
```javascript
actions: {
async fetchUser({ commit }) {
try {
const user = await Auth.currentAuthenticatedUser();
const expires =
user.getSignInUserSession().getIdToken().payload.exp -
Math.floor(new Date().getTime() / 1000);
console.log(`Token expires in ${expires} seconds.`);
commit("user", user);
commit("userEmail", user.attributes.email);
} catch (err) {
commit("user", null);
commit("userEmail", null);
}
}
},
```
### getters
You should define getters to access the state. These getters will cache - so they will only update if one of the things used to calculate the state updates.
```javascript
getters: {
userEmail: state => {
return state.userEmail;
},
authorized: state => {
return state.authorized;
}
}
```
## Triggering state for apps
In addition to getting the user to determine router actions, you should get the user in the main component so you can set the state.
In the main view (parent of all components `App.vue`) you should define a `created` method. This method should:
- Fetch the user.
- Change the state to reflect this.
```javascript
async created() {
try {
await this.$store.dispatch("fetchUser");
} catch (error) {
console.log(error);
} finally {
console.log(this.email);
console.log(this.$store.state.userEmail);
}
},
```
This needs to be done right at the start to make sure this triggers a state change before anything else.
In the views for signing in/out/registering etc you should make sure that when you call the auth methods you wrote, that you also update the state at this time.
This way, any component in the app can use the state to determine if someone is logged in, and display/toggle components dynamically.
A signin method for a button might look like:
```javascript
async submit() {
if (this.$refs.form.validate()) {
console.log(
`SIGN IN username: ${this.username}, password: ${this.password}`
);
try {
await signIn(this.username, this.password);
} catch (err) {
console.log(err);
} finally {
this.$store.dispatch("fetchUser");
}
console.log("Signed in");
}
}
```
A sign out button might look like:
```javascript
async dosignOut() {
try {
await signOut();
} finally {
this.$store.dispatch("fetchUser");
}
}
```
Remember:
- Call `this.$store.dispatch("fetchUser)` when you sign in - do it in a try/finally block to make sure the state gets updated after the ajax call.
- Call `this.$store.dispatch("fetchUser)` when you sign out do it in a try/finally bock to make sure the state gets updated after the ajax call.

View File

@@ -1,3 +1,13 @@
# AWS amplify
## Example repo
Use the following repo as an example/baseline:
<https://git.panaetius.co.uk/web-development/medium-aws-tutorial-1>
## Installaing amplify
Install amplify globally Install amplify globally
`yarn global add @aws-amplify/cli` `yarn global add @aws-amplify/cli`
@@ -6,6 +16,8 @@ Create new amplify app in project
`amplify init` `amplify init`
### Amplify commands
Amplify commands after init Amplify commands after init
`amplify status` - will show you what you've added already and if it's locally configured or deployed `amplify status` - will show you what you've added already and if it's locally configured or deployed
@@ -14,13 +26,18 @@ Amplify commands after init
`amplify console` - to open the Amplify Console and view your project status `amplify console` - to open the Amplify Console and view your project status
`amplify publish` - will build all your local backend and frontend resources (if you have hosting category added) and provision it in the cloud `amplify publish` - will build all your local backend and frontend resources (if you have hosting category added) and provision it in the cloud
`amplify add api` - to create a backend API and then `amplify add api` - to create a backend API and then
`amplify publish` - to deploy everything `amplify publish` - to deploy everything
Install aws amplify dependencies into project Install aws amplify dependencies into project
`yarn add aws-amplify aws-amplify-vue` `yarn add aws-amplify aws-amplify-vue`
## Create auth flow
Add the Amplify instance to Vue instance in `main.js` Add the Amplify instance to Vue instance in `main.js`
### Auth.js
Create a new js file in `./src/utils/auth.js` - write the aws amplify code in here. Create a new js file in `./src/utils/auth.js` - write the aws amplify code in here.
You need to write a function for each auth flow. Signup, GetUser etc. You need to write a function for each auth flow. Signup, GetUser etc.
@@ -28,3 +45,238 @@ You need to write a function for each auth flow. Signup, GetUser etc.
Remember to use Remember to use
`export {getUser, signUp, confirmSignUp, resendSignUp, signIn, signOut}` `export {getUser, signUp, confirmSignUp, resendSignUp, signIn, signOut}`
at the end so it can be imported later. at the end so it can be imported later.
You should write a view for each action, login, signup etc.
These views should define the UI, and also call the function in the `auth.js`. E.g if you need to sign in, you should write the UI, write any computed methods to verify the form, and write a submit method that takes the form details and calls the `signIn` function from the `auth.js` file.
In `./utils/auth.js` you can write the functions needed to actually log in with amplify.
For example, to signUp you can do:
```javascript
function signUp(username, password) {
return Auth.signUp({
username,
password,
attributes: {
email: username
}
})
.then(data => {
AmplifyEventBus.$emit("localUser", data.user);
if (data.userConfirmed === false) {
AmplifyEventBus.$emit("authState", "confirmSignUp");
} else {
AmplifyEventBus.$emit("authState", "signIn");
}
})
.catch(err => {
console.log(err);
});
}
```
### router/index.js
In the `./router/index.js` you should write the router flow.
#### Define routes
Each route should set whether they need authentication or not.
```javascript
{
path: "/",
name: "Home",
component: Home,
meta: { requiresAuth: true }
},
```
#### Additional functionality
In this file, you can write functions that control the behaviour. For example, you can force anyone logged in to go to the home page only:
```javascript
getUser().then(user => {
if (user) {
router.push({ path: "/" });
}
});
```
#### Respond to events
AWS Amplify has the ability to natively `emit` events in your auth flow functions. When you emit these, you can use `AmplifyEventBus` to do something.
We can push people to different routes based on the sign in flow.
An example emit:
```javascript
AmplifyEventBus.$emit("authState", "signedIn");
```
An example listener in the router:
```javascript
AmplifyEventBus.$on("authState", async state => {
const pushPathes = {
signedOut: () => {
router.push({ path: "/signIn" });
},
signUp: () => {
router.push({ path: "/signUp" });
},
confirmSignUp: () => {
router.push({ path: "/signUpConfirm" });
},
signIn: () => {
router.push({ path: "/signIn" });
},
signedIn: () => {
router.push({ path: "/" });
}
};
if (typeof pushPathes[state] === "function") {
pushPathes[state]();
}
});
```
## Managing sign in/out + displaying name
### Create the Vuex store
#### state
Your state should contain 3 items:
- authorized:bool = flag if logged in or not.
- user: str = user object.
- userEmail: str = username or email.
#### mutations
You should define two mutations (to update this state):
```javascript
mutations: {
user(state, user) {
state.authorized =
!!user && user.attributes && user.attributes.email_verified;
state.user = user;
},
userEmail(state, userEmail) {
state.userEmail = userEmail;
}
},
```
- When you set the user state you should check to make sure the user object you pass in exists and set the authorized flag at the same time.
#### actions
You should define an action that fetches the user. This should call the `getUser` method of whatever auth system you're using and pass this in to the method.
This `getUser` dispatch should either get the user and set the store state, or set it to null if no user exists. This way, any interaction with `getUser` will make sure the store is up to date.
```javascript
actions: {
async fetchUser({ commit }) {
try {
const user = await Auth.currentAuthenticatedUser();
const expires =
user.getSignInUserSession().getIdToken().payload.exp -
Math.floor(new Date().getTime() / 1000);
console.log(`Token expires in ${expires} seconds.`);
commit("user", user);
commit("userEmail", user.attributes.email);
} catch (err) {
commit("user", null);
commit("userEmail", null);
}
}
},
```
#### getters
You should define getters to access the state. These getters will cache - so they will only update if one of the things used to calculate the state updates.
```javascript
getters: {
userEmail: state => {
return state.userEmail;
},
authorized: state => {
return state.authorized;
}
}
```
### Triggering state for apps
In addition to getting the user to determine router actions, you should get the user in the main component so you can set the state.
In the main view (parent of all components `App.vue`) you should define a `created` method. This method should:
- Fetch the user.
- Change the state to reflect this.
```javascript
async created() {
try {
await this.$store.dispatch("fetchUser");
} catch (error) {
console.log(error);
} finally {
console.log(this.email);
console.log(this.$store.state.userEmail);
}
},
```
This needs to be done right at the start to make sure this triggers a state change before anything else.
In the views for signing in/out/registering etc you should make sure that when you call the auth methods you wrote, that you also update the state at this time.
This way, any component in the app can use the state to determine if someone is logged in, and display/toggle components dynamically.
A signin method for a button might look like:
```javascript
async submit() {
if (this.$refs.form.validate()) {
console.log(
`SIGN IN username: ${this.username}, password: ${this.password}`
);
try {
await signIn(this.username, this.password);
} catch (err) {
console.log(err);
} finally {
this.$store.dispatch("fetchUser");
}
console.log("Signed in");
}
}
```
A sign out button might look like:
```javascript
async dosignOut() {
try {
await signOut();
} finally {
this.$store.dispatch("fetchUser");
}
}
```
Remember:
- Call `this.$store.dispatch("fetchUser)` when you sign in - do it in a try/finally block to make sure the state gets updated after the ajax call.
- Call `this.$store.dispatch("fetchUser)` when you sign out do it in a try/finally bock to make sure the state gets updated after the ajax call.

View File

@@ -7653,6 +7653,11 @@ loose-envify@^1.0.0:
dependencies: dependencies:
js-tokens "^3.0.0 || ^4.0.0" js-tokens "^3.0.0 || ^4.0.0"
lottie-web@^5.1.9:
version "5.6.6"
resolved "https://registry.yarnpkg.com/lottie-web/-/lottie-web-5.6.6.tgz#106fa3dc54337588517591b2e3e348248c6a3f1b"
integrity sha512-N2c5+VjWFFEv8AQIDohFQaFiPudcSTSKE6WrMmUqtjc+/tQWe23eTu7XHqzXuAf7HDQclHhBorDeSelPNaYiHQ==
lower-case-first@^1.0.0: lower-case-first@^1.0.0:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/lower-case-first/-/lower-case-first-1.0.2.tgz#e5da7c26f29a7073be02d52bac9980e5922adfa1" resolved "https://registry.yarnpkg.com/lower-case-first/-/lower-case-first-1.0.2.tgz#e5da7c26f29a7073be02d52bac9980e5922adfa1"
@@ -11524,6 +11529,13 @@ vue-loader@^15.8.3:
vue-hot-reload-api "^2.3.0" vue-hot-reload-api "^2.3.0"
vue-style-loader "^4.1.0" vue-style-loader "^4.1.0"
vue-lottie@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/vue-lottie/-/vue-lottie-0.2.1.tgz#f9f62c34a276e6834255406118fb05b51f765e4b"
integrity sha512-zInUX69Ij8MhVR3XArpu4PqqBoufwKxS5UMutWCPm59VUaB5H6GtnaIzf9M+l6aYU+Kr8gF/W9dzWLgRuU6V+Q==
dependencies:
lottie-web "^5.1.9"
vue-router@^3.1.5: vue-router@^3.1.5:
version "3.1.6" version "3.1.6"
resolved "https://registry.npm.taobao.org/vue-router/download/vue-router-3.1.6.tgz#45f5a3a3843e31702c061dd829393554e4328f89" resolved "https://registry.npm.taobao.org/vue-router/download/vue-router-3.1.6.tgz#45f5a3a3843e31702c061dd829393554e4328f89"