Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature Request: Be able to reference map keys in validation rules #219

Open
alixaxel opened this issue Oct 24, 2017 · 6 comments
Open

Feature Request: Be able to reference map keys in validation rules #219

alixaxel opened this issue Oct 24, 2017 · 6 comments

Comments

@alixaxel
Copy link
Contributor

I'm having trouble with a DB structure that looks like this:

{
  "post-flag": {
    "-KvXU5eiR4oZM-vBihib": {
      "6t5KEJjAOSYbf8dleabDlTyxhux2": 1507542071604,
      "6t5KEJjAOSYbf8dleabDlTyxhux3": 1507542071605
    }
  }
}

I'd like to validate that the data is valid and that the keys follow a certain format.

I've tried several variations of the following, but none of them really work.

path /post-flag is Map<PushID, Map<UserID, UnixTimestamp>>;
path /post-flag/{post}/{user} is UnixTimestamp {
  write() {
    return isCurrentUser(user) && root.post[post] != null
  }
}

type PushID extends String {
  validate() { return this.test(/^[-_0-9a-zA-Z]{20}$/); }
}

type UserID extends String {
  validate() { return this.test(/^[0-9a-zA-Z]{27}[0-9]$/); }
}

type UnixTimestamp extends Number {
  validate() { return this >= 0; }
}

For the example above I get the following JSON:

{
  "rules": {
    "post-flag": {
      "$key1": {
        ".validate": "newData.hasChildren() && $key1.matches(/^[-_0-9a-zA-Z]{20}$/)",
        "$key2": {
          ".validate": "$key2.matches(/^[0-9a-zA-Z]{27}[0-9]$/) && newData.isNumber() && newData.val() >= 0"
        }
      },
      ".validate": "newData.hasChildren()",
      "$post": {
        "$user": {
          ".validate": "newData.isNumber() && newData.val() >= 0",
          ".write": "auth != null && auth.uid == $user && newData.parent().parent().parent().child('post').child($post).val() != null"
        }
      }
    }
  }
}

Which yields the following error in Firebase simulator:

Cannot have multiple default rules ('$key1' and '$post').

I'm not sure if I'm writing my rules wrongly or if this is a limitation of Bolt.

@rockwotj
Copy link
Collaborator

rockwotj commented Oct 24, 2017

Possibly you want something like:

function isCurrentUser(user) {
  return auth != null && auth.uid == user;
}

path /post-flag is Map<PushID, Map<UserID, UnixTimestamp>> {
  write() {
    return isCurrentUser(key2) && root.post[key1] != null
  }
}

type PushID extends String {
  validate() { return this.test(/^[-_0-9a-zA-Z]{20}$/); }
}

type UserID extends String {
  validate() { return this.test(/^[0-9a-zA-Z]{27}[0-9]$/); }
}

type UnixTimestamp extends Number {
  validate() { return this >= 0; }
}

Slightly tricky but you have to know the first map gets a key generated of $key1. Bolt doesn't really understand how to merge paths between types and wildcards.

@alixaxel
Copy link
Contributor Author

alixaxel commented Oct 24, 2017

@rockwotj Thanks for the quick reply.

I thought that might be the case... user should then become key2, right?

path /post-flag is Map<PushID, Map<UserID, UnixTimestamp>> {
  write() {
    return isCurrentUser(key2) && root.post[key1] != null
  }
}

This generates the following JSON:

    "post-flag": {
      "$key1": {
        ".validate": "newData.hasChildren() && $key1.matches(/^[-_0-9a-zA-Z]{20}$/)",
        "$key2": {
          ".validate": "$key2.matches(/^[0-9a-zA-Z]{27}[0-9]$/) && newData.isNumber() && newData.val() >= 0"
        }
      },
      ".validate": "newData.hasChildren()",
      ".write": "auth != null && auth.uid == key2 && newData.parent().child('post').child(key1).val() != null"
    },

And error message:

Unknown variable 'key2'.

@rockwotj
Copy link
Collaborator

Oops yes, good catch. I've updated my answer as well.

@alixaxel
Copy link
Contributor Author

alixaxel commented Oct 24, 2017

So the only way I've made it to work is if duplicate the rule and use keyX as variable name:

path /post-flag is Map<PushID, Map<UserID, UnixTimestamp>>;

path /post-flag/{key1} {
  write() {
    return root.post[key1] != null
  }
}

path /post-flag/{key1}/{key2} {
  write() {
    return isCurrentUser(key2)
  }
}

Isn't there any sort of aliasing to instruct Bolt to map variables to certain names? Like:

path /post-flag is Map<PushID as post, Map<UserID as user, UnixTimestamp>>;

@rockwotj
Copy link
Collaborator

Currently there is not, but feel free to open a PR 😄

@alixaxel
Copy link
Contributor Author

alixaxel commented Oct 25, 2017

I'll have a go at it. 😄

@rockwotj rockwotj changed the title How to validate values and keys? Feature Request: Be able to reference map keys in validation rules Oct 25, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants