Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/quiet-badgers-poll.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@shopify/app': patch

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Non-blocking: could we fold this into schedule-subscription-migrations.md? Polling progress is part of the same unreleased feature as the new commands, so a separate patch changeset creates an artificial second changelog entry.

---

Show subscription migration operation progress while watching status and submissions.
5 changes: 5 additions & 0 deletions .changeset/schedule-subscription-migrations.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@shopify/app': minor
---

Add commands for scheduling and managing app subscription migrations.
393 changes: 393 additions & 0 deletions docs-shopify.dev/generated/generated_docs_data_v2.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions packages/app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
"@shopify/theme-check-node": "3.27.0",
"@shopify/toml-patch": "0.3.0",
"chokidar": "3.6.0",
"csv-parse": "7.0.2",
"diff": "5.2.2",
"esbuild": "0.28.1",
"graphql-request": "6.1.0",
Expand Down
71 changes: 71 additions & 0 deletions packages/app/src/cli/api/graphql/subscription_migrations.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import {gql} from 'graphql-request'

// 250 matches the maximum migration submission batch size; operation results are currently bounded by that contract.
// eslint-disable-next-line @shopify/cli/no-inline-graphql
export const AppSubscriptionMigrationOperationCreateMutation = gql`
mutation AppSubscriptionMigrationOperationCreate($input: AppSubscriptionMigrationOperationCreateInput!) {
appSubscriptionMigrationOperationCreate(input: $input) {
operation {
id
status
total
results(first: 250) {
edges {
node {
shopId
code
}
}
}
}
userErrors {
message
field
}
}
}
`

// eslint-disable-next-line @shopify/cli/no-inline-graphql
export const AppSubscriptionMigrationOperationQuery = gql`
query AppSubscriptionMigrationOperation($apiKey: String!, $id: ID!) {
appSubscriptionMigrationOperation(apiKey: $apiKey, id: $id) {
id
status
total
results(first: 250) {
edges {
node {
shopId
code
}
}
}
}
}
`

// eslint-disable-next-line @shopify/cli/no-inline-graphql
export const AppSubscriptionMigrationOperationCancelMutation = gql`
mutation AppSubscriptionMigrationOperationCancel($input: AppSubscriptionMigrationOperationCancelInput!) {
appSubscriptionMigrationOperationCancel(input: $input) {
operation {
id
status
total
results(first: 250) {
edges {
node {
shopId
code
}
}
}
}
userErrors {
message
field
}
}
}
`
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import {operationFlags} from './flags.js'
import {cancelMigrationOperations} from '../../../services/subscription-migrations/cancel-operations.js'
import {outputOperations} from '../../../services/subscription-migrations/command-output.js'
import {resolveSubscriptionMigrationClientId} from '../../../services/subscription-migrations/resolve-client-id.js'
import BaseCommand from '@shopify/cli-kit/node/base-command'

export default class Cancel extends BaseCommand {
static summary = 'Cancels app subscription migration operations.'

static descriptionWithMarkdown = `Cancels app subscription migration operations.

Canceling stops additional unprocessed shops, but does not undo shops that have already been scheduled or migrated. Use \`unschedule\` for reversible schedules.

Repeat \`--id\` to cancel every operation GID returned by a multi-batch submission. Use \`--json\` to output the resulting operation states and per-shop results as structured JSON.

By default, the command 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 explicitly override the active configuration; this only selects the app and does not change Partners authentication.`

static description = this.descriptionWithoutMarkdown()

static examples = [
'<%= config.bin %> <%= command.id %> --id <operation-id>',
'<%= config.bin %> <%= command.id %> --path ../my-app --config staging --id <operation-id-1> --id <operation-id-2>',
'<%= config.bin %> <%= command.id %> --client-id <client-id> --id <operation-id> --json',
]

static flags = {...operationFlags}

async run(): Promise<void> {
const {flags} = await this.parse(Cancel)
const clientId = await resolveSubscriptionMigrationClientId({
clientId: flags['client-id'],
directory: flags.path,
configName: flags.config,
})
const operations = await cancelMigrationOperations({
clientId,
operationIds: flags.id,
})
outputOperations(operations, flags.json)
}
}
Loading
Loading