play: a new language is created with everything Play needs to commit it (icon, feature graphic, phone and tablet screenshots); play images creates a missing listing

This commit is contained in:
CRTkafa 2026-10-24 19:46:57 +08:00
parent 2ec1f0ab13
commit ed8d138a8e
7 changed files with 172 additions and 32 deletions

View file

@ -421,6 +421,10 @@ export class FakePlay {
priceLimits: Record<string, { min: number; max: number; symbol: string }> = {};
/** product id -> 400 message for its PATCH */
rejectProducts: Record<string, string> = {};
/** live Play: committing a language that is new in the edit needs its hi-res icon, feature graphic and 2+ phone screenshots
* in the same edit (403 PERMISSION_DENIED), 2+ tablet sets too when `requireTablets` */
requireNewLangAssets = false;
requireTablets = false;
/** validate calls that carried changesNotSentForReview (live Play answers them 400 "Unknown name") */
validateWithNotSent = 0;
@ -445,12 +449,21 @@ export class FakePlay {
return json({ error: { code: 400, status: 'INVALID_ARGUMENT', message: 'Changes cannot be sent for review automatically. Please set the query parameter changesNotSentForReview to true. Once committed, the changes in this edit can be sent for review from the Google Play Console UI.', errors: [{ message: 'Changes cannot be sent for review automatically. Please set the query parameter changesNotSentForReview to true.', domain: 'global', reason: 'badRequest' }] } }, 400);
}
if (method === 'POST' && rest === ':validate') return json({ id });
if (method === 'POST' && rest === ':commit' && this.requireNewLangAssets) {
for (const lang of Object.keys(e.listings).filter(l => !this.live.listings[l])) {
const n = (t: string) => (e.images[`${lang}/${t}`] ?? []).length;
const miss = n('icon') < 1 ? `This app has no high resolution icon set for language ${lang}` : n('featureGraphic') < 1 ? `This app has no feature graphic set for language ${lang}`
: n('phoneScreenshots') < 2 || (this.requireTablets && (n('sevenInchScreenshots') < 2 || n('tenInchScreenshots') < 2)) ? `This app has too few screenshots for language ${lang}` : '';
if (miss) return err(403, 'PERMISSION_DENIED', miss);
}
}
if (method === 'POST' && rest === ':commit') { this.live = e; this.edits.delete(id!); this.committed.push(id!); return json({ id }); }
if (rest === '/details') { if (method === 'PUT') e.details = body as Record<string, unknown>; return json(e.details); }
if (rest === '/listings') return json({ listings: Object.values(e.listings) });
if ((m = /^\/listings\/([^/]+)$/.exec(rest!))) { if (method === 'PUT') e.listings[m[1]!] = body as Record<string, unknown>; return json(e.listings[m[1]!] ?? {}); }
if ((m = /^\/listings\/([^/]+)\/(\w+)$/.exec(rest!))) {
const k = `${m[1]}/${m[2]}`;
if (this.requireNewLangAssets && method === 'GET' && !e.listings[m[1]!]) return err(404, 'NOT_FOUND', `Listing for language '${m[1]}' not found.`);
if (method === 'DELETE') { e.images[k] = []; return json({ deleted: [] }); }
if (method === 'POST') { const h = new Bun.CryptoHasher('sha256').update(body as Uint8Array).digest('hex'); (e.images[k] ??= []).push(h); return json({ image: { sha256: h } }); }
return json({ images: (e.images[k] ?? []).map(sha256 => ({ sha256 })) });