mirror of
https://github.com/stack-auth/stack.git
synced 2026-06-04 21:04:37 +08:00
Merge branch 'dev' into promptless/changelog-payments-bug-fixes
Some checks failed
DB migration compat / Check if migrations changed (push) Has been cancelled
DB migration compat / Back-compat — Current branch migrations with ${{ needs.check-migrations-changed.outputs.base_branch }} branch code (push) Has been cancelled
DB migration compat / Forward-compat — Current branch code with ${{ needs.check-migrations-changed.outputs.base_branch }} branch migrations (push) Has been cancelled
DB migration compat / No migration changes (skipped) (push) Has been cancelled
Some checks failed
DB migration compat / Check if migrations changed (push) Has been cancelled
DB migration compat / Back-compat — Current branch migrations with ${{ needs.check-migrations-changed.outputs.base_branch }} branch code (push) Has been cancelled
DB migration compat / Forward-compat — Current branch code with ${{ needs.check-migrations-changed.outputs.base_branch }} branch migrations (push) Has been cancelled
DB migration compat / No migration changes (skipped) (push) Has been cancelled
This commit is contained in:
commit
aea98ea7fc
@ -361,3 +361,6 @@ A: Invalid `tools` entries are rejected by `requestBodySchema` in `apps/backend/
|
||||
|
||||
## Q: Why did the internal metrics E2E snapshots need to change in April 2026?
|
||||
A: The `/api/v1/internal/metrics` response now intentionally includes `analytics_overview.daily_anonymous_visitors_fallback`, `analytics_overview.anonymous_visitors_fallback`, and `active_users_by_country`. Those additions are reflected in `packages/stack-shared/src/interface/admin-metrics.ts` and the backend route, so the E2E snapshots must include them instead of treating them as regressions.
|
||||
|
||||
## Q: Why can environment config override writes fail with a product/product-line customer type warning after creating a preview project?
|
||||
A: The environment override endpoint validates the new environment override against the rendered branch config. Preview dummy payments data must therefore be internally coherent: products assigned to a product line need the same `customerType` as that product line, otherwise unrelated environment patches can fail with warnings like `Product "growth" has customer type "user" but its product line "workspace" has customer type "team"`.
|
||||
|
||||
23
apps/backend/src/lib/seed-dummy-data.test.ts
Normal file
23
apps/backend/src/lib/seed-dummy-data.test.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { branchConfigSchema, getConfigOverrideErrors, getIncompleteConfigWarnings } from "@stackframe/stack-shared/dist/config/schema";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { buildDummyPaymentsSetup } from "./seed-dummy-data";
|
||||
|
||||
describe("dummy payments seed config", () => {
|
||||
it("is valid branch payments config", async () => {
|
||||
const { paymentsBranchOverride } = buildDummyPaymentsSetup();
|
||||
const branchConfigOverride = { payments: paymentsBranchOverride };
|
||||
|
||||
expect(await getConfigOverrideErrors(branchConfigSchema, branchConfigOverride)).toMatchInlineSnapshot(`
|
||||
{
|
||||
"data": null,
|
||||
"status": "ok",
|
||||
}
|
||||
`);
|
||||
expect(await getIncompleteConfigWarnings(branchConfigSchema, branchConfigOverride)).toMatchInlineSnapshot(`
|
||||
{
|
||||
"data": null,
|
||||
"status": "ok",
|
||||
}
|
||||
`);
|
||||
});
|
||||
});
|
||||
@ -11,6 +11,7 @@ import { getPrismaClientForTenancy, globalPrismaClient, type PrismaClientTransac
|
||||
import { ALL_APPS } from '@stackframe/stack-shared/dist/apps/apps-config';
|
||||
import { DEFAULT_EMAIL_THEME_ID } from '@stackframe/stack-shared/dist/helpers/emails';
|
||||
import { type AdminUserProjectsCrud, type ProjectsCrud } from '@stackframe/stack-shared/dist/interface/crud/projects';
|
||||
import { type Config } from '@stackframe/stack-shared/dist/config/format';
|
||||
import { DayInterval } from '@stackframe/stack-shared/dist/utils/dates';
|
||||
import { getEnvVariable } from '@stackframe/stack-shared/dist/utils/env';
|
||||
import { throwErr } from '@stackframe/stack-shared/dist/utils/errors';
|
||||
@ -94,18 +95,21 @@ type SeedDummyUsersOptions = {
|
||||
teamNameToId: Map<string, string>,
|
||||
};
|
||||
|
||||
type PaymentsProducts = {
|
||||
[productId: string]: Config | undefined,
|
||||
};
|
||||
|
||||
type PaymentsSetup = {
|
||||
paymentsProducts: Record<string, unknown>,
|
||||
paymentsBranchOverride: Record<string, unknown>,
|
||||
paymentsEnvironmentOverride: Record<string, unknown>,
|
||||
paymentsProducts: PaymentsProducts,
|
||||
paymentsBranchOverride: Config,
|
||||
paymentsEnvironmentOverride: Config,
|
||||
};
|
||||
|
||||
type TransactionsSeedOptions = {
|
||||
prisma: PrismaClientTransaction,
|
||||
tenancyId: string,
|
||||
teamNameToId: Map<string, string>,
|
||||
userEmailToId: Map<string, string>,
|
||||
paymentsProducts: Record<string, unknown>,
|
||||
paymentsProducts: PaymentsProducts,
|
||||
};
|
||||
|
||||
type EmailSeedOptions = {
|
||||
@ -707,16 +711,16 @@ async function seedDummyUsers(options: SeedDummyUsersOptions): Promise<Map<strin
|
||||
return userEmailToId;
|
||||
}
|
||||
|
||||
function buildDummyPaymentsSetup(): PaymentsSetup {
|
||||
export function buildDummyPaymentsSetup(): PaymentsSetup {
|
||||
const monthlyInterval: DayInterval = [1, 'month'];
|
||||
const yearlyInterval: DayInterval = [1, 'year'];
|
||||
const twoWeekInterval: DayInterval = [2, 'week'];
|
||||
|
||||
const paymentsProducts: Record<string, unknown> = {
|
||||
const paymentsProducts: PaymentsProducts = {
|
||||
'starter': {
|
||||
displayName: 'Starter',
|
||||
productLineId: 'workspace',
|
||||
customerType: 'user',
|
||||
customerType: 'team',
|
||||
serverOnly: false,
|
||||
stackable: false,
|
||||
freeTrial: twoWeekInterval as any,
|
||||
@ -744,7 +748,7 @@ function buildDummyPaymentsSetup(): PaymentsSetup {
|
||||
'growth': {
|
||||
displayName: 'Growth',
|
||||
productLineId: 'workspace',
|
||||
customerType: 'user',
|
||||
customerType: 'team',
|
||||
serverOnly: false,
|
||||
stackable: false,
|
||||
prices: {
|
||||
@ -780,7 +784,7 @@ function buildDummyPaymentsSetup(): PaymentsSetup {
|
||||
'regression-addon': {
|
||||
displayName: 'Regression Add-on',
|
||||
productLineId: 'add_ons',
|
||||
customerType: 'user',
|
||||
customerType: 'team',
|
||||
serverOnly: false,
|
||||
stackable: true,
|
||||
prices: {
|
||||
@ -818,19 +822,19 @@ function buildDummyPaymentsSetup(): PaymentsSetup {
|
||||
items: {
|
||||
studio_seats: {
|
||||
displayName: 'Studio Seats',
|
||||
customerType: 'user',
|
||||
customerType: 'team',
|
||||
},
|
||||
review_passes: {
|
||||
displayName: 'Reviewer Passes',
|
||||
customerType: 'user',
|
||||
customerType: 'team',
|
||||
},
|
||||
automation_minutes: {
|
||||
displayName: 'Automation Minutes',
|
||||
customerType: 'user',
|
||||
customerType: 'team',
|
||||
},
|
||||
snapshot_credits: {
|
||||
displayName: 'Snapshot Credits',
|
||||
customerType: 'user',
|
||||
customerType: 'team',
|
||||
},
|
||||
},
|
||||
products: paymentsProducts,
|
||||
@ -895,12 +899,10 @@ async function seedDummyTransactions(options: TransactionsSeedOptions) {
|
||||
prisma,
|
||||
tenancyId,
|
||||
teamNameToId,
|
||||
userEmailToId,
|
||||
paymentsProducts,
|
||||
} = options;
|
||||
|
||||
const resolveTeamId = (teamName: string) => teamNameToId.get(teamName) ?? throwErr(`Unknown dummy project team ${teamName}`);
|
||||
const resolveUserId = (email: string) => userEmailToId.get(email) ?? throwErr(`Unknown dummy project user ${email}`);
|
||||
const resolveProduct = (productId: string): Prisma.InputJsonValue => {
|
||||
const product = paymentsProducts[productId];
|
||||
if (!product) {
|
||||
@ -944,8 +946,8 @@ async function seedDummyTransactions(options: TransactionsSeedOptions) {
|
||||
},
|
||||
{
|
||||
id: DUMMY_SEED_IDS.subscriptions.mateoGrowthAnnual,
|
||||
customerType: CustomerType.USER,
|
||||
customerId: resolveUserId('mateo.silva@dummy.dev'),
|
||||
customerType: CustomerType.TEAM,
|
||||
customerId: resolveTeamId('Growth Loop'),
|
||||
productId: 'growth',
|
||||
priceId: 'annual',
|
||||
product: resolveProduct('growth'),
|
||||
@ -1089,8 +1091,8 @@ async function seedDummyTransactions(options: TransactionsSeedOptions) {
|
||||
const oneTimePurchaseSeeds: OneTimePurchaseSeed[] = [
|
||||
{
|
||||
id: DUMMY_SEED_IDS.oneTimePurchases.ameliaSeatPack,
|
||||
customerType: CustomerType.USER,
|
||||
customerId: resolveUserId('amelia.chen@dummy.dev'),
|
||||
customerType: CustomerType.TEAM,
|
||||
customerId: resolveTeamId('Design Systems Lab'),
|
||||
productId: 'starter',
|
||||
priceId: 'monthly',
|
||||
product: resolveProduct('starter'),
|
||||
@ -2094,7 +2096,6 @@ export async function seedDummyProject(options: SeedDummyProjectOptions): Promis
|
||||
prisma: dummyPrisma,
|
||||
tenancyId: dummyTenancy.id,
|
||||
teamNameToId,
|
||||
userEmailToId,
|
||||
paymentsProducts,
|
||||
});
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user