From ee1f61c0ad30e8c156dfef6de03187ef1208b659 Mon Sep 17 00:00:00 2001 From: Kevin Johnson Date: Fri, 16 Oct 2026 08:30:06 -0400 Subject: [PATCH] Align subscription migration failure reason types and schema --- .changeset/migration-refusal-known-values.md | 6 ++++++ packages/app/src/cli/models/subscription-migrations.ts | 6 +++++- .../app/src/cli/services/subscription-migrations/types.ts | 5 ++++- packages/cli/README.md | 2 +- packages/cli/oclif.manifest.json | 2 +- 5 files changed, 17 insertions(+), 4 deletions(-) create mode 100644 .changeset/migration-refusal-known-values.md diff --git a/.changeset/migration-refusal-known-values.md b/.changeset/migration-refusal-known-values.md new file mode 100644 index 00000000000..1262a6820a9 --- /dev/null +++ b/.changeset/migration-refusal-known-values.md @@ -0,0 +1,6 @@ +--- +'@shopify/app': patch +'@shopify/cli': patch +--- + +Document plan requirement and shop verification failure reasons in the subscription migration JSON schema. diff --git a/packages/app/src/cli/models/subscription-migrations.ts b/packages/app/src/cli/models/subscription-migrations.ts index 4384897ad2f..792c33c5a25 100644 --- a/packages/app/src/cli/models/subscription-migrations.ts +++ b/packages/app/src/cli/models/subscription-migrations.ts @@ -8,7 +8,11 @@ export const MIGRATABLE_SUBSCRIPTION_STATUSES = ['UNSCHEDULED', 'SCHEDULED', 'MI export type MigratableSubscriptionStatus = (typeof MIGRATABLE_SUBSCRIPTION_STATUSES)[number] export type ManualSubscriptionInterval = 'EVERY_30_DAYS' | 'ANNUAL' -export type MigrationFailureReason = 'SUPERSEDED' | 'SCHEDULING_FAILED' +export type MigrationFailureReason = + | 'SUPERSEDED' + | 'SCHEDULING_FAILED' + | 'PLAN_REQUIREMENTS_NOT_MET' + | 'SHOP_FEATURES_UNVERIFIED' export type MigratableSubscriptionNotificationKind = 'NONE' | 'OPT_OUT' | 'WHEN_REQUIRED' export type MigratableSubscriptionPriceBehavior = 'HONOR_BILLING_PRICE' | 'PLAN_PRICE' diff --git a/packages/app/src/cli/services/subscription-migrations/types.ts b/packages/app/src/cli/services/subscription-migrations/types.ts index daebbdbd7c3..aba1cadc889 100644 --- a/packages/app/src/cli/services/subscription-migrations/types.ts +++ b/packages/app/src/cli/services/subscription-migrations/types.ts @@ -116,7 +116,10 @@ const MigratableSubscriptionSchema = zod .union([CalendarDateSchema, UtcInstantSchema]) .nullable() .describe('The upstream calendar date or whole-second UTC instant; date-only values retain their format.'), - lastFailureReason: zod.string().nullable().describe('Known values: SUPERSEDED, SCHEDULING_FAILED.'), + lastFailureReason: zod + .string() + .nullable() + .describe('Known values: SUPERSEDED, SCHEDULING_FAILED, PLAN_REQUIREMENTS_NOT_MET, SHOP_FEATURES_UNVERIFIED.'), }) .strict() .describe('All subscription projection fields are present; unavailable values are null.') diff --git a/packages/cli/README.md b/packages/cli/README.md index ac2f43d35c7..2fe3a6948f7 100644 --- a/packages/cli/README.md +++ b/packages/cli/README.md @@ -6577,7 +6577,7 @@ DESCRIPTION "string", "null" ], - "description": "Known values: SUPERSEDED, SCHEDULING_FAILED." + "description": "Known values: SUPERSEDED, SCHEDULING_FAILED, PLAN_REQUIREMENTS_NOT_MET, SHOP_FEATURES_UNVERIFIED." } }, "required": [ diff --git a/packages/cli/oclif.manifest.json b/packages/cli/oclif.manifest.json index 3929a5bc852..b88af6d66f7 100644 --- a/packages/cli/oclif.manifest.json +++ b/packages/cli/oclif.manifest.json @@ -4935,7 +4935,7 @@ "args": { }, "customPluginName": "@shopify/app", - "description": "Lists every app subscription eligible for migration.\n\nBy default, the command writes CSV to stdout, streaming each page of results as it arrives. If a later page fails, the rows already written remain valid CSV. Use `--json` to fetch all pages first and then write a single JSON envelope to stdout. Use shell redirection to save either format, for example `shopify app subscription-migrations list > subscriptions.csv` or `shopify app subscription-migrations list --json > subscriptions.json`.\n\nUse `--status` to filter subscriptions by migration status. Supported values are `UNSCHEDULED`, `SCHEDULED`, and `MIGRATED`.\n\nRun the command from an app project. By default, it uses the Client ID from the active app configuration. Use `--path` to select an app directory or `--config` to select a configuration. Pass `--client-id` to select a different app within the project. Use `--reset` to relink the app.\n\nUse `--json-schema` to print the result, error, and event schemas.\n\nOutput from `--json` conforms to the `MigrationListResult` schema.\n\n```json\n{\n \"type\": \"object\",\n \"properties\": {\n \"subscriptions\": {\n \"type\": \"array\",\n \"items\": {\n \"$ref\": \"#/definitions/MigratableSubscription\"\n },\n \"description\": \"The complete list across every fetched page.\"\n }\n },\n \"required\": [\n \"subscriptions\"\n ],\n \"additionalProperties\": false,\n \"title\": \"MigrationListResult\",\n \"definitions\": {\n \"MigratableSubscription\": {\n \"type\": \"object\",\n \"properties\": {\n \"shopGid\": {\n \"type\": \"string\",\n \"pattern\": \"^gid:\\\\/\\\\/shopify\\\\/Shop\\\\/\\\\d+$\",\n \"description\": \"The Shopify Shop GID.\"\n },\n \"status\": {\n \"type\": \"string\",\n \"description\": \"Known values: UNSCHEDULED, SCHEDULED, MIGRATED.\"\n },\n \"manualSubscriptionName\": {\n \"type\": [\n \"string\",\n \"null\"\n ]\n },\n \"manualSubscriptionPrice\": {\n \"anyOf\": [\n {\n \"$ref\": \"#/definitions/MigratableSubscriptionPrice\"\n },\n {\n \"type\": \"null\"\n }\n ]\n },\n \"manualSubscriptionInterval\": {\n \"type\": \"string\",\n \"description\": \"Known values: EVERY_30_DAYS, ANNUAL.\"\n },\n \"targetPlanHandle\": {\n \"type\": [\n \"string\",\n \"null\"\n ]\n },\n \"notification\": {\n \"anyOf\": [\n {\n \"$ref\": \"#/definitions/MigratableSubscriptionNotification\"\n },\n {\n \"type\": \"null\"\n }\n ]\n },\n \"priceBehavior\": {\n \"type\": [\n \"string\",\n \"null\"\n ],\n \"description\": \"Known values: HONOR_BILLING_PRICE, PLAN_PRICE.\"\n },\n \"effectiveDate\": {\n \"anyOf\": [\n {\n \"anyOf\": [\n {\n \"type\": \"string\",\n \"format\": \"date\"\n },\n {\n \"type\": \"string\",\n \"format\": \"date-time\",\n \"pattern\": \"^\\\\d{4}-\\\\d{2}-\\\\d{2}T\\\\d{2}:\\\\d{2}:\\\\d{2}Z$\"\n }\n ]\n },\n {\n \"type\": \"null\"\n }\n ],\n \"description\": \"The upstream calendar date or whole-second UTC instant; date-only values retain their format.\"\n },\n \"lastFailureReason\": {\n \"type\": [\n \"string\",\n \"null\"\n ],\n \"description\": \"Known values: SUPERSEDED, SCHEDULING_FAILED.\"\n }\n },\n \"required\": [\n \"shopGid\",\n \"status\",\n \"manualSubscriptionName\",\n \"manualSubscriptionPrice\",\n \"manualSubscriptionInterval\",\n \"targetPlanHandle\",\n \"notification\",\n \"priceBehavior\",\n \"effectiveDate\",\n \"lastFailureReason\"\n ],\n \"additionalProperties\": false,\n \"description\": \"All subscription projection fields are present; unavailable values are null.\"\n },\n \"MigratableSubscriptionPrice\": {\n \"type\": \"object\",\n \"properties\": {\n \"amount\": {\n \"type\": \"string\",\n \"pattern\": \"^-?\\\\d+(?:\\\\.\\\\d+)?$\"\n },\n \"currencyCode\": {\n \"type\": \"string\",\n \"pattern\": \"^[A-Z]{3}$\"\n }\n },\n \"required\": [\n \"amount\",\n \"currencyCode\"\n ],\n \"additionalProperties\": false\n },\n \"MigratableSubscriptionNotification\": {\n \"type\": \"object\",\n \"properties\": {\n \"kind\": {\n \"type\": \"string\",\n \"description\": \"Known values: NONE, OPT_OUT, WHEN_REQUIRED.\"\n },\n \"optOutDeadline\": {\n \"anyOf\": [\n {\n \"$ref\": \"#/definitions/MigratableSubscription/properties/effectiveDate/anyOf/0/anyOf/1\"\n },\n {\n \"type\": \"null\"\n }\n ]\n },\n \"sentAt\": {\n \"anyOf\": [\n {\n \"$ref\": \"#/definitions/MigratableSubscription/properties/effectiveDate/anyOf/0/anyOf/1\"\n },\n {\n \"type\": \"null\"\n }\n ]\n }\n },\n \"required\": [\n \"kind\",\n \"optOutDeadline\",\n \"sentAt\"\n ],\n \"additionalProperties\": false\n }\n },\n \"$schema\": \"http://json-schema.org/draft-07/schema#\"\n}\n```", + "description": "Lists every app subscription eligible for migration.\n\nBy default, the command writes CSV to stdout, streaming each page of results as it arrives. If a later page fails, the rows already written remain valid CSV. Use `--json` to fetch all pages first and then write a single JSON envelope to stdout. Use shell redirection to save either format, for example `shopify app subscription-migrations list > subscriptions.csv` or `shopify app subscription-migrations list --json > subscriptions.json`.\n\nUse `--status` to filter subscriptions by migration status. Supported values are `UNSCHEDULED`, `SCHEDULED`, and `MIGRATED`.\n\nRun the command from an app project. By default, it uses the Client ID from the active app configuration. Use `--path` to select an app directory or `--config` to select a configuration. Pass `--client-id` to select a different app within the project. Use `--reset` to relink the app.\n\nUse `--json-schema` to print the result, error, and event schemas.\n\nOutput from `--json` conforms to the `MigrationListResult` schema.\n\n```json\n{\n \"type\": \"object\",\n \"properties\": {\n \"subscriptions\": {\n \"type\": \"array\",\n \"items\": {\n \"$ref\": \"#/definitions/MigratableSubscription\"\n },\n \"description\": \"The complete list across every fetched page.\"\n }\n },\n \"required\": [\n \"subscriptions\"\n ],\n \"additionalProperties\": false,\n \"title\": \"MigrationListResult\",\n \"definitions\": {\n \"MigratableSubscription\": {\n \"type\": \"object\",\n \"properties\": {\n \"shopGid\": {\n \"type\": \"string\",\n \"pattern\": \"^gid:\\\\/\\\\/shopify\\\\/Shop\\\\/\\\\d+$\",\n \"description\": \"The Shopify Shop GID.\"\n },\n \"status\": {\n \"type\": \"string\",\n \"description\": \"Known values: UNSCHEDULED, SCHEDULED, MIGRATED.\"\n },\n \"manualSubscriptionName\": {\n \"type\": [\n \"string\",\n \"null\"\n ]\n },\n \"manualSubscriptionPrice\": {\n \"anyOf\": [\n {\n \"$ref\": \"#/definitions/MigratableSubscriptionPrice\"\n },\n {\n \"type\": \"null\"\n }\n ]\n },\n \"manualSubscriptionInterval\": {\n \"type\": \"string\",\n \"description\": \"Known values: EVERY_30_DAYS, ANNUAL.\"\n },\n \"targetPlanHandle\": {\n \"type\": [\n \"string\",\n \"null\"\n ]\n },\n \"notification\": {\n \"anyOf\": [\n {\n \"$ref\": \"#/definitions/MigratableSubscriptionNotification\"\n },\n {\n \"type\": \"null\"\n }\n ]\n },\n \"priceBehavior\": {\n \"type\": [\n \"string\",\n \"null\"\n ],\n \"description\": \"Known values: HONOR_BILLING_PRICE, PLAN_PRICE.\"\n },\n \"effectiveDate\": {\n \"anyOf\": [\n {\n \"anyOf\": [\n {\n \"type\": \"string\",\n \"format\": \"date\"\n },\n {\n \"type\": \"string\",\n \"format\": \"date-time\",\n \"pattern\": \"^\\\\d{4}-\\\\d{2}-\\\\d{2}T\\\\d{2}:\\\\d{2}:\\\\d{2}Z$\"\n }\n ]\n },\n {\n \"type\": \"null\"\n }\n ],\n \"description\": \"The upstream calendar date or whole-second UTC instant; date-only values retain their format.\"\n },\n \"lastFailureReason\": {\n \"type\": [\n \"string\",\n \"null\"\n ],\n \"description\": \"Known values: SUPERSEDED, SCHEDULING_FAILED, PLAN_REQUIREMENTS_NOT_MET, SHOP_FEATURES_UNVERIFIED.\"\n }\n },\n \"required\": [\n \"shopGid\",\n \"status\",\n \"manualSubscriptionName\",\n \"manualSubscriptionPrice\",\n \"manualSubscriptionInterval\",\n \"targetPlanHandle\",\n \"notification\",\n \"priceBehavior\",\n \"effectiveDate\",\n \"lastFailureReason\"\n ],\n \"additionalProperties\": false,\n \"description\": \"All subscription projection fields are present; unavailable values are null.\"\n },\n \"MigratableSubscriptionPrice\": {\n \"type\": \"object\",\n \"properties\": {\n \"amount\": {\n \"type\": \"string\",\n \"pattern\": \"^-?\\\\d+(?:\\\\.\\\\d+)?$\"\n },\n \"currencyCode\": {\n \"type\": \"string\",\n \"pattern\": \"^[A-Z]{3}$\"\n }\n },\n \"required\": [\n \"amount\",\n \"currencyCode\"\n ],\n \"additionalProperties\": false\n },\n \"MigratableSubscriptionNotification\": {\n \"type\": \"object\",\n \"properties\": {\n \"kind\": {\n \"type\": \"string\",\n \"description\": \"Known values: NONE, OPT_OUT, WHEN_REQUIRED.\"\n },\n \"optOutDeadline\": {\n \"anyOf\": [\n {\n \"$ref\": \"#/definitions/MigratableSubscription/properties/effectiveDate/anyOf/0/anyOf/1\"\n },\n {\n \"type\": \"null\"\n }\n ]\n },\n \"sentAt\": {\n \"anyOf\": [\n {\n \"$ref\": \"#/definitions/MigratableSubscription/properties/effectiveDate/anyOf/0/anyOf/1\"\n },\n {\n \"type\": \"null\"\n }\n ]\n }\n },\n \"required\": [\n \"kind\",\n \"optOutDeadline\",\n \"sentAt\"\n ],\n \"additionalProperties\": false\n }\n },\n \"$schema\": \"http://json-schema.org/draft-07/schema#\"\n}\n```", "descriptionWithMarkdown": "Lists every app subscription eligible for migration.\n\nBy default, the command writes CSV to stdout, streaming each page of results as it arrives. If a later page fails, the rows already written remain valid CSV. Use `--json` to fetch all pages first and then write a single JSON envelope to stdout. Use shell redirection to save either format, for example `shopify app subscription-migrations list > subscriptions.csv` or `shopify app subscription-migrations list --json > subscriptions.json`.\n\nUse `--status` to filter subscriptions by migration status. Supported values are `UNSCHEDULED`, `SCHEDULED`, and `MIGRATED`.\n\nRun the command from an app project. By default, it uses the Client ID from the active app configuration. Use `--path` to select an app directory or `--config` to select a configuration. Pass `--client-id` to select a different app within the project. Use `--reset` to relink the app.", "examples": [ "<%= config.bin %> <%= command.id %>",