/**
* @module method/create
*/
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const Timestamp = admin.firestore.Timestamp;
const {has} = require('lodash');
const generate = require('nanoid/generate');
const db = admin.firestore();
/**
* Create a new one-time invite code
*
* @param {string} uid - Referrer’s Firebase User ID, e.g. hk_wx5555556.
*
* @return {object} Data object of Generated one-time invite code.
*/
const create = (uid) => {
/**
* Assign set of character used for invite code
* @see https://www.notion.so/goodfinancial/App-Invite-Code-23cfec110c434c6f93421dbab36a5eb8
*/
const charSet = 'EFGHJKLMNQRSUVWXY';
// Trial count of invite code registration
let trialCount = 5;
// Register invite code to Cloud Firestore
const codeRegistration = db.runTransaction((transaction) => {
// Generate invite code
const inviteCode = generate(charSet, 6);
// Assign references to Cloud Firestore
const userMetaRef = db.collection('appUserMeta').doc(uid);
const inviteCodeRef = db.collection('appGlobalSettings')
.doc('inviteCode')
.collection('codes')
.doc(inviteCode);
return transaction.getAll(userMetaRef, inviteCodeRef).then((docs) => {
// Reassign docs
const [userMetaDoc, inviteCodeDoc] = docs;
// Check if generated invite code exists
if (inviteCodeDoc.exists) {
return Promise.reject('Generated invite code already exists');
}
// Check if the userMeta exists
if (userMetaDoc.exists) {
// Get document data
const userMeta = userMetaDoc.data();
// Assign new count of invite code generated
const inviteCodeGeneratedVal = has(userMeta, 'inviteCodeGenerated')
? userMeta['inviteCodeGenerated'] + 1
: 1
// Update code generation count
transaction.update(userMetaRef, {
inviteCodeGenerated: inviteCodeGeneratedVal,
});
// Expiration time
const expiredInHours = 168;
const expiredInMill = expiredInHours * 3600 * 1000;
const expirationTimestamp = Date.now() + expiredInMill;
// Save invite code to Cloud Firestore
transaction.set(inviteCodeRef, {
description: uid,
expiration: Timestamp.fromMillis(expirationTimestamp),
quota: 1,
});
return Promise.resolve({
inviteCode: inviteCode,
description: uid,
expiration: expirationTimestamp,
quota: 1,
});
} else {
return Promise.reject('Cannot find specified user in user meta');
}
})
});
// Run invite code registration
const runCodeRegistration = () => {
return new Promise((resolve, reject) => {
// If trial count is more than 0
if (trialCount > 0) {
// Consume one trial count
trialCount--;
// Run codeRegistration
return codeRegistration.then((registeredCode) => {
return resolve(registeredCode);
}).catch((error) => {
// If trial count is more than 0
if (trialCount > 0) {
// Log error
console.error('Error while generating invite code: '
+ error
+ '. Retrying registration…'
);
// Rerun runCodeRegistration
return runCodeRegistration().then(resolve).catch(reject);
} else {
// Log error
console.error('Error while generating invite code: ' + error);
// Abort function
return reject('Cancelled invite code generation: Reached retry limit');
}
});
}
});
};
// Initial run of runCodeRegistration
return runCodeRegistration().then((inviteCode) => {
return inviteCode
}).catch((error) => {
throw new functions.https.HttpsError('cancelled', error);
});
};
module.exports = create;