Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,8 @@ import {
UserMetadata,
} from "./user"
import { validateOrgApiKey, validatePersonalApiKey } from "./validators"
import { AttritionReportInterval, ChampionReportInterval, ChurnReportInterval, GrowthReportInterval, OrgReport, OrgReportType, ReengagementReportInterval, ReportPagination, TopInviterReportInterval, UserReport, UserReportType } from "./reports"
import { fetchOrgReport, fetchUserReport } from "./api/reports"

export function getApis(authUrl: URL, integrationApiKey: string) {
function fetchTokenVerificationMetadataWrapper(): Promise<TokenVerificationMetadata> {
Expand Down Expand Up @@ -462,6 +464,62 @@ export function getApis(authUrl: URL, integrationApiKey: string) {
return verifySmsChallenge(authUrl, integrationApiKey, verifySmsChallengeRequest)
}

function fetchUserTopInviterReportWrapper(
reportInterval?: TopInviterReportInterval,
pagination?: ReportPagination,
): Promise<UserReport> {
return fetchUserReport(authUrl, integrationApiKey, UserReportType.TOP_INVITERS, reportInterval, pagination)
}

function fetchUserChampionReportWrapper(
reportInterval?: ChampionReportInterval,
pagination?: ReportPagination,
): Promise<UserReport> {
return fetchUserReport(authUrl, integrationApiKey, UserReportType.CHAMPION, reportInterval, pagination)
}

function fetchUserReengagementReportWrapper(
reportInterval?: ReengagementReportInterval,
pagination?: ReportPagination,
): Promise<UserReport> {
return fetchUserReport(authUrl, integrationApiKey, UserReportType.REENGAGEMENT, reportInterval, pagination)
}

function fetchUserChurnReportWrapper(
reportInterval?: ChurnReportInterval,
pagination?: ReportPagination,
): Promise<UserReport> {
return fetchUserReport(authUrl, integrationApiKey, UserReportType.CHURN, reportInterval, pagination)
}

function fetchOrgReengagementReportWrapper(
reportInterval?: ReengagementReportInterval,
pagination?: ReportPagination,
): Promise<OrgReport> {
return fetchOrgReport(authUrl, integrationApiKey, OrgReportType.REENGAGEMENT, reportInterval, pagination)
}

function fetchOrgChurnReportWrapper(
reportInterval?: ChurnReportInterval,
pagination?: ReportPagination,
): Promise<OrgReport> {
return fetchOrgReport(authUrl, integrationApiKey, OrgReportType.CHURN, reportInterval, pagination)
}

function fetchOrgGrowthReportWrapper(
reportInterval?: GrowthReportInterval,
pagination?: ReportPagination,
): Promise<OrgReport> {
return fetchOrgReport(authUrl, integrationApiKey, OrgReportType.GROWTH, reportInterval, pagination)
}

function fetchOrgAttritionReportWrapper(
reportInterval?: AttritionReportInterval,
pagination?: ReportPagination,
): Promise<OrgReport> {
return fetchOrgReport(authUrl, integrationApiKey, OrgReportType.ATTRITION, reportInterval, pagination)
}

return {
// fetching functions
fetchTokenVerificationMetadata: fetchTokenVerificationMetadataWrapper,
Expand Down Expand Up @@ -536,5 +594,14 @@ export function getApis(authUrl: URL, integrationApiKey: string) {
verifySmsChallenge: verifySmsChallengeWrapper,
// employee functions
fetchEmployeeById: fetchEmployeeByIdWrapper,
// report data fetching functions
fetchUserTopInviterReport: fetchUserTopInviterReportWrapper,
fetchUserChampionReport: fetchUserChampionReportWrapper,
fetchUserReengagementReport: fetchUserReengagementReportWrapper,
fetchUserChurnReport: fetchUserChurnReportWrapper,
fetchOrgReengagementReport: fetchOrgReengagementReportWrapper,
fetchOrgChurnReport: fetchOrgChurnReportWrapper,
fetchOrgGrowthReport: fetchOrgGrowthReportWrapper,
fetchOrgAttritionReport: fetchOrgAttritionReportWrapper,
}
}
75 changes: 75 additions & 0 deletions src/api/reports.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import {
ApiKeyCreateException,
ApiKeyDeleteException,
ApiKeyFetchException,
ApiKeyUpdateException,
ApiKeyValidateException,
ApiKeyValidateRateLimitedException,
RateLimitedException,
ApiKeyImportException
} from "../exceptions"
import { httpRequest } from "../http"
import { OrgReport, OrgReportType, ReportPagination, UserReport, UserReportType } from "../reports"
import { ApiKeyFull, ApiKeyNew, ApiKeyResultPage, ApiKeyValidation } from "../user"
import { formatQueryParameters, isValidHex, parseSnakeCaseToCamelCase, removeBearerIfExists } from "../utils"

const USER_REPORTS_PATH = "/api/backend/v1/user_report"
const ORG_REPORTS_PATH = "/api/backend/v1/org_report"

// GET
export function fetchUserReport(
authUrl: URL,
integrationApiKey: string,
reportType: UserReportType,
reportInterval?: string,
pagination?: ReportPagination,
): Promise<UserReport> {
const request = {
report_interval: reportInterval,
page_size: pagination?.pageSize,
page_number: pagination?.pageNumber,
}
const queryString = formatQueryParameters(request)

return httpRequest(authUrl, integrationApiKey, `${USER_REPORTS_PATH}/${reportType}?${queryString}`, "GET").then((httpResponse) => {
if (httpResponse.statusCode === 401) {
throw new Error("integrationApiKey is incorrect")
} else if (httpResponse.statusCode === 429) {
throw new RateLimitedException(httpResponse.response)
} else if (httpResponse.statusCode === 400) {
throw new ApiKeyFetchException(httpResponse.response)
} else if (httpResponse.statusCode && httpResponse.statusCode >= 400) {
throw new Error("Unknown error when creating the end user api key")
}

return parseSnakeCaseToCamelCase(httpResponse.response)
})
}
export function fetchOrgReport(
authUrl: URL,
integrationApiKey: string,
reportType: OrgReportType,
reportInterval?: string,
pagination?: ReportPagination,
): Promise<OrgReport> {
const request = {
report_interval: reportInterval,
page_size: pagination?.pageSize,
page_number: pagination?.pageNumber,
}
const queryString = formatQueryParameters(request)

return httpRequest(authUrl, integrationApiKey, `${ORG_REPORTS_PATH}/${reportType}?${queryString}`, "GET").then((httpResponse) => {
if (httpResponse.statusCode === 401) {
throw new Error("integrationApiKey is incorrect")
} else if (httpResponse.statusCode === 429) {
throw new RateLimitedException(httpResponse.response)
} else if (httpResponse.statusCode === 400) {
throw new ApiKeyFetchException(httpResponse.response)
} else if (httpResponse.statusCode && httpResponse.statusCode >= 400) {
throw new Error("Unknown error when creating the end user api key")
}

return parseSnakeCaseToCamelCase(httpResponse.response)
})
}
14 changes: 14 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,20 @@ export {
} from "./exceptions"
export type { SocialLoginProvider, SamlLoginProvider, LoginMethod } from "./loginMethod"
export type { CustomRoleMappings, CustomRoleMapping } from "./customRoleMappings"
export type {
ReengagementReportInterval,
ChampionReportInterval,
ChurnReportInterval,
GrowthReportInterval,
AttritionReportInterval,
TopInviterReportInterval,
ReportPagination,
UserReport,
UserReportRecord,
OrgReport,
OrgReportRecord,
UserOrgMembershipForReport,
} from './reports'
export type {
UserProperties,
User,
Expand Down
102 changes: 102 additions & 0 deletions src/reports.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
export type ReportPagination = {
pageSize?: number,
pageNumber?: number,
}

// org report types

export type OrgReportRecord = {
id: string,
reportId: string,
orgId: string,
name: string,
numUsers: number,
orgCreatedAt: number,
extraProperties: { [key: string]: any },
}

export type OrgReport = {
orgReports: OrgReportRecord[],
currentPage: number,
totalCount: number,
pageSize: number,
hasMoreResults: boolean,
reportTime: number,
}

export enum OrgReportType {
ATTRITION = "attrition",
REENGAGEMENT = "reengagement",
GROWTH = "growth",
CHURN = "churn",
}

// user report types

export type UserOrgMembershipForReport = {
displayName: string,
orgId: string,
userRole: string,
}

export type UserReportRecord = {
id: string,
reportId: string,
userId: string,
email: string,
userCreatedAt: number,
lastActiveAt: number,
username?: string,
firstName?: string,
lastName?: string,
orgData?: UserOrgMembershipForReport[],
extraProperties: { [key: string]: any },
}

export type UserReport = {
userReports: UserReportRecord[],
currentPage: number,
totalCount: number,
pageSize: number,
hasMoreResults: boolean,
reportTime: number,
}

export enum UserReportType {
REENGAGEMENT = "reengagement",
CHURN = "churn",
TOP_INVITERS = "top_inviter",
CHAMPION = "champion",
}

// report interval options

export enum ReengagementReportInterval {
WEEKLY = "Weekly",
MONTHLY = "Monthly",
}
export enum ChurnReportInterval {
SEVEN_DAYS = "7",
FOURTEEN_DAYS = "14",
THIRTY_DAYS = "30",
}
export enum GrowthReportInterval {
THIRTY_DAYS = "30",
SIXTY_DAYS = "60",
NINETY_DAYS = "90",
}
export enum TopInviterReportInterval {
THIRTY_DAYS = "30",
SIXTY_DAYS = "60",
NINETY_DAYS = "90",
}
export enum ChampionReportInterval {
THIRTY_DAYS = "30",
SIXTY_DAYS = "60",
NINETY_DAYS = "90",
}
export enum AttritionReportInterval {
THIRTY_DAYS = "30",
SIXTY_DAYS = "60",
NINETY_DAYS = "90",
}