(core) add free team site product

Summary:
This adds a Feature object that is an approximation of what we
plan for free team sites. It includes restrictions that are not
yet implemented, and an endpoint for testing.

Test Plan: added a test

Reviewers: georgegevoian

Reviewed By: georgegevoian

Differential Revision: https://phab.getgrist.com/D3243
This commit is contained in:
Paul Fitzpatrick 2022-02-02 14:30:50 -05:00
parent 64abfcb0ac
commit 4890a1fe89
4 changed files with 43 additions and 3 deletions

View File

@ -34,6 +34,18 @@ export interface Features {
// (default: unlimited)
readOnlyDocs?: boolean; // if set, docs can only be read, not written.
snapshotWindow?: { // if set, controls how far back snapshots are kept.
count: number; // TODO: not honored at time of writing.
unit: 'month'|'year';
};
baseMaxRowsPerDocument?: number; // If set, establishes a default maximum on the
// number of rows (total) in a single document.
// Actual max for a document may be higher.
// TODO: not honored at time of writing.
// TODO: nuances about how rows are counted.
baseMaxApiUnitsPerDocumentPerDay?: number; // Similar for api calls.
}
// Check whether it is possible to add members at the org level. There's no flag

View File

@ -72,11 +72,15 @@ export function addOrg(
dbManager: HomeDBManager,
userId: number,
props: Partial<OrganizationProperties>,
options?: {
planType?: 'free'
}
): Promise<number> {
return dbManager.connection.transaction(async manager => {
const user = await manager.findOne(User, userId);
if (!user) { return handleDeletedUser(); }
const query = await dbManager.addOrg(user, props, {
...options,
setUserAsOwner: false,
useNewPlan: true
}, manager);

View File

@ -24,6 +24,21 @@ export const teamFeatures: Features = {
maxSharesPerDoc: 2
};
/**
* A summary of features available in free team sites.
* At time of writing, this is a placeholder, as free sites are fleshed out.
*/
export const teamFreeFeatures: Features = {
workspaces: true,
vanityDomain: true,
maxSharesPerWorkspace: 0, // all workspace shares need to be org members.
maxSharesPerDoc: 2,
maxDocsPerOrg: 20,
snapshotWindow: { count: 1, unit: 'month' },
baseMaxRowsPerDocument: 5000,
baseMaxApiUnitsPerDocumentPerDay: 5000
};
/**
* A summary of features used in unrestricted grandfathered accounts, and also
* in some test settings.
@ -101,6 +116,10 @@ const PRODUCTS: IProduct[] = [
name: 'suspended',
features: suspendedFeatures,
},
{
name: 'teamFree',
features: teamFreeFeatures,
},
];
/**
@ -112,7 +131,8 @@ export function getDefaultProductNames() {
teamInitial: 'stub', // Team site starts off on a limited plan, requiring subscription.
teamCancel: 'suspended', // Team site that has been 'turned off'.
team: 'team', // Functional team site.
};
teamFree: 'teamFree',
};
}
/**

View File

@ -1077,11 +1077,14 @@ export class HomeDBManager extends EventEmitter {
* @param useNewPlan: by default, the individual billing account associated with the
* user's personal org will be used for all other orgs they create. Set useNewPlan
* to force a distinct non-individual billing account to be used for this org.
* @param planType: if set, controls the type of plan used for the org. Only
* meaningful for team sites currently.
*
*/
public async addOrg(user: User, props: Partial<OrganizationProperties>,
options: { setUserAsOwner: boolean,
useNewPlan: boolean,
planType?: 'free',
externalId?: string,
externalOptions?: ExternalBillingOptions },
transaction?: EntityManager): Promise<QueryResult<number>> {
@ -1110,10 +1113,11 @@ export class HomeDBManager extends EventEmitter {
let billingAccount;
if (options.useNewPlan) {
const productNames = getDefaultProductNames();
let productName = options.setUserAsOwner ? productNames.personal : productNames.teamInitial;
let productName = options.setUserAsOwner ? productNames.personal :
options.planType === 'free' ? productNames.teamFree : productNames.teamInitial;
// A bit fragile: this is called during creation of support@ user, before
// getSupportUserId() is available, but with setUserAsOwner of true.
if (!options.setUserAsOwner && user.id === this.getSupportUserId()) {
if (!options.setUserAsOwner && user.id === this.getSupportUserId() && options.planType !== 'free') {
// For teams created by support@getgrist.com, set the product to something
// good so payment not needed. This is useful for testing.
productName = productNames.team;