From 5b1357d4d110e6b50f20d5604d5a7d9251f9d3d2 Mon Sep 17 00:00:00 2001 From: rbellens Date: Wed, 22 Aug 2018 15:33:53 +0200 Subject: [PATCH 1/7] allow wildcard properties in type statement --- docs/language.md | 7 +++++++ samples/issue-212.bolt | 40 ++++++++++++++++++++++++++++++++++++++ samples/issue-212.json | 42 ++++++++++++++++++++++++++++++++++++++++ src/rules-generator.ts | 9 +++++++-- src/test/sample-files.ts | 1 + 5 files changed, 97 insertions(+), 2 deletions(-) create mode 100644 samples/issue-212.bolt create mode 100644 samples/issue-212.json diff --git a/docs/language.md b/docs/language.md index 6c377bf..45c5fe4 100644 --- a/docs/language.md +++ b/docs/language.md @@ -31,6 +31,7 @@ A (user-defined) type statement describes a value that can be stored in the Fire type MyType [extends BaseType] { property1: Type, property2: Type, + $wildcardProp: Type ... validate() { } @@ -55,6 +56,12 @@ to use any other character in a property name, you can enclose them in quotes (n that Firebase allows any character in a path *except* for `.`, `$`, `#`, `[`, `[`, `/`, or control characters). +When a type statement contains a wildcard property, i.e. a property starting with the +`$`-character, the value is allowed to have an arbitrary number of extra properties. When +there is no wildcard property, the value can only have the properties defined in the +type statement. A type can have at most one wildcard property. + + Built-in base types are also similar to JavaScript types: String - Character strings diff --git a/samples/issue-212.bolt b/samples/issue-212.bolt new file mode 100644 index 0000000..5151c40 --- /dev/null +++ b/samples/issue-212.bolt @@ -0,0 +1,40 @@ +type MyType { + num: Number; + str: String; + any: Any; + $extras: Any; +} + +type AnyMap { + $extras: Any; +} + +type OtherType extends AnyMap { + name: String; +} + +type WithExtras extends T { + $extras: Any; +} + +type AnotherType { + name: String; +} + + +path /path is MyType { + read() { true } + write() { true } +} + +path /other is OtherType { + read() { true } +} + +path /another is AnotherType { + read() { true } +} + +path /anotherWithExtras is WithExtras { + read() { true } +} diff --git a/samples/issue-212.json b/samples/issue-212.json new file mode 100644 index 0000000..c27df4b --- /dev/null +++ b/samples/issue-212.json @@ -0,0 +1,42 @@ +{ + "rules": { + "path": { + ".validate": "newData.hasChildren(['num', 'str', 'any'])", + "num": { + ".validate": "newData.isNumber()" + }, + "str": { + ".validate": "newData.isString()" + }, + "any": { + ".validate": "true" + }, + ".read": "true", + ".write": "true" + }, + "other": { + ".validate": "newData.hasChildren(['name'])", + "name": { + ".validate": "newData.isString()" + }, + ".read": "true" + }, + "another": { + ".validate": "newData.hasChildren(['name'])", + "name": { + ".validate": "newData.isString()" + }, + "$other": { + ".validate": "false" + }, + ".read": "true" + }, + "anotherWithExtras": { + ".validate": "newData.hasChildren(['name'])", + "name": { + ".validate": "newData.isString()" + }, + ".read": "true" + } + } +} diff --git a/src/rules-generator.ts b/src/rules-generator.ts index 25de82d..b1c88fe 100644 --- a/src/rules-generator.ts +++ b/src/rules-generator.ts @@ -448,6 +448,7 @@ export class Generator { let wildProperties = 0; Object.keys(schema.properties).forEach((propName) => { if (propName[0] === '$') { + delete validator.$other; wildProperties += 1; if (INVALID_KEY_REGEX.test(propName.slice(1))) { this.fatal(errors.invalidPropertyName + propName); @@ -467,7 +468,7 @@ export class Generator { extendValidator( validator[propName], this.ensureValidator(propType)); }); - if (wildProperties > 1 || wildProperties === 1 && requiredProperties.length > 0) { + if (wildProperties > 1) { this.fatal(errors.invalidWildChildren); } @@ -478,7 +479,11 @@ export class Generator { } // Disallow $other properties by default - if (hasProps) { + let hasWildProps = Object.keys(validator).some((v) => { + return v[0] === '$'; + }); + + if (hasProps && !hasWildProps ) { validator['$other'] = {}; extendValidator( validator['$other'], {'.validate': ast.boolean(false)}); diff --git a/src/test/sample-files.ts b/src/test/sample-files.ts index 8dab860..ef9aeca 100644 --- a/src/test/sample-files.ts +++ b/src/test/sample-files.ts @@ -13,6 +13,7 @@ export let samples = [ "issue-169", "issue-232", "issue-97", + "issue-212", "mail", "map-scalar", "multi-update", From 50bb3d504ef55fb328f2c3dc9704d3da28817d2d Mon Sep 17 00:00:00 2001 From: Sam Stern Date: Thu, 7 Feb 2019 10:37:22 -0800 Subject: [PATCH 2/7] Update README for move to FirebaseExtended --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index fa6c0d8..a79a6db 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # ![Bolt Icon](docs/images/flash.png) Bolt Compiler -[![Build Status](https://travis-ci.org/firebase/bolt.svg?branch=master)](https://travis-ci.org/firebase/bolt) +[![Build Status](https://travis-ci.org/FirebaseExtended/bolt.svg?branch=master)](https://travis-ci.org/FirebaseExtended/bolt) [![NPM Version](https://badge.fury.io/js/firebase-bolt.svg)](https://npmjs.org/package/firebase-bolt) [![NPM Downloads](http://img.shields.io/npm/dm/firebase-bolt.svg)](https://npmjs.org/package/firebase-bolt) @@ -11,7 +11,7 @@ using with production applications. Otherwise, we'd love to have feedback from early adopters. You can email questions to firebase-talk@googlegroups.com using "Bolt" in the subject line, or post bugs -on our [Issue Tracker](https://github.com/firebase/bolt/issues). +on our [Issue Tracker](https://github.com/FirebaseExtended/bolt/issues). # Language Definition From bf52f12c9051b5a108a932c1685562bc724cf034 Mon Sep 17 00:00:00 2001 From: rbellens Date: Wed, 21 Aug 2019 14:50:18 +0200 Subject: [PATCH 3/7] add sample with other type (not any) for wildchild --- samples/issue-212.bolt | 2 +- samples/issue-212.json | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/samples/issue-212.bolt b/samples/issue-212.bolt index 5151c40..1fe1fc4 100644 --- a/samples/issue-212.bolt +++ b/samples/issue-212.bolt @@ -2,7 +2,7 @@ type MyType { num: Number; str: String; any: Any; - $extras: Any; + $extras: String; } type AnyMap { diff --git a/samples/issue-212.json b/samples/issue-212.json index c27df4b..95bed69 100644 --- a/samples/issue-212.json +++ b/samples/issue-212.json @@ -11,6 +11,9 @@ "any": { ".validate": "true" }, + "$extras": { + ".validate": "newData.isString()" + }, ".read": "true", ".write": "true" }, From 5f3e6cedcf2d323d74e18b7a8462c6c89edfd85e Mon Sep 17 00:00:00 2001 From: rbellens Date: Wed, 21 Aug 2019 14:51:44 +0200 Subject: [PATCH 4/7] adjust spacing --- src/rules-generator.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rules-generator.ts b/src/rules-generator.ts index b1c88fe..9b5f947 100644 --- a/src/rules-generator.ts +++ b/src/rules-generator.ts @@ -483,7 +483,7 @@ export class Generator { return v[0] === '$'; }); - if (hasProps && !hasWildProps ) { + if (hasProps && !hasWildProps) { validator['$other'] = {}; extendValidator( validator['$other'], {'.validate': ast.boolean(false)}); From 58b0447c002dbd258094feff7a0b392c0cdfa0d9 Mon Sep 17 00:00:00 2001 From: rbellens Date: Wed, 22 Aug 2018 15:33:53 +0200 Subject: [PATCH 5/7] allow wildcard properties in type statement --- docs/language.md | 7 +++++++ samples/issue-212.bolt | 40 ++++++++++++++++++++++++++++++++++++++ samples/issue-212.json | 42 ++++++++++++++++++++++++++++++++++++++++ src/rules-generator.ts | 9 +++++++-- src/test/sample-files.ts | 1 + 5 files changed, 97 insertions(+), 2 deletions(-) create mode 100644 samples/issue-212.bolt create mode 100644 samples/issue-212.json diff --git a/docs/language.md b/docs/language.md index 6c377bf..45c5fe4 100644 --- a/docs/language.md +++ b/docs/language.md @@ -31,6 +31,7 @@ A (user-defined) type statement describes a value that can be stored in the Fire type MyType [extends BaseType] { property1: Type, property2: Type, + $wildcardProp: Type ... validate() { } @@ -55,6 +56,12 @@ to use any other character in a property name, you can enclose them in quotes (n that Firebase allows any character in a path *except* for `.`, `$`, `#`, `[`, `[`, `/`, or control characters). +When a type statement contains a wildcard property, i.e. a property starting with the +`$`-character, the value is allowed to have an arbitrary number of extra properties. When +there is no wildcard property, the value can only have the properties defined in the +type statement. A type can have at most one wildcard property. + + Built-in base types are also similar to JavaScript types: String - Character strings diff --git a/samples/issue-212.bolt b/samples/issue-212.bolt new file mode 100644 index 0000000..5151c40 --- /dev/null +++ b/samples/issue-212.bolt @@ -0,0 +1,40 @@ +type MyType { + num: Number; + str: String; + any: Any; + $extras: Any; +} + +type AnyMap { + $extras: Any; +} + +type OtherType extends AnyMap { + name: String; +} + +type WithExtras extends T { + $extras: Any; +} + +type AnotherType { + name: String; +} + + +path /path is MyType { + read() { true } + write() { true } +} + +path /other is OtherType { + read() { true } +} + +path /another is AnotherType { + read() { true } +} + +path /anotherWithExtras is WithExtras { + read() { true } +} diff --git a/samples/issue-212.json b/samples/issue-212.json new file mode 100644 index 0000000..c27df4b --- /dev/null +++ b/samples/issue-212.json @@ -0,0 +1,42 @@ +{ + "rules": { + "path": { + ".validate": "newData.hasChildren(['num', 'str', 'any'])", + "num": { + ".validate": "newData.isNumber()" + }, + "str": { + ".validate": "newData.isString()" + }, + "any": { + ".validate": "true" + }, + ".read": "true", + ".write": "true" + }, + "other": { + ".validate": "newData.hasChildren(['name'])", + "name": { + ".validate": "newData.isString()" + }, + ".read": "true" + }, + "another": { + ".validate": "newData.hasChildren(['name'])", + "name": { + ".validate": "newData.isString()" + }, + "$other": { + ".validate": "false" + }, + ".read": "true" + }, + "anotherWithExtras": { + ".validate": "newData.hasChildren(['name'])", + "name": { + ".validate": "newData.isString()" + }, + ".read": "true" + } + } +} diff --git a/src/rules-generator.ts b/src/rules-generator.ts index 25de82d..b1c88fe 100644 --- a/src/rules-generator.ts +++ b/src/rules-generator.ts @@ -448,6 +448,7 @@ export class Generator { let wildProperties = 0; Object.keys(schema.properties).forEach((propName) => { if (propName[0] === '$') { + delete validator.$other; wildProperties += 1; if (INVALID_KEY_REGEX.test(propName.slice(1))) { this.fatal(errors.invalidPropertyName + propName); @@ -467,7 +468,7 @@ export class Generator { extendValidator( validator[propName], this.ensureValidator(propType)); }); - if (wildProperties > 1 || wildProperties === 1 && requiredProperties.length > 0) { + if (wildProperties > 1) { this.fatal(errors.invalidWildChildren); } @@ -478,7 +479,11 @@ export class Generator { } // Disallow $other properties by default - if (hasProps) { + let hasWildProps = Object.keys(validator).some((v) => { + return v[0] === '$'; + }); + + if (hasProps && !hasWildProps ) { validator['$other'] = {}; extendValidator( validator['$other'], {'.validate': ast.boolean(false)}); diff --git a/src/test/sample-files.ts b/src/test/sample-files.ts index 8dab860..ef9aeca 100644 --- a/src/test/sample-files.ts +++ b/src/test/sample-files.ts @@ -13,6 +13,7 @@ export let samples = [ "issue-169", "issue-232", "issue-97", + "issue-212", "mail", "map-scalar", "multi-update", From 6bf99d49d0d52813a0e731cf5b059473fcc0a085 Mon Sep 17 00:00:00 2001 From: rbellens Date: Wed, 21 Aug 2019 14:50:18 +0200 Subject: [PATCH 6/7] add sample with other type (not any) for wildchild --- samples/issue-212.bolt | 2 +- samples/issue-212.json | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/samples/issue-212.bolt b/samples/issue-212.bolt index 5151c40..1fe1fc4 100644 --- a/samples/issue-212.bolt +++ b/samples/issue-212.bolt @@ -2,7 +2,7 @@ type MyType { num: Number; str: String; any: Any; - $extras: Any; + $extras: String; } type AnyMap { diff --git a/samples/issue-212.json b/samples/issue-212.json index c27df4b..95bed69 100644 --- a/samples/issue-212.json +++ b/samples/issue-212.json @@ -11,6 +11,9 @@ "any": { ".validate": "true" }, + "$extras": { + ".validate": "newData.isString()" + }, ".read": "true", ".write": "true" }, From 714d44afa159d2718761d86ae13189108d8f2270 Mon Sep 17 00:00:00 2001 From: rbellens Date: Wed, 21 Aug 2019 14:51:44 +0200 Subject: [PATCH 7/7] adjust spacing --- src/rules-generator.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rules-generator.ts b/src/rules-generator.ts index b1c88fe..9b5f947 100644 --- a/src/rules-generator.ts +++ b/src/rules-generator.ts @@ -483,7 +483,7 @@ export class Generator { return v[0] === '$'; }); - if (hasProps && !hasWildProps ) { + if (hasProps && !hasWildProps) { validator['$other'] = {}; extendValidator( validator['$other'], {'.validate': ast.boolean(false)});