Skip to content

Commit

Permalink
Extract host theme wait logic to host-theme-watcher utility and add t…
Browse files Browse the repository at this point in the history
…ests
  • Loading branch information
jamesmengo committed Jul 4, 2024
1 parent be34097 commit 3119ebc
Show file tree
Hide file tree
Showing 4 changed files with 107 additions and 74 deletions.
52 changes: 15 additions & 37 deletions packages/app/src/cli/utilities/host-theme-manager-next.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import {HostThemeManager, UPDATER_TIMEOUT} from './host-theme-manager-next.js'
import {HostThemeManager} from './host-theme-manager-next.js'
import {waitForThemeToBeProcessed} from './host-theme-watcher.js'
import {createHostTheme} from '@shopify/cli-kit/node/themes/api'
import {AdminSession} from '@shopify/cli-kit/node/session'
import {fetchTheme} from '@shopify/cli-kit/node/themes/api'
import {beforeEach, describe, expect, test, vi} from 'vitest'

vi.mock('@shopify/cli-kit/node/themes/api')
vi.mock('./host-theme-watcher.js')

describe('HostThemeManager', () => {
let themeManager: HostThemeManager
Expand All @@ -13,47 +15,23 @@ describe('HostThemeManager', () => {
themeManager = new HostThemeManager(adminSession)
})

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

// When
await themeManager.waitForThemeToBeProcessed(themeId)

// Then
expect(fetchTheme).toHaveBeenCalledTimes(2)
})
// When
await themeManager.create()

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

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

// Then
await expect(promise).rejects.toThrowError('Theme Processing Timeout')
})
})
41 changes: 4 additions & 37 deletions packages/app/src/cli/utilities/host-theme-manager-next.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
import {waitForThemeToBeProcessed} from './host-theme-watcher.js'
import {getHostTheme, removeHostTheme, setHostTheme} from '@shopify/cli-kit/node/themes/conf'
import {ThemeManager} from '@shopify/cli-kit/node/themes/theme-manager'
import {AdminSession} from '@shopify/cli-kit/node/session'
import {createHostTheme, fetchTheme} from '@shopify/cli-kit/node/themes/api'
import {AbortError, BugError} from '@shopify/cli-kit/node/error'
import {sleep} from '@shopify/cli-kit/node/system'
import {Theme} from '@shopify/cli-kit/node/themes/types'
import {createHostTheme} from '@shopify/cli-kit/node/themes/api'
import {BugError} from '@shopify/cli-kit/node/error'
import {DEVELOPMENT_THEME_ROLE} from '@shopify/cli-kit/node/themes/utils'

// 5 minutes
export const UPDATER_TIMEOUT = 5 * 60 * 1000
export class HostThemeManager extends ThemeManager {
protected context = 'App Ext. Host'

Expand All @@ -17,12 +14,6 @@ export class HostThemeManager extends ThemeManager {
this.themeId = getHostTheme(adminSession.storeFqdn)
}

async findOrCreate(): Promise<Theme> {
const theme = await super.findOrCreate()
await this.waitForThemeToBeProcessed(theme.id)
return theme
}

async create(themeRole = DEVELOPMENT_THEME_ROLE, themeName?: string) {
const name = themeName || this.context
const src = 'https://cdn.shopify.com/theme-store/uhrdefhlndzaoyrgylhto59sx2i7.jpg'
Expand All @@ -31,6 +22,7 @@ export class HostThemeManager extends ThemeManager {
throw new BugError(`Could not create theme with name "${name}" and role "${themeRole}"`)
}
this.setTheme(theme.id.toString())
await waitForThemeToBeProcessed(theme.id, this.adminSession)
return theme
}

Expand All @@ -41,29 +33,4 @@ export class HostThemeManager extends ThemeManager {
protected removeTheme(): void {
removeHostTheme(this.adminSession.storeFqdn)
}

private async waitForThemeToBeProcessed(themeId: number, 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 this.themeIsProcessing(themeId, this.adminSession, startTime)) {}
}

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

if (!theme) {
throw new AbortError('No theme app extension host theme found.', 'Please try again.')
}

const elapsedTime = Date.now() - startTime

if (theme.processing && elapsedTime >= UPDATER_TIMEOUT) {
throw new AbortError('Theme Processing Timeout', 'Please try again.')
}

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

return theme.processing
}
}
56 changes: 56 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,56 @@
import {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('Theme Processing Timeout')
})
})
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'

// 5 minutes
export const UPDATER_TIMEOUT = 5 * 60 * 1000

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) {
throw new AbortError('No theme app extension host theme found.', 'Please try again.')
}

const elapsedTime = Date.now() - startTime

if (theme.processing && elapsedTime >= UPDATER_TIMEOUT) {
throw new AbortError('Theme Processing Timeout', 'Please try again.')
}

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

return theme.processing
}

0 comments on commit 3119ebc

Please sign in to comment.