fix: type errors
This commit is contained in:
@@ -3,11 +3,7 @@ import { Feature, ApiCusFeature } from "@autumn/shared";
|
||||
export interface CheckData {
|
||||
customerId: string;
|
||||
entityId?: string;
|
||||
// apiCustomer: ApiCustomer;
|
||||
cusFeature?: ApiCusFeature
|
||||
// cusEnts: FullCusEntWithFullCusProduct[];
|
||||
originalFeature: Feature;
|
||||
featureToUse: Feature;
|
||||
// cusProducts: FullCusProduct[];
|
||||
// entity?: Entity;
|
||||
}
|
||||
@@ -1,93 +1,19 @@
|
||||
import {
|
||||
ApiVersion,
|
||||
type ApiVersionClass,
|
||||
BillingInterval,
|
||||
type Feature,
|
||||
type FreeTrial,
|
||||
type FullCusProduct,
|
||||
type FullCustomer,
|
||||
type FullCustomerEntitlement,
|
||||
isTrialing,
|
||||
type ProductItem,
|
||||
SuccessCode,
|
||||
UsageModel,
|
||||
} from "@autumn/shared";
|
||||
import { Decimal } from "decimal.js";
|
||||
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
||||
import { featureToCusPrice } from "@/internal/customers/cusProducts/cusPrices/convertCusPriceUtils.js";
|
||||
import { getProration } from "@/internal/invoices/previewItemUtils/getItemsForNewProduct.js";
|
||||
import { priceToInvoiceAmount } from "@/internal/products/prices/priceUtils/priceToInvoiceAmount.js";
|
||||
import { isFeaturePriceItem } from "@/internal/products/product-items/productItemUtils/getItemType.js";
|
||||
import { itemToPriceOrTiers } from "@/internal/products/product-items/productItemUtils.js";
|
||||
import { notNullish } from "@/utils/genUtils.js";
|
||||
import { getCheckPreview } from "./getCheckPreview.js";
|
||||
|
||||
export const getBooleanEntitledResult = async ({
|
||||
db,
|
||||
fullCus,
|
||||
cusEnts,
|
||||
res,
|
||||
feature,
|
||||
apiVersion,
|
||||
withPreview,
|
||||
cusProducts,
|
||||
allFeatures,
|
||||
}: {
|
||||
db: DrizzleCli;
|
||||
fullCus: FullCustomer;
|
||||
cusEnts: FullCustomerEntitlement[];
|
||||
res: any;
|
||||
feature: Feature;
|
||||
apiVersion: ApiVersionClass;
|
||||
withPreview: boolean;
|
||||
cusProducts: FullCusProduct[];
|
||||
allFeatures: Feature[];
|
||||
}) => {
|
||||
const allowed = cusEnts.some((cusEnt) => {
|
||||
const featureMatch = cusEnt.internal_feature_id === feature.internal_id;
|
||||
|
||||
const entityFeatureId = cusEnt.entitlement.entity_feature_id;
|
||||
const compareEntity =
|
||||
notNullish(entityFeatureId) && notNullish(fullCus.entity);
|
||||
|
||||
const entityMatch = compareEntity
|
||||
? entityFeatureId === fullCus.entity!.feature_id
|
||||
: true;
|
||||
|
||||
return featureMatch && entityMatch;
|
||||
});
|
||||
|
||||
if (apiVersion.gte(ApiVersion.V1_1)) {
|
||||
return res.status(200).json({
|
||||
customer_id: fullCus.id,
|
||||
feature_id: feature.id,
|
||||
code: SuccessCode.FeatureFound,
|
||||
allowed,
|
||||
preview: withPreview
|
||||
? await getCheckPreview({
|
||||
db,
|
||||
allowed,
|
||||
balance: undefined,
|
||||
feature,
|
||||
cusProducts,
|
||||
allFeatures,
|
||||
})
|
||||
: undefined,
|
||||
});
|
||||
} else {
|
||||
return res.status(200).json({
|
||||
allowed,
|
||||
balances: allowed
|
||||
? [
|
||||
{
|
||||
feature_id: feature.id,
|
||||
balance: null,
|
||||
},
|
||||
]
|
||||
: [],
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export const getOptions = ({
|
||||
prodItems,
|
||||
@@ -151,14 +77,15 @@ export const getOptions = ({
|
||||
);
|
||||
|
||||
let currentQuantity = currentOptions?.quantity;
|
||||
const internalFeatureId = currentOptions?.internal_feature_id;
|
||||
let prorationAmount = 0;
|
||||
|
||||
if (currentQuantity) {
|
||||
if (currentQuantity && internalFeatureId) {
|
||||
currentQuantity = currentQuantity * (i.billing_units || 1);
|
||||
|
||||
const curPrice = featureToCusPrice({
|
||||
internalFeatureId: currentOptions?.internal_feature_id!,
|
||||
cusPrices: cusProduct?.customer_prices!,
|
||||
internalFeatureId: internalFeatureId,
|
||||
cusPrices: cusProduct?.customer_prices ?? [],
|
||||
})?.price;
|
||||
|
||||
const curPriceAmount = priceToInvoiceAmount({
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import type { EntityData } from "@autumn/shared";
|
||||
import type { CustomerData } from "../../../../../../shared/api/common/customerData.js";
|
||||
import type { CustomerData, EntityData } from "@autumn/shared";
|
||||
import type { AutumnContext } from "../../../../honoUtils/HonoEnv.js";
|
||||
import { getCachedApiCustomer } from "../../../customers/cusUtils/apiCusCacheUtils/getCachedApiCustomer.js";
|
||||
import { getOrCreateApiCustomer } from "../../../customers/cusUtils/getOrCreateApiCustomer.js";
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import {
|
||||
type FullCusEntWithFullCusProduct,
|
||||
type FullCustomer,
|
||||
filterEntityProductCusEnts,
|
||||
filterOutEntityCusEnts,
|
||||
filterPerEntityCusEnts,
|
||||
@@ -6,8 +8,7 @@ import {
|
||||
sumValues,
|
||||
} from "@autumn/shared";
|
||||
import { Decimal } from "decimal.js";
|
||||
import type { FullCustomer } from "../../../../../../../shared/models/cusModels/fullCusModel.js";
|
||||
import type { FullCusEntWithFullCusProduct } from "../../../../../../../shared/models/cusProductModels/cusEntModels/cusEntWithProduct.js";
|
||||
|
||||
import type { RequestContext } from "../../../../../honoUtils/HonoEnv.js";
|
||||
|
||||
export const cusEntsToEntityBreakdown = ({
|
||||
|
||||
@@ -77,7 +77,7 @@ export const deleteCusById = async ({
|
||||
|
||||
// Delete customer and all entity caches atomically
|
||||
await deleteCachedApiCustomer({
|
||||
customerId: customer.id,
|
||||
customerId: customer.id ?? "",
|
||||
orgId,
|
||||
env,
|
||||
});
|
||||
|
||||
@@ -2,21 +2,19 @@ import {
|
||||
AppEnv,
|
||||
customers,
|
||||
ErrCode,
|
||||
RecaseError,
|
||||
organizations,
|
||||
member,
|
||||
organizations,
|
||||
RecaseError,
|
||||
} from "@autumn/shared";
|
||||
import { and, eq } from "drizzle-orm";
|
||||
import { zValidator } from "@hono/zod-validator";
|
||||
import { z } from "zod/v4";
|
||||
import type { Context } from "hono";
|
||||
import type { HonoEnv } from "@/honoUtils/HonoEnv.js";
|
||||
import { OrgService } from "@/internal/orgs/OrgService.js";
|
||||
import {
|
||||
deleteStripeAccounts,
|
||||
deleteSvixWebhooks,
|
||||
deleteStripeWebhooks,
|
||||
deleteSvixWebhooks,
|
||||
} from "@/internal/orgs/orgUtils/deleteOrgUtils.js";
|
||||
import { createRoute } from "../../../../honoMiddlewares/routeHandler.js";
|
||||
|
||||
const deleteOrgSchema = z.object({
|
||||
slug: z.string().min(1, "Organization slug is required"),
|
||||
@@ -26,9 +24,9 @@ const deleteOrgSchema = z.object({
|
||||
* DELETE /organizations
|
||||
* Deletes a platform organization by slug (for test cleanup)
|
||||
*/
|
||||
export const handleDeletePlatformOrg = [
|
||||
zValidator("json", deleteOrgSchema),
|
||||
async (c: Context<HonoEnv>) => {
|
||||
export const handleDeletePlatformOrg = createRoute({
|
||||
body: deleteOrgSchema,
|
||||
handler: async (c) => {
|
||||
const ctx = c.get("ctx");
|
||||
const { db, logger, org: masterOrg } = ctx;
|
||||
|
||||
@@ -42,8 +40,6 @@ export const handleDeletePlatformOrg = [
|
||||
if (!org) {
|
||||
throw new RecaseError({
|
||||
message: `Organization with slug "${slug}" not found`,
|
||||
code: ErrCode.NotFound,
|
||||
statusCode: 404,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -93,4 +89,4 @@ export const handleDeletePlatformOrg = [
|
||||
message: `Organization "${slug}" deleted successfully`,
|
||||
});
|
||||
},
|
||||
];
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type {
|
||||
type CreateFreeTrial,
|
||||
CreateFreeTrial,
|
||||
Entitlement,
|
||||
Feature,
|
||||
Price,
|
||||
@@ -83,7 +83,7 @@ export const convertProductV2ToV1 = ({
|
||||
is_add_on: productV2.is_add_on,
|
||||
entitlements: entitlementsRecord,
|
||||
prices,
|
||||
free_trial: productV2.free_trial,
|
||||
group: productV2.group,
|
||||
free_trial: productV2.free_trial ?? null,
|
||||
group: productV2.group ?? "",
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { CreateRewardProgram, RewardProgram } from "@autumn/shared";
|
||||
import { generateId } from "@/utils/genUtils.js";
|
||||
import { CreateRewardProgram, RewardProgram } from "@autumn/shared";
|
||||
|
||||
export const constructRewardProgram = ({
|
||||
rewardProgramData,
|
||||
@@ -10,7 +10,7 @@ export const constructRewardProgram = ({
|
||||
orgId: string;
|
||||
env: string;
|
||||
}) => {
|
||||
let rewardProgram: RewardProgram = {
|
||||
const rewardProgram: RewardProgram = {
|
||||
...rewardProgramData,
|
||||
internal_id: generateId("rs"),
|
||||
unlimited_redemptions: false,
|
||||
|
||||
@@ -17,6 +17,7 @@ import { getCustomerDetails } from "@/internal/customers/cusUtils/getCustomerDet
|
||||
import { toApiFeature } from "@/internal/features/utils/mapFeatureUtils.js";
|
||||
import type { AutumnContext } from "../honoUtils/HonoEnv.js";
|
||||
import type { CheckData } from "../internal/api/check/checkTypes/CheckData.js";
|
||||
import { getApiCustomerBase } from "../internal/customers/cusUtils/apiCusUtils/getApiCustomerBase.js";
|
||||
import { generateId } from "../utils/genUtils.js";
|
||||
|
||||
export const mergeNewCusEntsIntoCusProducts = ({
|
||||
@@ -118,35 +119,42 @@ export const handleAllowanceUsed = async ({
|
||||
}) => {
|
||||
const { db, org, env, features, logger } = ctx;
|
||||
|
||||
// Allowance used...
|
||||
// Make sure overage allowed is false
|
||||
const oldCusEnts = structuredClone(cusEnts);
|
||||
for (const cusEnt of oldCusEnts) {
|
||||
cusEnt.usage_allowed = false;
|
||||
const newFullCus = structuredClone(fullCus);
|
||||
for (const cusProduct of newFullCus.customer_products) {
|
||||
for (const cusEnt of cusProduct.customer_entitlements) {
|
||||
cusEnt.usage_allowed = false;
|
||||
}
|
||||
}
|
||||
|
||||
const clonedNewCusEnts = structuredClone(newCusEnts);
|
||||
for (const cusEnt of clonedNewCusEnts) {
|
||||
cusEnt.usage_allowed = false;
|
||||
}
|
||||
const { apiCustomer: prevApiCustomer } = await getApiCustomerBase({
|
||||
ctx,
|
||||
fullCus: fullCus,
|
||||
});
|
||||
|
||||
const { apiCustomer: newApiCustomer } = await getApiCustomerBase({
|
||||
ctx,
|
||||
fullCus: newFullCus,
|
||||
});
|
||||
|
||||
const prevCusFeature = prevApiCustomer.features[feature.id];
|
||||
const newCusFeature = newApiCustomer.features[feature.id];
|
||||
|
||||
const prevCheckData: CheckData = {
|
||||
fullCus,
|
||||
cusEnts: oldCusEnts,
|
||||
customerId: fullCus.id || "",
|
||||
entityId: fullCus.entity?.id,
|
||||
cusFeature: prevCusFeature,
|
||||
originalFeature: feature,
|
||||
featureToUse: feature,
|
||||
cusProducts: fullCus.customer_products,
|
||||
entity: fullCus.entity,
|
||||
};
|
||||
|
||||
const newCheckData: CheckData = {
|
||||
fullCus,
|
||||
cusEnts: clonedNewCusEnts,
|
||||
customerId: newFullCus.id || "",
|
||||
entityId: newFullCus.entity?.id,
|
||||
cusFeature: newCusFeature,
|
||||
originalFeature: feature,
|
||||
featureToUse: feature,
|
||||
cusProducts: fullCus.customer_products,
|
||||
entity: fullCus.entity,
|
||||
};
|
||||
|
||||
const prevCheckResponse = await getV2CheckResponse({
|
||||
ctx,
|
||||
checkData: prevCheckData,
|
||||
@@ -215,13 +223,36 @@ export const handleThresholdReached = async ({
|
||||
clickhouseClient: null as any,
|
||||
};
|
||||
|
||||
const newFullCus = structuredClone(fullCus);
|
||||
newFullCus.customer_products = mergeNewCusEntsIntoCusProducts({
|
||||
cusProducts: fullCus.customer_products,
|
||||
newCusEnts: newCusEnts,
|
||||
});
|
||||
|
||||
const { apiCustomer: prevApiCustomer } = await getApiCustomerBase({
|
||||
ctx,
|
||||
fullCus: fullCus,
|
||||
});
|
||||
|
||||
const { apiCustomer: newApiCustomer } = await getApiCustomerBase({
|
||||
ctx,
|
||||
fullCus: newFullCus,
|
||||
});
|
||||
|
||||
const checkData1: CheckData = {
|
||||
fullCus,
|
||||
cusEnts,
|
||||
customerId: fullCus.id || "",
|
||||
entityId: fullCus.entity?.id,
|
||||
cusFeature: prevApiCustomer.features[feature.id],
|
||||
originalFeature: feature,
|
||||
featureToUse: feature,
|
||||
};
|
||||
|
||||
const checkData2: CheckData = {
|
||||
customerId: newFullCus.id || "",
|
||||
entityId: newFullCus.entity?.id,
|
||||
cusFeature: newApiCustomer.features[feature.id],
|
||||
originalFeature: feature,
|
||||
featureToUse: feature,
|
||||
cusProducts: fullCus.customer_products,
|
||||
entity: fullCus.entity,
|
||||
};
|
||||
|
||||
const prevCheckResponse = await getV2CheckResponse({
|
||||
@@ -230,22 +261,6 @@ export const handleThresholdReached = async ({
|
||||
requiredBalance: 1,
|
||||
});
|
||||
|
||||
const newCusProducts = mergeNewCusEntsIntoCusProducts({
|
||||
cusProducts: fullCus.customer_products,
|
||||
newCusEnts: newCusEnts,
|
||||
});
|
||||
|
||||
fullCus.customer_products = newCusProducts;
|
||||
|
||||
const checkData2: CheckData = {
|
||||
fullCus,
|
||||
cusEnts: newCusEnts,
|
||||
originalFeature: feature,
|
||||
featureToUse: feature,
|
||||
cusProducts: newCusProducts,
|
||||
entity: fullCus.entity,
|
||||
};
|
||||
|
||||
const newCheckResponse = await getV2CheckResponse({
|
||||
ctx,
|
||||
checkData: checkData2,
|
||||
@@ -263,7 +278,7 @@ export const handleThresholdReached = async ({
|
||||
env,
|
||||
features,
|
||||
logger,
|
||||
cusProducts: newCusProducts,
|
||||
cusProducts: newFullCus.customer_products,
|
||||
expand: [],
|
||||
apiVersion,
|
||||
});
|
||||
|
||||
@@ -81,6 +81,7 @@ export const initCustomer = async ({
|
||||
name: customerId,
|
||||
email: `${customerId}@example.com`,
|
||||
fingerprint,
|
||||
metadata: {},
|
||||
};
|
||||
|
||||
const customer = await CusService.get({
|
||||
@@ -226,6 +227,7 @@ export const initCustomerV2 = async ({
|
||||
email,
|
||||
fingerprint: customerData?.fingerprint || undefined,
|
||||
stripe_id: stripeCus.id,
|
||||
metadata: {},
|
||||
});
|
||||
|
||||
// 3. Attach payment method
|
||||
|
||||
@@ -84,7 +84,7 @@ function createResourceProxy(resource: any, resourceName: string): any {
|
||||
export function createReadOnlyStripeCli(stripeCli: Stripe): Stripe {
|
||||
return new Proxy(stripeCli, {
|
||||
get(target, prop: string) {
|
||||
const value = target[prop];
|
||||
const value = target[prop as keyof typeof target];
|
||||
|
||||
// If accessing a resource (customers, invoices, etc.)
|
||||
if (value && typeof value === "object" && !Array.isArray(value)) {
|
||||
|
||||
@@ -51,8 +51,7 @@ export const createTestContext = async () => {
|
||||
|
||||
// Get org secret key for API calls
|
||||
// Priority: 1. Environment variable (set by test runner), 2. Org's secret_keys field
|
||||
const orgSecretKey =
|
||||
process.env.UNIT_TEST_AUTUMN_SECRET_KEY || org.secret_keys?.[env] || "";
|
||||
const orgSecretKey = process.env.UNIT_TEST_AUTUMN_SECRET_KEY || "";
|
||||
if (!orgSecretKey) {
|
||||
throw new Error(
|
||||
`No secret key found for org "${orgSlug}" in environment "${env}". ` +
|
||||
|
||||
@@ -71,13 +71,9 @@ export const CreateCustomerParamsSchema = z.object({
|
||||
example: "fp_123abc",
|
||||
}),
|
||||
|
||||
metadata: z
|
||||
.record(z.string(), z.any())
|
||||
.default({})
|
||||
.meta({
|
||||
description: "Additional metadata for the customer",
|
||||
example: { company: "Acme Inc" },
|
||||
}),
|
||||
metadata: z.record(z.string(), z.any()).default({}).meta({
|
||||
description: "Additional metadata for the customer",
|
||||
}),
|
||||
|
||||
stripe_id: z.string().optional().meta({
|
||||
description: "Stripe customer ID if you already have one",
|
||||
|
||||
@@ -24,21 +24,21 @@ export const RewardProgram = z.object({
|
||||
|
||||
export const CreateRewardProgram = z.object({
|
||||
id: z.string(),
|
||||
when: z.nativeEnum(RewardTriggerEvent),
|
||||
when: z.enum(RewardTriggerEvent),
|
||||
product_ids: z.array(z.string()).optional(),
|
||||
exclude_trial: z.boolean().optional(),
|
||||
internal_reward_id: z.string(),
|
||||
max_redemptions: z.number().optional(),
|
||||
received_by: z.enum(["referrer", "all"]),
|
||||
received_by: z.enum(RewardReceivedBy),
|
||||
});
|
||||
|
||||
export const UpdateRewardProgram = z.object({
|
||||
when: z.nativeEnum(RewardTriggerEvent),
|
||||
when: z.enum(RewardTriggerEvent),
|
||||
product_ids: z.array(z.string()).optional(),
|
||||
exclude_trial: z.boolean().optional(),
|
||||
internal_reward_id: z.string(),
|
||||
max_redemptions: z.number().optional(),
|
||||
received_by: z.nativeEnum(RewardReceivedBy),
|
||||
received_by: z.enum(RewardReceivedBy),
|
||||
});
|
||||
|
||||
export type RewardProgram = z.infer<typeof RewardProgram>;
|
||||
|
||||
Reference in New Issue
Block a user