fix(contracts): 广告位 404 码对齐 NOT_FOUND/title 必填/空 PATCH 语义/上限漂移守护
Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -2094,8 +2094,8 @@ paths:
|
|||||||
$ref: '#/components/schemas/AdminAdItem'
|
$ref: '#/components/schemas/AdminAdItem'
|
||||||
'400':
|
'400':
|
||||||
description: |
|
description: |
|
||||||
参数校验失败(VALIDATION_FAILED):字段非法、换图三字段未同时提供
|
参数校验失败(VALIDATION_FAILED):字段非法、空请求体、换图三字段
|
||||||
或 staging 核验失败(UPLOAD_VERIFY_FAILED)。
|
未同时提供或 staging 核验失败(UPLOAD_VERIFY_FAILED)。
|
||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
@@ -2109,7 +2109,7 @@ paths:
|
|||||||
schema:
|
schema:
|
||||||
$ref: '#/components/schemas/Error'
|
$ref: '#/components/schemas/Error'
|
||||||
'404':
|
'404':
|
||||||
description: 广告位不存在(AD_NOT_FOUND)。
|
description: 广告位不存在(NOT_FOUND)。
|
||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
@@ -2137,7 +2137,7 @@ paths:
|
|||||||
schema:
|
schema:
|
||||||
$ref: '#/components/schemas/Error'
|
$ref: '#/components/schemas/Error'
|
||||||
'404':
|
'404':
|
||||||
description: 广告位不存在(AD_NOT_FOUND)。
|
description: 广告位不存在(NOT_FOUND)。
|
||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
@@ -4774,6 +4774,7 @@ components:
|
|||||||
过期后重拉列表刷新。
|
过期后重拉列表刷新。
|
||||||
required:
|
required:
|
||||||
- id
|
- id
|
||||||
|
- title
|
||||||
- imageObjectKey
|
- imageObjectKey
|
||||||
- imageUrl
|
- imageUrl
|
||||||
- linkUrl
|
- linkUrl
|
||||||
@@ -4875,7 +4876,8 @@ components:
|
|||||||
AdUpdateRequest:
|
AdUpdateRequest:
|
||||||
type: object
|
type: object
|
||||||
description: |
|
description: |
|
||||||
部分更新请求(仅提交的字段被修改)。albumIds:未提供=不动,null=转
|
部分更新请求(仅提交的字段被修改,空请求体 400 VALIDATION_FAILED,
|
||||||
|
对照 EmbedSlotUpdateRequest 口径)。albumIds:未提供=不动,null=转
|
||||||
全局,数组=整体替换;imageStagingKey/imageBytes/imageContentType 三者
|
全局,数组=整体替换;imageStagingKey/imageBytes/imageContentType 三者
|
||||||
必须同时提供(换图,服务端核验 staging 后转存新版本键)。
|
必须同时提供(换图,服务端核验 staging 后转存新版本键)。
|
||||||
properties:
|
properties:
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
// packages/contracts/src/__tests__/ads.test.ts
|
||||||
|
// 广告位契约守护:openapi.yaml 中三处硬编码的广告图字节上限(maximum: 2097152)
|
||||||
|
// 必须与 constants.ts 的 AD_UPLOAD_MAX_BYTES 恒等(1:1 守护,对照 events.test.ts
|
||||||
|
// 的枚举同步守护)。契约文件无 js-yaml 依赖(pnpm 严格 node_modules),按文件
|
||||||
|
// 既有的固定缩进块风格做文本切片解析:schema 名 4 空格、属性名 8 空格、
|
||||||
|
// 属性约束 10 空格——缩进漂移会令定位失败而报错,本身就是守护的一部分。
|
||||||
|
import { readFileSync } from 'node:fs';
|
||||||
|
import { resolve } from 'node:path';
|
||||||
|
import { AD_UPLOAD_MAX_BYTES } from '../constants.js';
|
||||||
|
|
||||||
|
const openapiYaml = readFileSync(
|
||||||
|
resolve(__dirname, '../../openapi.yaml'),
|
||||||
|
'utf8',
|
||||||
|
);
|
||||||
|
|
||||||
|
/** 取 components/schemas 下指定 schema 的文本块(至下一个 4 空格缩进的键)。 */
|
||||||
|
function schemaBlock(name: string): string {
|
||||||
|
const start = openapiYaml.indexOf(`\n ${name}:`);
|
||||||
|
if (start === -1) {
|
||||||
|
throw new Error(`openapi.yaml 缺少 schema:${name}`);
|
||||||
|
}
|
||||||
|
const body = openapiYaml.slice(start + 1);
|
||||||
|
const next = body.search(/\n [A-Za-z]/);
|
||||||
|
return next === -1 ? body : body.slice(0, next);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 取 schema 块内指定属性的 maximum 约束值(属性块内首个 maximum)。 */
|
||||||
|
function maximumOf(name: string, prop: string): number {
|
||||||
|
const block = schemaBlock(name);
|
||||||
|
const match = block.match(
|
||||||
|
new RegExp(`^ {8}${prop}:\\n(?: {10}[^\\n]+\\n)*? {10}maximum: (\\d+)`, 'm'),
|
||||||
|
);
|
||||||
|
if (match === null) {
|
||||||
|
throw new Error(`${name}.${prop} 缺少 maximum 约束`);
|
||||||
|
}
|
||||||
|
return Number(match[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('openapi 广告图上限 ↔ AD_UPLOAD_MAX_BYTES 同步', () => {
|
||||||
|
it.each([
|
||||||
|
['AdCreateRequest', 'imageBytes'],
|
||||||
|
['AdUpdateRequest', 'imageBytes'],
|
||||||
|
['AdUploadInitRequest', 'bytes'],
|
||||||
|
] as const)('%s.%s 的 maximum 恒等于 AD_UPLOAD_MAX_BYTES', (schema, prop) => {
|
||||||
|
expect(maximumOf(schema, prop)).toBe(AD_UPLOAD_MAX_BYTES);
|
||||||
|
});
|
||||||
|
});
|
||||||
+7
-6
@@ -2437,7 +2437,7 @@ export interface components {
|
|||||||
/** @description 广告位 ID。 */
|
/** @description 广告位 ID。 */
|
||||||
readonly id: string;
|
readonly id: string;
|
||||||
/** @description 后台备注名(不展示观众)。 */
|
/** @description 后台备注名(不展示观众)。 */
|
||||||
title?: string | null;
|
title: string | null;
|
||||||
/** @description 版本化终键(换图递增版本,禁止覆盖)。 */
|
/** @description 版本化终键(换图递增版本,禁止覆盖)。 */
|
||||||
readonly imageObjectKey: string;
|
readonly imageObjectKey: string;
|
||||||
/** @description 现签 GET(TTL=SIGNED_URL_TTL_SECONDS)。 */
|
/** @description 现签 GET(TTL=SIGNED_URL_TTL_SECONDS)。 */
|
||||||
@@ -2489,7 +2489,8 @@ export interface components {
|
|||||||
albumIds?: string[];
|
albumIds?: string[];
|
||||||
};
|
};
|
||||||
/**
|
/**
|
||||||
* @description 部分更新请求(仅提交的字段被修改)。albumIds:未提供=不动,null=转
|
* @description 部分更新请求(仅提交的字段被修改,空请求体 400 VALIDATION_FAILED,
|
||||||
|
* 对照 EmbedSlotUpdateRequest 口径)。albumIds:未提供=不动,null=转
|
||||||
* 全局,数组=整体替换;imageStagingKey/imageBytes/imageContentType 三者
|
* 全局,数组=整体替换;imageStagingKey/imageBytes/imageContentType 三者
|
||||||
* 必须同时提供(换图,服务端核验 staging 后转存新版本键)。
|
* 必须同时提供(换图,服务端核验 staging 后转存新版本键)。
|
||||||
*/
|
*/
|
||||||
@@ -5264,7 +5265,7 @@ export interface operations {
|
|||||||
"application/json": components["schemas"]["Error"];
|
"application/json": components["schemas"]["Error"];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
/** @description 广告位不存在(AD_NOT_FOUND)。 */
|
/** @description 广告位不存在(NOT_FOUND)。 */
|
||||||
404: {
|
404: {
|
||||||
headers: {
|
headers: {
|
||||||
[name: string]: unknown;
|
[name: string]: unknown;
|
||||||
@@ -5301,8 +5302,8 @@ export interface operations {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
/**
|
/**
|
||||||
* @description 参数校验失败(VALIDATION_FAILED):字段非法、换图三字段未同时提供
|
* @description 参数校验失败(VALIDATION_FAILED):字段非法、空请求体、换图三字段
|
||||||
* 或 staging 核验失败(UPLOAD_VERIFY_FAILED)。
|
* 未同时提供或 staging 核验失败(UPLOAD_VERIFY_FAILED)。
|
||||||
*/
|
*/
|
||||||
400: {
|
400: {
|
||||||
headers: {
|
headers: {
|
||||||
@@ -5322,7 +5323,7 @@ export interface operations {
|
|||||||
"application/json": components["schemas"]["Error"];
|
"application/json": components["schemas"]["Error"];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
/** @description 广告位不存在(AD_NOT_FOUND)。 */
|
/** @description 广告位不存在(NOT_FOUND)。 */
|
||||||
404: {
|
404: {
|
||||||
headers: {
|
headers: {
|
||||||
[name: string]: unknown;
|
[name: string]: unknown;
|
||||||
|
|||||||
Reference in New Issue
Block a user