Skip to content

Instantly share code, notes, and snippets.

@ZainWWF
Last active November 24, 2019 13:59
Show Gist options
  • Save ZainWWF/8ac966ab338a93e630c4b27c74c49cb1 to your computer and use it in GitHub Desktop.
Save ZainWWF/8ac966ab338a93e630c4b27c74c49cb1 to your computer and use it in GitHub Desktop.
deployed code
import * as functions from "firebase-functions";
import * as admin from "firebase-admin"
import axios, { AxiosRequestConfig } from "axios"
const conmpanyName = "WWF Singapore"
const departmentName = "Pasti Service Desk"
/** add entry into user"s assistance map when a assistance doc is created*/
export default functions.region("asia-east2").firestore
.document("assistance/{assistanceId}").onCreate(async (snap, context) => {
try {
const userDocSnap = await admin.firestore().collection("users").doc(snap.get("userId")).get()
const userProfile = userDocSnap.get("profile")
const { accessToken, authToken } = await getAccessToken()
const orgId = await getEntityID('organizations', "companyName", conmpanyName, authToken);
const departmentsId = await getEntityID("departments", "name", departmentName, authToken, orgId);
if (!departmentsId) throw new Error("department id not found")
const contactsId =
await getEntityID("contacts", "mobile", userProfile.phoneNumber, authToken, orgId) ||
await createNewEntity("contacts", {
mobile: userProfile.phoneNumber,
lastName: userProfile.name
},
authToken,
orgId)
if (!contactsId) throw new Error("contacts id not found")
//arguments for new ticket
const ticketDetails = {
subject: `Assistance request - ${snap.get("type")}`,
description: snap.get("comment"),
channel: "App",
phone: userProfile.phoneNumber,
}
if (!orgId) throw new Error("org id not found")
await createTicket(orgId, accessToken, contactsId, departmentsId, ticketDetails)
return
} catch (error) {
console.error(error)
return
}
})
async function getAccessToken() {
const tokensZohoDeskDocSnap = await admin.firestore().collection("tokens").doc("zohoDesk").get()
// get access token from store
const existingAccessToken = tokensZohoDeskDocSnap.get("access_token")
const access_token = existingAccessToken ? existingAccessToken : await getNewAccessToken(tokensZohoDeskDocSnap)
// if access token exist, check its expiration
if (accessTokenIsExpired(tokensZohoDeskDocSnap)) {
// access token expired, get new by refresh token
const refreshedAcessToken = await getRefreshedAccessToken(tokensZohoDeskDocSnap)
return { accessToken: refreshedAcessToken, authToken: tokensZohoDeskDocSnap.get("authToken") }
}
return { accessToken: access_token, authToken: tokensZohoDeskDocSnap.get("authToken") }
}
async function getNewAccessToken(docSnap: FirebaseFirestore.DocumentSnapshot) {
try {
console.log("getNewAccessToken")
const existingAuthToken = docSnap.get("authToken")
const authtoken = !existingAuthToken || existingAuthToken === "INVALID_PASSWORD" ? await getNewAuthToken() : existingAuthToken
if (!authtoken) throw new Error("no auth token available")
const config: AxiosRequestConfig = {
url: "https://accounts.zoho.com/oauth/v2/token/self/authtooauth",
method: "POST",
params: {
client_id: functions.config().zohodesk.api.clientid,
client_secret: functions.config().zohodesk.api.clientsecret,
grant_type: "authtooauth",
authtoken,
scope: "Desk.tickets.ALL"
}
}
const result = await axios.request(config)
console.log("result: ", result.data)
//store new generated auth token
await admin.firestore().collection("tokens").doc("zohoDesk")
.update({
createdAt: admin.firestore.Timestamp.now(),
error: null,
...result.data
})
console.log("tokens updated")
return result.data.access_token
} catch (error) {
console.error(error)
return null
}
}
async function getNewAuthToken() {
console.log("getNewAuthToken")
try {
const config: AxiosRequestConfig = {
url: "https://accounts.zoho.com/apiauthtoken/nb/create",
method: "POST",
params: {
SCOPE: "ZohoSupport/supportapi,ZohoSearch/SearchAPI",
EMAIL_ID: functions.config().zohodesk.api.email,
PASSWORD: functions.config().zohodesk.api.emailpassword
}
}
const result = await axios.request(config)
const authTokenRequest = /((AUTHTOKEN=(.*)|CAUSE=(.*)))\nRESULT=(TRUE|FALSE)/.exec(result.data)
if (!authTokenRequest || authTokenRequest[5] === "FALSE") return null
const authToken = authTokenRequest[3]
//store new generated auth token
await admin.firestore().collection("tokens").doc("zohoDesk")
.set({
authToken
})
return authToken
} catch (error) {
console.log(error)
return null
}
}
function accessTokenIsExpired(docSnap: FirebaseFirestore.DocumentSnapshot) {
const createdAt: FirebaseFirestore.Timestamp = docSnap.get("createdAt")
const expiresIn: number = docSnap.get("expires_in")
console.log("accessTokenIsExpired: ", createdAt, expiresIn)
if (!createdAt) return false
// check is access token has expired
return createdAt.toMillis() + expiresIn < admin.firestore.Timestamp.now().toMillis()
}
async function getRefreshedAccessToken(docSnap: FirebaseFirestore.DocumentSnapshot) {
console.log("getRefreshedAccessToken")
try {
const config: AxiosRequestConfig = {
url: "https://accounts.zoho.com/oauth/v2/token",
method: "POST",
params: {
refresh_token: docSnap.get("refresh_token"),
client_id: functions.config().zohodesk.api.clientid,
client_secret: functions.config().zohodesk.api.clientsecret,
grant_type: "refresh_token",
scope: "Desk.tickets.ALL"
}
}
const result = await axios.request(config)
//store new generated auth token
await admin.firestore().collection("tokens").doc("zohoDesk")
.update({
createdAt: admin.firestore.Timestamp.now(),
...result.data
})
console.log("tokens updated with refresh token")
return result.data.access_token
} catch (error) {
console.error(error)
return
}
}
type EntityType = "organizations" | "departments" | "contacts"
type SearchKeyType = "organizationName" | "companyName" | "email" | "mobile" | "name";
async function getEntityID(entityType: EntityType, searchKey: SearchKeyType, searchValue: string, authtoken: string, orgId: string | null = null) {
try {
const params = (entityType === "organizations") ? { authtoken } : { orgId, authtoken }
const url = `https://desk.zoho.com/api/v1/${entityType}`
const config: AxiosRequestConfig = {
method: "GET",
url,
params
}
console.log(`getEntityID : entityType: ${entityType}, searchKey: ${searchKey}, searchValue ${searchValue}`)
const result = await axios.request(config);
console.log("result: ", result.data)
const [foundEntity] = result.data.data.filter(entity => entity[searchKey] && entity[searchKey] === searchValue)
if (!foundEntity) return null
console.log(`${entityType} id: `, foundEntity.id);
return foundEntity.id;
}
catch (error) {
console.error(error)
return null
}
}
//arguments - organization id, access token, contact id, department id, ticket subject, ticket description, ticket channel
async function createTicket(orgId: string, access_token: string, contactId: string, departmentId: string, ticketDetails: any) {
try {
const config: AxiosRequestConfig = {
url: "https://desk.zoho.com/api/v1/tickets",
method: "POST",
headers: {
orgId: orgId,
Authorization: "Zoho-oauthtoken " + access_token,
},
data: {
...ticketDetails,
contactId, //(required param) this contactId can be get through GET request. ID of the department to which the ticket belongs.
departmentId //(required param) this departmentId can be get through GET request. ID of the contact who raised the ticket.
}
}
await axios.request(config)
console.log("Successfully created new ticket");
return
} catch (error) {
console.log(error)
return
}
}
async function createNewEntity(entityType: EntityType, data: any, authtoken: string, orgId: string) {
try {
const params = (entityType === "organizations") ? { authtoken } : { orgId, authtoken }
const url = `https://desk.zoho.com/api/v1/${entityType}`
const config: AxiosRequestConfig = {
method: "POST",
url,
params,
data
}
const result = await axios.request(config)
if (result) return result.data.id;
return null
} catch (error) {
console.log(error)
return null
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment