Skip to content

Commit 8792dc6

Browse files
committed
Redirect to app redirect landing page in admin
1 parent c0efc0f commit 8792dc6

5 files changed

Lines changed: 106 additions & 6 deletions

File tree

packages/app/src/cli/services/dev/extension/payload/store.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ describe('getExtensionsPayloadStoreRawPayload()', () => {
3434
app: {
3535
title: 'mock-app-name',
3636
apiKey: 'mock-api-key',
37-
url: 'https://mock-store-fqdn.myshopify.com/admin/oauth/redirect_from_cli?client_id=mock-api-key',
37+
url: 'https://admin.shopify.com/store/mock-store-fqdn/extensions-dev/preview?client_id=mock-api-key',
3838
mobileUrl:
3939
'https://mock-store-fqdn.myshopify.com/admin/apps/mock-api-key?shop=mock-store-fqdn.myshopify.com&host=bW9jay1zdG9yZS1mcWRuLm15c2hvcGlmeS5jb20vYWRtaW4vYXBwcy9tb2NrLWFwaS1rZXk',
4040
},

packages/app/src/cli/services/dev/processes/setup-dev-processes.test.ts

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ import {getEnvironmentVariables} from '@shopify/cli-kit/node/environment'
3636
import {isStorefrontPasswordProtected} from '@shopify/theme'
3737
import {fetchTheme} from '@shopify/cli-kit/node/themes/api'
3838
import {firstPartyDev} from '@shopify/cli-kit/node/context/local'
39-
import {adminFqdn} from '@shopify/cli-kit/node/context/fqdn'
39+
import {adminFqdn, normalizeStoreFqdn, storeAdminUrl} from '@shopify/cli-kit/node/context/fqdn'
4040

4141
vi.mock('../../context/identifiers.js')
4242
vi.mock('@shopify/cli-kit/node/session.js')
@@ -50,6 +50,8 @@ vi.mock('@shopify/cli-kit/node/context/fqdn', async (importOriginal) => {
5050
return {
5151
...original,
5252
adminFqdn: vi.fn(),
53+
normalizeStoreFqdn: vi.fn(original.normalizeStoreFqdn),
54+
storeAdminUrl: vi.fn(original.storeAdminUrl),
5355
}
5456
})
5557

@@ -312,6 +314,69 @@ describe('setup-dev-processes', () => {
312314
})
313315
})
314316

317+
test('uses the admin-web preflight URL for local development stores', async () => {
318+
const developerPlatformClient: DeveloperPlatformClient = testDeveloperPlatformClient()
319+
const storeFqdn = 'test.my.shop.dev'
320+
const storeId = '123456789'
321+
const remoteAppUpdated = true
322+
const graphiqlPort = 1234
323+
const commandOptions: DevConfig['commandOptions'] = {
324+
...appContextResult,
325+
directory: '',
326+
update: false,
327+
commandConfig: new Config({root: ''}),
328+
skipDependenciesInstallation: false,
329+
tunnel: {mode: 'auto'},
330+
}
331+
const network: DevConfig['network'] = {
332+
proxyUrl: 'https://example.com/proxy',
333+
proxyPort: 444,
334+
backendPort: 111,
335+
frontendPort: 222,
336+
currentUrls: {
337+
applicationUrl: 'https://example.com/application',
338+
redirectUrlWhitelist: ['https://example.com/redirect'],
339+
},
340+
}
341+
const localApp = testAppWithConfig()
342+
vi.spyOn(loader, 'reloadApp').mockResolvedValue(localApp)
343+
vi.mocked(normalizeStoreFqdn).mockReturnValue('test.my.shop.dev')
344+
vi.mocked(storeAdminUrl).mockReturnValue('admin.shop.dev/store/test')
345+
346+
const remoteApp: DevConfig['remoteApp'] = {
347+
apiKey: 'api-key',
348+
apiSecretKeys: [{secret: 'api-secret'}],
349+
id: '1234',
350+
title: 'App',
351+
organizationId: '5678',
352+
grantedScopes: [],
353+
flags: [],
354+
developerPlatformClient,
355+
}
356+
357+
const res = await setupDevProcesses({
358+
localApp,
359+
commandOptions,
360+
network,
361+
remoteApp,
362+
remoteAppUpdated,
363+
storeFqdn,
364+
storeId,
365+
developerPlatformClient,
366+
partnerUrlsUpdated: true,
367+
graphiqlPort,
368+
graphiqlKey: 'somekey',
369+
})
370+
371+
expect(res.previewUrl).toBe('https://admin.shop.dev/store/test/extensions-dev/preview?client_id=api-key')
372+
expect(res.processes[1]).toMatchObject({
373+
type: 'graphiql',
374+
options: {
375+
appUrl: 'https://admin.shop.dev/store/test/extensions-dev/preview?client_id=api-key',
376+
},
377+
})
378+
})
379+
315380
test('process list includes dev-session when useDevSession is true', async () => {
316381
const developerPlatformClient: DeveloperPlatformClient = testDeveloperPlatformClient({supportsDevSessions: true})
317382
const storeFqdn = 'store.myshopify.io'

packages/app/src/cli/services/dev/processes/setup-dev-processes.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import {isTruthy} from '@shopify/cli-kit/node/context/utilities'
2525
import {firstPartyDev} from '@shopify/cli-kit/node/context/local'
2626
import {getEnvironmentVariables} from '@shopify/cli-kit/node/environment'
2727
import {outputInfo} from '@shopify/cli-kit/node/output'
28-
import {adminFqdn} from '@shopify/cli-kit/node/context/fqdn'
28+
import {adminFqdn, normalizeStoreFqdn} from '@shopify/cli-kit/node/context/fqdn'
2929

3030
interface ProxyServerProcess extends BaseProcess<{
3131
port: number
@@ -108,8 +108,10 @@ export async function setupDevProcesses({
108108

109109
// appPreviewUrl is the direct app URL (used by GraphiQL and dev session fallback)
110110
// previewURL is what's shown to the user (may be dev console for 1P devs)
111+
const isLocalStore = normalizeStoreFqdn(storeFqdn).endsWith('.my.shop.dev')
112+
111113
let appPreviewUrl: string
112-
if (is1PDev) {
114+
if (is1PDev || isLocalStore) {
113115
appPreviewUrl = buildAppURLForWeb(storeFqdn, apiKey)
114116
} else {
115117
const adminDomain = await adminFqdn()
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import {buildAppURLForWeb} from './app-url.js'
2+
import {describe, expect, test, vi} from 'vitest'
3+
import {normalizeStoreFqdn, storeAdminUrl} from '@shopify/cli-kit/node/context/fqdn'
4+
5+
vi.mock('@shopify/cli-kit/node/context/fqdn', async (importOriginal) => {
6+
const original = await importOriginal<typeof import('@shopify/cli-kit/node/context/fqdn')>()
7+
return {
8+
...original,
9+
normalizeStoreFqdn: vi.fn(original.normalizeStoreFqdn),
10+
storeAdminUrl: vi.fn(original.storeAdminUrl),
11+
}
12+
})
13+
14+
describe('buildAppURLForWeb', () => {
15+
test('builds the admin-web preflight preview URL for production stores', () => {
16+
const url = buildAppURLForWeb('my-store.myshopify.com', 'api-key')
17+
18+
expect(url).toBe('https://admin.shopify.com/store/my-store/extensions-dev/preview?client_id=api-key')
19+
})
20+
21+
test('uses the same admin-web path in local development with the admin.shop.dev host', () => {
22+
vi.mocked(normalizeStoreFqdn).mockReturnValue('my-store.my.shop.dev')
23+
vi.mocked(storeAdminUrl).mockReturnValue('admin.shop.dev/store/my-store')
24+
25+
const url = buildAppURLForWeb('my-store.my.shop.dev', 'api-key')
26+
27+
expect(url).toBe('https://admin.shop.dev/store/my-store/extensions-dev/preview?client_id=api-key')
28+
})
29+
})

packages/app/src/cli/utilities/app/app-url.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,12 @@ import {normalizeStoreFqdn, storeAdminUrl} from '@shopify/cli-kit/node/context/f
22

33
export function buildAppURLForWeb(storeFqdn: string, apiKey: string) {
44
const normalizedFQDN = normalizeStoreFqdn(storeFqdn)
5-
const adminUrl = storeAdminUrl(normalizedFQDN)
6-
return `https://${adminUrl}/admin/oauth/redirect_from_cli?client_id=${apiKey}`
5+
const storeName = normalizedFQDN.split('.')[0]
6+
const localAdminUrl = storeAdminUrl(normalizedFQDN)
7+
const adminDomain = localAdminUrl === normalizedFQDN ? 'admin.shopify.com' : localAdminUrl.split('/')[0]
8+
const searchParams = new URLSearchParams({client_id: apiKey})
9+
10+
return `https://${adminDomain}/store/${storeName}/extensions-dev/preview?${searchParams.toString()}`
711
}
812

913
export function buildAppURLForAdmin(storeFqdn: string, apiKey: string, adminDomain: string) {

0 commit comments

Comments
 (0)