If I correctly understood what you are looking for your $language
write rules will look like this:
{"rules":{
"codes":{
"$codeId":{
"snippets":{
"$language":{
".write": "(!data.exists() && newData.child('uid').val() == auth.uid ) || (data.child('uid').val() == auth.uid && newData.child('uid').val() == auth.uid)"
}
}
}
}
}
}
Update:
After adding that you still want a write rule on /codes/$codeId
I strongly recommend you to move the snippets branch from the codes branch since you are adding to much complexity to one single node. You will start having this complex rules logic to deal with and your application code also will become overwhelmed by this structure.
Try to work with something more uncoupled such as the structure bellow:
{
"rules":{
"codes":{
"$codeId":{
".write": "true",
"snippets":{
"$snippetID":{
}
}
}
},
"snippets":{
"$snippetID":{
"$language":{
".write": "(!data.exists() && newData.child('uid').val() == auth.uid ) || (data.child('uid').val() == auth.uid && newData.child('uid').val() == auth.uid)"
}
}
}
}
}
Explanations
As requested I'm adding some details for this specific write logic.
From the requirements I got that the user can write if he is writing a new data OR he "owns" the data he is trying to write.
!data.exists() || data.child('uid').val() == auth.uid
Since "owning" a data means that this data has the owner uid as a child I then assumed that if he is writing a data the user uid needs always to be there. Thats why I've added the newData.child('uid').val() == auth.uid
. (remembering that newData means the data the user is attempting to write).
(!data.exists() && newData.child('uid').val() == auth.uid ) || (data.child('uid').val() == auth.uid && newData.child('uid').val() == auth.uid)
I could normalize it as bellow to avoid the newData
check in both sides of the logic but, at least to me, it makes it much more clear for understading.
(!data.exists() || data.child('uid').val() == auth.uid) && newData.child('uid').val() == auth.uid