Skip to content

Commit

Permalink
on import of legacy link extensions make absolute urls relative for e…
Browse files Browse the repository at this point in the history
…mbedded apps
  • Loading branch information
alfonso-noriega committed Nov 21, 2024
1 parent 2450a80 commit 95ec4f0
Show file tree
Hide file tree
Showing 9 changed files with 99 additions and 13 deletions.
7 changes: 6 additions & 1 deletion packages/app/src/cli/commands/app/import-extensions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {appFlags} from '../../flags.js'
import {importExtensions} from '../../services/import-extensions.js'
import AppCommand, {AppCommandOutput} from '../../utilities/app-command.js'
import {linkedAppContext} from '../../services/app-context.js'
import {CurrentAppConfiguration} from '../../models/app/app.js'
import {renderSelectPrompt, renderFatalError} from '@shopify/cli-kit/node/ui'
import {Flags} from '@oclif/core'
import {globalFlags} from '@shopify/cli-kit/node/cli'
Expand All @@ -17,7 +18,11 @@ interface MigrationChoice {
label: string
value: string
extensionTypes: string[]
buildTomlObject: (ext: ExtensionRegistration, allExtensions: ExtensionRegistration[]) => string
buildTomlObject: (
ext: ExtensionRegistration,
allExtensions: ExtensionRegistration[],
appConfiguration: CurrentAppConfiguration,
) => string
}

const getMigrationChoices = (): MigrationChoice[] => [
Expand Down
1 change: 1 addition & 0 deletions packages/app/src/cli/models/app/app.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ const CORRECT_CURRENT_APP_SCHEMA: CurrentAppConfiguration = {
},
},
application_url: 'http://example.com',
embedded: false,
auth: {
redirect_urls: ['https://google.com'],
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {WebhooksConfig} from './app_config_webhook.js'
export interface AppConfigurationUsedByCli {
name: string
application_url: string
embedded: boolean
app_proxy?: {
url: string
prefix: string
Expand Down
62 changes: 56 additions & 6 deletions packages/app/src/cli/services/admin-link/extension-to-toml.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,16 @@ import {ExtensionRegistration} from '../../api/graphql/all_app_extension_registr
import {describe, expect, test} from 'vitest'

describe('extension-to-toml', () => {
test('correctly builds a toml string for a app_link', () => {
test('correctly builds a toml string for a app_link extension on a non embedded app', () => {
// Given
const appConfig = {
path: '',
name: 'app 1',
client_id: '12345',
application_url: 'http://example.com',
embedded: false,
}

const extension1: ExtensionRegistration = {
id: '26237698049',
uuid: 'ad9947a9-bc0b-4855-82da-008aefbc1c71',
Expand All @@ -17,7 +25,7 @@ describe('extension-to-toml', () => {
}

// When
const got = buildTomlObject(extension1)
const got = buildTomlObject(extension1, [], appConfig)

// Then
expect(got).toEqual(`[[extensions]]
Expand All @@ -32,21 +40,28 @@ handle = "admin-link-title"
`)
})

test('correctly builds a toml string for a bulk_action', () => {
test('correctly builds a toml string for bulk_action extension with path in an embedded app', () => {
// Given
const appConfig = {
path: '',
name: 'app 1',
client_id: '12345',
application_url: 'http://example.com',
embedded: true,
}
const extension1: ExtensionRegistration = {
id: '26237698049',
uuid: 'ad9947a9-bc0b-4855-82da-008aefbc1c71',
title: 'Bulk action title',
type: 'bulk_action',
draftVersion: {
context: 'PRODUCTS#ACTION',
config: '{"text":"bulk action label","url":"https://google.es"}',
config: '{"text":"bulk action label","url":"https://google.es/action/product?product_id=123#hash"}',
},
}

// When
const got = buildTomlObject(extension1)
const got = buildTomlObject(extension1, [], appConfig)

// Then
expect(got).toEqual(`[[extensions]]
Expand All @@ -56,7 +71,42 @@ handle = "bulk-action-title"
[[extensions.targeting]]
text = "bulk action label"
url = "https://google.es"
url = "app://action/product?product_id=123#hash"
target = "admin.product.selection.link"
`)
})
test('correctly builds a toml string for bulk_action extension with path in an embedded app', () => {
// Given
const appConfig = {
path: '',
name: 'app 1',
client_id: '12345',
application_url: 'http://example.com',
embedded: true,
}
const extension1: ExtensionRegistration = {
id: '26237698049',
uuid: 'ad9947a9-bc0b-4855-82da-008aefbc1c71',
title: 'Bulk action title',
type: 'bulk_action',
draftVersion: {
context: 'PRODUCTS#ACTION',
config: '{"text":"bulk action label","url":"https://google.es/"}',
},
}

// When
const got = buildTomlObject(extension1, [], appConfig)

// Then
expect(got).toEqual(`[[extensions]]
type = "admin_link"
name = "Bulk action title"
handle = "bulk-action-title"
[[extensions.targeting]]
text = "bulk action label"
url = "app://"
target = "admin.product.selection.link"
`)
})
Expand Down
22 changes: 21 additions & 1 deletion packages/app/src/cli/services/admin-link/extension-to-toml.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {contextToTarget} from './utils.js'
import {ExtensionRegistration} from '../../api/graphql/all_app_extension_registrations.js'
import {MAX_EXTENSION_HANDLE_LENGTH} from '../../models/extensions/schemas.js'
import {CurrentAppConfiguration} from '../../models/app/app.js'
import {encodeToml} from '@shopify/cli-kit/node/toml'
import {slugify} from '@shopify/cli-kit/common/string'

Expand All @@ -12,7 +13,11 @@ interface AdminLinkConfig {
/**
* Given an app_link or bulk_action extension config file, convert it to toml
*/
export function buildTomlObject(extension: ExtensionRegistration): string {
export function buildTomlObject(
extension: ExtensionRegistration,
_: ExtensionRegistration[],
appConfiguration: CurrentAppConfiguration,
): string {
const versionConfig = extension.activeVersion?.config ?? extension.draftVersion?.config
if (!versionConfig) throw new Error('No config found for extension')

Expand All @@ -21,6 +26,21 @@ export function buildTomlObject(extension: ExtensionRegistration): string {

const config: AdminLinkConfig = JSON.parse(versionConfig)

if (appConfiguration.embedded) {
try {
const linkUrl = new URL(config.url)
const baseUrl = new URL('app://')
const linkPath = linkUrl.pathname.startsWith('/') ? linkUrl.pathname.substring(1) : linkUrl.pathname
const fullUrl = new URL(linkPath, baseUrl)
fullUrl.search = linkUrl.search
fullUrl.hash = linkUrl.hash
config.url = fullUrl.toString()
// eslint-disable-next-line no-catch-all/no-catch-all
} catch (error) {
// Keep original URL if parsing fails
}
}

const localExtensionRepresentation = {
extensions: [
{
Expand Down
2 changes: 2 additions & 0 deletions packages/app/src/cli/services/app-context.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ describe('linkedAppContext', () => {
name: 'test-app',
application_url: 'https://test-app.com',
path: joinPath(tmp, 'shopify.app.toml'),
embedded: false,
},
})

Expand Down Expand Up @@ -195,6 +196,7 @@ describe('linkedAppContext', () => {
name: 'test-app',
application_url: 'https://test-app.com',
path: joinPath(tmp, 'shopify.app.toml'),
embedded: false,
},
})

Expand Down
6 changes: 4 additions & 2 deletions packages/app/src/cli/services/app/config/link.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -582,11 +582,12 @@ test('the local configuration is discarded if the client_id is different from th
scopes: 'write_products',
webhooks: {api_version: '2023-04'},
application_url: 'https://myapp.com',
embedded: false,
build: {
automatically_update_urls_on_dev: true,
dev_store_url: 'my-store.myshopify.com',
},
} as CurrentAppConfiguration,
},
}
vi.mocked(loadApp).mockResolvedValue(await mockApp(tmp, localApp, [], 'current'))
vi.mocked(fetchOrCreateOrganizationApp).mockResolvedValue(
Expand Down Expand Up @@ -1358,7 +1359,8 @@ test('the api client configuration is deep merged with the remote app_config ext
api_version: '2023-04',
},
application_url: 'https://myapp.com',
} as CurrentAppConfiguration,
embedded: true,
},
}
vi.mocked(loadApp).mockResolvedValue(await mockApp(tmp, localApp, [], 'current'))
vi.mocked(fetchOrCreateOrganizationApp).mockResolvedValue(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,7 @@ const APP_CONFIGURATION: CurrentAppConfiguration = {
api_version: '2023-04',
},
application_url: 'https://myapp.com',
embedded: false,
build: {
include_config_on_deploy: true,
},
Expand Down
10 changes: 7 additions & 3 deletions packages/app/src/cli/services/import-extensions.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {ensureExtensionDirectoryExists} from './extensions/common.js'
import {getExtensions} from './fetch-extensions.js'
import {AppLinkedInterface} from '../models/app/app.js'
import {AppLinkedInterface, CurrentAppConfiguration} from '../models/app/app.js'
import {updateAppIdentifiers, IdentifiersExtensions} from '../models/app/identifiers.js'
import {ExtensionRegistration} from '../api/graphql/all_app_extension_registrations.js'
import {DeveloperPlatformClient} from '../utilities/developer-platform-client.js'
Expand All @@ -17,7 +17,11 @@ interface ImportOptions {
remoteApp: OrganizationApp
developerPlatformClient: DeveloperPlatformClient
extensionTypes: string[]
buildTomlObject: (ext: ExtensionRegistration, allExtensions: ExtensionRegistration[]) => string
buildTomlObject: (
ext: ExtensionRegistration,
allExtensions: ExtensionRegistration[],
appConfig: CurrentAppConfiguration,
) => string
}

export async function importExtensions(options: ImportOptions) {
Expand Down Expand Up @@ -54,7 +58,7 @@ export async function importExtensions(options: ImportOptions) {
const extensionUuids: IdentifiersExtensions = {}
const importPromises = extensionsToMigrate.map(async (ext) => {
const directory = await ensureExtensionDirectoryExists({app: options.app, name: ext.title})
const tomlObject = options.buildTomlObject(ext, extensionRegistrations)
const tomlObject = options.buildTomlObject(ext, extensionRegistrations, options.app.configuration)
const path = joinPath(directory, 'shopify.extension.toml')
await writeFile(path, tomlObject)
const handle = slugify(ext.title.substring(0, MAX_EXTENSION_HANDLE_LENGTH))
Expand Down

0 comments on commit 95ec4f0

Please sign in to comment.