Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature: Fetch billing historicals and display them on the billing page #731

Merged
merged 4 commits into from
Aug 29, 2023
Merged
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
73 changes: 57 additions & 16 deletions src/api/billing.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import { PostgrestSingleResponse } from '@supabase/postgrest-js';
import { format } from 'date-fns';
import {
PostgrestMaybeSingleResponse,
PostgrestSingleResponse,
} from '@supabase/postgrest-js';
import { format, isBefore, parse, startOfMonth } from 'date-fns';
import {
FUNCTIONS,
invokeSupabase,
RPCS,
supabaseClient,
TABLES,
} from 'services/supabase';

const OPERATIONS = {
Expand Down Expand Up @@ -52,6 +56,10 @@ interface InvoiceLineItem {
}

export interface BillingRecord {
// In `billing_historicals` this is called tenant
// and so needs to be included here in order to
// filter by tenant
tenant?: string;
billed_prefix: string;
billed_month: string; // Timestamp
processed_data_gb: number | null;
Expand All @@ -61,32 +69,65 @@ export interface BillingRecord {
subtotal: number;
}

const billingHistoricalQuery = [
'billed_prefix:tenant',
'billed_month',
'report->processed_data_gb',
'report->recurring_fee',
'report->task_usage_hours',
'report->line_items',
'report->subtotal',
].join(', ');

export const getBillingRecord = (
billed_prefix: string,
month: string | Date
) => {
): PromiseLike<PostgrestMaybeSingleResponse<BillingRecord>> => {
const fmt = "yyyy-MM-dd' 00:00:00+00'";
const formattedMonth: string =
typeof month === 'string'
? month
: format(month, "yyyy-MM-dd' 00:00:00+00'");
typeof month === 'string' ? month : format(month, fmt);

return supabaseClient
.rpc<BillingRecord>(RPCS.BILLING_REPORT, {
billed_prefix,
billed_month: formattedMonth,
})
.throwOnError()
.single();
// If we're asking for a previous month, look up billing_historicals
if (
isBefore(
startOfMonth(parse(formattedMonth, fmt, new Date())),
startOfMonth(new Date())
)
) {
return supabaseClient
.from<BillingRecord>(TABLES.BILLING_HISTORICALS)
.select(billingHistoricalQuery)
.filter('tenant', 'eq', billed_prefix)
.filter('billed_month', 'eq', formattedMonth)
.throwOnError()
.maybeSingle();
} else {
return supabaseClient
.rpc<BillingRecord>(RPCS.BILLING_REPORT, {
billed_prefix,
billed_month: formattedMonth,
})
.throwOnError()
.single();
}
};

function isSingleResponse<T>(
arg: PostgrestMaybeSingleResponse<T>
): arg is PostgrestSingleResponse<T> {
return arg.body !== null;
}

// TODO (billing) need to make this so it does not cause multipl calls
// and instead just builds up a single query that matches all months
export const getBillingHistory = async (
tenant: string,
dateRange: string[]
): Promise<PostgrestSingleResponse<BillingRecord>[]> => {
const promises: PromiseLike<PostgrestSingleResponse<BillingRecord>>[] =
const promises: PromiseLike<PostgrestMaybeSingleResponse<BillingRecord>>[] =
dateRange.map((date) => getBillingRecord(tenant, date));

const res = await Promise.all(promises);
const resolved = await Promise.all(promises);

return res;
return resolved.filter(isSingleResponse);
};
3 changes: 2 additions & 1 deletion src/services/supabase.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { PostgrestError, PostgrestFilterBuilder } from '@supabase/postgrest-js';
import { User, createClient } from '@supabase/supabase-js';
import { createClient, User } from '@supabase/supabase-js';
import { ToPostgrestFilterBuilder } from 'hooks/supabase-swr';
import { forEach, isEmpty } from 'lodash';
import LogRocket from 'logrocket';
Expand Down Expand Up @@ -36,6 +36,7 @@ export const DEFAULT_FILTER = '__unknown__';

export enum TABLES {
APPLIED_DIRECTIVES = 'applied_directives',
BILLING_HISTORICALS = 'billing_historicals',
CATALOG_STATS = 'catalog_stats',
COMBINED_GRANTS_EXT = 'combined_grants_ext',
CONNECTOR_TAGS = 'connector_tags',
Expand Down