firestore-document-type-patterns
v6.0.11
Published
> firestore-document-type-patterns gives you type test patterns. > Then you might be able to check value types for security rule test easily.
Downloads
167
Maintainers
Readme
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-patterns
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-patterns/security-rule'
import {
DocumentType,
TypePattern,
ALL_FIELD_TYPES,
} from 'firestore-document-type-patterns/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 type | rule | name | | ---------- | ------------ | ------------------------- | | string | is string | ALL_FIELD_TYPES.string | | number | is number | ALL_FIELD_TYPES.number | | int | is int | ALL_FIELD_TYPES.int | | float | is float | ALL_FIELD_TYPES.float | | bool | is bool | ALL_FIELD_TYPES.bool | | map | is map | ALL_FIELD_TYPES.map | | list | is list | ALL_FIELD_TYPES.list | | null | == null | ALL_FIELD_TYPES.null | | timestamp | is timestamp | ALL_FIELD_TYPES.timestamp | | latlng | is latlng | ALL_FIELD_TYPES.latlng | | path | is path | ALL_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-patterns/core'
import { Inclusions } from 'firestore-document-type-patterns/types/inclusion-types'
import { KeyTypePatterns } from 'firestore-document-type-patterns/types/key-type-patterns'
import { KeyTypeValueFnc, KeyTypeValues, KeyValues } from 'firestore-document-type-patterns/types/key-type-values'
import { KeyType } from 'firestore-document-type-patterns/types/key-type'
import { REQUIRED_TYPE_VALUES } from 'firestore-document-type-patterns/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
)