Skip to content

Commit 619f448

Browse files
authored
Merge pull request #42 from invertase/add-export-docs
chore: add docs for export user data
2 parents 62dc91b + 71728a2 commit 619f448

File tree

5 files changed

+238
-3
lines changed

5 files changed

+238
-3
lines changed

.github/workflows/validate.yaml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,3 @@ jobs:
1919
run: npm run check:formatting
2020
- name: Check TS Compiles
2121
run: npm run check:typescript
22-
- name: Check Spelling & Grammar
23-
run: npm run check:spelling

docs.json

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,15 @@
1919
"Record User Acknowledgements",
2020
[
2121
["Overview", "/record-user-acknowledgements"],
22-
["Refernce API", "/record-user-acknowledgements/reference"]
22+
["Reference API", "/record-user-acknowledgements/reference"]
23+
]
24+
],
25+
[
26+
"Export User Data",
27+
[
28+
["Overview", "/export-user-data"],
29+
["Usage", "/export-user-data/usage"],
30+
["Reference API", "/export-user-data/reference"]
2331
]
2432
]
2533
]

docs/export-user-data/index.mdx

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Export User Data
2+
3+
Use this extension to export certain data to Cloud Storage from Cloud Firestore, Realtime Database or Cloud Storage.
4+
5+
This extension exports certain data keyed to a given user ID from Firebase Authentication. Data is exported as individual files (one file per Firestore collection or doc depending on which the path pointed to, one per node in RTDB, and one per file in Cloud Storage) to a Cloud Storage directory. Alternatively, the extension can be configured to combine all exported files into a single zip archive. This extension will only export data that it is explicitly configured to delete.
6+
7+
Note: To use this extension, you need to manage your users with Firebase Authentication.
8+
9+
**This extension may be useful in helping you respect user privacy and fulfill compliance requirements you may be subject to. However, you are responsible for assessing and determining your compliance needs and obligations, and using this extension does not guarantee compliance with government and industry regulations.**
10+
11+
## Additional setup
12+
13+
Make sure you’ve set up Cloud Storage in your Firebase project, as this is where data will be exported to. Depending on where you'd like to export data from, make sure that you've set up [Cloud Firestore](https://firebase.google.com/docs/firestore) and/or [Realtime Database](https://firebase.google.com/docs/database) in your Firebase project before installing this extension.
14+
15+
Also, make sure that you've set up Firebase Authentication to manage your users.
16+
17+
## Billing
18+
19+
To install an extension, your project must be on the Blaze (pay as you go) plan
20+
21+
- You will be charged a small amount (typically around $0.01/month) for the Firebase resources required by this extension (even if it is not used).
22+
- This extension uses other Firebase and Google Cloud Platform services, which have associated charges if you exceed the service’s no-cost tier:
23+
- Cloud Firestore
24+
- Firebase Realtime Database
25+
- Cloud Storage
26+
- Cloud Functions (Node.js 10+ runtime. [See FAQs](https://firebase.google.com/support/faq#extensions-pricing))

docs/export-user-data/reference.mdx

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
Below we give an overview of the specifications for the Export User Data extension, including TypeScript definitions & detailed descriptions.
2+
3+
### ExportUserDataResponse Interface
4+
5+
The response returned from a call to the `exportUserData` callable.
6+
7+
```typescript
8+
// A string representing the unique Export ID
9+
type ExportUserDataResponse = { exportId: string };
10+
```
11+
12+
### ExportDocument Interface
13+
14+
The specification for a single document within the configured collection.
15+
16+
```typescript
17+
type ExportDocument = {
18+
// A user’s UID which triggered the export.
19+
uid: string;
20+
// The timestamp of when the export was triggered.
21+
startedAt: Timestamp;
22+
// The export status.
23+
status:pending|complete’;
24+
// When status is ‘complete’, the storage path to where the file have been exported.
25+
storagePath?: string;
26+
// When status is ‘complete’ and zip archiving is enabled, the path to the archived zip file containing all exported files.
27+
zipPath?: string;
28+
// When status is ‘complete’, the total exported file count.
29+
exportedFileCount?: number;
30+
}
31+
```

docs/export-user-data/usage.mdx

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
# Usage
2+
3+
## Using the extension
4+
5+
The extension uses multiple mechanisms to find data to be exported.
6+
7+
## By path
8+
9+
When configuring the Cloud Firestore, Realtime Database & Cloud Storage paths in the configuration, it’s possible to define a `UID` variable in the paths which will be replaced with the authenticated users UID. When the `exportUserData` callable function is called from an authenticated client, the extension will start to export data from the given paths, for example:
10+
11+
- Cloud Firestore path(s): `users/{UID},movies`
12+
- Realtime Database path(s): `likes/{UID}`
13+
- Cloud Storage path(s): `{DEFAULT}/uploads/{UID},{DEFAULT}/avatars/{UID}.jpeg`
14+
15+
Be aware of the following behavioral differences between each service:
16+
17+
- Firestore: if a document path is specified, a single CSV file for that document will be created. If a collection path is specified, a single CSV file containing all document data for that collection will be created.
18+
- Realtime Database: all data at the specified node will be exported to a single CSV per specified path.
19+
- Storage: if a file path is specified, the single file will be exported (copied). If a directory path is specified, all files within that directory will be exported.
20+
21+
## Custom function hook
22+
23+
For cases where data discovery requires complex queries to identify, it is possible to define a custom function hook URL. The function that accepts a UID should return an object containing Firestore, Realtime Database and/or Storage paths to export. For example:
24+
25+
```js
26+
export const getCustomExportPaths = functions.https.onRequest(
27+
async (req, res) => {
28+
const uid = req.body.uid;
29+
30+
// Perform custom query
31+
32+
return {
33+
firestorePaths: [‘/document/path’],
34+
databasePaths: [‘path/to/node’],
35+
storagePaths: [‘/storage/path’],
36+
};
37+
});
38+
```
39+
40+
## Archiving exported data
41+
42+
By default the extension will generate a list of CSV files. If you wish to additionally export a single zip archive containing all files, set the `Zip’ configuration option to true.
43+
44+
**Note: Archiving is resource intensive and you may run into resource limitations with large volumes of exported data.**
45+
46+
To manually export the data into an archive, the following code snippet can be used on a NodeJS environment using the archiver package:
47+
48+
```js
49+
const admin = require('firebase-admin');
50+
const archiver = require('archiver');
51+
const fs = require('fs');
52+
53+
function zipDirectory(storagePath, bucketName, outPath) {
54+
const archive = archiver('zip', { zlib: { level: 9 } });
55+
const stream = fs.createWriteStream(outPath);
56+
archive.pipe(stream);
57+
58+
return new Promise((resolve, reject) => {
59+
admin
60+
.storage(bucketName)
61+
.getFiles({
62+
prefix: storagePath,
63+
})
64+
.then(files => {
65+
files.forEach(file => {
66+
archive.append(file.createReadStream(), { name: file.name });
67+
});
68+
archive.finalize();
69+
});
70+
71+
stream.on('close', () => resolve(false));
72+
});
73+
}
74+
```
75+
76+
## Firestore Security rules
77+
78+
To enable your clients to read the export document, add the following security rules to match the user’s UID with the document uid:
79+
80+
```
81+
rules_version = '2';
82+
service cloud.firestore {
83+
match /databases/{database}/documents {
84+
match /exports/{exportId} {
85+
allow read: if request.auth != null && request.auth.uid == resource.data.uid;
86+
}
87+
}
88+
}
89+
```
90+
91+
If you initialized Firestore in production mode, your rules will be configured by default such that no clients can read the export document. If you created Firestore in test mode, your rules will be configured by default such that all clients can read the export document.
92+
93+
## Storage Security rules
94+
95+
By default, security rules will prevent any client reads to exported files. To allow such reads, security rules can be set up so that access is only granted when the user’s UID matches the UID field of the export record document, for example:
96+
97+
```
98+
rules_version = '2';
99+
service firebase.storage {
100+
match /b/{bucket}/o {
101+
match /${param:FIRESTORE_EXPORTS_COLLECTION}/{exportId}/{allPaths=**} {
102+
allow read: if firestore.exists(/databases/(default)/documents/${param:FIRESTORE_EXPORTS_COLLECTION}/$(exportId))
103+
&& (request.auth != null && firestore.get(/databases/(default)/documents/${param:FIRESTORE_EXPORTS_COLLECTION}/$(exportId)).data.uid == request.auth.uid)
104+
}
105+
}
106+
```
107+
108+
## Client SDK Usage
109+
110+
The extension requires an authenticated user to call the `exportUserData` callable function. The function returns a unique export ID which can be used to subscribe to a Firestore document within the `${param:FIRESTORE_EXPORTS_COLLECTION}` collection. The document contains the real time status of an export, containing a `status` property. See the reference API for full details.
111+
112+
Note: The following code requires that security rules have been added to the project.
113+
114+
The following code snippet could be used to trigger an export and list out all files that have been exported once complete:
115+
116+
```js
117+
import { getFirestore(), doc, onSnapshot } from 'firebase/firestore';
118+
import { getStorage, listAll, ref } from 'firebase/storage';
119+
120+
121+
// Trigger the export.
122+
const result = await httpsCallable(functions, `ext-${param:EXT_INSTANCE_ID}-exportUserData`)();
123+
124+
// Get the returned export id.
125+
const exportId = result.data.exportId;
126+
127+
// Make a Firestore reference to the export.
128+
const documentRef = doc(getFirestore(), '${param:FIRESTORE_EXPORTS_COLLECTION}', exportId);
129+
130+
// Listen for changes to the export - when complete returned the storage path of the export items.
131+
const { storagePath, zipPath } = await new Promise<string>((resolve, reject) => {
132+
const unsubscribe = onSnapshot(documentRef, snapshot => {
133+
if (!snapshot.exists) {
134+
unsubscribe();
135+
return reject(new Error("Export document not found"));
136+
}
137+
138+
const data = snapshot.data()!;
139+
140+
if (data.status === 'complete') {
141+
unsubscribe();
142+
return resolve({
143+
storagePath: data.storagePath,
144+
zipPath: data.zipPath,
145+
});
146+
}
147+
});
148+
});
149+
150+
// Get a list of all the files in the export.
151+
const listResult = await listAll(ref(getStorage(), storagePath));
152+
153+
// Log out all exported file paths
154+
listResults.forEach((result) => {
155+
console.log(result.fullPath);
156+
});
157+
```
158+
159+
To generate download links for the client, use the `getDownloadURL` function using a export files path:
160+
161+
```js
162+
import { listAll, ref, getDownloadURL } from 'firebase/storage';
163+
164+
const url = await getDownloadURL(ref(getStorage(), fullPath));
165+
```
166+
167+
## Sending an export email
168+
169+
To send an email to your users once an export has completed with the export, you can deploy a [Custom Event Trigger](https://firebase.google.com/docs/functions/beta/custom-events). Listen to the completed export event, and send an email with the export as an attachment, for example using [nodemailer](https://nodemailer.com/).
170+
171+
Monitoring
172+
As a best practice, you can [monitor the activity](https://firebase.google.com/docs/extensions/manage-installed-extensions#monitor) of your installed extension, including checks on its health, usage, and logs.

0 commit comments

Comments
 (0)