Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import {getEnvironmentVariables} from '@shopify/cli-kit/node/environment'
import {isStorefrontPasswordProtected} from '@shopify/theme'
import {fetchTheme} from '@shopify/cli-kit/node/themes/api'
import {firstPartyDev} from '@shopify/cli-kit/node/context/local'
import {adminFqdn} from '@shopify/cli-kit/node/context/fqdn'

vi.mock('../../context/identifiers.js')
vi.mock('@shopify/cli-kit/node/session.js')
Expand All @@ -44,6 +45,13 @@ vi.mock('@shopify/cli-kit/node/environment')
vi.mock('@shopify/theme')
vi.mock('@shopify/cli-kit/node/themes/api')
vi.mock('@shopify/cli-kit/node/context/local')
vi.mock('@shopify/cli-kit/node/context/fqdn', async (importOriginal) => {
const original = await importOriginal<typeof import('@shopify/cli-kit/node/context/fqdn')>()
return {
...original,
adminFqdn: vi.fn(),
}
})

beforeEach(() => {
// mocked for draft extensions
Expand Down Expand Up @@ -71,6 +79,7 @@ beforeEach(() => {
})
// By default, firstPartyDev is false (local dev console only shown for 1P devs)
vi.mocked(firstPartyDev).mockReturnValue(false)
vi.mocked(adminFqdn).mockResolvedValue('admin.shopify.com')
})

const appContextResult = {
Expand Down Expand Up @@ -167,7 +176,7 @@ describe('setup-dev-processes', () => {
})

// Dev console is NOT shown by default (only shown for 1P devs)
expect(res.previewUrl).toBe('https://store.myshopify.io/admin/oauth/redirect_from_cli?client_id=api-key')
expect(res.previewUrl).toBe('https://admin.shopify.com/store/store/apps/api-key')
expect(res.processes[0]).toMatchObject({
type: 'web',
prefix: 'web-backend-frontend',
Expand Down Expand Up @@ -197,7 +206,7 @@ describe('setup-dev-processes', () => {
apiKey: 'api-key',
apiSecret: 'api-secret',
port: expect.any(Number),
appUrl: 'https://store.myshopify.io/admin/oauth/redirect_from_cli?client_id=api-key',
appUrl: 'https://admin.shopify.com/store/store/apps/api-key',
key: 'somekey',
storeFqdn: 'store.myshopify.io',
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {AppLinkedInterface, getAppScopes, WebType} from '../../../models/app/app
import {OrganizationApp} from '../../../models/organization.js'
import {DevOptions} from '../../dev.js'
import {LocalhostCert, getProxyingWebServer} from '../../../utilities/app/http-reverse-proxy.js'
import {buildAppURLForWeb} from '../../../utilities/app/app-url.js'
import {buildAppURLForAdmin, buildAppURLForWeb} from '../../../utilities/app/app-url.js'
import {ApplicationURLs} from '../urls.js'
import {DeveloperPlatformClient} from '../../../utilities/developer-platform-client.js'
import {AppEventWatcher} from '../app-events/app-event-watcher.js'
Expand All @@ -25,6 +25,7 @@ import {isTruthy} from '@shopify/cli-kit/node/context/utilities'
import {firstPartyDev} from '@shopify/cli-kit/node/context/local'
import {getEnvironmentVariables} from '@shopify/cli-kit/node/environment'
import {outputInfo} from '@shopify/cli-kit/node/output'
import {adminFqdn} from '@shopify/cli-kit/node/context/fqdn'

interface ProxyServerProcess
extends BaseProcess<{
Expand Down Expand Up @@ -91,7 +92,6 @@ export async function setupDevProcesses({
}> {
const apiKey = remoteApp.apiKey
const apiSecret = remoteApp.apiSecretKeys[0]?.secret ?? ''
const appPreviewUrl = buildAppURLForWeb(storeFqdn, apiKey)
const env = getEnvironmentVariables()
const shouldRenderGraphiQL = !isTruthy(env[environmentVariableNames.disableGraphiQLExplorer])

Expand All @@ -100,10 +100,24 @@ export async function setupDevProcesses({
const appWatcher = new AppEventWatcher(reloadedApp, network.proxyUrl)

// Decide on the appropriate preview URL for a session with these processes
// Use local dev console only for 1P developers (SHOPIFY_CLI_1P_DEV is enabled)
// - 1P developers with previewable extensions: use local dev console
// - 1P developers without previewable extensions: use legacy OAuth redirect URL
// - 3P developers: use unified admin URL
const anyPreviewableExtensions = reloadedApp.allExtensions.some((ext) => ext.isPreviewable)
const devConsoleURL = `${network.proxyUrl}/extensions/dev-console`
const useDevConsole = firstPartyDev() && anyPreviewableExtensions
const is1PDev = firstPartyDev()

// appPreviewUrl is the direct app URL (used by GraphiQL and dev session fallback)
// previewURL is what's shown to the user (may be dev console for 1P devs)
let appPreviewUrl: string
if (is1PDev) {
appPreviewUrl = buildAppURLForWeb(storeFqdn, apiKey)
} else {
const adminDomain = await adminFqdn()
appPreviewUrl = buildAppURLForAdmin(storeFqdn, apiKey, adminDomain)
}

const useDevConsole = is1PDev && anyPreviewableExtensions
const previewURL = useDevConsole ? devConsoleURL : appPreviewUrl

const graphiqlURL = shouldRenderGraphiQL
Expand Down
7 changes: 7 additions & 0 deletions packages/app/src/cli/utilities/app/app-url.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@ export function buildAppURLForWeb(storeFqdn: string, apiKey: string) {
return `https://${adminUrl}/admin/oauth/redirect_from_cli?client_id=${apiKey}`
}

export function buildAppURLForAdmin(storeFqdn: string, apiKey: string, adminDomain: string) {
const normalizedFQDN = normalizeStoreFqdn(storeFqdn)
const storeName = normalizedFQDN.split('.')[0]

return `https://${adminDomain}/store/${storeName}/apps/${apiKey}`
}

export function buildAppURLForMobile(storeFqdn: string, apiKey: string) {
const normalizedFQDN = normalizeStoreFqdn(storeFqdn)
const adminUrl = storeAdminUrl(normalizedFQDN)
Expand Down
Loading