5.0.2 • Published 8 months ago

firestore-document-type-partterns v5.0.2

Weekly downloads
-
License
ISC
Repository
github
Last release
8 months ago

firestore-document-type-patterns

firestore-document-type-patterns gives you type test patterns. Then you might be able to check value types for security rule test easily.

Installation

from v2.0.0 Please set the 'type' field in your package.json to 'module' since this package supports Pure ESM.

{
  "type": "module"
}

NPM Packages

$ npm i firestore-document-type-partterns

How To Use

import { assertSucceeds, assertFails } from '@firebase/rules-unit-testing'
import { doc, setDoc } from 'firebase/firestore'
import { v4 as randomStr } from 'uuid'
import {
  getKeyTypePatterns,
  convertTypeToValue,
  getRecursiveWrongTypes,
} from 'firestore-document-type-partterns/security-rule'
import {
  DocumentType,
  TypePattern,
  ALL_FIELD_TYPES,
} from 'firestore-document-type-partterns/types/firestore-field-types'

const documentTypes: DocumentType = {
  uid: ALL_FIELD_TYPES.string,
  'tags[]': ALL_FIELD_TYPES.string,
  createdAt: ALL_FIELD_TYPES.timestamp,
}

const wrongTypes = getRecursiveWrongTypes([documentTypes])
const rightTypes = getKeyTypePatterns([documentTypes])
const path = 'users'
const uid = randomStr()
const pathSegments = [uid]

describe('', () => {
  rightTypes.forEach(data => {
    test(`${JSON.stringify(data, null, 2)}`, async () => {
      await assertSucceeds(setDoc(doc(db, path, ...pathSegments), convertTypeToValue(data, db)))
    })
  })

  wrongTypes.forEach(data => {
    test(`${JSON.stringify(data, null, 2)}`, async () => {
      await assertFails(setDoc(doc(db, path, ...pathSegments), convertTypeToValue(data, db)))
    })
  })
})

Type Variables

Field typerulename
stringis stringALL_FIELD_TYPES.string
numberis numberALL_FIELD_TYPES.number
intis intALL_FIELD_TYPES.int
floatis floatALL_FIELD_TYPES.float
boolis boolALL_FIELD_TYPES.bool
mapis mapALL_FIELD_TYPES.map
listis listALL_FIELD_TYPES.list
null== nullALL_FIELD_TYPES.null
timestampis timestampALL_FIELD_TYPES.timestamp
latlngis latlngALL_FIELD_TYPES.latlng
pathis pathALL_FIELD_TYPES.path

DocumentType Rule

Ex1: Simple

function isStr(data) {
    return data is string;
}
const documentTypes: DocumentType = {
  uid: ALL_FIELD_TYPES.string,
}

getKeyTypePatterns

[
  { uid: 'hoge' }
]

getRecursiveWrongTypes

[
  { uid: -1 },
  { uid: 1 },
  { uid: 1.1 },
  { uid: true },
  { uid: {} },
  { uid: [] },
  { uid: null },
  { uid: FieldValue },
  { uid: GeoPoint },
  { uid: DocumentReference<DocumentData> },

]

Ex2: Multiple types of One Field

function(data) {
    return data.uid is string
        && (data.createdAt is timestamp || data.createdAt == null);
}
const documentTypes: DocumentType = {
  uid: ALL_FIELD_TYPES.string,
  createdAt: [ALL_FIELD_TYPES.timestamp, ALL_FIELD_TYPES.string.null],
}

getKeyTypePatterns

[
  {
    uid: 'hoge',
    createdAt: FieldValue,
  },
  {
    uid: 'hoge',
    createdAt: null,
  }
]

getRecursiveWrongTypes

[

  { uid: 1, createdAt: FieldValue },
  { uid: 1, createdAt: null },

  { uid: -1, createdAt: FieldValue },
  { uid: -1, createdAt: null },

  { uid: 1.1, createdAt: FieldValue },
  { uid: 1.1, createdAt: null },

  { uid: true, createdAt: FieldValue },
  { uid: true, createdAt: null },

  { uid: {}, createdAt: FieldValue },
  { uid: {}, createdAt: null },

  { uid: [], createdAt: FieldValue },
  { uid: [], createdAt: null },

  { uid: null, createdAt: FieldValue },
  { uid: null, createdAt: null },

  { uid: FieldValue, createdAt: FieldValue },
  { uid: FieldValue, createdAt: null },

  { uid: GeoPoint, createdAt: FieldValue },
  { uid: GeoPoint, createdAt: null },

  { uid: DocumentReference<DocumentData>, createdAt: FieldValue },
  { uid: DocumentReference<DocumentData>, createdAt: null },

  { uid: 'hoge', createdAt: 'hoge' },
  { uid: 'hoge', createdAt: -1 },
  { uid: 'hoge', createdAt: 1 },
  { uid: 'hoge', createdAt: 1.1 },
  { uid: 'hoge', createdAt: true },
  { uid: 'hoge', createdAt: {} },
  { uid: 'hoge', createdAt: [] },
  { uid: 'hoge', createdAt: GeoPoint },
  { uid: 'hoge', createdAt: DocumentReference<DocumentData> },
]

Ex3: Array Field

function isStrings(strings) {
    return strings.size() == 0 || ( strings.size() >= 1 && strings[strings.size() - 1] is string )
}
const documentTypes: DocumentType = {
  'tags[]': ALL_FIELD_TYPES.string,
}

getKeyTypePatterns

[
  {
    tags: ['hoge'],
  }
]

getRecursiveWrongTypes

[
  { tags: [-1] },
  { tags: [1] },
  { tags: [1.1] },
  { tags: [true] },
  { tags: [{}] },
  { tags: [null] },
  { tags: [FieldValue] },
  { tags: [GeoPoint] },
  { tags: [DocumentReference<DocumentData>] },

]

Ex4: Map Field

function(data) {
    return data.map.size() == 1
        && data.map.uid is string
}
const documentTypes: DocumentType = {
  map: {
    uid: ALL_FIELD_TYPES.string,
  },
}

getKeyTypePatterns

[
  {
    map: {
      uid: 'hoge',
    }
  }
]

getRecursiveWrongTypes

[
  { map: { uid: 'hoge' } },
  { map: { uid: -1 } },
  { map: { uid: 1 } },
  { map: { uid: 1.1 } },
  { map: { uid: true } },
  { map: { uid: {} },
  { map: { uid: [] } },
  { map: { uid: null } },
  { map: { uid: FieldValue } },
  { map: { uid: GeoPoint } },
  { map: { uid: DocumentReference<DocumentData> } }
]

Ex5: Specific Value

function isStatus(status) {
    return status == 'success' || status == 'error';
}
function hasStatusKey(data) {
  return 'status' in data && isStatus(data.status);
}
const documentTypes: DocumentType = {
  status: ['success', 'error'],
}

getKeyTypePatterns

[
  { status: 'success' },
  { status: 'error' },
]

getRecursiveWrongTypes

[
  { status: 'hoge' },
  { status: -1 },
  { status: 1 },
  { status: 1.1 },
  { status: true },
  { status: {} },
  { status: [] },
  { status: null },
  { status: FieldValue },
  { status: GeoPoint },
  { status: DocumentReference<DocumentData> },

]

Tips

Might be work

const documentTypes: DocumentType = {
  uid: ALL_FIELD_TYPES.string,
  'list[]': [
    ALL_FIELD_TYPES.number,
    {
      uid: ALL_FIELD_TYPES.string,
      'list[]': ALL_FIELD_TYPES.bool,
    },
  ],
  map: {
    'list[]': [ALL_FIELD_TYPES.bool, ALL_FIELD_TYPES.number],
    nestedMap: [
      ALL_FIELD_TYPES.null,
      {
        hoge: ALL_FIELD_TYPES.string,
      },
    ],
  },
}

Timestamp for In Array

usually use serverTimestamp(), but use new Date() in Array because FieldValue.serverTimestamp() is not currently supported inside array.

number, int, float

number's concept include int and float

ex:

int: allow 1 and -1, reject 1.1 and -1.1
float: allow 1.1 and -1.1, reject 1 and -1
number: allow all these above (1, -1, 1.1, and -1.1)
then, wrong type of number not contain int or float neither.
and also wrong type of (int / float) not contain number.

Set Key Types / Type Values

import {
  getKeyTypePatterns,
  getRecursiveWrongTypes,
  getRecursiveRightTypeValues,
  getRecursiveWrongTypeValues,
} from 'firestore-document-type-partterns/core'
import { Inclusions } from 'firestore-document-type-partterns/types/inclusion-types'
import { KeyTypePatterns } from 'firestore-document-type-partterns/types/key-type-patterns'
import { KeyTypeValueFnc, KeyTypeValues, KeyValues } from 'firestore-document-type-partterns/types/key-type-values'
import { KeyType } from 'firestore-document-type-partterns/types/key-type'
import { REQUIRED_TYPE_VALUES } from 'firestore-document-type-partterns/types/types'

interface Data {
  hoge: string
  foo: string
}

type KeyTypeConst = {
  hoge: string
  foo: string
}

type KeyValue = {
  hoge: number
  foo: number
}

const keyType: KeyType<KeyTypeConst> = {
  hoge: 'hoge',
  foo: 'foo',
}
const inclusions: Inclusions<KeyTypeConst> = {
  [keyType.hoge]: [keyType.hoge, keyType.foo],
}
const keyTypesList: KeyTypePatterns<Data>[] = [
  {
    hoge: keyType.hoge,
    foo: keyType.foo,
  },
]

const KEY_VALUES: KeyTypeValues<KeyValue> = {
  hoge: 1,
  foo: 2,
  ...REQUIRED_TYPE_VALUES,
}
const keyValueFnc: KeyTypeValueFnc<KeyValue> = () => {
  return KEY_VALUES
}

const rightTypes = getKeyTypePatterns<D>(keyTypesList)
const wrongTypes = getRecursiveWrongTypes<Data, KeyTypeConst>(keyTypesList, keyType, inclusions)
const rightTypeValues = getRecursiveRightTypeValues<Data, KeyValue>(keyTypesList, keyValueFnc)
const wrongTypeValues = getRecursiveWrongTypeValues<Data, KeyTypeConst, KeyValue>(
  keyTypesList,
  keyType,
  inclusions,
  keyValueFnc
)
5.0.2

8 months ago

5.0.1

8 months ago

4.0.2

8 months ago

3.2.2

9 months ago

3.1.1

9 months ago

3.0.2

9 months ago

2.0.1

10 months ago

1.0.11

2 years ago

1.0.12

2 years ago

1.0.10

2 years ago

1.0.9

2 years ago

1.0.7

2 years ago

1.0.6

2 years ago

1.0.5

2 years ago

1.0.4

2 years ago