Skip to content

Commit

Permalink
fix(billing): require org id for service/asset usage TASK-1476 (#5443)
Browse files Browse the repository at this point in the history
### 📣 Summary
Removes option for API calls to retrieve user-based service/asset usage.

### 💭 Notes
We previously allowed for the possibility that users would not be
organization members. If a user wasn't an org member, we used a separate
API endpoint to retrieve their usage data. Now, however, from a frontend
perspective users are always organization members and we should always
use the organization usage endpoints.


### 👀 Preview steps
1. As a member of an MMO with at least one admin member, create and
deploy a project and make a submission.
2. Access the organization usage page as an admin and view the
per-project usage table.
3. On main, this table will be empty. On this PR branch, it will include
the project.
4. Access the organization usage page as the owner and view the
per-project usage table.
5. On main, the project will be shown as a draft. On this branch it will
show up as deployed.
  • Loading branch information
jamesrkiger authored Jan 24, 2025
1 parent b237f87 commit 0fc340b
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 45 deletions.
59 changes: 23 additions & 36 deletions jsapp/js/account/usage/usage.api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,46 +44,30 @@ export interface UsageResponse {
};
}

const USAGE_URL = '/api/v2/service_usage/';
const ORGANIZATION_USAGE_URL = '/api/v2/organizations/:organization_id/service_usage/';
const ORG_SERVICE_USAGE_URL =
'/api/v2/organizations/:organization_id/service_usage/';
const ORG_ASSET_USAGE_URL =
'/api/v2/organizations/:organization_id/asset_usage/';

const ASSET_USAGE_URL = '/api/v2/asset_usage/';
const ORGANIZATION_ASSET_USAGE_URL = '/api/v2/organizations/:organization_id/asset_usage/';

export async function getUsage(organization_id: string | null = null) {
if (organization_id) {
return fetchGet<UsageResponse>(
ORGANIZATION_USAGE_URL.replace(':organization_id', organization_id),
{
includeHeaders: true,
errorMessageDisplay: t('There was an error fetching usage data.'),
}
);
}
return fetchGet<UsageResponse>(USAGE_URL, {
includeHeaders: true,
errorMessageDisplay: t('There was an error fetching usage data.'),
});
}

export async function getAssetUsage(url = ASSET_USAGE_URL) {
return fetchGet<AssetUsage>(url, {
includeHeaders: true,
errorMessageDisplay: t('There was an error fetching asset usage data.'),
});
export async function getOrgServiceUsage(organization_id: string) {
return fetchGet<UsageResponse>(
ORG_SERVICE_USAGE_URL.replace(':organization_id', organization_id),
{
includeHeaders: true,
errorMessageDisplay: t('There was an error fetching usage data.'),
}
);
}

export async function getAssetUsageForOrganization(
export async function getOrgAssetUsage(
pageNumber: number | string,
order?: ProjectsTableOrder,
organizationId = ''
organizationId: string,
order?: ProjectsTableOrder
) {
// if the user isn't in an organization, just get their personal asset usage
if (!organizationId) {
return await getAssetUsage(ASSET_USAGE_URL);
}

const apiUrl = ORGANIZATION_ASSET_USAGE_URL.replace(':organization_id', organizationId);
const apiUrl = ORG_ASSET_USAGE_URL.replace(
':organization_id',
organizationId
);

const params = new URLSearchParams({
page: pageNumber.toString(),
Expand All @@ -99,5 +83,8 @@ export async function getAssetUsageForOrganization(
params.set('ordering', orderingPrefix + fieldDefinition.apiOrderingName);
}

return await getAssetUsage(`${apiUrl}?${params}`);
return fetchGet<AssetUsage>(`${apiUrl}?${params}`, {
includeHeaders: true,
errorMessageDisplay: t('There was an error fetching asset usage data.'),
});
}
16 changes: 9 additions & 7 deletions jsapp/js/account/usage/usageProjectBreakdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import AssetStatusBadge from 'jsapp/js/components/common/assetStatusBadge';
import LoadingSpinner from 'jsapp/js/components/common/loadingSpinner';
import prettyBytes from 'pretty-bytes';
import type {AssetUsage, AssetWithUsage} from 'js/account/usage/usage.api';
import {getAssetUsageForOrganization} from 'js/account/usage/usage.api';
import {getOrgAssetUsage} from 'js/account/usage/usage.api';
import {USAGE_ASSETS_PER_PAGE} from 'jsapp/js/constants';
import SortableProjectColumnHeader from 'jsapp/js/projects/projectsTable/sortableProjectColumnHeader';
import type {ProjectFieldDefinition} from 'jsapp/js/projects/projectViews/constants';
Expand Down Expand Up @@ -35,11 +35,11 @@ const ProjectBreakdown = () => {
const orgQuery = useOrganizationQuery();

useEffect(() => {
async function fetchData() {
const data = await getAssetUsageForOrganization(
async function fetchData(orgId: string) {
const data = await getOrgAssetUsage(
currentPage,
order,
orgQuery.data?.id
orgId,
order
);
const updatedResults = data.results.map((projectResult) => {
const assetParts = projectResult.asset.split('/');
Expand All @@ -57,8 +57,10 @@ const ProjectBreakdown = () => {
setLoading(false);
}

fetchData();
}, [currentPage, order]);
if (orgQuery.data) {
fetchData(orgQuery.data.id);
}
}, [currentPage, order, orgQuery.data]);

if (loading) {
return <LoadingSpinner />;
Expand Down
4 changes: 2 additions & 2 deletions jsapp/js/account/usage/useUsage.hook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {createContext} from 'react';
import type {RecurringInterval} from 'js/account/stripe.types';
import {getSubscriptionInterval} from 'js/account/stripe.api';
import {convertSecondsToMinutes, formatRelativeTime} from 'js/utils';
import {getUsage} from 'js/account/usage/usage.api';
import {getOrgServiceUsage} from 'js/account/usage/usage.api';
import {useApiFetcher, withApiFetcher} from 'js/hooks/useApiFetcher.hook';

export interface UsageState {
Expand Down Expand Up @@ -34,7 +34,7 @@ const loadUsage = async (
throw Error(t('No organization found'));
}
const trackingPeriod = await getSubscriptionInterval();
const usage = await getUsage(organizationId);
const usage = await getOrgServiceUsage(organizationId);
if (!usage) {
throw Error(t("Couldn't get usage data"));
}
Expand Down

0 comments on commit 0fc340b

Please sign in to comment.