gristlabs_grist-core/app/common/DocLimits.ts
George Gevoian f48d579f64 (core) Add API endpoint to get site usage summary
Summary:
The summary includes a count of documents that are approaching
limits, in grace period, or delete-only. The endpoint is only accessible
to site owners, and is currently unused. A follow-up diff will add usage
banners to the site home page, which will use the response from the
endpoint to communicate usage information to owners.

Test Plan: Browser and server tests.

Reviewers: alexmojaki

Reviewed By: alexmojaki

Differential Revision: https://phab.getgrist.com/D3420
2022-05-16 11:16:19 -07:00

75 lines
2.4 KiB
TypeScript

import {ApiError} from 'app/common/ApiError';
import {APPROACHING_LIMIT_RATIO, DataLimitStatus, DocumentUsage, getUsageRatio} from 'app/common/DocUsage';
import {Features} from 'app/common/Features';
import * as moment from 'moment-timezone';
/**
* Error class indicating failure due to limits being exceeded.
*/
export class LimitExceededError extends ApiError {
constructor(message: string) {
super(message, 413);
}
}
export interface GetDataLimitStatusParams {
docUsage: DocumentUsage | null;
productFeatures: Features | undefined;
gracePeriodStart: Date | null;
}
/**
* Given a set of params that includes document usage, current product features, and
* a grace-period start (if any), returns the data limit status of a document.
*/
export function getDataLimitStatus(params: GetDataLimitStatusParams): DataLimitStatus {
const {docUsage, productFeatures, gracePeriodStart} = params;
const ratio = getDataLimitRatio(docUsage, productFeatures);
if (ratio > 1) {
const start = gracePeriodStart;
const days = productFeatures?.gracePeriodDays;
if (start && days && moment().diff(moment(start), 'days') >= days) {
return 'deleteOnly';
} else {
return 'gracePeriod';
}
} else if (ratio > APPROACHING_LIMIT_RATIO) {
return 'approachingLimit';
} else {
return null;
}
}
/**
* Given `docUsage` and `productFeatures`, returns the highest usage ratio
* across all data-related limits (currently only row count and data size).
*/
export function getDataLimitRatio(
docUsage: DocumentUsage | null,
productFeatures: Features | undefined
): number {
if (!docUsage) { return 0; }
const {rowCount, dataSizeBytes} = docUsage;
const maxRows = productFeatures?.baseMaxRowsPerDocument;
const maxDataSize = productFeatures?.baseMaxDataSizePerDocument;
const rowRatio = getUsageRatio(rowCount, maxRows);
const dataSizeRatio = getUsageRatio(dataSizeBytes, maxDataSize);
return Math.max(rowRatio, dataSizeRatio);
}
/**
* Maps `dataLimitStatus` status to an integer and returns it; larger integer
* values indicate a more "severe" status.
*
* Useful for relatively comparing the severity of two statuses.
*/
export function getSeverity(dataLimitStatus: DataLimitStatus): number {
switch (dataLimitStatus) {
case null: { return 0; }
case 'approachingLimit': { return 1; }
case 'gracePeriod': { return 2; }
case 'deleteOnly': { return 3; }
}
}