0.1.16 • Published 3 years ago

@firestore-rules/cli v0.1.16

Weekly downloads
-
License
MIT
Repository
-
Last release
3 years ago

@firestore-rules/cli

CLI library for automatic generation of Firestore rules.

Install

yarn add -D @firestore-rules/cli
# or npm add @firestore-rules/cli --save-dev

Usage

Create Firestore Rules Model

create ./src/firestore-rules.ts

// ./src/firestore-rules.ts

import { FirestoreRulesModel, type, op } from '@firestore-rules/cli'

export default class extends FirestoreRulesModel {
  get path() {
    return '/databases/{database}/documents'
  }
  get isAuthenticated() {
    return this.defineFunc(op.ne(this.ctx.request.auth, new type.Null()))
  }
  get isMe() {
    const uid = this.args('uid')
    return this.defineFunc(
      op.and(
        this.isAuthenticated.call([]),
        op.eq(uid, this.ctx.request.auth.uid)
      )
    )
  }

  get userCollection() {
    const self = this
    return this.defineModel(
      class extends FirestoreRulesModel {
        get path() {
          return '/users/{uid}'
        }
        get() {
          return self.isMe.call([this.variables.uid])
        }
        create() {
          return self.isAuthenticated.call([])
        }
        update() {
          return self.isMe.call([this.variables.uid])
        }
      }
    )
  }
}

Then run the following command

yarn firestore-rules
# or ./node_modules/.bin/firestore-rules

Then you will get the following firestore.rules

// !!! DO NOT EDIT !!!
// This file is generated by @firestore-rules/cli
rules_version = "2";
service cloud.firestore {
  match /databases/{database}/documents {
    function isAuthenticated() {
      return (request.auth != null);
    }
    function isMe(uid) {
      return (isAuthenticated() && (uid == request.auth.uid));
    }
    match /users/{uid} {
      allow get: if isMe(uid);
      allow create: if isAuthenticated();
      allow update: if isMe(uid);
    }
  }
}

Options

There are several ways to specify the options

  • "firestore-rules" in package.json
  • .firestore-rulesrc (both json or yaml format is ok)
  • .firestore-rulesrc.{json,yaml,yml,js,cjs}
  • firestore-rules.config.{js,cjs}

See cosmiconfig for other specification methods.

Option Properties

rulesPath: string # path to firestore-rules file. (default is './src/firestore-rules.ts')
typescript: boolean # if you do not use typescript, turn off. (default is true)
tsconfigPath: string # path to your tsconfig file. (default is './tsconfig.json')
outputPath: string # path to generate file. (default is 'firestore.rules')
backupFile: boolean # if you don't want to create backup file, turn off. (default is true)
formatOption: # options for formatter
  indent: tab or space # indent type. (default is space)
  indentSize: number # indent size. (default is 2)
  lineLength: number # Maximum length of a line. (* Currently under development)

rulesPath

Specify the path of the entry file to be read by cli.

Default is ./src/firestore-rules.ts.

typescript

Set to true if you want to use Typescript.

Default is true.

tsconfigPath

If you use TypeScript, you should provide valid tscondif file.

Default is ./tsconfig.json

outputPath

Specify the path of the file to output.

Default is firestore.rules.

backupFile

Set to true if you want the original rule to be backed up when generating the file.

Default is true.

The name of the file to be backed up will be outputPath + '.backup'.

formatOption

Options to be passed to @firestore-rules/formatter.

Check @firestore-rules/formatter for details

Writing Style

1. Export default class that extends FirestoreRulesModel

import { FirestoreRulesModel } from '@firestore-rules/cli'

export default class extends FirestoreRulesModel {
  // ...
}

Define a getter named path

The returned value will be the value to apply to the match statement in firestore.rules.

// ...
export default class extends FirestoreRulesModel {
  get path() {
    return '/databases/{database}/documents'
  }
}

Define a function with no arguments

You can use a method called defineFunc.

You can use this.ctx to refer to a global value such as request.

The literal and op that can be imported from @firestore-rules/cli contain all types and operators except for the Bytes type

import { FirestoreRulesModel, literal, op } from '@firestore-rules/cli'

export default class extends FirestoreRulesModel {
  /*
   * function isAuthenticated() {
   *   return (request.auth != null);
   * }
   */
  get isAuthenticated() {
    return this.defineFunc(
      op.ne(this.ctx.request.auth, new literal.FirestoreRulesLiteralNull())
    )
  }
}

Define a function with arguments

You can use a method called defineFunc, too.

Arguments can be defined by using the arg method.

Also, if you want to use a predefined function, you can use this.<funcName>.call(). The first argument of the call should be the argument to be passed to the function. isAuthenticated does not require any argument, so it will pass an empty array

import { FirestoreRulesModel, literal, op } from '@firestore-rules/cli'

export default class extends FirestoreRulesModel {
  /*
   * function isMe(uid) {
   *   return (isAuthenticated() && (uid == request.auth.uid));
   * }
   */
  get isMe() {
    const uid = this.args('uid') // create argument
    return this.defineFunc(
      op.and(
        this.isAuthenticated.call([]), // call defined function
        op.eq(uid, this.ctx.request.auth.uid)
      )
    )
  }
}

Nesting match statements

You can use the defineModel method to nest match expressions.

The argument should be clsass as well as the class you are default export.

It is also a good idea to replace the this reference with a variable such as self so that it can be used again.

import { FirestoreRulesModel, literal, op } from '@firestore-rules/cli'

export default class extends FirestoreRulesModel {
  get userCollection() {
    const self = this
    return this.defineModel(
      class extends FirestoreRulesModel {
        // ...
      }
    )
  }
}

Similarly, define the path

Define the path in the same way as you did for /databases/{database}/documents.

import { FirestoreRulesModel, literal, op } from '@firestore-rules/cli'

export default class extends FirestoreRulesModel {
  get userCollection() {
    const self = this
    return this.defineModel(
      class extends FirestoreRulesModel {
        get path() {
          return '/users/{uid}'
        }
      }
    )
  }
}

Declare the permit conditions

Permission conditions can be specified by overriding the get, list, create, update, and delete methods.

Also, the document ID (uid in this example) will be automatically associated with this.variables.uid.

Since we just used self, we can also call functions in the parent hierarchy from self.

The isMe function needs an argument, so let's give it one.

import { FirestoreRulesModel, literal, op } from '@firestore-rules/cli'

export default class extends FirestoreRulesModel {
  /*
   * match /users/{uid} {
   *   allow get: if isMe(uid);
   *   allow create: if isAuthenticated();
   *   allow update: if isMe(uid);
   * }
   */
  get userCollection() {
    const self = this
    return this.defineModel(
      class extends FirestoreRulesModel {
        get path() {
          return '/users/{uid}'
        }
        get() {
          return self.isMe.call([this.variables.uid])
        }
        create() {
          return self.isAuthenticated.call([])
        }
        update() {
          return self.isMe.call([this.variables.uid])
        }
      }
    )
  }
}

Others

If you have any questions, please open an Issue!

0.1.16

3 years ago

0.1.15

3 years ago

0.1.14

3 years ago

0.1.13

3 years ago

0.1.12

3 years ago

0.1.11

3 years ago

0.1.10

3 years ago

0.1.9

3 years ago

0.1.8

3 years ago

0.1.7

3 years ago

0.1.5

3 years ago

0.1.4

3 years ago

0.1.3

3 years ago