☰
App Store Connect API 4.4.1 订阅稀疏字段:ASC CLI 的 17 端点能力扩展与实现剖析
2026/9/29 2:36:58 网站建设 项目流程

【免费下载链接】App-Store-Connect-CLI

Fast, scriptable CLI for the App Store Connect API. Automate TestFlight, builds, submissions, signing, analytics, screenshots, subscriptions, and more

项目地址:https://gitcode.com/gh_mirrors/ap/App-Store-Connect-CLI
点击查看免费下载

本文以 App-Store-Connect-CLI 仓库中的设计文档 docs/design/app-store-connect-api-4.4.1-subscription-sparse-fields.md 为骨架,讲解该 CLI 如何跟随 Apple App Store Connect API 4.4.1 版本为订阅(subscription)相关资源新增稀疏字段(sparse fields)查询能力:涉及 17 个 GET 端点、fields[subscriptions]等参数枚举的扩展、CLI 标志命名约定、自动 include 关系注入,以及--next冲突等失败语义。读完本文,你将掌握这些新增标志的准确用法、底层参数校验机制,以及如何用源码与测试验证该功能的完整行为。

切片定位:不新增命令名词,只扩展既有命令

这一特性切片没有引入任何新的命令名词,而是扩展既有的订阅资源、优惠(offer)、定价(pricing)、审核(review)与推广购买(promoted-purchase)命令。唯一一个此前仅存在于客户端库(client-only)的关系读取,通过既有的 scoped view 命令对外暴露:

asc subscriptions promoted-purchases view --app "APP_ID" --subscription-id "SUBSCRIPTION_SELECTOR"

从源码看,该命令由 internal/cli/subscriptions/promoted_purchases.go 中的SubscriptionsPromotedPurchasesCommand()构建,它复用了通用推广购买命令树,并通过ConfigureScopedPromotedPurchasesCommand注入订阅专属的路径前缀(asc subscriptions promoted-purchases)、产品类型(SUBSCRIPTION)与选择器解析逻辑。

相关资源稀疏字段的 CLI 标志命名

新增的稀疏字段标志遵循 CLI 既有的命名约定,每个标志精确对应 OpenAPI 中的一个fields[...]查询参数:

  • --subscription-fields映射fields[subscriptions]
  • --group-fields映射fields[subscriptionGroups]
  • --price-point-fields映射fields[subscriptionPricePoints]
  • --iap-fields映射fields[inAppPurchases]
  • --fields仍是主资源稀疏字段标志,用于subscriptions pricing price-points view

在本次变更之前,受影响的命令没有任何途径请求这些新增的稀疏字段值:列表命令虽然接受--next,但若将新稀疏字段选项塞进一个不透明的 next URL 中,会被静默丢弃——这正是本切片要修复的痛点。

OpenAPI 契约与实现台账:17 个端点的稀疏字段新增

Apple 的 4.4.1 schema 对本切片只改动下述参数枚举,所有操作仍返回既有的 JSON:API 响应类型。设计文档给出的完整台账如下,全部 17 项均已在仓库中实现(标记为[x]):

DoneMethod and path4.4.1 sparse-field additionCLI command
[x]GET /v1/subscriptionAppStoreReviewScreenshots/{id}fields[subscriptions]=versionssubscriptions review screenshots view
[x]GET /v1/subscriptionGroupLocalizations/{id}fields[subscriptionGroups]=versionssubscriptions groups localizations view
[x]GET /v1/subscriptionImages/{id}fields[subscriptions]=versionssubscriptions images view
[x]GET /v1/subscriptionLocalizations/{id}fields[subscriptions]=versionssubscriptions localizations view
[x]GET /v1/subscriptionOfferCodes/{id}fields[subscriptions]=versionssubscriptions offers offer-codes view
[x]GET /v1/subscriptionPricePoints/{id}fields[subscriptionPricePoints]=adjustedEqualizationssubscriptions pricing price-points view
[x]GET /v1/subscriptionPromotionalOffers/{id}fields[subscriptions]=versionssubscriptions offers promotional view
[x]GET /v1/subscriptionGroups/{id}/subscriptionGroupLocalizationsfields[subscriptionGroups]=versionssubscriptions groups localizations list
[x]GET /v1/subscriptionOfferCodes/{id}/pricesfields[subscriptionPricePoints]=adjustedEqualizationssubscriptions offers offer-codes prices
[x]GET /v1/subscriptionPromotionalOffers/{id}/pricesfields[subscriptionPricePoints]=adjustedEqualizationssubscriptions offers promotional prices
[x]GET /v1/subscriptions/{id}/appStoreReviewScreenshotfields[subscriptions]=versionssubscriptions review app-store-screenshot view
[x]GET /v1/subscriptions/{id}/imagesfields[subscriptions]=versionssubscriptions images list
[x]GET /v1/subscriptions/{id}/introductoryOffersfields[subscriptions]=versions;fields[subscriptionPricePoints]=adjustedEqualizationssubscriptions offers introductory list
[x]GET /v1/subscriptions/{id}/offerCodesfields[subscriptions]=versionssubscriptions offers offer-codes list
[x]GET /v1/subscriptions/{id}/promotedPurchasefields[subscriptions]=versions;fields[inAppPurchases]=versionssubscriptions promoted-purchases view --subscription-id
[x]GET /v1/subscriptions/{id}/promotionalOffersfields[subscriptions]=versionssubscriptions offers promotional list
[x]GET /v1/subscriptions/{id}/subscriptionLocalizationsfields[subscriptions]=versionssubscriptions localizations list

其中两个端点同时获得两个稀疏字段参数:/v1/subscriptions/{id}/introductoryOffers同时携带fields[subscriptions]与fields[subscriptionPricePoints];/v1/subscriptions/{id}/promotedPurchase同时携带fields[subscriptions]与fields[inAppPurchases]。

台账的源码级印证

这份台账并非纸面设计,仓库用测试将其固化。在 internal/asc/subscription_sparse_fields_4_4_1_test.go 中:

  • TestSubscriptionSparseFields441OpenAPILedger直接读取 docs/openapi/latest.json(Apple OpenAPI 4.4.1 schema 的仓库镜像),逐路径断言每个fields[...]参数枚举包含预期值(如versions、adjustedEqualizations),并硬校验台账路径数为 17;
  • TestSubscriptionSparseFields441ExactQueries对 14 种调用组合断言最终发出的 HTTP GET 请求路径与查询串完全精确,例如 introductory offers 的查询串为fields[subscriptions]=versions&fields[subscriptionPricePoints]=adjustedEqualizations&include=subscription,subscriptionPricePoint。

这意味着只要 schema 或客户端实现发生漂移,测试就会先行失败(RED)。

参数校验与自动 include:客户端创建前的严格把关

CLI 在创建客户端之前,会针对每个资源类型的确切枚举校验每一个稀疏字段值;非法值会以 usage error 形式在发起任何 HTTP 请求之前被拒绝。相关稀疏字段会自动添加其所需的 include 关系,共四类:

  • subscription(fields[subscriptions]触发)
  • subscriptionGroup(fields[subscriptionGroups]触发)
  • subscriptionPricePoint(fields[subscriptionPricePoints]触发)
  • inAppPurchaseV2(fields[inAppPurchases]触发)

自动 include 的实现集中在 internal/cli/subscriptions/sparse_fields_4_4_1.go:includeRelationshipForFields在字段非空时返回对应的关系名,appendIncludeForFields负责去重追加,避免重复 include。

可请求的枚举值同样由源码直接给出,例如subscriptionPricePointFieldsList()返回customerPrice、proceeds、proceedsYear2、territory、equalizations、adjustedEqualizations——其中adjustedEqualizations正是本次 4.4.1 新增值;subscriptionFieldsList()(见 internal/cli/subscriptions/version_selections.go)则覆盖name、productId、familySharable、marketSettings、multiSeatStatus、state、subscriptionPeriod、reviewNote、groupLevel以及各类关系字段与本次新增的versions等 23 个取值。

分页命令的 --next 互斥规则

在分页命令上,显式传入稀疏字段标志与--next冲突:此时不透明的 next URL 是唯一的查询来源,不允许再叠加其他查询选项。这一规则由两个辅助函数落实:

  • normalizeSparseFieldsFlag:当--next非空且某稀疏字段标志被显式提供时,返回--next cannot be combined with --<name>的 usage error;
  • validateNextExclusiveFlags:批量校验多个标志名与--next的互斥关系。

测试 internal/asc/subscription_sparse_fields_4_4_1_test.go 中的TestSubscriptionSparseFields441NextURLConflictsBeforeHTTP覆盖了 localizations、images、introductory offers、promotional offers、offer codes、price points 等 8 个场景,并断言冲突错误消息包含"next URL cannot be combined with query options"且 HTTP 请求数为 0——即冲突在客户端层就被拦截,绝不触网。

双选择器:promoted-purchases view 的扩展语义

subscriptions promoted-purchases view接受且仅接受以下两种选择器之一:

  • 既有的--promoted-purchase-id(直接指定推广购买资源 ID),或
  • 新增的--subscription-id(订阅选择器)

新增的--subscription-id选择器接受三种输入形式:ASC 订阅 ID、产品 ID(product ID),或精确的当前名称;其中产品 ID 与名称形式要求同时提供--app或设置ASC_APP_ID环境变量。既有的直接资源调用方式保持不变。

两种选择器形式都接受完全相同的推广购买稀疏字段,因为两个底层端点暴露的是同一套fields[inAppPurchases]、fields[subscriptions]与 include 契约。从 internal/cli/subscriptions/promoted_purchases.go 可以看到,ResolveOwnerID通过resolveSubscriptionLookupIDWithTimeout将订阅选择器解析为订阅 ID,随后调用client.GetSubscriptionPromotedPurchase完成查询。该文件的命令长帮助信息中也给出了完整的示例:

asc subscriptions promoted-purchases list --app "APP_ID" asc subscriptions promoted-purchases view --promoted-purchase-id "PROMO_ID" asc subscriptions promoted-purchases view --app "APP_ID" --subscription-id "SUBSCRIPTION_SELECTOR" asc subscriptions promoted-purchases create --app "APP_ID" --product-id "SUB_ID" --visible-for-all-users true asc subscriptions promoted-purchases update --promoted-purchase-id "PROMO_ID" --enabled false asc subscriptions promoted-purchases delete --promoted-purchase-id "PROMO_ID" --confirm asc subscriptions promoted-purchases link --app "APP_ID" --promoted-purchase-id "PROMO_ID"

响应模型中的 adjustedEqualizations

在客户端数据模型层面,adjustedEqualizations对应 internal/asc/subscriptions.go 中SubscriptionPricePointResponseRelationships的AdjustedEqualizations字段,类型为SubscriptionPricePointLinkRelationship——该类型只包含links、不包含资源 linkage 数据,与 OpenAPI 中该关系仅返回链接的契约一致。

输出、兼容性与失败行为

本切片的输出行为完全保持既有实现,不引入新的输出格式:

  • 输出仍为感知 TTY 的 JSON / 表格 / Markdown 三种形态;
  • 成功读取以退出码 0 结束;
  • 非法稀疏字段、显式空值、缺失 ID、以及--next冲突均属于 usage error(退出码 2),且在鉴权与 HTTP 请求之前就写入 stderr;
  • 既有的调用方式与响应模型完全不变;
  • 已废弃的 v1 localization 与 image 命令仍保留,并维持既有告警。

这些退出码语义与命令层校验逻辑在仓库的命令测试与 cmd/exit_codes.go 体系中是一致的。显式空值(如--subscription-fields ""且标志被显式提供)会触发normalizeSelectionFlag中的--<name> must not be empty错误,见 internal/cli/subscriptions/version_selections.go。

验证计划与工程质量门禁

设计文档为本切片定义了五步验证计划,仓库中的测试基建与之对应:

  1. RED 先行:用客户端 HTTP 测试覆盖全部 17 个路径,并为每个新标志族(自动 include、非法枚举、显式空值、--next冲突)建立 CLI 测试;
  2. 端点专属实现:实现端点专属的 option 类型与查询构建器,不使用通用的 query map;
  3. 聚焦测试:运行internal/asc、internal/cli/subscriptions与cmdtest相关包,然后构建二进制检查变更后的 help 输出与退出行为;
  4. 文档同步:重新生成 docs/COMMANDS.md,并跑 format、docs、lint 与完整测试门禁;
  5. 只读冒烟:仅在具备凭据与合适既有资源时,执行一次安全的只读 App Store Connect 冒烟测试,无需任何在线变更。

备选方案与设计权衡

文档记录了两种被否决的备选实现,其取舍值得借鉴:

  • 原生map[string]string查询逃生舱:实现更小,但会接受不支持的参数,且会静默偏离端点专属的 OpenAPI 事实。因此被否决。
  • 跨无关操作复用一个通用函数式 option 类型:会在编译期允许非法的跨端点参数组合。因此被否决,实现改为保持端点专属的 option 类型,仅在三个查询契约完全一致的 promoted-purchase 端点间共享同一 option 类型。

这种“端点专属、契约共享最小化”的设计与 internal/asc/subscription_sparse_fields_4_4_1_test.go 中WithSubscriptionOfferCodePricesPricePointFields、WithSubscriptionIntroductoryOffersSubscriptionFields等大量With*Fieldsoption 一一对应,验证了每一份 option 类型都精确绑定其端点查询契约。

小结

本切片以最小的命令面改动,将 App Store Connect API 4.4.1 新增的订阅稀疏字段能力完整接入 CLI:17 个端点逐一落实、标志命名与既有约定一致、枚举校验与自动 include 前置到客户端创建之前、--next冲突显式报错,且全程保持既有输出与退出码语义不变。若需深入,可从 docs/design/app-store-connect-api-4.4.1-subscription-sparse-fields.md 出发,对照 internal/asc/subscription_sparse_fields_4_4_1_test.go、internal/cli/subscriptions/sparse_fields_4_4_1.go 与 internal/cli/subscriptions/promoted_purchases.go 追踪完整实现链路。

【免费下载链接】App-Store-Connect-CLI

Fast, scriptable CLI for the App Store Connect API. Automate TestFlight, builds, submissions, signing, analytics, screenshots, subscriptions, and more

项目地址:https://gitcode.com/gh_mirrors/ap/App-Store-Connect-CLI
点击查看免费下载
上一篇:vue-hackernews-2.0用户行为分析:Google Analytics集成与事件跟踪
下一篇:AutoHotkey鼠标轨迹记录工具:实现精确操作回放

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询