Vue-router beforeRouteEnter Vue-Resource request - vuejs2

I have used this link as a reference to make a request before entering a route:
https://router.vuejs.org/en/advanced/data-fetching.html
import Vue from 'vue'
import VueResource from 'vue-resource'
Vue.use(VueResource)
function getCities () {
return Vue.http({
method: 'GET',
url: process.env.base_url + 'cities'
})
}
export default {
data () {
return {
cities: []
}
},
beforeRouteEnter (to, from, next) {
getCities((err, cities) => {
if (err) {
next(false)
} else {
next(vm => {
vm.cities = cities.data
})
}
})
},
watch: {
$route () {
this.cities = []
getCities((err, cities) => {
if (err) {
this.error = err.toString()
} else {
this.cities = cities.data
}
})
}
}
However it doesn't seem to be working for me. I have tested this code and the request is successfully being made. However the result is not being returned. Currently, the request itself is being returned from the function, but I cannot show it in the beforeRouteEnter callback where it supposedly should assign it to vm.cities neither in the watch $route section.
Any help/opinion is appreciated.

The Vue.http method returns a promise, so the code should read:
beforeRouteEnter (to, from, next) {
getCities().then(response => {
next(vm => vm.cities = response.body)
}
}

Related

beforeRouteUpdate not recognizing function

When I try to call this.getData() in beforeRouteUpdate it just spits out this error
"TypeError: this.getData is not a function"
From looking at other peoples examples this work, but they weren't using async/await.
<script>
export default {
async beforeRouteUpdate(to, from, next) {
await this.getData()
next()
},
data() {
return {
word: null,
}
},
async created() {
await this.getData()
},
methods: {
async getData() {
const resp = await axios.get(
'http://127.0.0.1:8000/api/word/' + this.$route.params.word,
{ validateStatus: false }
)
console.log(resp)
switch (resp.status) {
case 200:
this.word = {
word: resp.data.word,
definition: resp.data.definition,
}
break
case 404:
this.word = null
break
}
},
},
}
</script>
The concept you want use it, is called "prefetch".
It's better use this solution:
beforeRouteEnter(to, from, next) {
axios.get(
'http://127.0.0.1:8000/api/word/' + this.$route.params.word,
{validateStatus: false}
)
.then(resp => {
next()
})
.catch(error => {
})
},
beforeRouteUpdate(to, from, next) {
axios.get(
'http://127.0.0.1:8000/api/word/' + this.$route.params.word,
{validateStatus: false}
)
.then(resp => {
next()
})
.catch(error => {
})
}
NOTE 1: You don't access to this in beforeRouteEnter (to use methods). Because your component doesn't mounted yet.
NOTE 2: To avoid fetch duplication (DRY principle), you can modularize fetching (like vuex actions) and call it.

use firebase auth with vue 3 route guard

I have the needings to use firebase auth with vue router.
I have this simple guard, but I've noticed that sometimes the users will see for a while the pages also if they are not logged.
router.beforeEach( async (to, from) => {
onAuthStateChanged( getAuth(app), (user) => {
console.log(user, to.meta.requireAuth)
if( to.meta.requireAuth && !user ) {
return {
name: 'Signin'
}
}
})
})
I also have this kind of control inside my components, but I'm looking for something global to use to prevent unregistered users to see the app.
Any suggestion?
You can wrap the onAuthStateChanged in a Promise and make your before each an async function.
// in some global file
export async function getCurrentUser(): Promise<User | null> {
return new Promise((resolve, reject) => {
const unsubscribe = auth.onAuthStateChanged((user) => {
unsubscribe();
resolve(user);
}, reject);
});
}
// your router file
router.beforeEach(async (to, from, next) => {
if (to.matched.some((record) => record.meta.publicAccess)) {
next();
} else {
const currentUser = await getCurrentUser();
if (currentUser) {
next();
} else {
next({ name: "Login" });
}
}
});
// Your route object
{
name: "Login",
path: "/login",
component: () => import("#/views/authentication/Login.vue"),
}

Mock .get() Function using Jest on VueJS

I am trying to mock a GET request to get some Posts using the ID. This is the code I am trying to mock:
getPost() {
this.refreshToken();
http
.get(`/posts/${this.$cookie.get('postid')}`, {
headers: {
"Authorization": `Bearer ${this.$cookie.get('token')}`,
"Content-type": "application/json",
},
})
.then((response) => {
this.post = response.data;
})
.catch((error) => {
console.log(error.response);
});
}
This is my attempt at a test:
import {getPost} from '#/views/Post.vue'
import axios from 'axios';
jest.mock('axios');
describe('get Post by ID', () => {
afterEach(() => {
jest.resetAllMocks();
});
it('should return empty when axios.get failed', async () => {
const getError = new Error('error');
axios.get = jest.fn().mockRejectedValue(getError);
const actualValue = await getPost();
expect(actualValue).toEqual(new Map());
expect(axios.get).toBeCalledWith('/posts/postid');
});
it('should return users', async () => {
const mockedUsers = [{ postID: 1 }];
axios.get = jest.fn().mockResolvedValue(mockedUsers);
const actualValue = await getPost(['1']);
expect(actualValue).toEqual(mockedUsers);
expect(axios.get).toBeCalledWith('/posts/postid');
});
})
The error I am getting is:
TypeError: (0 , _Post.getPost) is not a function
I am not sure what to do, and any help would be super appreciated. Thanks!
Assuming you have getPost() defined in the Post component's methods, you can't use named imports to access getPost. Instead, you'll have to mount the component, and use the wrapper's vm:
// Post.spec.js
import { shallowMount } from '#vue/test-utils'
import Post from '#/views/Post.vue'
it('...', () => {
const wrapper = shallowMount(Post)
await wrapper.vm.getPost()
expect(wrapper.vm.post).toEqual(...)
})
Also make sure to return the axios call in getPost() so that it could be awaited:
// Post.vue
export default {
methods: {
getPost() {
this.refreshToken();
👇
return http.get(/*...*/)
.then(/*...*/)
.catch(/*...*/);
}
}
}

How to get Vuex updated getters value in Vue custom middleware for permission check?

I have loaded all permissions when the sidebar is loading after login and getters are updated. I can access all permissions from the sidebar component.
Now I want to access all permissions in my middleware. Is it possible? What to do?
Please give a suggestion.
Here is my permission store:
const state = {
permissions: [],
user: [],
}
const getters = {
getPermissions: state => state.permissions,
getUserInfo: state => state.user,
}
const actions = {
userPermission({commit}, data) {
if (data != null) {
axios.get("/api/auth/user", {params: { token: data.token}})
.then(res => {
const per = res.data.data.permissions;
commit("setPermissions", per);
// console.log(res.data.data.permissions);
})
.catch(err => {
console.log(err);
});
}
},
userInfo({commit}, data) {
if (data != null) {
axios.get("/api/auth/user", {params: { token: data.token}})
.then(res => {
const info = res.data.data.user;
commit("setUserInfo", info);
// console.log(res.data.data.user);
})
.catch(err => {
console.log(err);
});
}
},
}
const mutations = {
setPermissions(state, data) {
state.permissions = data;
},
setUserInfo(state, data) {
state.user = data;
}
}
export default {
state,
getters,
actions,
mutations
}
Here is the middleware function:
import store from '../store';
export default (to, from, next) => {
if (isAuthenticated()) {
if (!hasPermissionsNeeded(to)) {
next('admin/permission-denied');
} else {
next();
}
next();
} else {
next('/admin/session/login');
}
};
function isAuthenticated() {
if (localStorage.getItem("userInfo") != null && localStorage.getItem("userInfo").length > 0) {
return true;
} else {
localStorage.removeItem("userInfo");
return false;
}
};
function hasPermissionsNeeded(to) {
var permissions = store.getters.getPermissions;
if(permissions.includes(to.meta.permissions) || to.meta.permissions == '*') {
return true;
} else {
return false;
}
};
Here is the router logic:
path: "/admin/country",
component: () => import("./views/admin/country/country"),
beforeEnter: authenticate,
meta : {
permissions: 'browse country'
}
I can't see where you're dispatching the userPermission action to load the permissions, but I assume you're only dispatching it somewhere that only gets called after the middleware has run. So it looks like the permissions might not have been loaded by the time you're running the middleware. You might want to dispatch the permission in the middleware, wait for it to finish and only then check the permissions. For example:
export default (to, from, next) => {
store.dispatch('userPermission').then(() => {
if (isAuthenticated()) {
...
})

Why can't I pass my user_name value into my component? (Auth)

I am trying to pass the name of the user after authentication into a Vue component, but I get a name: undefined value after load.
Here is my AuthService.js:
//config details taken from OAUTH JS doc: https://github.com/andreassolberg/jso
import { JSO, Fetcher } from 'jso';
const client = new JSO({
providerID: '<my-provider>',
default_lifetime: 1800,
client_id: '<my-client-id>',
redirect_uri: 'http://localhost:8080/',
authorization:'<my-auth-server>/oauth/authorize'
//scopes: { request: ['https://www.googleapis.com/auth/userinfo.profile'] }
});
export default {
getProfile() {
// JSO plugin provides a simple wrapper around the fetch API to handle headers
let f = new Fetcher(client);
let url = 'https://www.googleapis.com/auth/userinfo.profile';
f.fetch(url, {})
.then(data => {
return data.json();
})
.then(data => {
return data.user_name;
})
.catch(err => {
console.error('Error from fetcher', err);
});
}
};
Then, in my single file component named MainNav, I have:
import AuthService from "#/AuthService";
export default {
name: "MainNav",
data() {
return {
name: ""
};
},
created() {
this.name = AuthService.getProfile();
}
};
</script>
Anyone have any tips on how I can get the user_name value from the AuthService to my component? I will then need to then display the name in my nav template. Doing a console.log test works fine, just can't return it to my SFC. Also, the JSO library is here: https://github.com/andreassolberg/jso#fetching-data-from-a-oauth-protected-endpoint
Because getProfile returns nothing (undefined). I see you use es6 then you can use async functions
//config details taken from OAUTH JS doc: https://github.com/andreassolberg/jso
import { JSO, Fetcher } from 'jso';
const client = new JSO({
providerID: '<my-provider>',
default_lifetime: 1800,
client_id: '<my-client-id>',
redirect_uri: 'http://localhost:8080/',
authorization:'<my-auth-server>/oauth/authorize'
//scopes: { request: ['https://www.googleapis.com/auth/userinfo.profile'] }
});
export default {
getProfile() {
// JSO plugin provides a simple wrapper around the fetch API to handle headers
let f = new Fetcher(client);
let url = 'https://www.googleapis.com/auth/userinfo.profile';
return f.fetch(url, {}) // return promise here
.then(data => {
return data.json();
})
.then(data => {
return data.user_name;
})
.catch(err => {
console.error('Error from fetcher', err);
});
}
};
And
import AuthService from "#/AuthService";
export default {
name: "MainNav",
data() {
return {
name: ""
};
},
async created() {
try {
this.name = await AuthService.getProfile();
} catch(error) {
// handle
}
}
};
</script>
Or without async (add one more then)
import AuthService from "#/AuthService";
export default {
name: "MainNav",
data() {
return {
name: ""
};
},
created() {
AuthService.getProfile().then((userName) => this.name = userName))
.catch((error) => { /* handle */ })
}
};
</script>