How to do a relatedQuery (many2many) with feathers-objection - feathersjs

Regarding to this post: https://stackoverflow.com/a/69903276/1547821
it shows an approach with less boilerplate, to prevent a users query output (GET) to only the user who is logged in.
But how to do this, to get i.e. all members of the related group which the logged-in user participates?
This example below is my hook, but i want to have it in before->find hook as a query injection or addition.
module.exports = (options = {}) => {
return async context => {
const groupMembers = await context.app.service('groups').options.model.relatedQuery('users').for(context.params.user.id)
console.log(groupMembers)
return context
}
}
It doesn't help me as after->find hook, because total and etc. doesn't match then. Aswell my keeps doesn't work.
Appreciating any hints.

One solution would be to add a userId column to your groups model. This will contain a user of the group.
//in your /services/groups.hook.js
const { authenticate } = require('#feathersjs/authentication').hooks;
const { setField } = require('feathers-authentication-hooks');
module.exports = {
before: {
all: [],
find: [
authenticate('jwt'),
setField({
from: 'params.user.id',
as: 'params.query.userId',
}),
],
get: [],
create: [],
update: [],
patch: [],
remove: [disallow('external')],
},
//after and error options go here
};
This should return only results where the logged in user's Id matched the Id in the group table.
Ps: try console logging the context object for more insight. Also the userId column in groups model should be associated to the id column of users model.

Thanks. Meanwhile I found a solution for the before->find.hook:
context => {
if(context.params.user !== undefined) {
const groupMembers = await context.service.options.model.relatedQuery('groups').for(context.params.user.id)
if(groupMembers) {
context.params.query['$joinEager'] = 'groups'
context.params.query['groups.id'] = groupMembers[0].id
}
}
return context;
};

Related

Check if an user exist in my database with Node and MySQL [duplicate]

I need to check if entry with specific ID exists in the database using Sequelize in Node.js
function isIdUnique (id) {
db.Profile.count({ where: { id: id } })
.then(count => {
if (count != 0) {
return false;
}
return true;
});
}
I call this function in an if statement but the result is always undefined
if(isIdUnique(id)){...}
I don't prefer using count to check for record existence. Suppose you have similarity for hundred in million records why to count them all if you want just to get boolean value, true if exists false if not?
findOne will get the job done at the first value when there's matching.
const isIdUnique = id =>
db.Profile.findOne({ where: { id} })
.then(token => token !== null)
.then(isUnique => isUnique);
Update: see the answer which suggests using findOne() below. I personally prefer; this answer though describes an alternative approach.
You are not returning from the isIdUnique function:
function isIdUnique (id) {
return db.Profile.count({ where: { id: id } })
.then(count => {
if (count != 0) {
return false;
}
return true;
});
}
isIdUnique(id).then(isUnique => {
if (isUnique) {
// ...
}
});
You can count and find.
Project
.findAndCountAll({
where: {
title: {
[Op.like]: 'foo%'
}
},
offset: 10,
limit: 2
})
.then(result => {
console.log(result.count);
console.log(result.rows);
});
Doc link, v5 Beta Release
I found the answer by #alecxe to be unreliable in some instances, so I tweaked the logic:
function isIdUnique (id, done) {
db.Profile.count({ where: { id: id } })
.then(count => {
return (count > 0) ? true : false
});
}
As Sequelize is designed around promises anyway, alecxe's answer probably makes most sense, but for the sake of offering an alternative, you can also pass in a callback:
function isIdUnique (id, done) {
db.Profile.count({ where: { id: id } })
.then(count => {
done(count == 0);
});
}
}
isIdUnique(id, function(isUnique) {
if (isUnique) {
// stuff
}
});
Extending #Jalal's answer, if you're very conscious about performance implications while maintaining a simple Sequelize structure and you do not need the row data, I suggest you only request one column from the database. Why waste bandwidth and time asking the database to return all columns when you won't even use them?
const isIdUnique = id =>
db.Profile.findOne({ where: { id }, attributes: ['id'] })
.then(token => token !== null)
.then(isUnique => isUnique);
The attributes field tells Sequelize to only request the id column from the database and not sending the whole row's content.
Again this may seem a bit excessive but at scale and if you have many columns that hold a lot of data, this could make a giant difference in performance.
Try the below solution. I tried it and it works well.
const isIdUnique = async (id, model) => {
return await model.count({ where: { id: id } });
};
const checkExistId = await isIdUnique(idUser, User);
console.log("checkExistId: ", checkExistId);

Include ressource link in Sequelize result

I'm building a rest api that uses Sequelize to interact with the database. A query looks like this:
function read_category(req, res) {
Category.findById(req.params.categoryId, {rejectOnEmpty: true}).then(category => {
res.json(category);
}).catch(Sequelize.EmptyResultError, function () {
res.status(404).json({message: 'No category found'});
}
).catch(function (err) {
res.send(err);
}
);
}
Now I want the category object that is returned from Sequelize and then returned to the user to include the linkto the ressource. I could do:
category.dataValues.link = config.base_url + 'categories/' + category.dataValues.id;
Which would result in:
{
"id": 1,
"name": "TestCategory 1",
"position": 1,
"createdAt": "2018-08-19T11:42:09.000Z",
"updatedAt": "2018-08-19T11:42:09.000Z",
"link": "http://localhost:3000/categories/1"
}
Since I have more routes than this one I'm wondering if there's a dynamic way to add the link property to every category. I don't want to save it in the database because the base-url might differ.
Thanks!
Better way to do it is , create a getter method :
const Category = sequelize.define( 'category' , {
....
your_fields
....
},
{
getterMethods:{
link() {
return config.base_url + 'categories/' + this.id;
}
}
});
module.exports = Category;
Then
Category.findAll(...).then(categories => {
// Now there is no need to append data manually , it will added each time when you query
console.log(categories); // <-- Check the output
})

Too tidious hooks when querying in REST. Any ideas?

I've just started using feathers to build REST server. I need your help for querying tips. Document says
When used via REST URLs all query values are strings. Depending on the service the values in params.query might have to be converted to the right type in a before hook. (https://docs.feathersjs.com/api/databases/querying.html)
, which puzzles me. find({query: {value: 1} }) does mean value === "1" not value === 1 ? Here is example client side code which puzzles me:
const feathers = require('#feathersjs/feathers')
const fetch = require('node-fetch')
const restCli = require('#feathersjs/rest-client')
const rest = restCli('http://localhost:8888')
const app = feathers().configure(rest.fetch(fetch))
async function main () {
const Items = app.service('myitems')
await Items.create( {name:'one', value:1} )
//works fine. returns [ { name: 'one', value: 1, id: 0 } ]
console.log(await Items.find({query:{ name:"one" }}))
//wow! no data returned. []
console.log(await Items.find({query:{ value:1 }})) // []
}
main()
Server side code is here:
const express = require('#feathersjs/express')
const feathers = require('#feathersjs/feathers')
const memory = require('feathers-memory')
const app = express(feathers())
.configure(express.rest())
.use(express.json())
.use(express.errorHandler())
.use('myitems', memory())
app.listen(8888)
.on('listening',()=>console.log('listen on 8888'))
I've made hooks, which works all fine but it is too tidious and I think I missed something. Any ideas?
Hook code:
app.service('myitems').hooks({
before: { find: async (context) => {
const value = context.params.query.value
if (value) context.params.query.value = parseInt(value)
return context
}
}
})
This behaviour depends on the database and ORM you are using. Some that have a schema (like feathers-mongoose, feathers-sequelize and feathers-knex), will convert values like that automatically.
Feathers itself does not know about your data format and most adapters (like the feathers-memory you are using here) do a strict comparison so they will have to be converted. The usual way to deal with this is to create some reusable hooks (instead of one for each field) like this:
const queryToNumber = (...fields) => {
return context => {
const { params: { query = {} } } = context;
fields.forEach(field => {
const value = query[field];
if(value) {
query[field] = parseInt(value, 10)
}
});
}
}
app.service('myitems').hooks({
before: {
find: [
queryToNumber('age', 'value')
]
}
});
Or using something like JSON schema e.g. through the validateSchema common hook.

Redux and Calendar repeating events

What should be the proper way of storing / handling repeating events in the redux store ?
Problem: Let's say that we have a backend API that generates repeating events trough a complicated business logic.Some of the events might have the same ID. Lets say that generated output looks this way :
[
{
"id": 1,
"title": "Weekly meeting",
"all_day": true,
"starts_at": "2017-09-12",
"ends_at": "2017-09-12"
},
{
"id": 3,
"title": "Daily meeting1",
"all_day": false,
"starts_at": "2017-09-12",
"ends_at": "2017-09-12",
},
{
"id": 3,
"title": "Daily meeting1",
"all_day": false,
"starts_at": "2017-09-13",
"ends_at": "2017-09-13",
},
{
"id": 3,
"title": "Daily meeting1",
"all_day": false,
"starts_at": "2017-09-14",
"ends_at": "2017-09-14",
}
]
Possible solution would be: generate unique ID by having additional property uid composed like this: id + # + starts_at. This way we could identify each occurrence uniquely. (I'm using this right now)
Example:
[
{
"id": 1,
"uid": "1#2017-09-12",
"title": "Weekly meeting",
"all_day": true,
"starts_at": "2017-09-12",
"ends_at": "2017-09-12"
}
]
I'm wondering is there some other way, maybe more elegant than having composed unique id ?
There is a possible pitfall with your current solution. What will happen if id and start_id of two events will be the same? Is it possible scenario in your domain?
Because of that I usually use this nice lib in such cases. It produces really short unique ids, which have some nice properties, like guaranties not to intersect, to be unpredictable and so on.
Also ask yourself if you actually need unique ids in your case. Looks like your back-end have no chance to distinguish events anyways, so why bother? Redux store will happily keep your events event without uid.
Maybe not much of an improvement (if at all) but just using JSON.stringify to check for duplicates could make unique id's obsolete.
const existingEvents = [
{
"id": 3,
"title": "Daily meeting1",
"all_day": false,
"starts_at": "2017-09-14",
"ends_at": "2017-09-14",
}
];
const duplicate = {
"id": 3,
"title": "Daily meeting1",
"all_day": false,
"starts_at": "2017-09-14",
"ends_at": "2017-09-14",
};
const eventIsDuplicate = (existingEvents, newEvent) => {
const duplicate =
existingEvents.find(event => JSON.stringify(event) == JSON.stringify(newEvent));
return typeof duplicate != 'undefined';
};
console.log(eventIsDuplicate(existingEvents, duplicate)); // true
I guess this would only be preferable to your existing solution if, for some reason, you'd want to keep all the uniqueness logic on the client side.
As far as I understand the examples you've given, it seems like the server is sending a particular event whenever the details of the event change.
If that is so, and you want to track the changes to events, your might shape might be an array of objects with all the fields of the event that hold the current data, and a history property which is an array of all previous (or n most recent) event objects and the timestamps at which they were received. This is how your reducers would look, storing only the five most recent event changes for each event. I'm expecting the action to have a payload property which has your standard event property and a timestamp property, which can be easily accomplished in the action creator.
const event = (state = { history: [] }, action) => {
switch (action.type) {
case 'EVENT_FETCHED':
return ({
...action.payload.event,
history: [...state.history, action.payload].slice(-5),
});
default:
return state;
}
};
const events = (state = { byID: {}, IDs: [] }, action) => {
const id = action.payload.event.ID;
switch (action.type) {
case 'EVENT_FETCHED':
return id in state.byID
? {
...state,
byID: { ...state.byID, [id]: event(state.byID[id], action) },
}
: {
byID: { ...state.byID, [id]: event(undefined, action) },
IDs: [id],
};
default:
return state;
}
};
Doing this, you don't need any unique ID. Please let me know if I have misunderstood your problem.
Edit: This is a slight extension of the pattern in the Redux documentation, to store previous events.
At the end this is what I've implemented (for demonstration purpose only - unrelated code is omitted):
eventRoot.js:
import { combineReducers } from 'redux'
import ranges from './events'
import ids from './ids'
import params from './params'
import total from './total'
export default resource =>
combineReducers({
ids: ids(resource),
ranges: ranges(resource),
params: params(resource)
})
events.js:
import { GET_EVENTS_SUCCESS } from '#/state/types/data'
export default resource => (previousState = {}, { type, payload, requestPayload, meta }) => {
if (!meta || meta.resource !== resource) {
return previousState
}
switch (type) {
case GET_EVENTS_SUCCESS:
const newState = Object.assign({}, previousState)
payload.data[resource].forEach(record => {
// ISO 8601 time interval string -
// http://en.wikipedia.org/wiki/ISO_8601#Time_intervals
const range = record.start + '/' + record.end
if (newState[record.id]) {
if (!newState[record.id].includes(range)) {
// Don't mutate previous state, object assign is only a shallow copy
// Create new array with added id
newState[record.id] = [...newState[record.id], range]
}
} else {
newState[record.id] = [range]
}
})
return newState
default:
return previousState
}
}
There is also a data reducer but it's linked in parent reducer due to generic implementation that is re-used for common list responses. Events data are updated and start/end property is removed as it's composed by range (ISO 8601 time interval string). This can be later used by moment.range or split by '/' to get start/end data. I've opted for array of range strings to simplify checking of existing ranges, as they might grow large. I think that primitive string comparison (indexOf or es6 includes) would be faster than looping over complex structure in such cases.
data.js (stripped down version):
import { END } from '#/state/types/fetch'
import { GET_EVENTS } from '#/state/types/data'
const cacheDuration = 10 * 60 * 1000 // ten minutes
const addRecords = (newRecords = [], oldRecords, isEvent) => {
// prepare new records and timestamp them
const newRecordsById = newRecords.reduce((prev, record) => {
if (isEvent) {
const { start, end, ...rest } = record
prev[record.id] = rest
} else {
prev[record.id] = record
}
return prev
}, {})
const now = new Date()
const newRecordsFetchedAt = newRecords.reduce((prev, record) => {
prev[record.id] = now
return prev
}, {})
// remove outdated old records
const latestValidDate = new Date()
latestValidDate.setTime(latestValidDate.getTime() - cacheDuration)
const oldValidRecordIds = oldRecords.fetchedAt
? Object.keys(oldRecords.fetchedAt).filter(id => oldRecords.fetchedAt[id] > latestValidDate)
: []
const oldValidRecords = oldValidRecordIds.reduce((prev, id) => {
prev[id] = oldRecords[id]
return prev
}, {})
const oldValidRecordsFetchedAt = oldValidRecordIds.reduce((prev, id) => {
prev[id] = oldRecords.fetchedAt[id]
return prev
}, {})
// combine old records and new records
const records = {
...oldValidRecords,
...newRecordsById
}
Object.defineProperty(records, 'fetchedAt', {
value: {
...oldValidRecordsFetchedAt,
...newRecordsFetchedAt
}
}) // non enumerable by default
return records
}
const initialState = {}
Object.defineProperty(initialState, 'fetchedAt', { value: {} }) // non enumerable by default
export default resource => (previousState = initialState, { payload, meta }) => {
if (!meta || meta.resource !== resource) {
return previousState
}
if (!meta.fetchResponse || meta.fetchStatus !== END) {
return previousState
}
switch (meta.fetchResponse) {
case GET_EVENTS:
return addRecords(payload.data[resource], previousState, true)
default:
return previousState
}
}
This can be then used by an calendar component with event selector:
const convertDateTimeToDate = (datetime, timeZoneName) => {
const m = moment.tz(datetime, timeZoneName)
return new Date(m.year(), m.month(), m.date(), m.hour(), m.minute(), 0)
}
const compileEvents = (state, filter) => {
const eventsRanges = state.events.list.ranges
const events = []
state.events.list.ids.forEach(id => {
if (eventsRanges[id]) {
eventsRanges[id].forEach(range => {
const [start, end] = range.split('/').map(d => convertDateTimeToDate(d))
// You can add an conditional push, filtered by start/end limits
events.push(
Object.assign({}, state.events.data[id], {
start: start,
end: end
})
)
})
}
})
return events
}
And here is how the data structure looks in the redux dev tools:
Each time the events are fetched, their data is updated (if there is a change) and references are added. Here is an screenshot of redux diff after fetching new events range:
Hope this helps somebody, I'll just add that this still isn't battle tested but more a proof of a concept that's working.
[EDIT] Btw. I'll probably move some of this logic to the backend as then there will be no need to split / join / delete properties.

Async action creator not dispatching action as expected

guys.
I have the following async action creator that dispatches another action creator or returns null depending on the current state. The problem is that it is invariably returning null no matter what the current state is.
// actions.js
export const loadPosts = category => (dispatch, getState) => {
const currentState = getState()
const posts = currentState.postsByCategory
const categoryPosts = posts[category]
const items = categoryPosts.items
if(items === []) {
return dispatch(fetchPosts(category))
}
return null
}
As you can see, the action creator dispatching fetchPosts() depends on the value of itemsbeing equal to an empty array. I am testing it providing it an intial state with the following structure:
// initialState
const initialState = {
postsByCategory: {
hot: {
isFetching: false,
items: []
}
}
}
I am obviously not accessing the items property appropriately, but I cannot see where the error in my code is.
I am testing this with redux-mock-store, creating an instance of a mockStore, and providing it with initialState.
Hope you guys can pinpoint the error in my code.
Thanks in advance.
Your problem lies in the comparison
if(items === [])
In the above code, items will not be === to [] as they both are of different instances. If you want to check if items is empty, please use items.length === 0. Hope that helps.