/**
* @module method/adminCreate
*/
const admin = require('firebase-admin');
const functions = require('firebase-functions');
const axios = require('axios');
const {assign, has, pick} = require('lodash');
const {format} = require('date-fns');
const db = admin.firestore();
const {loandisk} = functions.config();
const config = {
headers: {
'Content-Type': 'application/json',
'Authorization': `Basic ${loandisk.authcode}`,
},
};
const errorMessages = require('../error/error.json');
/**
* Create a new user on Firebase with borrower creation on Loandisk
*
* @param {object} user - User object
* @param {string} user.uid - Firebase User ID, e.g. hk_wx5555556.
* @param {number} user.branchID - Branch ID of target branch where the user belongs to.
* @param {string} user.country - Country of employment, e.g. HK.
* @param {string} user.idNumber - ID number at country of employment, e.g. WX555555(6).
* @param {string} user.passwordHash - Hashed password.
*
* @return {userMeta} User’s user meta.
*/
const create = {
run: (user) => {
const requiredParams = [
'uid',
'branchID',
'country',
'idNumber',
'passwordHash',
];
// Throw error if there are missing parameters
requiredParams.forEach((param) => {
if (!has(user, param)) {
// Log the error
console.error('[Failure] Error during user creation for UID ' +
user.uid +
': The function has been called with missing parameter (' +
param + ')'
);
throw new functions.https.HttpsError(
'invalid-argument',
'The function has been called with missing parameter: ' + param,
);
}
})
// Create new auth user on Firebase
return create.newFirebaseUser(user).then((userRecord) =>
// Create a new borrower on Loandisk
create.newLoandiskBorrower(user)
).then((borrowerID) => {
const userMeta = pick(user, [
'branchID',
'country',
'idNumber',
'passwordHash',
]);
userMeta['borrowerID'] = Number(borrowerID);
const userMetaDoc = db.collection('appUserMeta').doc(user.uid);
// Create userMeta on Firebase
return db.runTransaction((transaction) => {
return transaction.get(userMetaDoc).then((doc) => {
if (!doc.exists) {
return transaction.set(userMetaDoc, userMeta);
}
})
});
}).then((result) => {
// Log the success
console.info('[Success] User created for: ' + user.uid);
return true;
}).catch((error) => {
// Assign error response to response
let response = {
"code": "unknown",
"message": "Sorry, there are some issues on our side",
"details": "Please try again in a few minutes. If it’s still not working, call us at +852 9847 4943."
};
// Check if an HttpError has been thrown
if (has(error, 'code')) {
// Assign response
response = error;
}
// Check if an cloud function error has been thrown
if (has(error, 'errorInfo.code')) {
if (has(errorMessages, error.errorInfo.code)) {
// Continue if error code is not auth/uid-already-exists
if (error.errorInfo.code !== 'auth/uid-already-exists') {
// Delete the created auth user
admin.auth().deleteUser(user.uid);
}
// Assign response to error
response = errorMessages[error.errorInfo.code];
}
}
// Throw final error message
throw new functions.https.HttpsError(
response.code,
response.message,
response.details
);
});
},
// Create new Firebase user
newFirebaseUser: ({uid}) => {
// Set up retry function
const retry = async (fn, retriesLeft = 5, interval = 1000, exponential = false) => {
try {
const val = await fn();
return val;
} catch (error) {
if (retriesLeft) {
// Hold the function from rerunning for an interval
await new Promise((resolve) => setTimeout(resolve, interval));
// Rerun the function
return retry(fn, retriesLeft - 1, exponential ? interval * 2 : interval, exponential);
} else {
throw error;
}
}
}
return retry(
() => admin.auth().createUser({
uid: uid
}), 5, 200
).then((userRecord) => {
// console.info('[Success] Created new Firebase user: ', userRecord.uid);
return userRecord;
}).catch((error) => {
console.error('[Failure] The provided UID already exist on Firebase: ' + uid);
throw error;
});
},
// Create new Loandisk borrower
newLoandiskBorrower: ({country, idNumber, uid, branchID}) => {
const borrowerData = {
'borrower_country': country,
'borrower_dob': null,
'borrower_firstname': 'New',
'borrower_gender': 'Female',
'borrower_lastname': 'User',
'borrower_unique_number': idNumber,
'custom_field_3012': uid,
};
return axios.post(
`${loandisk.url}/${loandisk.publickey}/${branchID}/borrower`,
borrowerData,
config
).then((response) => {
const {data} = response;
// [Loandisk bypass] Throw error if error key exists
if (data.error) {
throw new functions.https.HttpsError(
'unknown',
"An issue occured while communicating with Loandisk.",
data.error.message
);
}
if (data.response.Errors) {
throw new functions.https.HttpsError(
"unknown",
"An issue occured while communicating with Loandisk.",
data.response.Errors,
);
}
// return borrower ID from Loandisk
// console.info('[Success] Created new borrower with borrower ID: ', data.response.borrower_id);
return data.response.borrower_id;
}).catch((error) => {
console.error('[Failure] An issue occured while communicating with Loandisk:', error.details);
throw error;
})
}
};
module.exports = create.run;