Skip to content

Commit 07ccc99

Browse files
committed
add validate command
1 parent 5300218 commit 07ccc99

5 files changed

Lines changed: 5350 additions & 5485 deletions

File tree

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import {appFlags} from '../../flags.js'
2+
import {validateApp} from '../../services/validate.js'
3+
import AppLinkedCommand, {AppLinkedCommandOutput} from '../../utilities/app-linked-command.js'
4+
import {linkedAppContext} from '../../services/app-context.js'
5+
import {globalFlags} from '@shopify/cli-kit/node/cli'
6+
7+
export default class Validate extends AppLinkedCommand {
8+
static summary = 'Validate your app configuration and extensions.'
9+
10+
static descriptionWithMarkdown = `Validates your app's \`shopify.app.toml\` and all extension configurations against their schemas and reports any errors found.`
11+
12+
static description = this.descriptionWithoutMarkdown()
13+
14+
static flags = {
15+
...globalFlags,
16+
...appFlags,
17+
}
18+
19+
public async run(): Promise<AppLinkedCommandOutput> {
20+
const {flags} = await this.parse(Validate)
21+
22+
const {app} = await linkedAppContext({
23+
directory: flags.path,
24+
clientId: flags['client-id'],
25+
forceRelink: flags.reset,
26+
userProvidedConfigName: flags.config,
27+
unsafeReportMode: true,
28+
})
29+
30+
await validateApp(app)
31+
32+
return {app}
33+
}
34+
}

packages/app/src/cli/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import GenerateSchema from './commands/app/generate/schema.js'
2323
import ImportExtensions from './commands/app/import-extensions.js'
2424
import AppInfo from './commands/app/info.js'
2525
import Init from './commands/app/init.js'
26+
import Validate from './commands/app/validate.js'
2627
import Release from './commands/app/release.js'
2728
import VersionsList from './commands/app/versions/list.js'
2829
import WebhookTrigger from './commands/app/webhook/trigger.js'
@@ -55,6 +56,7 @@ export const commands: {[key: string]: typeof AppLinkedCommand | typeof AppUnlin
5556
'app:import-extensions': ImportExtensions,
5657
'app:info': AppInfo,
5758
'app:init': Init,
59+
'app:validate': Validate,
5860
'app:release': Release,
5961
'app:config:link': ConfigLink,
6062
'app:config:use': ConfigUse,
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import {validateApp} from './validate.js'
2+
import {testAppLinked} from '../models/app/app.test-data.js'
3+
import {AppErrors} from '../models/app/loader.js'
4+
import {describe, expect, test, vi} from 'vitest'
5+
import {renderError, renderSuccess} from '@shopify/cli-kit/node/ui'
6+
import {AbortError} from '@shopify/cli-kit/node/error'
7+
8+
vi.mock('@shopify/cli-kit/node/ui')
9+
10+
describe('validateApp', () => {
11+
test('renders success when there are no errors', async () => {
12+
// Given
13+
const app = testAppLinked()
14+
15+
// When
16+
await validateApp(app)
17+
18+
// Then
19+
expect(renderSuccess).toHaveBeenCalledWith({headline: 'App configuration is valid.'})
20+
expect(renderError).not.toHaveBeenCalled()
21+
})
22+
23+
test('renders errors and throws when there are validation errors', async () => {
24+
// Given
25+
const errors = new AppErrors()
26+
errors.addError('/path/to/shopify.app.toml', 'client_id is required')
27+
errors.addError('/path/to/extensions/my-ext/shopify.extension.toml', 'invalid type "unknown"')
28+
const app = testAppLinked()
29+
app.errors = errors
30+
31+
// When / Then
32+
await expect(validateApp(app)).rejects.toThrow(AbortError)
33+
expect(renderError).toHaveBeenCalledWith({
34+
headline: 'Validation errors found.',
35+
body: expect.stringContaining('client_id is required'),
36+
})
37+
expect(renderSuccess).not.toHaveBeenCalled()
38+
})
39+
40+
test('renders success when errors object exists but is empty', async () => {
41+
// Given
42+
const errors = new AppErrors()
43+
const app = testAppLinked()
44+
app.errors = errors
45+
46+
// When
47+
await validateApp(app)
48+
49+
// Then
50+
expect(renderSuccess).toHaveBeenCalledWith({headline: 'App configuration is valid.'})
51+
})
52+
})
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import {AppLinkedInterface} from '../models/app/app.js'
2+
import {stringifyMessage} from '@shopify/cli-kit/node/output'
3+
import {renderError, renderSuccess} from '@shopify/cli-kit/node/ui'
4+
import {AbortError} from '@shopify/cli-kit/node/error'
5+
6+
export async function validateApp(app: AppLinkedInterface): Promise<void> {
7+
const errors = app.errors
8+
9+
if (!errors || errors.isEmpty()) {
10+
renderSuccess({headline: 'App configuration is valid.'})
11+
return
12+
}
13+
14+
const errorMessages = errors.toJSON().map((error) => stringifyMessage(error).trim())
15+
16+
renderError({
17+
headline: 'Validation errors found.',
18+
body: errorMessages.join('\n\n'),
19+
})
20+
21+
throw new AbortError('App configuration is invalid.')
22+
}

0 commit comments

Comments
 (0)