Skip to content
Draft
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/app-versions-list-json-schema.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@shopify/app': minor
---

Add a JSON output schema for `app versions list`.
1 change: 1 addition & 0 deletions packages/app/src/cli/api/graphql/get_versions_list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export interface AppVersionsQuerySchema {
}
message?: string | null
status: string
versionId: string
versionTag?: string | null
}[]
pageInfo: {
Expand Down
111 changes: 111 additions & 0 deletions packages/app/src/cli/commands/app/versions/list.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import {testAppLinked, testDeveloperPlatformClient, testOrganizationApp} from '../../../models/app/app.test-data.js'
import {Organization, OrganizationSource} from '../../../models/organization.js'
import {afterEach, describe, expect, test, vi} from 'vitest'

vi.mock('../../../services/app-context.js')
vi.mock('../../../services/versions-list/result.js')

const originalUnitTestEnvironment = process.env.SHOPIFY_UNIT_TEST

afterEach(() => {
if (originalUnitTestEnvironment === undefined) {
delete process.env.SHOPIFY_UNIT_TEST
} else {
process.env.SHOPIFY_UNIT_TEST = originalUnitTestEnvironment
}
vi.resetModules()
})

describe('app versions list command', () => {
test('passes the typed result to the JSON output boundary', async () => {
process.env.SHOPIFY_UNIT_TEST = 'false'
vi.resetModules()

const organization: Organization = {
id: 'org-id',
businessName: 'name of org 1',
source: OrganizationSource.BusinessPlatform,
}
const app = testAppLinked({})
const remoteApp = testOrganizationApp({organizationId: organization.id, apiKey: 'api-key'})
const developerPlatformClient = testDeveloperPlatformClient({
appVersions: () =>
Promise.resolve({
app: {
id: 'app-id',
title: 'app-title',
organizationId: organization.id,
appVersions: {
nodes: [
{
message: 'message',
versionTag: 'versionTag',
versionId: 'gid://shopify/Version/1',
status: 'active',
createdAt: '2021-01-01',
createdBy: {displayName: 'createdBy'},
},
],
pageInfo: {totalResults: 1},
},
},
}),
})
const {linkedAppContext} = await import('../../../services/app-context.js')
const {renderAppVersionsListResult} = await import('../../../services/versions-list/result.js')
vi.mocked(linkedAppContext).mockResolvedValue({
app,
remoteApp,
organization,
developerPlatformClient,
} as unknown as Awaited<ReturnType<typeof linkedAppContext>>)
const {default: VersionsList} = await import('./list.js')

await VersionsList.run(['--json'], import.meta.url)

expect(renderAppVersionsListResult).toHaveBeenCalledWith(
{
app,
remoteApp,
organization,
developerPlatformClient,
appVersions: [
{
message: 'message',
versionTag: 'versionTag',
status: 'active',
createdAt: '2021-01-01 00:00:00',
createdBy: 'createdBy',
versionId: 'gid://shopify/Version/1',
},
],
totalResults: 1,
},
'json',
)
})

test('keeps the existing invalid API key error', async () => {
const app = testAppLinked({})
const remoteApp = testOrganizationApp({apiKey: 'api-key'})
const {linkedAppContext} = await import('../../../services/app-context.js')
vi.mocked(linkedAppContext).mockResolvedValue({
app,
remoteApp,
organization: {
id: 'org-id',
businessName: 'name of org 1',
source: OrganizationSource.BusinessPlatform,
},
developerPlatformClient: testDeveloperPlatformClient({
appVersions: () => Promise.resolve({app: null}),
}),
} as unknown as Awaited<ReturnType<typeof linkedAppContext>>)
const {default: VersionsList} = await import('./list.js')
vi.spyOn(VersionsList.prototype, 'catch').mockImplementation(async (error) => {
throw error
})

await expect(VersionsList.run(['--json'], import.meta.url)).rejects.toThrow('Invalid API Key: api-key')
})
})
28 changes: 20 additions & 8 deletions packages/app/src/cli/commands/app/versions/list.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
import {appFlags} from '../../../flags.js'
import versionList from '../../../services/versions-list.js'
import {appVersionsListJsonOutputSchema, getAppVersions} from '../../../services/versions-list.js'
import {renderAppVersionsListResult} from '../../../services/versions-list/result.js'
import AppLinkedCommand, {AppLinkedCommandOutput} from '../../../utilities/app-linked-command.js'
import {linkedAppContext} from '../../../services/app-context.js'
import {globalFlags, jsonFlag} from '@shopify/cli-kit/node/cli'
import {AbortError} from '@shopify/cli-kit/node/error'

export default class VersionsList extends AppLinkedCommand {
static summary = 'List deployed versions of your app.'

static descriptionWithMarkdown = `Lists the deployed app versions. An app version is a snapshot of your app extensions.`

static get jsonOutputSchema() {
return appVersionsListJsonOutputSchema
}

static description = this.descriptionForHelp()

static flags = {
Expand All @@ -27,13 +33,19 @@ export default class VersionsList extends AppLinkedCommand {
userProvidedConfigName: flags.config,
})

await versionList({
app,
remoteApp,
organization,
developerPlatformClient,
json: flags.json,
})
const result = await getAppVersions(developerPlatformClient, remoteApp)
if (!result) throw new AbortError(`Invalid API Key: ${remoteApp.apiKey}`)

await renderAppVersionsListResult(
{
app,
remoteApp,
organization,
developerPlatformClient,
...result,
},
flags.json ? 'json' : 'text',
)

return {app}
}
Expand Down
Loading
Loading