Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is MapDiff in Cloud Firestore Security Rules? [closed]

I have heard of a new MapDiff feature in Cloud Firestore Security rules.

I want to use this to more efficiently solve the problem described here, i.e. avoid having to write out the following:

allow update: if request.resource.data.size() == 6
              && request.resource.data.likes == resource.data.likes
              && request.resource.data.name == resource.data.name
              && request.resource.data.date == resource.data.date
              && request.resource.data.body == resource.data.body
              && request.resource.data.title == resource.data.title
              && request.resource.data.cakes is int;

In the past, writeFields existed, but they are unsupported now.

How can I effeciently (concerning code conciseness) check if only a single field has been altered and use MapDiff in general?

like image 648
creativecreatorormaybenot Avatar asked Dec 23 '22 18:12

creativecreatorormaybenot


1 Answers

TL;DR

allow update: if request.resource.data.diff(resource.data).affectedKeys().hasOnly(['cakes'])
              && request.resource.data.cakes is int;

MapDiff explanation

rules.MapDiff is a powerful feature recently added to Cloud Firestore Security Rules and is an efficient way of comparing two Map objects.
Since request.resource.data and resource.data are maps, MapDiff is perfect for this.

Map.diff()

In order to use any of the MapDiff functionality, you will first have to call diff on your map. This is very easy and looks like this:

request.resource.data.diff(resource.data) // Now you have a MapDiff object!

affectedKeys

The MapDiff.affectedKeys function is the most useful because it combines addedKeys, removedKeys, and changedKeys. This means that affectedKeys is what will solve the problem from the original question.
This means that all of:

allow update: if request.resource.data.size() == 6
              && request.resource.data.likes == resource.data.likes
              && request.resource.data.name == resource.data.name
              && request.resource.data.date == resource.data.date
              && request.resource.data.body == resource.data.body
              && request.resource.data.title == resource.data.title
              && request.resource.data.cakes is int;

Turns into just:

allow update: if request.resource.data.diff(resource.data).affectedKeys().hasOnly(['cakes'])
              && request.resource.data.cakes is int;

So affectedKeys returns a rules.Set of all keys that are affected between the two maps, i.e. added, removed, or changed.

addedKeys

This works the same way affectedKeys does, now only returning a set with added keys.

{'cakes': 1}.diff({}).addedKeys() == ['cakes'].toSet() // true

removedKeys

MapDiff.removedKeys is the opposite of MapDiff.addedKeys and returns a set of only the keys removed between the two maps:

{}.diff({'cakes': 1}).removedKeys() == ['cakes'].toSet() // true

changedKeys

MapDiff.changedKeys returns a Set of all keys that were changed between the two maps:

{'cakes': 0}.diff({'cakes': 1, 'pies': 4}).changedKeys() == ['cakes'].toSet() //true

A change is whenever the values in both sets are not equal.

unchangedKeys

MapDiff.unchangedKeys is the opposite of MapDiff.changedKeys and returns a set of all keys that were not changed between the two maps, i.e. all keys with equal values:

{'cakes': 0}.diff({'cakes': 0}).unchangedKeys() == ['cakes'].toSet() // true
like image 52
creativecreatorormaybenot Avatar answered Apr 09 '23 15:04

creativecreatorormaybenot