firestore security: exist and get within the same function does not work - firebase

I'm trying to get() a document and then based on the result, to see if another document exists using exists(). However, this fails because the variable that should be storing a string remains an object, namely parentId below.
Observed
It does not convert parentId to a string and it fails when calling exists().
function validateAuth() {
let parentId = get(/databases/$(database)/documents/items/$(resource.data.itemId)).data.parentId
return exists(/databases/$(database)/documents/other-items/$(parentId):$(request.auth.uid))
}
Expected
It should convert parentId to a string so that exists can be performed.
What I've tried
This below works, and the function is able to evaluate when the document ID using parentId exists and doesn't exist.
function validateAuth() {
// let parentId = get(/databases/$(database)/documents/items/$(resource.data.itemId)).data.parentId
let parentId = 'some-id';
return exists(/databases/$(database)/documents/other-items/$(parentId):$(request.auth.uid))
}
Now, I removed the string concatenation below but this still throws an error
function validateAuth() {
// let parentId = get(/databases/$(database)/documents/items/$(resource.data.itemId)).data.parentId
let parentId = 'some-id';
return exists(/databases/$(database)/documents/other-items/$(parentId))
}
The below does not work, and the parentId is still an object when ran in the simulator.
function validateAuth() {
let parentId = get(/databases/$(database)/documents/items/$(resource.data.itemId)).data.parentId;
let parentIdStr = string(parentId);
return exists(/databases/$(database)/documents/other-items/$(parentIdStr):$(request.auth.uid))
}
Error details
I get this error after the console processes it for about 5 ~ 10 seconds, which is a lot longer than if the requests are processed correctly.
Error: Invalid argument provided to call. Function: [exists], Argument: ["||invalid_argument||"]
I couldn't find any documentation about this. Does anyone know if this should work or if there are workarounds?

The part with colon seems to be the problem:
$(parentIdStr):$(request.auth.uid)
Try refactoring that to a single element in path:
$(parentIdStr+':'+request.auth.uid)

Related

QML Firebase startAt returns undefined

I am working on a 'typeahead’ type function which will check my Database with the current typed text to provide search suggestions of users using Felgo.
Here is the link for Felgos Firebase documentation
As to not search every entry I am looking to use the startAt and limitTo for a lower data use.
However when applying the startAt my searches only return undefined, I have tried testing this by changing my startAt from a variable to explicit data but this still only returns undefined.
My function is below:
function searchUsers(searchString) {
db.getValue("public/nameList/", {
orderByChild: true,
startAt: searchString, //searchString is a variable with my .currentText to search.
limitToFirst: 10,
}, function(success, key, value) {
if(success) {
searchArr = []
searchArr = value
console.debug("Read user value for key", key, "from DB:", value)
}
})
}
I have also tried by passing my var searchString through JSON.stringify(searchString) and also return undefined!
Removing the startAt: query entirely returns the entire result of nameList as expected, but no matter how I try to implement my startAt it always returns undefined.
A sample of my nameList JSON is:
nameList: {
"EddieLaw245" : 530343772383,
"EddieLawrence91" : 530343772385,
"EdwardL91" : 530343772386,
"EdwardLaw" : 530343772384,
"Edwardlawrence91" : 530343772380,
"JoBrownLondon" : 530343772381,
"KatiePrescottHair" : 543592635596,
"Tracey-Sweeting" : 530343772382
}
So with the above example, When I type E it should remove the last 3 entries, and so on.
The problem is that you're specifying orderByChild: true. If we look at the documentation of that:
orderByChild: If present, the queried object will have its properties ordered by values at sub-paths defined by the value of this property. Ordering by child properties makes the filter properties startAt, endAt and equalTo filter by the child property values
It may not be immediately clear from this, but orderByChild allows you to order the results on a property value under each of those nodes. So your code tries to order the child nodes on the value of a property true, which isn't possible (and should actually generate a compile-time error in the library) as the nodes under nameList don't have any child properties of their own. They merely have a key and a value.
What you're looking for is orderByKeys, which orders the child nodes on their keys. So:
db.getValue("public/nameList/", {
orderByKeys: true,
startAt: searchString,
limitToFirst: 10,
}
You'll typically also want to specify an endAt value, to ensure your type-ahead only shows values that start with the search string. If you only allow ASCII values in the keys, the simplest way to do this is:
startAt: searchString,
endAt: searchString + "~",
The ~ here is no magic operator, but merely the last ASCII characters. If you want to allow a broader character set, you'll need to use the last character in that character set - for example \uF7FF is the last code point for Unicode.
Update from OP
Though I'm certian Franks correct with typical Firebase usage; I suspect due to the Felgo plugin I am using the full solution has a slight adjustment;
db.getValue("public/nameList/", {
"orderByKey": true,
"startAt": searchString,
"endAt": searchString+"~",
"limitToFirst": 10,
}, function(success, key, value) {....}
})
Notes on the above - my filters/queries are surrounded by quotation marks "startAt", also instead of orderByKeys, I have used orderByKey

How to get document id from firestore

I'm trying to get my every document id in of my collection "meinprofilsettings". But I'm a bit struggling with that so maybe anyone can help.
First here's my code:
List<String> alluserids = [];
getHashtags() async {
final ref = FirebaseFirestore.instance;
QuerySnapshot snapshots = await ref.collection('meinprofilsettings').get();
for (QueryDocumentSnapshot idofuser in snapshots.docs) {
allVideoHastags.addAll(idofuser.id);
}
}
And then here's the error I get:
The argument type 'String' can't be assigned to the parameter type 'Iterable'.
This is a screenshot of my database:
I I just want every id of of every doc inside the list alluserids.
Perhaps you want to change this
allVideoHastags.addAll(idofuser.id);
To this:
alluserids.add(idofuser.id);
addAll method receives an Iterable as parameter. You want to add a single value so you should use .add
Also, you could use the .map method instead of for in:
allVideoHastags.addAll(snapshots.docs.map((idofuser)=>idofuser.id));
The behavior behind the last statement is that map returns an Iterable which has a value for each value that the base Iterable (in this case, your docs) contains.
In this case, the base iterable is snapshots.docs and our mapping returns Iterable<String> as we're returning every doc id. Hope this is enough for helping

How to get data from firebase query based on value from another firebase query in FutureBuilder in Flutter?

I am new to flutter and I am sure there is a simple way of doing this. Let me first give you a background. I have 2 tables(collections). The first one store a mapping. Therefore it returns a key based on an id which will be used to query the second table and retrieve the data from firebase.
I have written 2 data models and 2 functions which return Future<> data. They are as follows-
Future<SpecificDevice> getSpecificDevice(String deviceId) {
Future<SpecificDevice> obj =_database.reference().child("deviceMapping").orderByChild("deviceId").equalTo(deviceId).once().then((snapshot) {
SpecificDevice specificDevice = new SpecificDevice(deviceId, "XXXX", new List<String> ());
if(snapshot.value.isNotEmpty){
print(snapshot.value);
snapshot.value.forEach((key,values) {
if(values["deviceId"] == deviceId) {
specificDevice.deviceKey = values["deviceDesc"];
specificDevice.vendorList = List.from(values["vendorList"]);
}
});
}
return specificDevice;
});
return obj;
}
This function gets the mapping deviceId -> deviceKey.
This is the key of record stored in another table. Following is the function for it.
Future<Device> getDeviceDescription(String deviceKey) {
Future<Device> device = _database.reference().child("deviceDescription").once().then((snapshot) {
Device deviceObj = new Device("YYYY", "YYYY", "YYY", "YYYY", "YYYY");
if(snapshot.value.isNotEmpty){
print(snapshot.value);
//Future<SpecificDevice> obj = getSpecificDevice(deviceId);
//obj.then((value) {
snapshot.value.forEach((key,values) {
if(key == deviceKey) { // compare with value.deviceKey instead
print(values["deviceDescription"]); // I get the correct data here.
deviceObj.manual = values["deviceManual"];
deviceObj.deviceType = values["deviceType"];
deviceObj.description = values["deviceDescription"];
deviceObj.brand = values["deviceBrand"];
deviceObj.picture = values["devicePicture"];
}
// });
});
}
return deviceObj;
});
return device;
}
Now both of these functions work. I want to make it work one after the other. In the above function, if I uncomment the lines of code, the data is retrieved properly in the inner function but it returns initial default values set because the values get returned before setting the obj of SpecificDevice.
Here is where I am getting the error. I am calling the second function in FutureBuilder<> code with the above lines uncommented and taking input param as deviceId.
return FutureBuilder<Device>(
future: getDeviceDescription(deviceId),
builder:(BuildContext context,AsyncSnapshot snapshot){... // using snapshot.data in its child.
Here in snapshot.data. would give me YYYY. But it should get me the value from the database.
I am stuck with this for a while. Any help in fixing this? or if what I am trying to do is clear then please suggest me a better way to approach this. Thanks in advance!
The answer is rather simple:
first and foremost - you forgot to use async / await keywords, which will guarantee synchronous data retrieval from the database. Always use them, if you are connecting to any network service
to make one command work after another - use .then((value) {}). It will get data from the first function (which you pass using return) and use it in the second function.
Solved the problem by changing the calling function to -
return FutureBuilder<Device>(
future: getSpecificDevice(deviceId).then((value){
return getDeviceDescription(value.deviceKey);
}),
builder:(BuildContext context,AsyncSnapshot snapshot){

Unable to cast object of type 'System.Data.Objects.ObjectQuery`1 ..... to type 'DatabaseModel1......'. ASP

Hi im trying to assign a default group to any user that registers with my site. Since the admin Id of that group is the administrator, I query my database for a group with a admin id of my administrator and then assign that group to the user. However the following error is being thrown
"Unable to cast object of type 'System.Data.Objects.ObjectQuery`1[DatabaseModel1.Group]' to type 'DatabaseModel1.Group'. "
Heres the code
Dim defaultGroup = (From group As Group In context.Groups
Where group.AdminID = ((From users As User0 In context.User0
Where users.Name Like "Administrator"
Select users.UserID).First)
Select group)
currentUser.Groups.Add(defaultGroup)//the error is being thrown here
any help would be appreciated
Thanks
It sounds like you've got a query which can return multiple results, but you're trying to assign it to a single-value variable.
You probably just need to use something like:
currentUser.Groups.Add(defaultGroup.First)
The probable options are:
First() - allows multiple results and returns the first; will throw an exception if there are none
FirstOrDefault() - allows multiple results and returns the first; will return the default value for the element type (e.g. null) if there are no results
Last() - allows multiple results and returns the last; will throw an exception if there are none
LastOrDefault() - allows multiple results and returns the last; will return the default value for the element type (e.g. null) if there are no results
Single() - expects exactly one result; if there are no results or mulitple results, an exception is thrown
Now that you've shown the query, it sounds like you should probably use a join rather than a nested query. I'm not hot on the VB query syntax, but in C# you might want:
var defaultGroupQuery =
from group in context.Groups
join user in context.User0 on group.AdminID equals user.UserID
where user.Name == "Administrator"
select group;
List<int> adminIDs = context.User0.Where(u=> u.Name.Contains("Administrator"))
.Select(u=>u.UserId);
//you can use `FirstOrDefault()` above and it will return only int not `List<int>`
// but i'm showing you how to get all IDs then you can get the first one
int adminId = adminIDs.FirstOrDefault();
//and then get only one Group using this adminID
var defaultGroup = context.Groups.FirstOrDefault(g=>g.AdminID == adminId);
if(defaultGroup != null)
{
currentUser.Groups.Add(defaultGroup);
}
//you can use the `List<int> adminIDs` if you need to get all admin groups
var groups = context.Groups.Where(gg => adminIDs.Contains(gg.AdminID));
Hope it helps.

db_or() in hook_query_TAG_alter?

I'm new to Drupal 7, but not to Drupal. I'm afraid, however, that the new database query system is a bit over my head.
I'm attempting to alter a view query using hook_query_TAG_alter(). In essence, I'm attempting to append an OR to the filter, saying "list nodes where the current user is either referenced in a particular_field OR is the author."
I'm attempting to append this last condition (or is the author) do this by adding a db_or()->condition(…) to the query, but I keep getting a fatal error:
Call to undefined method SelectQuery::db_or() in /Volumes/...
It's driving me batshit, because I don't understand this system enough to know why this method is unavailable. Here is my implementation:
function sentinel_network_query_views_networks_alter(QueryAlterableInterface $query) {
$query->db_or()->condition('node.uid', '***CURRENT_USER***', '=');
}
db_or() returns a database condition instance so you need to pass it to the query as a condition. Also, you need more than one condition in db_or() to actually perform an or!
$or = db_or()
->condition('node.uid', '***CURRENT_USER***', '=')
->condition('node.nid', 'SECOND CONDITION');
$query->condition($or);
Hope that helps
db_or() is a function not a method.
As example of how db_or() is used, you can look at the example code given for hook_query_TAG_alter():
$access_alias = $query->join('node_access', 'na', '%alias.nid = n.nid');
$or = db_or();
// If any grant exists for the specified user, then user has access to the node for the specified operation.
foreach (node_access_grants($op, $query->getMetaData('account')) as $realm => $gids) {
foreach ($gids as $gid) {
$or->condition(db_and()
->condition($access_alias . '.gid', $gid)
->condition($access_alias . '.realm', $realm)
);
}
}
if (count($or->conditions())) {
$query->condition($or);
}
$query->condition($access_alias . 'grant_' . $op, 1, '>=');
$query->condition() doesn't replace the existing condition; it adds a new condition.
In SelectQuery::__construct(), SelectQuery::$where (which contains the DatabaseCondition object for the query) is initialized using the following code:
$this->where = new DatabaseCondition('AND');
This means that all the conditions added to a SelectQuery object will be joined to each other using the "AND" operator.

Resources