Skip to content

Commit

Permalink
Feat: Wait for theme to finish processing
Browse files Browse the repository at this point in the history
  • Loading branch information
jamesmengo committed Aug 9, 2024
1 parent 5a9af1d commit 4cb5f23
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ export async function findOrCreateHostTheme(adminSession: AdminSession, theme?:
throw new AbortError(`Could not find a theme on shop ${adminSession.storeFqdn} with id ${theme}`)
}
} else {
const themeManager = new HostThemeManager(adminSession)
const themeManager = new HostThemeManager(adminSession, {devPreview: true})
hostTheme = await themeManager.findOrCreate()
}
return hostTheme.id.toString()
Expand Down
67 changes: 67 additions & 0 deletions packages/app/src/cli/utilities/host-theme-watcher.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import {FAILED_TO_CREATE_THEME_MESSAGE, UPDATER_TIMEOUT, waitForThemeToBeProcessed} from './host-theme-watcher.js'
import {AdminSession} from '@shopify/cli-kit/node/session'
import {sleep} from '@shopify/cli-kit/node/system'
import {fetchTheme} from '@shopify/cli-kit/node/themes/api'
import {describe, expect, test, vi} from 'vitest'

vi.mock('@shopify/cli-kit/node/themes/api')
vi.mock('@shopify/cli-kit/node/system')

describe('HostThemeWatcher', () => {
const themeId = 12345
const adminSession: AdminSession = {token: 'token', storeFqdn: 'storeFqdn'}

test('should wait for the theme to be processed', async () => {
// Given
vi.mocked(sleep).mockResolvedValue()
vi.mocked(fetchTheme)
.mockResolvedValueOnce({
processing: true,
id: themeId,
name: 'Theme',
role: 'development',
createdAtRuntime: true,
})
.mockResolvedValue({
processing: false,
id: themeId,
name: 'Theme',
role: 'development',
createdAtRuntime: true,
})

// When
await waitForThemeToBeProcessed(themeId, adminSession, Date.now())

// Then
expect(fetchTheme).toHaveBeenCalledTimes(2)
})

test('should throw an error if the theme is not processed within the timeout', async () => {
// Given
vi.mocked(fetchTheme).mockResolvedValue({
processing: true,
id: themeId,
name: 'Theme',
role: 'development',
createdAtRuntime: true,
})

// When
const promise = waitForThemeToBeProcessed(themeId, adminSession, Date.now() - UPDATER_TIMEOUT)

// Then
await expect(promise).rejects.toThrowError(FAILED_TO_CREATE_THEME_MESSAGE)
})

test('should throw an error if the theme is not found', async () => {
// Given
vi.mocked(fetchTheme).mockResolvedValue(undefined)

// When
const promise = waitForThemeToBeProcessed(themeId, adminSession, Date.now())

// Then
await expect(promise).rejects.toThrowError(FAILED_TO_CREATE_THEME_MESSAGE)
})
})
32 changes: 32 additions & 0 deletions packages/app/src/cli/utilities/host-theme-watcher.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import {AbortError} from '@shopify/cli-kit/node/error'
import {AdminSession} from '@shopify/cli-kit/node/session'
import {sleep} from '@shopify/cli-kit/node/system'
import {fetchTheme} from '@shopify/cli-kit/node/themes/api'
import {Theme} from '@shopify/cli-kit/node/themes/types'

// 5 minutes
export const UPDATER_TIMEOUT = 5 * 60 * 1000
export const FAILED_TO_CREATE_THEME_MESSAGE =
'The host theme could not be created to host your theme app extension. Please try again or use the "--theme" flag to use an existing theme as the host theme.'

export async function waitForThemeToBeProcessed(themeId: number, adminSession: AdminSession, startTime = Date.now()) {
// Each iteration must wait for the response before the next poll is initiated.
// eslint-disable-next-line no-await-in-loop, no-empty
while (await themeIsProcessing(themeId, adminSession, startTime)) {}
}

async function themeIsProcessing(themeId: number, session: AdminSession, startTime: number) {
const theme = await fetchTheme(themeId, session)

if (!theme || themeProcessingTimedOut(theme, startTime)) {
throw new AbortError(FAILED_TO_CREATE_THEME_MESSAGE)
}

// Sleep for 3 seconds before polling again
await sleep(3)

return theme.processing
}
function themeProcessingTimedOut(theme: Theme, startTime: number): boolean {
return theme.processing && Date.now() - startTime >= UPDATER_TIMEOUT
}

0 comments on commit 4cb5f23

Please sign in to comment.