As of April 12th, you must go to Progress SupportLink to create new support cases or to access existing cases. Please, bookmark the SupportLink URL and use the new portal to contact the support team.
Hi Laurie,
Indeed this sounds like a simple problem at first place, but the users collection is a special collection as it holds personal information, that is why it is recommended the access to it to be restricted and that is why it has a separate store in the SDK, different than the other collections. When working with such collections, BL is the perfect solution to achieve what you want.
The given custom endpoint code is just a sample and you can adjust it the way you want to server your needs. So you may adjust it to return not only the user_id but other user attributes as well like so:
... // Return only part of the users information var teamUsersInfo = teamUsers.map(function(teamUser) { return {_id: teamUser._id, username: teamUser.username, email: teamUser.email };}); ...
Still, from the provided additional information for Many-to-Many relationship, it seems that a separate collection for storing the team owner and members would serve best your needs.
Regards
Martin
Hi Laurie,
You are correct, supporting a separate table with users is inconvenient and is unnecessary.
As already mentioned, getting a list with all users in the same team is achievable by making an HTTPS request to the REST API endpoint (as a method in the HTML5 SDK is not exposed). The request would look like so, passing a query string in the URL to filter the results and get only the users in your team:
GET /user/:appKey?query={"team":"your-team-here"} Authorization: [user credentials]
Still, this would return all user details which may also include some private personal information that should not be exposed to other users. Therefore it is always a good practice to restrict the access to this collection by setting the permissions of All users
role for Users collection to Create(Always), Read(Entity), Update(Entity). Delete(Entity).
Let me provided some additional information on the custom endpoint approach and why it is necessary.
Once you have set the above permissions, the above request would no longer return the users in your team. Therefore, you would need to create a custom endpoint to get these users and return only part of their information that is needed. Through the custom endpoint and the use of the collectionAccess module in the endpoint code, the set permissions would not apply. As this code remains on the server and not in your app, the code can only be changed by you or another administrator that you have given access to the Kinvey Console, thus making it secure.
With the use of the custom endpoint, you would create your own User lookup functionality that would serve the following functions:
have access to a restricted collection that regular user would not be able to retrieve;
preserve other user privacy by returning only minimal information for the users, not all their details;
be secure - code changes can only be done by a Kinvey Console administrator. Thus you eliminate the risk that someone else can get hold and change the code and retrieve the users details.
I hope this has helped.
Current solution:
I created my own 'users' collection, and maintain an 'app user' for every Kinvey.User. When a Kinvey,User is registered, I create a new 'app user' in the 'users' collection. Account management is handled via Kinvey.User, but everything else is done with the shadow 'app user'.
This is silly and inconvenient, but workable. I think that creating my own collection of users that shadow the platform's collection of users is pretty kludgy and shouldn't be necessary, but so far it's the only solution I"ve been able to come up with.
I'll try out the custom BL approach and see if that lets me eliminate my shadow user collection, but it seems crazy that this basic collaboration support isn't easier to achieve :-/
Martin,
agreed, I don't want to query for all users, just the set of users that encompass a team of people that have already agreed to work together and engaged with each other. Abhijeet's suggestion of using a team collection where each team item contains a list of team members is obviously the right thing to do, but where I'm having problem is retrieving the set of users listed for a team.
I haven't worked with BL yet; it seems like overkill to have to use it to solve so simple a problem. Having read through the code you provided, it looks like all that would give me is a list of user IDs, which I would still have no way of turning into a list of users. However, if I skip the step of mapping users down to user IDs and return the users, that should get me what I need.
Just to make the use case clear:
Abhijeet, thanks but that doesn't work. The link you supplied is to the android SDK; there is no `.in` operator in the html5 sdk, but I think the `.contains` operator is the equivalent there. It produces the same error as I described in my original post when querying on username instead of _id.
Hi Laurie,
The HTML5 SDK does not expose a method to get all users from the user collection. Still, there is an endpoint in the REST API to do so which can be triggered with an AJAX request for example.
Still, this is not a good practice - it is even recommended to restrict the access permissions to the User collection in order to prevent one user from accessing another user's data in order to preserve privacy. To do so, set the permissions of All users
role for Users collection to Create(Always), Read(Entity), Update(Entity). Delete(Entity).
The _lookup
method allows an app to respect user privacy but enable user discovery at the same time by returning only part of the users properties. The method supports only single user lookup, not searching for multiple users by passing a query.
The solution provided by Abhijeet (using a separate collection to store the teams and users that are in each team) is one approach that can be used. Another is to use a custom endpoint using Business logic, where you would find the users that are in the same team and return again only part of the users data. For instance a sample code would look like this:
function onRequest(request, response, modules) { // Get the username of the user making the request var activeUserName = request.username; var userCollection = modules.collectionAccess.collection('user'); // Get details about user making the request userCollection.findOne({ "username": activeUserName }, function(err, user) { if (err) { response.body = { error: err.message }; response.complete(400); } // Get the team of the user making the request var team = user.team; // Find all users that are in the same team userCollection.find({ "team": team }, function(error, teamUsers) { if (error) { response.body = { error: error.message }; response.complete(400); } // Return only part of the users information var teamUserIds = teamUsers.map(function(teamUser) { return teamUser._id; }); response.body = teamUserIds; response.complete(200); }); }); }
You may invoke the custom endpoint as noted here:
Kinvey.CustomEndpoint.execute('<endpoint>') .then(function(response) { // ... }) .catch(function(error) { // ... });
Let me know if this would work in your case.
Hi Laurie,
Thank you for providing us more information about your use case. Your thinking is correct, For your use case it will be inefficient to look up each user with separate query. Also querying by ID is not supported with User Discovery so you will have to use username as you mentioned. Kinvey usernames are mandatory and unique. So (assuming you have a ‘team' collection in which you have list of usernames of users in a team) you can get a list of users that are members of the active user’s team using the ‘.in(key, stringArray)’ query. With this you can get the list of users whose username is present in the string array. Please find the documentation here.
Let us know if this helps or if you have any questions about this.
Regards,
Abhijeet
Thanks for the links. My application allows users to organize into teams. Users within a team need to be able to interact. Therefore I need to be able to get a list of users that are members of the active user's team. It'd be grossly inefficient to look up each user with a separate query, which is why I used a 'contains' constraint to get the users in one query.
It appears from the links you supplied that querying by ID is not supported; I can switch to storing usernames instead (as I assume those are unique and guaranteed to be populated) but I still have a problem if I can only query for one user at a time.
Using a 'contains' query with User.lookup() still doesn't work if I query against username instead of _id.
Laurie,
You shouldn't be using datastore for querying 'user' collection because 'user' collection has it's own store i.e. "User". It has different methods and please review this link for better understanding of User collection methods.
Regarding your question about 'User Discovery', please try the example mentioned on this link and let me know if it works for you. If it works for you then modify that code for "_id" with a single value and retry your scenario. Let me know if it works for you.
Thanks,
Pranav
Laurie Harper
I'm trying to retrieve User objects from the backend and nothing is working. I tried querying the user collection directly:
```
const dataStore = Kinvey.DataStore.collection('user')
const query = new Kinvey.Query()
query.contains('_id', ids)
dataStore.find(query).subscribe(...)
```
That resulted in an error,
```
{name: "IndirectCollectionAccessDisallowedError", message: "Please use the appropriate API to access this collection for this app backend", debug: "You cannot access the user collection through the appdata API", code: 403...
```
After some digging I found the User.lookup() API and tried this (sing the same query as above):
```
Kinvey.User.lookup(query).subscribe(...)
```
That results in this error:
```
{name: "InvalidQuerySyntaxError", message: "The query string in the request has an invalid syntax", debug: "complex queries disallowed", code: 400...
```
I don't see how the query syntax could be invalid when it was built by a supported API.
User management is one of the backbone services of any PaaS; why is a basic lookup API failing? What is the correct way to obtain a collection of users?