Skip to content

Commit b9d9b05

Browse files
authored
Merge pull request #6 from GitLiveApp/flag-to-delete-subcollection
Flag to delete subcollection
2 parents f9e8130 + 9b02ca9 commit b9d9b05

File tree

5 files changed

+300
-33
lines changed

5 files changed

+300
-33
lines changed

README.md

Lines changed: 45 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,14 @@
11
# 𝚒𝚗𝚝𝚎𝚐𝚛𝚒𝚏𝚢
22

3-
[![Build Status](https://travis-ci.com/anishkny/integrify.svg?branch=master)](https://travis-ci.com/anishkny/integrify)
4-
[![Coverage Status](https://coveralls.io/repos/github/anishkny/integrify/badge.svg?branch=master)](https://coveralls.io/github/anishkny/integrify?branch=master)
5-
[![Greenkeeper badge](https://badges.greenkeeper.io/anishkny/integrify.svg)](https://greenkeeper.io/)
6-
[![Security](https://img.shields.io/badge/security-GitHub-blue)](https://github.com/anishkny/integrify/network/alerts)
7-
[![npm package](https://img.shields.io/npm/v/integrify.svg)](https://www.npmjs.com/package/integrify)
8-
[![Mentioned in Awesome Firebase](https://awesome.re/mentioned-badge.svg)](https://github.com/jthegedus/awesome-firebase)
93

104
🤝 Enforce referential and data integrity in [Cloud Firestore](https://firebase.google.com/docs/firestore/) using [triggers](https://firebase.google.com/docs/functions/firestore-events)
115

12-
[Introductory blog post](https://dev.to/anishkny/---firestore-referential-integrity-via-triggers-kpb)
6+
This library was forked from [anishkny/integrify](https://github.com/anishkny/integrify)
137

148
## Usage
159

10+
#### REPLICATE
11+
1612
```js
1713
// index.js
1814

@@ -61,20 +57,42 @@ module.exports.replicateMasterToDetail = integrify({
6157
},
6258
},
6359
});
60+
```
61+
62+
#### DELETE
63+
64+
```js
65+
// index.js
66+
67+
const { integrify } = require('integrify');
68+
69+
const functions = require('firebase-functions');
70+
const admin = require('firebase-admin');
71+
admin.initializeApp();
72+
const db = admin.firestore();
73+
74+
integrify({ config: { functions, db } });
6475

6576
// Automatically delete stale references
6677
module.exports.deleteReferencesToMaster = integrify({
6778
rule: 'DELETE_REFERENCES',
6879
source: {
69-
collection: 'master',
80+
collection: 'master', // <-- This will append {masterId}
81+
// OR
82+
collection: 'master/{masterId}', // <-- Can be any string as in Firebase
7083
},
7184
targets: [
7285
{
7386
collection: 'detail1',
74-
foreignKey: 'masterId',
75-
76-
// Optional:
77-
isCollectionGroup: true, // Delete from collection group, see more below
87+
foreignKey: 'masterId', // Optional: Delete document with matching foreign key
88+
deleteAll: false, // Optional: Delete all from collection
89+
// EITHER 'foreignKey' OR 'deleteAll' MUST BE PROVIDED
90+
isCollectionGroup: true, // Optional: Delete from collection group, see more below
91+
},
92+
{
93+
collection: 'detail1/$master/details', // Can reference source ID, will throw error if it doesn't exist
94+
// OR
95+
collection: 'detail1/$fieldValue/details', // Can reference a field value, will throw error if it doesn't exist
7896
},
7997
],
8098

@@ -86,6 +104,21 @@ module.exports.deleteReferencesToMaster = integrify({
86104
},
87105
},
88106
});
107+
```
108+
109+
#### MAINTAIN COUNT
110+
111+
```js
112+
// index.js
113+
114+
const { integrify } = require('integrify');
115+
116+
const functions = require('firebase-functions');
117+
const admin = require('firebase-admin');
118+
admin.initializeApp();
119+
const db = admin.firestore();
120+
121+
integrify({ config: { functions, db } });
89122

90123
// Automatically maintain count
91124
module.exports.maintainFavoritesCount = integrify({

src/rules/deleteReferences.ts

Lines changed: 42 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@ export interface DeleteReferencesRule extends Rule {
66
};
77
targets: {
88
collection: string;
9-
foreignKey: string;
9+
foreignKey?: string;
1010
isCollectionGroup?: boolean;
11+
deleteAll?: boolean;
1112
}[];
1213
hooks?: {
1314
pre?: Function;
@@ -60,13 +61,20 @@ export function integrifyDeleteReferences(
6061
// Loop over each target
6162
const db = config.config.db;
6263
rule.targets.forEach(target => {
63-
console.log(
64-
`integrify: Deleting all docs in collection ${
65-
target.isCollectionGroup ? 'group ' : ''
66-
}[${target.collection}] where foreign key [${
67-
target.foreignKey
68-
}] matches [${primaryKeyValue}]`
69-
);
64+
// Check delete all flag
65+
if (!target.deleteAll) {
66+
target.deleteAll = false;
67+
}
68+
// CHeck the foreign key
69+
if (!target.foreignKey) {
70+
target.foreignKey = '';
71+
}
72+
73+
if (!target.deleteAll && target.foreignKey.trim().length === 0) {
74+
throw new Error(
75+
`integrify: missing foreign key or set deleteAll to true`
76+
);
77+
}
7078

7179
// Replace the context.params in the target collection
7280
const paramSwap = replaceReferencesWith(
@@ -89,20 +97,33 @@ export function integrifyDeleteReferences(
8997
whereable = db.collection(target.collection);
9098
}
9199

100+
if (target.deleteAll) {
101+
console.log(
102+
`integrify: Deleting all docs in sub-collection [${target.collection}]`
103+
);
104+
} else {
105+
console.log(
106+
`integrify: Deleting all docs in collection ${
107+
target.isCollectionGroup ? 'group ' : ''
108+
}[${target.collection}] where foreign key [${
109+
target.foreignKey
110+
}] matches [${primaryKeyValue}]`
111+
);
112+
113+
whereable = whereable.where(target.foreignKey, '==', primaryKeyValue);
114+
}
115+
92116
promises.push(
93-
whereable
94-
.where(target.foreignKey, '==', primaryKeyValue)
95-
.get()
96-
.then(querySnap => {
97-
querySnap.forEach(doc => {
98-
console.log(
99-
`integrify: Deleting [${target.collection}]${
100-
target.isCollectionGroup ? ' (group)' : ''
101-
}, id [${doc.id}]`
102-
);
103-
promises.push(doc.ref.delete());
104-
});
105-
})
117+
whereable.get().then(querySnap => {
118+
querySnap.forEach(doc => {
119+
console.log(
120+
`integrify: Deleting [${target.collection}]${
121+
target.isCollectionGroup ? ' (group)' : ''
122+
}, id [${doc.id}]`
123+
);
124+
promises.push(doc.ref.delete());
125+
});
126+
})
106127
);
107128
});
108129
return Promise.all(promises);

test/functions/index.js

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,70 @@ module.exports.deleteReferencesWithMissingFields = integrify({
158158
},
159159
});
160160

161+
module.exports.deleteReferencesDeleteAllSubCollections = integrify({
162+
rule: 'DELETE_REFERENCES',
163+
source: {
164+
collection: 'master/{randomId}',
165+
},
166+
targets: [
167+
{
168+
collection: 'somecoll/$testId/detail2',
169+
foreignKey: 'randomId',
170+
deleteAll: true,
171+
},
172+
],
173+
hooks: {
174+
pre: (snap, context) => {
175+
setState({
176+
snap,
177+
context,
178+
});
179+
},
180+
},
181+
});
182+
183+
module.exports.deleteReferencesDeleteAllSubCollectionErrors = integrify({
184+
rule: 'DELETE_REFERENCES',
185+
source: {
186+
collection: 'master/{randomId}',
187+
},
188+
targets: [
189+
{
190+
collection: 'master/details',
191+
foreignKey: 'randomId',
192+
deleteAll: true,
193+
},
194+
],
195+
hooks: {
196+
pre: (snap, context) => {
197+
setState({
198+
snap,
199+
context,
200+
});
201+
},
202+
},
203+
});
204+
205+
module.exports.deleteReferencesMissingArgumentsErrors = integrify({
206+
rule: 'DELETE_REFERENCES',
207+
source: {
208+
collection: 'master/{randomId}',
209+
},
210+
targets: [
211+
{
212+
collection: 'master/details',
213+
},
214+
],
215+
hooks: {
216+
pre: (snap, context) => {
217+
setState({
218+
snap,
219+
context,
220+
});
221+
},
222+
},
223+
});
224+
161225
module.exports.maintainFavoritesCount = integrify({
162226
rule: 'MAINTAIN_COUNT',
163227
source: {

test/functions/integrify.rules.js

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,46 @@ module.exports = [
109109
},
110110
],
111111
},
112+
{
113+
rule: 'DELETE_REFERENCES',
114+
name: 'deleteReferencesDeleteAllSubCollections',
115+
source: {
116+
collection: 'master/{randomId}',
117+
},
118+
targets: [
119+
{
120+
collection: 'somecoll/$testId/detail2',
121+
foreignKey: 'randomId',
122+
deleteAll: true,
123+
},
124+
],
125+
},
126+
{
127+
rule: 'DELETE_REFERENCES',
128+
name: 'deleteReferencesDeleteAllSubCollectionErrors',
129+
source: {
130+
collection: 'master/{randomId}',
131+
},
132+
targets: [
133+
{
134+
collection: 'master/details',
135+
foreignKey: 'randomId',
136+
deleteAll: true,
137+
},
138+
],
139+
},
140+
{
141+
rule: 'DELETE_REFERENCES',
142+
name: 'deleteReferencesMissingArgumentsErrors',
143+
source: {
144+
collection: 'master/{randomId}',
145+
},
146+
targets: [
147+
{
148+
collection: 'master/details',
149+
},
150+
],
151+
},
112152
{
113153
rule: 'MAINTAIN_COUNT',
114154
name: 'maintainFavoritesCount',

0 commit comments

Comments
 (0)