I have a function to get the logged in user details and check for the 'permissions' in the 'teams array'.
However, after running the code, I got two errors, and after the UI is loaded, the currentUser displays null until any component in the UI is selected. See the image, currentUser is null when UI is loaded but as soon as I click on a component, it prints the currentUser. I do not understand how to get through these errors.
This is a part of the vue-store code from which I am getting the currentUser array:
state(){
return currentUser: null
}
mutations: {
setCurrentUser(state, payload) {
// console.log("set current user", payload);
state.currentUser = payload;
}
},
actions: {
loadCurrentUser(context, data) {
// console.log("loadcurrentuser");
return new Promise((resolve, reject) => {
axiosService({ requiresAuth: true })
.get(`/administration/users/${data.id}`)
.then(response => {
context.commit("setCurrentUser", response.data);
resolve(response.data);
})
.catch(error => reject(error));
});
},
}
getters: {
isCurrentUser(state){
return state.currentUser;
}
}
here is the code from the router.js
{
path: "/administration",
name: "Administration",
component: PageAdministration,
meta: {
requiresAuth: true,
neededPermissions: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
},
function authorizeUser(to, from,next) {
let isAllowed = true;
const currentUser = store.getters["auth/isCurrentUser"];
console.log(currentUser);
for(let i = 0;i<currentUser.teams.length;i++){
const team = currentUser.teams[i]
const validPermissions = team.permissions.filter((item) => { return to.meta.neededPermissions.includes(item.permissionType); }); //returns array of objects
const mappedValidPermissions = validPermissions.map((item) => { return item.permissionType; });// returns array with permissionType
if (!to.meta.neededPermissions.every(i=>mappedValidPermissions.includes(i))) {
isAllowed = false;
next({ path: "/:notFound(.*)" });
break;
}
}
if(isAllowed) next();
}
router.beforeEach((to, from, next) => {
if (to.meta.requiresAuth && !store.getters["auth/isLoggedIn"]) {
next({
name: "Authentication",
params: {
desiredRoute: to.fullPath,
},
});
} else {
authorizeUser(to,next)
}
});
These are the two errors
Related
I write test in Jest for my app trying to test if the data downloads correctly from the api. These are the data of the movies.
export const programsMock: FetchProgramsParameters = {
meta: {
status: 200,
pagination: {
page: 1,
perPage: 15,
hasNext: true,
},
},
data: [
{
id: "1",
title: "No Abras la Puerta",
genres: ["Drama", "Komedi", "Komedi", "Drama", "Romantik"],
imdb: {
rating: "7.1",
},
playProviders: [],
},
]}
jest.mock("./server/services/api");
render(<App />);
const fetchProgramsMocked = jest.mocked(fetchAllProgramsParameters);
describe("GET ", () => {
it("responds with 200", async () => {
fetchProgramsMocked.mockResolvedValueOnce(programsMock);
expect(fetchProgramsMocked).toHaveBeenCalledTimes(1);
expect(screen.getByText(/No Abras la Puerta/i)).toBeTruthy();
});
But I get error in getData: Cannot destructure property 'data' of '(intermediate value)' as it is undefined.
getData:
export const getAllPrograms = async (category: string) => {
const { data } = await fetchAllProgramsParameters(category);
const programs: TVProgram[] = data.map((program) => {
return {
id: program.id,
title: program.title,
imageLandscape: program.imageLandscape,
genres: program.genres,
playProviders: program.playProviders,
imdb: program.imdb,
};
});
return programs;
};
async function fetchApi<T>(pathname: string, filter?: string) {
const response = await fetch(`${pathname}${filter}`);
if (response.status === 404) {
throw new NotFoundError();
}
if (response.status >= 400) {
throw new HttpError("Bad response", response.status);
}
return response.json() as Promise<T>;
}
async function fetchAllProgramsParameters(category: string) {
return fetchApi<FetchProgramsParameters>(
API_URL,
["orderBy=views", "programType=" + category].join("&")
);
}
Please help how to solve it
I'm working with rxdb and I have pull and push handlers for the backend I have used supabase
I have setup the code for replication as follows:
replication.ts
import { RxDatabase } from "rxdb";
import { RxReplicationPullStreamItem } from "rxdb/dist/types/types";
import { replicateRxCollection } from "rxdb/plugins/replication";
import { Subject } from "rxjs";
import { supabaseClient, SUPABASE_URL } from "src/config/supabase";
import { DbTables } from "src/constants/db";
import {
blockPullHandler,
blockPushHandler,
} from "./repilicationhandlers/block";
import { CheckpointType, RxBlockDocument, RxBlocksCollections } from "./types";
export async function startReplication(
database: RxDatabase<RxBlocksCollections>
) {
const pullStream$ = new Subject<
RxReplicationPullStreamItem<RxBlockDocument, CheckpointType>
>();
supabaseClient
.from(DbTables.Block)
.on("*", (payload) => {
console.log("Change received!", payload);
const doc = payload.new;
pullStream$.next({
checkpoint: {
id: doc.id,
updated: doc.updated,
},
documents: [doc] as any,
});
})
.subscribe((status: string) => {
console.log("STATUS changed");
console.dir(status);
if (status === "SUBSCRIBED") {
pullStream$.next("RESYNC");
}
});
const replicationState = await replicateRxCollection({
collection: database.blocks,
replicationIdentifier: "supabase-replication-to-" + SUPABASE_URL,
deletedField: "archived",
pull: {
handler: blockPullHandler as any,
stream$: pullStream$.asObservable(),
batchSize: 10,
},
push: {
batchSize: 1,
handler: blockPushHandler as any,
},
});
replicationState.error$.subscribe((err) => {
console.error("## replicationState.error$:");
console.log(err);
});
return replicationState;
}
blockPullHandler:
export const blockPullHandler = async (
lastCheckpoint: any,
batchSize: number
) => {
const minTimestamp = lastCheckpoint ? lastCheckpoint.updated : 0;
console.log("Pulling data", batchSize, lastCheckpoint);
const { data, error } = await supabaseClient
.from(DbTables.Block)
.select()
.gt("updated", minTimestamp)
.order("updated", { ascending: true })
.limit(batchSize);
if (error) {
console.log(error);
throw error;
}
const docs: Array<Block> = data;
return {
documents: docs,
hasMoreDocuments: false,
checkpoint:
docs.length === 0
? lastCheckpoint
: {
id: lastOfArray(docs).id,
updated: lastOfArray(docs).updated,
},
};
};
blockPushHandler:
export const blockPushHandler = async (
rows: RxReplicationWriteToMasterRow<RxBlockDocumentType>[]
) => {
if (rows.length !== 1) {
throw new Error("# pushHandler(): too many push documents");
}
const row = rows[0];
const oldDoc: any = row.assumedMasterState;
const doc: Block = row.newDocumentState;
console.log(row, oldDoc, doc);
// insert
if (!row.assumedMasterState) {
const { error } = await supabaseClient.from(DbTables.Block).insert([doc]);
console.log("Error 1", error);
if (error) {
// we have an insert conflict
const conflictDocRes: any = await supabaseClient
.from(DbTables.Block)
.select()
.eq("id", doc.id)
.limit(1);
return [conflictDocRes.data[0]];
} else {
return [];
}
}
// update
console.log("pushHandler(): is update");
const { data, error } = await supabaseClient
.from(DbTables.Block)
.update(doc)
.match({
id: doc.id,
replicationRevision: oldDoc.replicationRevision,
});
console.log("Error 2", error);
if (error) {
console.log("pushHandler(): error:");
console.log(error);
console.log(data);
throw error;
}
console.log("update response:");
console.log(data);
if (data.length === 0) {
// we have an updated conflict
const conflictDocRes: any = await supabaseClient
.from(DbTables.Block)
.select()
.eq("id", doc.id)
.limit(1);
return [conflictDocRes.data[0]];
}
return [];
};
But the issue is when I start the application and the pull handler is called correctly but it doesn't stop calling the pull handler and it sends continuous request one after another even after it has fetched the documents even when I set hasMoreDocuments to false It keeps sending requests and running the replicator. Is there something wrong with my configuration?
database.ts:
export const createDatabase = async () => {
const database = await createRxDatabase({
name: "sundaedb",
storage: getRxStorageDexie(),
});
await database.addCollections({
blocks: {
schema: blockSchema as any,
conflictHandler: conflictHandler as any,
},
documents: {
schema: documentSchema as any,
conflictHandler: conflictHandler as any,
},
});
database.blocks.preInsert((docData) => {
docData.replicationRevision = createRevision(
database.hashFunction,
docData as any
);
return docData;
}, false);
database.blocks.preRemove((docData) => {
console.log(" PRE REMOVE !!");
console.log(JSON.stringify(docData, null, 4));
const oldRevHeight = parseRevision(docData.replicationRevision).height;
docData.replicationRevision =
oldRevHeight + 1 + "-" + database.hashFunction(JSON.stringify(docData));
console.log(JSON.stringify(docData, null, 4));
return docData;
}, false);
database.blocks.preSave((docData) => {
const oldRevHeight = parseRevision(docData.replicationRevision).height;
docData.replicationRevision =
oldRevHeight + 1 + "-" + database.hashFunction(JSON.stringify(docData));
return docData;
}, false);
return database;
};
So this is the scenario / premises:
In order to populate a chat queue in real time I need to open a connection to a websocket, send a message and then set the data to a websocket store. This store will basically manage all the websocket state.
Before populating the chat queue there's two parameters I need: a shiftId coming from one http API request and a connectionId coming from the websocket. Using those two parameters I finally can subscribe to a third http API and start receiving messages to populate the chat queue.
The problem is that due to the async behaviour of the websocket (or that's what I think, please feel to correct me if I'm wrong) I always get an empty "connectionId" when trying to make the put to that "subscription" API. I have tried with async/await and promises but nothing seems to work. I'm pretty new to async/await and websockets with Vuex so pretty sure I'm doing something wrong.
This is the user vuex module where I do all the login/token operations and dispatch a "updateEventsSubscription" action from the shift vuex module. In order for the "updateEventsSubscription" action to work I need to get the response from the "processWebsocket" action (to get the connectionId parameter) and from the "startShift" action (to get the shiftId parameter) coming from the shifts vuex module:
import UserService from '#/services/UserService.js'
import TokenService from '#/services/TokenService.js'
import router from '#/router'
export const namespaced = true
export const state = {
accessToken: '',
errorMessage: '',
errorState: false,
userEmail: localStorage.getItem('userEmail'),
userPassword: localStorage.getItem('userPassword'),
}
export const mutations = {
SET_TOKEN(state, accessToken) {
state.accessToken = accessToken
TokenService.saveToken(accessToken)
},
SET_USER(state, authUserJson) {
state.userEmail = authUserJson.email
state.userPassword = authUserJson.password
localStorage.setItem('userPassword', authUserJson.password)
localStorage.setItem('userEmail', authUserJson.email)
},
SET_ERROR(state, error) {
state.errorState = true
state.errorMessage = error.data.error_description
},
CLOSE_NOTIFICATION(state, newErrorState) {
state.errorState = newErrorState
},
}
export const actions = {
signIn({ commit, dispatch, rootState }, authUserJson) {
return UserService.authUser(authUserJson)
.then((result) => {
commit('SET_USER', authUserJson)
commit('SET_TOKEN', result.data.access_token)
dispatch('token/decodeToken', result.data.access_token, {
root: true,
})
dispatch(
'shifts/updateEventsSubscription',
rootState.token.agentId,
{
root: true,
}
)
router.push('/support')
})
.catch((error) => {
console.log(error)
if (error.response.status === 400) {
commit('SET_TOKEN', null)
commit('SET_USER', {})
commit('SET_ERROR', error.response)
} else {
console.log(error.response)
}
})
},
signOut({ commit }) {
commit('SET_TOKEN', null)
commit('SET_USER', {})
localStorage.removeItem('userPassword')
localStorage.removeItem('userEmail')
TokenService.removeToken()
router.push('/')
},
closeNotification({ commit }, newErrorState) {
commit('CLOSE_NOTIFICATION', newErrorState)
},
}
export const getters = {
getToken: (state) => {
return state.accessToken
},
errorState: (state) => {
return state.errorState
},
errorMessage: (state) => {
return state.errorMessage
},
isAuthenticated: (state) => {
return state.accessToken
},
userEmail: (state) => {
return state.userEmail
},
userPassword: (state) => {
return state.userPassword
},
}
This is websocket store: I pass the connectionId to the state in order to be able to use it in another vuex action to subscribe for new chats:
export const namespaced = true
export const state = {
connected: false,
error: null,
connectionId: '',
statusCode: '',
incomingChatInfo: [],
remoteMessage: [],
messageType: '',
ws: null,
}
export const actions = {
processWebsocket({ commit }) {
const v = this
this.ws = new WebSocket('mywebsocket')
this.ws.onopen = function (event) {
commit('SET_CONNECTION', event.type)
v.ws.send('message')
}
this.ws.onmessage = function (event) {
commit('SET_REMOTE_DATA', event)
}
this.ws.onerror = function (event) {
console.log('webSocket: on error: ', event)
}
this.ws.onclose = function (event) {
console.log('webSocket: on close: ', event)
commit('SET_CONNECTION')
ws = null
setTimeout(startWebsocket, 5000)
}
},
}
export const mutations = {
SET_REMOTE_DATA(state, remoteData) {
const wsData = JSON.parse(remoteData.data)
if (wsData.connectionId) {
state.connectionId = wsData.connectionId
console.log(`Retrieving Connection ID ${state.connectionId}`)
} else {
console.log(`We got chats !!`)
state.messageType = wsData.type
state.incomingChatInfo = wsData.documents
}
},
SET_CONNECTION(state, message) {
if (message == 'open') {
state.connected = true
} else state.connected = false
},
SET_ERROR(state, error) {
state.error = error
},
}
And finally this is the shift store (where the problem is), as you can see I have a startShift action (everything works fine with it) and then the "updateEventsSubscription" where I'm trying to wait for the response from the "startShift" action and the "processWebsocket" action. Debugging the app I realize that everything works fine with the startShift action but the websocket action sends the response after the "updateEventsSubscription" needs it causing an error when I try to make a put to that API (because it needs the connectionId parameter coming from the state of the websocket).
import ShiftService from '#/services/ShiftService.js'
export const namespaced = true
export const state = {
connectionId: '',
shiftId: '',
agentShiftInfo: '{}',
}
export const actions = {
startShift({ commit }, agentId) {
return ShiftService.startShift(agentId)
.then((response) => {
if (response.status === 200) {
commit('START_SHIFT', response.data.aggregateId)
}
})
.catch((error) => {
console.log(error)
if (error.response.status === 401) {
console.log('Error in Response')
}
})
},
async updateEventsSubscription({ dispatch, commit, rootState }, agentId) {
await dispatch('startShift', agentId)
const shiftId = state.shiftId
await dispatch('websocket/processWebsocket', null, { root: true })
let agentShiftInfo = {
aggregateId: state.shiftId,
connectionId: rootState.websocket.connectionId,
}
console.log(agentShiftInfo)
return ShiftService.updateEventsSubscription(shiftId, agentShiftInfo)
.then((response) => {
commit('UPDATE_EVENTS_SUBSCRIPTION', response.data)
})
.catch((error) => {
if (error.response.status === 401) {
console.log('Error in Response')
}
})
},
}
export const mutations = {
START_SHIFT(state, shiftId) {
state.shiftId = shiftId
console.log(`Retrieving Shift ID: ${state.shiftId}`)
},
UPDATE_EVENTS_SUBSCRIPTION(state, agentShiftInfo) {
state.agentShiftInfo = agentShiftInfo
},
}
You should convert your WebSocket action into a promise that resolves when WebSocket is connected.:
export const actions = {
processWebsocket({ commit }) {
return new Promise(resolve=> {
const v = this
this.ws = new WebSocket('mywebsocket')
this.ws.onopen = function (event) {
commit('SET_CONNECTION', event.type)
v.ws.send('message')
resolve();
}
this.ws.onmessage = function (event) {
commit('SET_REMOTE_DATA', event)
}
this.ws.onerror = function (event) {
console.log('webSocket: on error: ', event)
}
this.ws.onclose = function (event) {
console.log('webSocket: on close: ', event)
commit('SET_CONNECTION')
ws = null
setTimeout(startWebsocket, 5000)
}
});
},
}
So I realized that I have to resolve the promise on the this.ws.message instead. By doing that all my data is populated accordingly, there's still sync issues (I can't feed the websocket state at the moment because due to its async behaviour the state is not there yet when other components try to use it via: rootGetters.websocket.incomingChats for example) but I guess that's part of another question. Here's the final version of the module action:
export const actions = {
processWebsocket({ commit }) {
return new Promise((resolve) => {
const v = this
this.ws = new WebSocket('wss://ws.rubiko.io')
this.ws.onopen = function (event) {
commit('SET_CONNECTION', event.type)
v.ws.send('message')
}
this.ws.onmessage = function (event) {
commit('SET_REMOTE_DATA', event)
resolve(event)
}
this.ws.onerror = function (event) {
console.log('webSocket: on error: ', event)
}
this.ws.onclose = function (event) {
console.log('webSocket: on close: ', event)
commit('SET_CONNECTION')
ws = null
setTimeout(startWebsocket, 5000)
}
})
},
}
Anyways, thanks #Eldar you were in the right path.
I don't get redirected to login page when the role is "user".I have two getters in my route. One getter to check if the user is authenticated and another getter to get userRole. The userRole contains this: [“user”]. When i console log the getter i get the correct values but when i console.log a string, I don't see it as an orange string value in my console.
name: "GrantQuotes",
component: GrantQuotes,
beforeEnter: (to, from, next) => {
const authStatus = store.getters["auth/userRole"];
const myArrStr = JSON.stringify(authStatus);
const obj = JSON.parse(myArrStr);
console.log(obj);
console.log("user");
if (!store.getters["auth/authenticated"] && obj.includes("user")) {
return next({
name: "Login",
});
}
next();
},
},
Also my obj.includes("user")returns true.
export default {
namespaced: true,
state: {
accessToken: null,
user: null,
roles: null,
},
getters: {
authenticated(state) {
return state.accessToken && state.user;
},
user(state) {
return state.user;
},
userRole(state) {
console.log("test");
return state.roles;
},
},
mutations: {
SET_TOKEN(state, accessToken) {
state.accessToken = accessToken;
},
SET_USER(state, data) {
state.user = data;
},
SET_ROLE(state, data) {
state.roles = data;
console.log(data, "hej");
},
},
actions: {
async register({ dispatch }, credentials) {
let response = await axios.post(
"http://localhost:3000/api/auth/signup",
credentials
);
return dispatch("attempt", response.data.accessToken);
},
async signIn({ dispatch }, credentials) {
let response = await axios.post(
"http://localhost:3000/api/auth/signin",
credentials
);
return dispatch("attempt", response.data.accessToken);
},
async attempt({ commit, state }, accessToken) {
if (accessToken) {
commit("SET_TOKEN", accessToken);
}
if (!state.accessToken) {
return;
}
try {
let response = await axios.get(
"http://localhost:3000/api/auth/userMe",
{
headers: {
"x-access-token": accessToken,
},
}
);
console.log(response.data);
commit(
"SET_ROLE",
response.data.roles.map((role) => role.name)
);
commit("SET_USER", response.data);
} catch (e) {
commit("SET_TOKEN", null);
commit("SET_USER", null);
}
},
signOut({ commit }) {
commit("SET_TOKEN", null);
commit("SET_USER", null);
},
},
};
This is my user object
1. {roles: Array(1), quotes: Array(5), _id: "6089da8bd846c7e42a196ac5", username: "jon", email: "jon#gmail.com", …}
1. email: "jon#gmail.com"
2. quotes: (5) ["6089e8346dba89414317e646", "6089e8d96dba89414317e647", "608abd11462bf51a1583ab05", "60c5e89d1a6cfb0f3b16f51f", "60c62dc21a6cfb0f3b16f520"]
3. roles: Array(1)
1. 0: {_id: "607a0a5d102d2f5ad7738adf", name: "user", __v: 0}
2. length: 1
3. __proto__: Array(0)
4. username: "jon"
5. __v: 10
6. _id: "6089da8bd846c7e42a196ac5"
7. __proto__: Object
I'm extremely new when it comes to using VueJS and so I am working on a small App that should list out an Authenticated person's Github repositories.
I'm having trouble when it comes to being able to access or even traverse the Array in the Picture below. I keep getting an error of undefinedif I try userRepos. Please see my code below.
I do apologize for the "Wall of Code". But I thought these javascript code snippets are the most pertinent to the issue.
This is the GitHub repo I am using as the boilerplate for this project. GitHub-Electron-Vue-OAuth
const getAxiosClient = (state) => {
return axios.create({
baseURL: state.server.url, // this is "https://api.github.com
headers: {
'Authorization': 'token ' + state.session.access_token
},
responseType: 'json'
})
}
// Mutation
[types.SET_USER_REPOS](state, repos) {
state.session.repos = repos;
},
// State Object
const state = {
server: {
url: 'http://api.github.com'
},
session: {
access_token: window.localStorage.getItem('access_token'),
ready: false,
authenticated: false,
user: {}
}
};
// Actions
export const getRepos = ({
commit,
state
}) => {
return new Promise((resolve, reject) => {
getAxiosClient(state).get('/user/repos').then(response => {
commit(types.SET_USER_REPOS, response.data)
resolve(response.data)
}, err => {
console.log(err)
reject(err)
})
})
}
export const userRepos = (state) => {
console.log(state.session.repos)
return state.session.repos;
}
<template lang="jade">
.home
span Hello {{ username }}
span {{ userRepos }}
</template>
<script>
export default {
name: 'home',
computed: {
username() {
return this.$store.getters.username;
},
userRepos() {
return this.$store.getters.userRepos;
}
},
// TODO: Push this in router
beforeRouteEnter(to, from, next) {
next(vm => {
if (!vm.$store.getters.isAuthenticated) {
vm.$router.push({
name: 'login'
});
} else {
next();
}
});
}
}
</script>