55 lines
3.5 KiB
TypeScript
55 lines
3.5 KiB
TypeScript
// Price changes of existing (live) products: the owner's call, never confirmed by --yes. Used by play products,
|
|
// play subscriptions, apple subscriptions and prices import (apple iap never changes the prices of an existing product).
|
|
// Imported only by those commands, so --allow-price-changes is a flag of theirs alone (tools/gen-flags.ts).
|
|
import { confirm, type Ctx, declined } from './ctx';
|
|
import { stdinIsTTY } from './tty';
|
|
|
|
/** The flag that lets a write command change the price of an existing product. `flow run` never adds it and refuses it in steps. */
|
|
export const ALLOW_PRICE_CHANGES = 'allow-price-changes';
|
|
|
|
/** A held price change of one existing product: per region / territory, current -> wanted. */
|
|
export interface HeldPrice { id: string; changes: { region: string; from: string; to: string }[] }
|
|
|
|
/**
|
|
* Price changes of existing (live) products are the owner's call, and only ever with --allow-price-changes, TTY or
|
|
* not: no confirmation of creates or other updates (a terminal "y", `--yes`, `flow run --apply --yes`) covers them.
|
|
* Without the flag: false (the command applies everything else and ends with holdPriceChanges()). With the flag they
|
|
* get their own question that lists each change old -> new: asked in a terminal (even with --yes, which never answers
|
|
* it there), answered by --yes outside a terminal (the flag plus --yes is the explicit owner's run), by the desktop
|
|
* owner dialog under serve. Nothing to change: true.
|
|
*/
|
|
export async function allowPriceChanges(ctx: Ctx, changes: HeldPrice[], question: string): Promise<boolean> {
|
|
if (!changes.length) return true;
|
|
if (!ctx.args.has(ALLOW_PRICE_CHANGES)) return false;
|
|
const list = changes.flatMap(h => h.changes.map(c => ` ${h.id} ${c.region} ${c.from} -> ${c.to}`)).join('\n');
|
|
const q = `${question}\n${list}\n`;
|
|
return confirm(stdinIsTTY() && process.env.STOREBOUND_SERVE !== '1' ? withoutYes(ctx) : ctx, q);
|
|
}
|
|
|
|
/** The end of a run that left price changes alone: one line per product, then declined (exit DECLINED_EXIT), so a flow stops there. */
|
|
export function holdPriceChanges(held: HeldPrice[]): void {
|
|
if (!held.length) return;
|
|
for (const h of held) console.error(`price change on ${h.id} (${priceDiff(h.changes)}) needs the owner: re-run with --${ALLOW_PRICE_CHANGES}`);
|
|
declined(`${held.length} price change(s) of existing products need the owner (re-run with --${ALLOW_PRICE_CHANGES}); everything else was applied`);
|
|
}
|
|
|
|
/** "US USD 2.99 -> USD 3.99, DE ..." (at most 4 regions, then "+N more"). Pure. */
|
|
export function priceDiff(changes: HeldPrice['changes']): string {
|
|
const shown = changes.slice(0, 4).map(c => `${c.region} ${c.from} -> ${c.to}`).join(', ');
|
|
return changes.length > 4 ? `${shown}, +${changes.length - 4} more` : shown;
|
|
}
|
|
|
|
/** The same context with --yes hidden: for questions --yes must not answer. */
|
|
export function withoutYes(ctx: Ctx): Ctx {
|
|
return { ...ctx, args: { ...ctx.args, has: (n: string) => n !== 'yes' && ctx.args.has(n) } };
|
|
}
|
|
|
|
/**
|
|
* The dry run's own section for price changes of existing products: held unless the real run gets
|
|
* --allow-price-changes. One line per product and region, old -> new. Empty when nothing is held. Pure.
|
|
*/
|
|
export function heldSection(held: HeldPrice[], allowed = false): string {
|
|
if (!held.length) return '';
|
|
const head = `price changes on existing products (${allowed ? 'with --allow-price-changes: asked separately, each old -> new' : 'held: only with --allow-price-changes'}):`;
|
|
return [head, ...held.flatMap(h => h.changes.map(c => ` ${h.id} ${c.region} ${c.from} -> ${c.to}`))].join('\n');
|
|
}
|