Unable to access meta object in routes-VueJs - vue.js

I am unable to access the neededPermissions array in the routes. I clearly have the array neededPermissions in routes but every time I try to access the array, console logs undefined.
How can I get the neededPermissions array from the meta object in routes? Clearly to.meta.neededPermissions prints undefined, which make's it impossible to loop through currentUser.
{
path: "/administration",
name: "Administration",
component: PageAdministration,
meta: {
requiresAuth: true,
neededPermissions: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
},
}
function to authorize users
console.log(to.meta.neededPermissions );
if(currentUser!==null && to.meta.neededPermissions !== undefined)
{
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); });
const mappedValidPermissions = validPermissions.map((item) => { return item.permissionType; });
if (!to.meta.neededPermissions.every(i=>mappedValidPermissions.includes(i))) {
isAllowed = false;
next({ path: "/:notFound(.*)" });
break;
}
} if(isAllowed) next();
}

Related

Error: Exception in HostFunction: Attempting to create an object of type 'sets' with an existing primary key value '6' in react native

I'm trying to store history of workout in realm, my addHistory function looks like this
export function addHistory(workout, exercise, sets, _id) {
console.log({
workout,
exercise,
sets,
_id,
});
if (
_id !== undefined &&
workout !== undefined &&
exercise !== undefined &&
sets !== undefined
) {
// return console.log("HISTORY ", { workout, exercise, sets, _id });
return realm.write(() => {
return realm.create("workoutData", {
_id: _id,
exercise,
workout,
sets,
workoutDate: new Date(Date.now()),
});
});
} else {
alert("History is incomplete");
}
}
Schema of the workoutData is as follows:
exports.workoutData = {
name: "workoutData",
primaryKey: "_id",
properties: {
_id: "int",
workout: "workouts",
exercise: "exercise",
workoutDate: "date",
sets: "sets[]",
},
};
Now when I add sets and click on finishWorkoutHandler the logic works fine before the addHistory function but when addHistory is executed it throws the error as stated in the question.
//finish workout handler
const finishWorkoutHandler = () => {
if (sets.length == 0) {
return;
}
let setsFromRealm = realm.objects("sets");
let workoutData = realm.objects("workoutData");
let setsArray = [];
exercises.forEach((exercise) => {
sets
.filter((items) => items.exercise._id == exercise._id)
.forEach((sets) => {
let _id = 0;
if (setsFromRealm.length > 0) {
_id = realm.objects("sets").max("_id") + 1;
}
addSet(
sets.name,
parseInt(sets.weight),
parseInt(sets.reps),
parseInt(sets.rmValue),
sets.isHeighest,
sets.exercise,
_id,
sets.profile,
sets.failedSet,
sets.warmupSet,
sets.notes
);
let indiSet = {
name: sets.name,
weight: parseInt(sets.weight),
reps: parseInt(sets.reps),
rmValue: parseInt(sets.rmValue),
isHeighest: sets.isHeighest,
_id: _id,
profile: sets.profile,
failedSet: sets.failedSet,
warmupSet: sets.warmupSet,
notes: sets.notes,
createdDate: new Date(Date.now()),
};
setsArray.push(indiSet);
});
let workoutDataId = 0;
let setsArrcopy = setsArray;
console.log("SETS ", realm.objects("sets"));
console.log("SETS ", setsArrcopy);
if (workoutData.length > 0) {
workoutDataId = realm.objects("workoutData").max("_id") + 1;
}
**WORKING AS EXPECTED TILL HERE**
// problem lies here
addHistory(params.workout, exercise, setsArrcopy, workoutDataId);
});
dispatch(setsEx([]));
goBack();
};
the structure of setsArrCopy containing sets is as follows
[
({
_id: 6,
createdDate: 2022-09-29T16:27:06.128Z,
failedSet: false,
isHeighest: false,
name: "Thai",
notes: "",
profile: [Object],
reps: 12,
rmValue: 64,
warmupSet: false,
weight: 56,
},
{
_id: 7,
createdDate: 2022-09-29T16:27:06.151Z,
failedSet: false,
isHeighest: false,
name: "Thsi 3",
notes: "",
profile: [Object],
reps: 10,
rmValue: 75,
warmupSet: false,
weight: 66,
})
];
the logic is also working fine in terms of assigning new ids to the sets being added in a loop. But somehow its throwing error when passing setArrCopy to addHistory function. Although its an array of sets not a single object?

React-Native AsyncStorage: I retrieve an array, but then it becomes only a single object of the array

I'm using AsyncStorage to store and retrieve an array of objects. The structure of the array is like this:
const tracks = [
{
title: title1,
exercises: [
{
object1Info...
},
{
object2Info...
}
]
},
{
title: title2,
exercises: [
{
object1Info...
}
]
},
{
title: title3,
exercises: [
{
object1Info...
}
]
}
]
As you can see, the objects in the array do themselves contain arrays, which again contain objects.
I'm storing the array like this:
const storeData = async (array) => {
try {
const stringifiedArray = JSON.stringify(array)
await AsyncStorage.setItem('#tracks_array', stringifiedArray)
} catch (e) {
console.log("Error saving data")
}
}
This seems to work fine. I then retrieve the data like this:
const retrieveData = async () => {
try {
const jsonValue = await AsyncStorage.getItem('#tracks_array');
console.log('Parsed value: ' + JSON.parse(jsonValue)); //This prints 'Parsed value: [object Object],[object Object],[object Object],[object Object]'
return jsonValue !== null ? JSON.parse(jsonValue) : null;
} catch (e) {
console.log("Error retrieving data")
}
}
This seems to work fine as well.
I have the array stored also as state. So what I want to do is add an object to the array in state, store that new array in the AsyncStorage, and then retrieve the array and set this new array back to state. Storing the object seems to have no problems.
When I retrieve the new array, and console.log(JSON.parse(jsonValue)) inside retrieveData, it prints [object Object],[object Object],[object Object],[object Object]. However after I call const newData = retrieveData(), console.log(newData) prints just [object Object]. This is my first time using AsyncStorage so I must be misunderstanding something. Why does it only return one object, instead of the whole array?
EDIT: Sharing the whole component code:
import {
StyleSheet,
ScrollView,
View,
Text
} from 'react-native';
import Modal from 'react-native-modal';
import AsyncStorage from '#react-native-community/async-storage'
import Track from './Track.js';
import New from './New.js';
class Main extends Component {
constructor(props) {
super(props);
this.state = {
tracksData: tracks,
newTrack: false,
newExercise: false
}
storeData(this.state.tracksData);
}
renderTracks(data) {
console.log('Main data = ' + data)
return data.map((item, i) => {
console.log('Item = ' + item)
return (
<Track key={i} data={item} />
)
});
}
render() {
return (
<ScrollView horizontal={true} style={styles.Main}>
{this.renderTracks(this.state.tracksData)}
<Track data={{title: 'NewTrack', exercises: 'NewTrack'}} newTrackBox={this.toggleTrackBox} />
<Modal isVisible={this.state.newTrack} coverScreen={true}>
<New type={'track'} visible={this.toggleTrackBox} add={(name) => this.addTrack(name)}/>
</Modal>
</ScrollView>
);
}
toggleTrackBox = () => {
this.setState({
newTrack: !this.state.newTrack
})
}
addTrack = (name) => {
this.setState({
newTrack: false
});
var newTracks = this.state.tracksData;
newTracks.push({title: name, exercises: []})
console.log('newTracks = ' + newTracks)
storeData(newTracks);
this.updateData();
}
updateData() {
var newData = retrieveData();
console.log('newData = ' + newData)
setTimeout(() => {
console.log('Retrieved data = ' + newData);
if (newData) {
this.setState({
tracksData: newData
});
console.log("Data updated");
return true;
} else {
console.log("Data couldn't be retrieved");
return false;
}
}, 5000)
}
}
const storeData = async (value) => {
try {
const stringifiedArray = JSON.stringify(value)
console.log('Value to store: ' + value)
console.log('Stringified value to store: ' + stringifiedArray)
await AsyncStorage.setItem('#tracks_array', stringifiedArray)
//alert("Success saving data!")
} catch (e) {
console.log("Error saving data")
alert("Error saving data");
}
}
const retrieveData = async () => {
try {
const jsonValue = await AsyncStorage.getItem('#tracks_array');
console.log('Stringified value retrieved: ' + jsonValue)
console.log('Parsed value: ' + JSON.parse(jsonValue))
return jsonValue !== null ? JSON.parse(jsonValue) : null;
} catch (e) {
console.log("Error retrieving data")
alert("Error retrieving data");
}
}
const tracks = [ //each member of this array is sent to a Track
{
title: 'Pull-up', // used in Track
exercises: [ // each member of this array is sent to an Exercise by Track
{
name: 'Pull-up', // used in Exercise
setStart: 2, // this and below used to calculate no of tiles and their contents, which are then sent to Tile
setEnd: 3,
repStart: 5,
repEnd: 8,
isInSeconds: false,
inProgress: null,
completed: true
},
{
name: 'Weighted Pull-up',
setStart: 3,
setEnd: 3,
repStart: 5,
repEnd: 8,
isInSeconds: false,
inProgress: [3, 5],
completed: false
}
]
},
{
title: 'Dip',
exercises: [
{
name: 'Dip',
setStart: 2,
setEnd: 3,
repStart: 5,
repEnd: 8,
isInSeconds: false,
inProgress: null,
completed: true
}
]
},
{
title: 'Squat',
exercises: [
{
name: 'Pistol squat',
setStart: 2,
setEnd: 3,
repStart: 5,
repEnd: 8,
isInSeconds: false,
inProgress: [2, 8],
completed: false
}
]
}
]
const styles = StyleSheet.create({
Main: {
flex: 1,
flexDirection: 'row',
backgroundColor: '#022763'
}
})
export default Main;
Also, I should have mentioned, the actual error I'm getting is:
TypeError: undefined is not a function (near '...data.map...')
"retrieveData" is async function and hence returns a Promise.
What happened was it didn't finish retrieving the data and hence newData got 1 object out of all the array.
Try changing updateData like this:
updateData() {
var newData = retrieveData().then(data => {
console.log('newData = ' + newData)
setTimeout(() => {
console.log('Retrieved data = ' + newData);
if (newData) {
this.setState({
tracksData: newData
});
console.log("Data updated");
return true;
} else {
console.log("Data couldn't be retrieved");
return false;
}
}, 5000)
};
}
I've figured out the issue. I was retrieving data with AsyncStorage, then setting that data to the state something like this:
var newData = asyncRetrieveDataFunction();
this.setState({state1: newData})
However, because I declared the retrieveData() function as async, it was setting the state before the data had finished retrieving. The solution was to use the then keyword and change it to something like this:
asyncRetrieveDataFunction().then(data => this.setState({state1: data}));
This ensures that the data has been returned BEFORE assigning it to a state.

Sequelize query with a where clause on an include of an include

I'm struggling to create a query with sequelize.
Some context
I have the following models:
A Manifestation can have [0..n] Event
An Event belongs to one Manifestation (an Event cannot exist without a Manifestation)
A Place can have [0..n] Event
An Event belongs to one Place (an Event cannot exist without a Place)
A Manifestation can have [1..n] Place
A Place can have [0..n] Manifestation
I model the relations as the following:
Manifestation.hasMany(Event, { onDelete: 'CASCADE', hooks: true })
Event.belongsTo(Manifestation)
Place.hasMany(Event, { onDelete: 'CASCADE', hooks: true })
Event.belongsTo(Place)
Manifestation.belongsToMany(Place, { through: 'manifestation_place' })
Place.belongsToMany(Manifestation, { through: 'manifestation_place' })
For me it seems rather correct, but don't hesitate if you have remarks.
The question
I'm trying to query the Place in order to get all Manifestation and Event happening in a given Place. But for the Event ones, I want to include them within their Manifestation even if the Manifestation doesn't happen in the given Place.
Below is the "JSON" structure I'm trying to achieve:
{
id: 1,
name: "Place Name",
address: "Place address",
latitude: 47.00000,
longitude: -1.540000,
manifestations: [
{
id: 10,
title: "Manifestation one",
placeId: 1,
events: []
},
{
id: 11,
title: "Manifestation two",
placeId: 3,
events: [
id: 5,
title: "3333",
manifestationId: 11,
placeId: 1
]
}
]
}
So I want to include the Manifestation with id: 11, because one of its Event occurs in the given Place (with id: 1)
Update (04/06/20): For now I rely on javascript to get the expected result
I figured out it would be nice if I posted my current solution before asking.
router.get('/test', async (req, res) => {
try {
const placesPromise = place.findAll()
const manifestationsPromise = manifestation.findAll({
include: [
{ model: event },
{
model: place,
attributes: ['id'],
},
],
})
const [places, untransformedManifestations] = await Promise.all([
placesPromise,
manifestationsPromise,
])
const manifestations = untransformedManifestations.map(m => {
const values = m.toJSON()
const places = values.places.map(p => p.id)
return { ...values, places }
})
const result = places
.map(p => {
const values = p.toJSON()
const relatedManifestations = manifestations
.filter(m => {
const eventsPlaceId = m.events.map(e => e.placeId)
return (
m.places.includes(values.id) ||
eventsPlaceId.includes(values.id)
)
})
.map(m => {
const filteredEvents = m.events.filter(
e => e.placeId === values.id
)
return { ...m, events: filteredEvents }
})
return { ...values, manifestations: relatedManifestations }
})
.filter(p => p.manifestations.length)
return res.status(200).json(result)
} catch (err) {
console.log(err)
return res.status(500).send()
}
})
But I'm pretty sure I could do that directly with sequelize. Any ideas or recommendations ?
Thanks
This is not optimum. But you can try it out:
const findPlace = (id) => {
return new Promise(resolve => {
db.Place.findOne({
where: {
id: id
}
}).then(place => {
db.Manefestation.findAll({
include: [{
model: db.Event,
where: {
placeId: id
}
}]
}).then(manifestations => {
const out = Object.assign({}, {
id: place.id,
name: place.name,
address: place.address,
latitude: place.latitude,
longitude: place.longitude,
manifestations: manifestations.reduce((res, manifestation) => {
if (manifestation.placeId === place.id || manifestation.Event.length > 0) {
res.push({
id: manifestation.id,
title: manifestation.id,
placeId: manifestation.placeId,
events: manifestation.Event
})
}
return res;
}, [])
})
})
resolve(out);
})
})
}
From this, you get all manifestations that assigned to place or have any event that assigns. All included events in the manefestations are assigned to the place.
Edit :
You will be able to use the following one too:
const findPlace = (id) => {
return new Promise(resolve => {
db.Place.findOne({
include: [{
model: db.Manefestation,
include: [{
model: db.Event,
where: {
placeId: id
}
}]
}],
where: {
id: id
}
}).then(place => {
db.Manefestation.findAll({
include: [{
model: db.Event,
where: {
placeId: id
}
}],
where: {
placeId: {
$not: id
}
}
}).then(manifestations => {
place.Manefestation = place.Manefestation.concat(manifestations.filter(m=>m.Event.length>0))
resolve(place);// or you can rename, reassign keys here
})
})
})
}
Here I take only direct manifestations in the first query. Then, manifestations that not included and concatenate.
I do not know if you figure it out by now. But the solution is provided below.
Search with Sequelize could get funny :). You have to include inside another include. If the query gets slow use separate:true.
Place.findAll({
include: [
{
model: Manifestation,
attributes: ['id'],
include: [{
model: Event ,
attributes: ['id']
}]
},
],
})
I tried to complete it in a single query but you will still need JavaScript to be able to get the type of output that you want.
(Note: 💡 You need manifestation which is not connected to places but should be included if a event is present of that place. The only SQL way to get that starts by doing a CROSS JOIN between all tables and then filtering out the results which will be a very hefty query)
I came up with this code(tried & executed) which doesn't need you to execute 2 findAll that fetches all data as what you are currently using. Instead it fetched only the data needed for final output in 1 query.
const places = await Place.findAll({
include: [{
model: Manifestation,
// attributes: ['id']
through: {
attributes: [], // this helps not get keys/data of join table
},
}, {
model: Event,
include: [{
model: Manifestation,
// attributes: ['id']
}],
}
],
});
console.log('original output places:', JSON.stringify(places, null, 2));
const result = places.map(p => {
// destructuring to separate out place, manifestation, event object keys
const {
manifestations,
events,
...placeData
} = p.toJSON();
// building modified manifestation with events array
const _manifestations = manifestations.map(m => {
return ({ ...m, events: [] })
});
// going through places->events to push them to respective manifestation events array
// + add manifestation which is not directly associated to place but event is of that manifestation
events.map(e => {
const {
manifestation: e_manifestation, // renaming variable
...eventData
} = e;
const mIndex = _manifestations.findIndex(m1 => m1.id === e.manifestationId)
if (mIndex === -1) { // if manifestation not found add it with the events array
_manifestations.push({ ...e_manifestation, events: [eventData] });
} else { // if found push it into events array
_manifestations[mIndex].events.push(eventData);
}
});
// returning a place object with manifestations array that contains events array
return ({ ...placeData, manifestations: _manifestations });
})
// filter `.filter(p => p.manifestations.length)` as used in your question
console.log('modified places', JSON.stringify(result, null, 2));

Ramda: Split a list to segments

If we have a list such as:
[
{
type: 'a',
},
{
type: 'a',
},
{
type: 'b',
},
{
type: 'a',
}
]
... and we want to segment it to create a list, such that the new list is made up of each segment of the initial list, here split by type, looking like:
[
[
{
type: 'a',
},
{
type: 'a',
},
],
[
{
type: 'b',
},
],
[
{
type: 'a',
}
]
]
I'd like to create a general purpose 'segmenting' function, which takes a function to compare two items, and determine whether or not a new segment is required. Here, the 'segmenter' for that function simply compares type.
I can write that in vanilla javascript, but is there a good way to do this with Ramda?
const data = [
{
type: 'a',
},
{
type: 'a',
},
{
type: 'b',
},
{
type: 'a',
}
];
const segmentBy = segmenter => items => {
const segmentReducer = (prev = [], curr) => {
let lastSegment = [];
let lastItem = null;
try {
lastSegment = prev[prev.length - 1];
lastItem = lastSegment[lastSegment.length - 1];
} catch (e) {
return [...prev, [curr]];
}
const requiresNewSegment = segmenter(lastItem, curr);
if (requiresNewSegment) {
return [...prev, [curr]];
}
return [...prev.slice(0, prev.length - 1), [...lastSegment, curr]];
};
return items.reduce(segmentReducer, []);
};
const segmentByType = segmentBy((a, b) => a.type !== b.type);
const segments = segmentByType(data);
console.dir(segments);
With Ramda you can use R.groupWith:
Takes a list and returns a list of lists where each sublist's elements
are all satisfied pairwise comparison according to the provided
function. Only adjacent elements are passed to the comparison
function.
const data = [{"type":"a"},{"type":"a"},{"type":"b"},{"type":"a"}];
const segmentByType = R.groupWith(R.eqBy(R.prop('type')));
const segments = segmentByType(data);
console.dir(segments);
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>
In vanilla, the main problem is when to add a new subarray to the accumulator. You need to add another subarray when it's the 1st item, or if segmenter returns true.
const data = [{"type":"a"},{"type":"a"},{"type":"b"},{"type":"a"}];
const segmentBy = segmenter => items =>
items.reduce((r, item, i, arr) => {
if(i === 0 || segmenter(item, arr[i - 1])) r.push([]);
r[r.length - 1].push(item);
return r;
}, []);
const segmentByType = segmentBy((a, b) => a.type !== b.type);
const segments = segmentByType(data);
console.dir(segments);

How to implement Ngx-Datatable server side pagination if pagination is not zero based

I am trying to implement server-side pagination in angular 5 based web app.
Problem is the API requires pagination to start from index 1 whereas the library "ngx-datatable" pagination is 0 based.
Here's my current implementation:
mycomponent.html
<ngx-datatable
#usersTable
class='material text-centered'
[rows]='users'
[columns]="usersColumn"
[columnMode]="'force'"
[headerHeight]="50"
[footerHeight]="50"
[rowHeight]="'auto'"
[externalPaging]="true"
[offset]="page.offset"
[limit]="page.pageSize"
[count]="page.totalElements"
(page)='fetchList($event)'>
</ngx-datatable>
mycomponent.ts
page: any = {
offset: 0,
pageNumber: 1,
pageSize: 10,
totalElements: '',
sortBy: "id",
sortOrder: "desc"
};
ngOnInit() {
this.usersColumn = [
{
name: 'S.NO',
prop: 'sno'
},
{
name: 'First Name',
prop: 'firstName'
},
{
name: 'Last Name',
prop: 'lastName'
},
{
name: 'Email',
prop: 'email',
width: 200
},
{
name: 'Action',
cellTemplate: this.action
}
];
this.fetchList({ offset: 0 });
}
generateSerialNo(pageNo, size, i) {
const index = i + 1;
return pageNo == 1 ? index : (pageNo - 1) * size + index;
}
fetchList(pageInfo) {
this.page.pageNumber = pageInfo.offset + 1;
const { pageNumber, pageSize, sortBy, sortOrder } = this.page);
this.ListService.fetchList(pageNumber, pageSize, sortBy, sortOrder).subscribe(
success => {
if (success && !success['isError']) {
const responseObj = success['responseObject'];
if (responseObj) {
const List = responseObj.content || [];
const { totalElements } = responseObj;
this.users = List.map((item, i) => {
// set serial no. for user in current iteration
const serialNumber = this.generateSerialNo(this.page.pageNumber, this.page.pageSize, i);
return { sno: serialNumber, ...item };
});
this.page.totalElements = totalElements;
}
} else {
this.toastr.error(success['message'], 'Oops!');
}
},
errorResp => {
const error = errorResp['error'];
this.toastr.error(error['message'], 'Oops!');
}
);
Now Say, there's an action column in the table which consists of buttons to block or unblock user which on click calls a function as follows:
blockUnblockUser(toBlock: boolean) {
this.ListService.blockUnblockUser(toBlock, this.selectedUser.id).subscribe(
success => {
if (success && !success['isError']) {
this.fetchList(this.page);
this.utilService.closeModal();
this.toastr.success(success['message'], 'Success!');
} else {
this.utilService.closeModal();
this.toastr.error(success['message'], 'Oops!');
}
},
errorResp => {
const error = errorResp['error'];
this.utilService.closeModal();
this.toastr.error(error['message'], 'Oops!');
});
}
Here's the problem. On page load, I get my list for page number one and page number one is the active page. Now Click on page number 2, in the request param of API this is what goes:
list?pageNumber=2&pageSize=10&sortBy=id&sortOrder=desc
and in response, I get 3 data for page number 2 with 3 items.
Now if I click on the action button to either block or unblock particular user this is what sent in request params:
list?pageNumber=1&pageSize=10&sortBy=id&sortOrder=desc
and in response, I get data based on the page number 1 with 10 items.
but this time the active page in pagination is still "2"?
Please let me know where I am making mistake and how do I fix this. The backend team cannot make the pagination zero-based index for some reason.