It is requirement to run loops in data validation. In my case I have document with schema:
interface SomeDoc {
// ...other props
"prop-with-map": { [key: string]: number };
}
still there is no way to validate SomeDoc["prop-with-map"]
I let user create this document, then they can't update so need to check schema in firestore-rules. Without loops or schema check support in rules I have to make background function.
OR
I know there can't be more then 5 fields in SomeDoc["prop-with-map"]
. So I can check them one by one. Or create js function that generates code firestore-rule-function that checks in arr one by one.
SOLUTION
/**
* @param {string} functionName what function name you would like.
* @param {string} validate what is validator function name.
* @param {number} iteration max iteration before exiting loop.
* @return {string} function code used in firestore rules
*/
function generateFirestoreLoopForList(functionName, validate, iteration) {
let func = `function ${functionName}(arr) {\n return arr.size() == 0 ? true : ${validate}(arr[0])`;
for (let i = 1; i <= iteration; i++) {
let addAtIndex = func.length - i + 1;
func =
func.substring(0, addAtIndex) +
`&& (arr.size() == ${i} ? true : ${validate}(arr[${i}]))` +
func.substring(addAtIndex);
}
func += "\n }";
console.log(func);
return func;
}
then your rules can go like
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if false;
}
match /TEST/TEST {
allow create: if checkOtherProps() && hasAllOdd(request.resource.data["prop-with-map"].values());
function checkOtherProps() {
return // validate props and return accordingly;
}
function isInt(x) {
return x is int;
}
// outuput of generateFirestoreLoopForList("hasAllInts", "isInt", 5);
function hasAllInts(arr) {
return arr.size() == 0 ? true : isInt(arr[0])&& (arr.size() == 1 ? true : isInt(arr[1])&& (arr.size() == 2 ? true : isInt(arr[2])&& (arr.size() == 3 ? true : isInt(arr[3])&& (arr.size() == 4 ? true : isInt(arr[4])&& (arr.size() == 5 ? true : isInt(arr[5]))))));
}
}
}
}