Completed API module

Added calls to an external API and JWT authentication to the webapplication
master
AndroidDrew 7 years ago
parent 160dd6e20d
commit 8aef2f5177

@ -25,8 +25,8 @@
module: { module: {
rules: [ rules: [
{ {
enforce: 'pre', //check source files before they are loader by other loaders enforce: 'pre', // check source files before they are loader by other loaders
test: /(\.js$)|(\.vue$)/, //linting for both js and vue files test: /(\.js$)|(\.vue$)/, // linting for both js and vue files
loader: 'eslint-loader', loader: 'eslint-loader',
exclude: /node_modules/ exclude: /node_modules/
}, },

@ -10,6 +10,7 @@
"author": "Drew Bednar", "author": "Drew Bednar",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"axios": "^0.17.1",
"bulma": "^0.6.1", "bulma": "^0.6.1",
"express": "^4.16.2", "express": "^4.16.2",
"vue": "^2.5.6", "vue": "^2.5.6",

@ -0,0 +1,60 @@
import axios from 'axios'
axios.defaults.baseURL = 'https://api.fullstackweekly.com'
// To avoid adding the token to every request we make to a service we can
// instead use an interceptor
axios.interceptor.request.use(function (config) {
// added for when the code is running on a server for serverside rendering
if (typeof window === 'undefined') {
return config
}
const token = window.localStorage.getItem('token')
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
return config
})
const appService = {
getPosts (categoryId) {
// I guess we can do some string format like operations here
// This is a promise so once it is resolved we will receive the response
return new Promise((resolve) => {
axios.get(`/wp-json/wp/v2/posts?categories=${categoryId}&per_page=6`)
.then(response => {
resolve(response.data)
})
})
}, // getPosts
getProfile () {
return new Promise((resolve) => {
axios.get('/services/profile.php' // ,
/*
{
headers: {
'Authorization': `Bearer ${window.localStorage.getItem('token')}`
}
}
*/
)
.then(response => {
resolve(response.data)
})
})
}, // getProfile
login (credentials) { // credemtials will be a javascript object
// returns a new promise
return new Promise((resolve, reject) => {
axios.post('/services/auth.php', credentials)
.then(response => {
resolve(response.data)
}).catch(response => {
reject(response.status)
})
})
} // login
}
export default appService

@ -2,15 +2,16 @@
<div class="columns"> <div class="columns">
<div class="column is-one-third" v-for="(post, title) in posts" v-bind:key="post.id"> <div class="column is-one-third" v-for="(post, title) in posts" v-bind:key="post.id">
<app-post> <app-post>
<h3 slot="title">{{ post.title }}</h3> <h3 slot="title" v-html="post.title.rendered"></h3>
<span slot="content">{{ post.content }}</span> <span slot="content" v-html="post.excerpt.rendered"></span>
<a slot="link" class="card-footer-item" :href="post.link" target="_blank">Read More</a> <a slot="link" class="card-footer-item" :href="post.rest_api_enabler.Link" target="_blank">Read More</a>
</app-post> </app-post>
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import Post from './Post.vue' import Post from './Post.vue'
import appService from '../app.service.js'
export default { export default {
components: { components: {
'app-post': Post 'app-post': Post
@ -22,7 +23,7 @@
// actually use the object to navigate by doing something like this.$route.push('/') // actually use the object to navigate by doing something like this.$route.push('/')
// or obtain data like we are below // or obtain data like we are below
id: this.$route.params.id, id: this.$route.params.id,
postsFrontEnd: [ /* postsFrontEnd: [
{ id: 1, title: 'PWA Stats', content: 'A community-driven list of stats and news related to Progressive Web Apps', link: 'https://www.pwastats.com/' }, { id: 1, title: 'PWA Stats', content: 'A community-driven list of stats and news related to Progressive Web Apps', link: 'https://www.pwastats.com/' },
{ id: 2, title: 'A Comprehensive Guide To HTTP/2 Server Push', content: 'No longer is HTTP/2 a feature we pine for. It has arrived, and with it comes server push!', link: 'https://www.smashingmagazine.com/2017/04/guide-http2-server-push/' }, { id: 2, title: 'A Comprehensive Guide To HTTP/2 Server Push', content: 'No longer is HTTP/2 a feature we pine for. It has arrived, and with it comes server push!', link: 'https://www.smashingmagazine.com/2017/04/guide-http2-server-push/' },
{ id: 3, title: 'So whats this GraphQL thing I keep hearing about?', content: 'Why now is the perfect time to learn what exactly this GraphQL thing you keep hearing about really is.', link: 'https://medium.freecodecamp.com/so-whats-this-graphql-thing-i-keep-hearing-about-baf4d36c20cf' } { id: 3, title: 'So whats this GraphQL thing I keep hearing about?', content: 'Why now is the perfect time to learn what exactly this GraphQL thing you keep hearing about really is.', link: 'https://medium.freecodecamp.com/so-whats-this-graphql-thing-i-keep-hearing-about-baf4d36c20cf' }
@ -32,16 +33,19 @@
{ id: 5, title: 'Learning JavaScript Design Patterns', content: 'Design patterns are reusable solutions to commonly occurring problems in software design.', link: 'https://addyosmani.com/resources/essentialjsdesignpatterns/book/' }, { id: 5, title: 'Learning JavaScript Design Patterns', content: 'Design patterns are reusable solutions to commonly occurring problems in software design.', link: 'https://addyosmani.com/resources/essentialjsdesignpatterns/book/' },
{ id: 6, title: 'The Power of Custom Directives in Vue', content: 'The beautiful thing about Vue is that it\'s incredibly feature-rich.', link: 'https://css-tricks.com/power-custom-directives-vue/' } { id: 6, title: 'The Power of Custom Directives in Vue', content: 'The beautiful thing about Vue is that it\'s incredibly feature-rich.', link: 'https://css-tricks.com/power-custom-directives-vue/' }
], ],
*/
posts: [] posts: []
} }
}, },
methods: { methods: {
loadPosts () { loadPosts () {
if (this.id === 'front-end') { let categoryId = 2 // For his wp this is the frontend category
this.posts = this.postsFrontEnd if (this.id === 'mobile') {
} else { categoryId = 11 // For his wp site this is the mobile category
this.posts = this.postsMobile
} }
appService.getPosts(categoryId).then(data => {
this.posts = data
})
} }
}, },
watch: { watch: {

@ -1,5 +1,14 @@
<template> <template>
<div class="content"> <div class="content">
<div v-if="isAuthenticated">
<p>Hello isAuthenticated users!</p>
<p>Name: {{profile.firstName}}</p>
<p>Favorite Sandwich: {{profile.favoriteSandwich}}</p>
<button v-on:click="logout()" class="button is-primary">
Logout
</button>
</div>
<div v-else>
<h2>Login</h2> <h2>Login</h2>
<div class="field is-horizontal"> <div class="field is-horizontal">
<div class="field-label is-normal"> <div class="field-label is-normal">
@ -8,7 +17,7 @@
<div class="field-body"> <div class="field-body">
<div class="field"> <div class="field">
<div class="control"> <div class="control">
<input class="input" type="text" <input v-model="username" class="input" type="text"
placeholder="Your username"> placeholder="Your username">
</div> </div>
</div> </div>
@ -21,7 +30,7 @@
<div class="field-body"> <div class="field-body">
<div class="field"> <div class="field">
<div class="control"> <div class="control">
<input class="input" type="password" <input v-model="password" class="input" type="password"
placeholder="Your password"> placeholder="Your password">
</div> </div>
</div> </div>
@ -34,7 +43,7 @@
<div class="field-body"> <div class="field-body">
<div class="field"> <div class="field">
<div class="control"> <div class="control">
<button class="button is-primary"> <button v-on:click="login()" class="button is-primary">
Login Login
</button> </button>
</div> </div>
@ -42,4 +51,59 @@
</div> </div>
</div> </div>
</div> </div>
</div>
</template> </template>
<script>
import appService from '../app.service.js'
export default {
data () {
return {
username: '',
password: '',
isAuthenticated: false,
profile: {}
}
}, // data,
methods: {
login () {
console.log('called login')
appService.login({username: this.username, password: this.password})
.then((data) => {
window.localStorage.setItem('token', data.token)
window.localStorage.setItem('tokenExpiration', data.expiration)
this.isAuthenticated = true
this.username = ''
this.password = ''
})
.catch(() => window.alert('Could not login!'))
}, // login
logout () {
window.localStorage.setItem('token', null)
window.localStorage.setItem('tokenExpiration', null)
this.isAuthenticated = false
} // logout
}, // methods
created () {
let expiration = window.localStorage.getItem('tokenExpiration')
var unixTimestamp = new Date().getTime() / 1000
if (expiration !== null && parseInt(expiration) - unixTimestamp > 0) {
this.isAuthenticated = true
}
}, // created
watch: {
// once isAuthenticated is set to true we will run the getProfile methods
// to update the webapp with the users profile information
isAuthenticated: function (val) {
console.log('Change in isAuthenticated status')
if (val) {
appService.getProfile()
.then(profile => {
this.profile = profile
})
} else {
this.profile = {}
}
} // watch isAuthenticated
}
}
</script>

Loading…
Cancel
Save