cleaned up usage windows
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
import type { DbUsageLimit } from "../../../models/cusModels/billingControls/usageLimit.js";
|
||||
import type { FullSubject } from "../../../models/cusModels/fullSubject/fullSubjectModel.js";
|
||||
import type { UsageWindowLimit } from "../../../models/cusProductModels/cusEntModels/usageWindowModels.js";
|
||||
import type { CusProductStatus } from "../../../models/cusProductModels/cusProductEnums.js";
|
||||
import type { Feature } from "../../../models/featureModels/featureModels.js";
|
||||
import { resetIntvToEntIntv } from "../../productV2Utils/productItemUtils/convertProductItem/planItemIntervals.js";
|
||||
import { buildUsageWindowKey } from "../buildUsageWindowKey.js";
|
||||
import { findUsageWindowAnchor } from "../findUsageWindowAnchor/findUsageWindowAnchor.js";
|
||||
import { getUsageWindowAnchorTimestamp } from "../getUsageWindowAnchorTimestamp.js";
|
||||
import { getUsageWindowBounds } from "../getUsageWindowBounds.js";
|
||||
import { getUsageWindowDimension } from "./getUsageWindowDimension.js";
|
||||
|
||||
/** Non-null = the cap is the entity's own: its counter is entity-scoped. */
|
||||
export type UsageWindowEntityScope = {
|
||||
entityId: string | null;
|
||||
internalEntityId: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Resolves one stored usage-limit entry into the enforceable UsageWindowLimit
|
||||
* handed to the deduction script. The entry's ResetInterval converts to the
|
||||
* internal EntInterval here -- the single edge between the API/storage
|
||||
* vocabulary and the window internals. Window bounds align to the customer's
|
||||
* billing cycle via the anchor entitlement when one exists, else UTC calendar.
|
||||
*/
|
||||
export const usageLimitToUsageWindowLimit = ({
|
||||
fullSubject,
|
||||
usageLimit,
|
||||
feature,
|
||||
features,
|
||||
now,
|
||||
inStatuses,
|
||||
entityScope = null,
|
||||
}: {
|
||||
fullSubject: FullSubject;
|
||||
usageLimit: DbUsageLimit;
|
||||
feature: Feature;
|
||||
features: Feature[];
|
||||
now: number;
|
||||
inStatuses?: CusProductStatus[];
|
||||
entityScope?: UsageWindowEntityScope | null;
|
||||
}): UsageWindowLimit | null => {
|
||||
// No catalog internal_id => unstorable counter row (NOT NULL FK); the cap
|
||||
// is unenforceable, so skip it.
|
||||
if (feature.internal_id == null) return null;
|
||||
|
||||
const interval = resetIntvToEntIntv({ resetIntv: usageLimit.interval });
|
||||
if (interval == null) return null;
|
||||
|
||||
const { dimensionType, dimensionFeatureId } = getUsageWindowDimension({
|
||||
feature,
|
||||
});
|
||||
|
||||
const scopeType = entityScope ? "entity" : "customer";
|
||||
const { anchorCustomerEntitlementId, anchorCustomerEntitlement } =
|
||||
findUsageWindowAnchor({
|
||||
fullSubject,
|
||||
featureId: feature.id,
|
||||
features,
|
||||
isCreditSystem: dimensionType === "balance",
|
||||
inStatuses,
|
||||
scopeType,
|
||||
});
|
||||
|
||||
const { windowStartAt, windowEndAt } = getUsageWindowBounds({
|
||||
interval,
|
||||
now,
|
||||
anchor: getUsageWindowAnchorTimestamp({ anchorCustomerEntitlement }),
|
||||
});
|
||||
|
||||
return {
|
||||
feature_id: feature.id,
|
||||
internal_feature_id: feature.internal_id,
|
||||
internal_customer_id: fullSubject.internalCustomerId,
|
||||
key: buildUsageWindowKey({
|
||||
scopeType,
|
||||
internalEntityId: entityScope?.internalEntityId ?? null,
|
||||
dimensionType,
|
||||
dimensionFeatureId,
|
||||
interval,
|
||||
windowStartAt,
|
||||
}),
|
||||
dimension_type: dimensionType,
|
||||
dimension_feature_id: dimensionFeatureId,
|
||||
scope_type: scopeType,
|
||||
entity_id: entityScope?.entityId ?? null,
|
||||
internal_entity_id: entityScope?.internalEntityId ?? null,
|
||||
interval,
|
||||
window_start_at: windowStartAt,
|
||||
window_end_at: windowEndAt,
|
||||
limit: usageLimit.limit,
|
||||
anchor_customer_entitlement_id: anchorCustomerEntitlementId,
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user