Pup

v2.x

ExtrasData Export

Pup includes the ability for users to export their data as a .zip file containing individual .txt files for each of their documents stored in the Documents collection. The export is maintained by an action called exportUserData stored at /api/Users/actions/exportUserData.

Inside of that action, we provide three methods getDocuments which retrieves all of the users documents data, addDocumentsToZip which populates the .zip file with individual .txt files for each of the user's documents and generateZip which finalizes the .zip file and returns it to us as a base64 string that we can send back to the client.

By default, the export functionality is included in the /profile page of the app for all users. To handle the export, Pup relies on the b64-to-blob package to convert the base64 string we received from the server into a file blob which is then passed to the file-saver NPM package's .saveAs() method to perform the download to the user's computer.

/api/Users/actions/exportUserData.js
/* eslint-disable consistent-return */

import JSZip from 'jszip';
import Documents from '../../Documents/Documents';

let action;

const generateZip = (zip) => {
  try {
    zip.generateAsync({ type: 'base64' }).then((content) => action.resolve({ zip: content }));
  } catch (exception) {
    throw new Error(`[exportUserData.generateZip] ${exception.message}`);
  }
};

const addDocumentsToZip = (documents, zip) => {
  try {
    documents.forEach((document) => {
      zip.file(`${document.title}.txt`, `${document.title}\n\n${document.body}`);
    });
  } catch (exception) {
    throw new Error(`[exportUserData.addDocumentsToZip] ${exception.message}`);
  }
};

const getDocuments = ({ _id }) => {
  try {
    return Documents.find({ owner: _id }).fetch();
  } catch (exception) {
    throw new Error(`[exportUserData.getDocuments] ${exception.message}`);
  }
};

const validateOptions = (options) => {
  try {
    if (!options) throw new Error('options object is required.');
    if (!options.user) throw new Error('options.user is required.');
  } catch (exception) {
    throw new Error(`[exportUserData.validateOptions] ${exception.message}`);
  }
};

const exportUserData = (options) => {
  try {
    validateOptions(options);
    const zip = new JSZip();
    const documents = getDocuments(options.user);
    addDocumentsToZip(documents, zip);
    generateZip(zip);
  } catch (exception) {
    action.reject(`[exportUserData] ${exception.message}`);
  }
};

export default (options) =>
  new Promise((resolve, reject) => {
    action = { resolve, reject };
    exportUserData(options);
  });