-
Notifications
You must be signed in to change notification settings - Fork 128
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add import admin link extensions to CLI and migration on deployment
- Loading branch information
1 parent
b564bd8
commit 9848bad
Showing
7 changed files
with
224 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
63 changes: 63 additions & 0 deletions
63
packages/app/src/cli/services/admin-link/extension-to-toml.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
import {buildTomlObject} from './extension-to-toml.js' | ||
import {ExtensionRegistration} from '../../api/graphql/all_app_extension_registrations.js' | ||
import {describe, expect, test} from 'vitest' | ||
|
||
describe('extension-to-toml', () => { | ||
test('correctly builds a toml string for a app_link', () => { | ||
// Given | ||
const extension1: ExtensionRegistration = { | ||
id: '26237698049', | ||
uuid: 'ad9947a9-bc0b-4855-82da-008aefbc1c71', | ||
title: 'Admin link title', | ||
type: 'app_link', | ||
draftVersion: { | ||
context: 'COLLECTIONS#SHOW', | ||
config: '{"text":"admin link label","url":"https://google.es"}', | ||
}, | ||
} | ||
|
||
// When | ||
const got = buildTomlObject(extension1) | ||
|
||
// Then | ||
expect(got).toEqual(`[[extensions]] | ||
type = "admin_link" | ||
name = "Admin link title" | ||
handle = "admin-link-title" | ||
[[extensions.targeting]] | ||
text = "admin link label" | ||
url = "https://google.es" | ||
target = "admin.collection.item.action" | ||
`) | ||
}) | ||
|
||
test('correctly builds a toml string for a bulk_action', () => { | ||
// Given | ||
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) | ||
|
||
// Then | ||
expect(got).toEqual(`[[extensions]] | ||
type = "admin_link" | ||
name = "Bulk action title" | ||
handle = "bulk-action-title" | ||
[[extensions.targeting]] | ||
text = "bulk action label" | ||
url = "https://google.es" | ||
target = "admin.product.selection.action" | ||
`) | ||
}) | ||
}) |
42 changes: 42 additions & 0 deletions
42
packages/app/src/cli/services/admin-link/extension-to-toml.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
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 {encodeToml} from '@shopify/cli-kit/node/toml' | ||
import {slugify} from '@shopify/cli-kit/common/string' | ||
|
||
interface AdminLinkConfig { | ||
text: string | ||
url: string | ||
} | ||
|
||
/** | ||
* Given an app_link or bulk_action extension config file, convert it to toml | ||
*/ | ||
export function buildTomlObject(extension: ExtensionRegistration): string { | ||
const versionConfig = extension.activeVersion?.config ?? extension.draftVersion?.config | ||
if (!versionConfig) throw new Error('No config found for extension') | ||
|
||
const context = extension.activeVersion?.context ?? extension.draftVersion?.context | ||
if (!context) throw new Error('No context found for link extension') | ||
|
||
const config: AdminLinkConfig = JSON.parse(versionConfig) | ||
|
||
const localExtensionRepresentation = { | ||
extensions: [ | ||
{ | ||
type: 'admin_link', | ||
name: extension.title, | ||
handle: slugify(extension.title.substring(0, MAX_EXTENSION_HANDLE_LENGTH)), | ||
targeting: [ | ||
{ | ||
text: config.text, | ||
url: config.url, | ||
target: contextToTarget(context), | ||
}, | ||
], | ||
}, | ||
], | ||
} | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
return encodeToml(localExtensionRepresentation as any) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import {contextToTarget} from './utils.js' | ||
import {describe, expect, test} from 'vitest' | ||
|
||
describe('admin link utils', () => { | ||
test('correctly parses from context `COLLECTIONS#SHOW` to target', () => { | ||
// Given | ||
const context = 'COLLECTIONS#SHOW' | ||
|
||
// When | ||
const target = contextToTarget(context) | ||
|
||
// Then | ||
expect(target).toEqual('admin.collection.item.action') | ||
}) | ||
test('correctly parses from context `ORDERS#INDEX` to target', () => { | ||
// Given | ||
const context = 'ORDERS#INDEX' | ||
|
||
// When | ||
const target = contextToTarget(context) | ||
|
||
// Then | ||
expect(target).toEqual('admin.order.index.action') | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
export const contextToTarget = (context: string) => { | ||
const splitContext = context.split('#') | ||
if (splitContext.length !== 2 || splitContext.some((part) => part === '' || part === undefined)) { | ||
throw new Error('Invalid context') | ||
} | ||
const domain = 'admin' | ||
const subDomain = typeToSubDomain(splitContext[0] || '') | ||
Check warning on line 7 in packages/app/src/cli/services/admin-link/utils.ts GitHub Actions / ESLint Report Analysispackages/app/src/cli/services/admin-link/utils.ts#L7
|
||
const entity = locationToEntity(splitContext[1] || '') | ||
Check warning on line 8 in packages/app/src/cli/services/admin-link/utils.ts GitHub Actions / ESLint Report Analysispackages/app/src/cli/services/admin-link/utils.ts#L8
|
||
const action = 'action' | ||
|
||
return [domain, subDomain, entity, action].join('.') | ||
} | ||
|
||
const locationToEntity = (location: string) => { | ||
switch (location.toLocaleLowerCase()) { | ||
case 'show': | ||
return 'item' | ||
case 'index': | ||
return 'index' | ||
case 'action': | ||
return 'selection' | ||
case 'fulfilled_card': | ||
return 'fulfilled_card' | ||
default: | ||
throw new Error(`Invalid context location: ${location}`) | ||
} | ||
} | ||
const typeToSubDomain = (word: string) => { | ||
return word.toLocaleLowerCase().replace(new RegExp(`(s)$`), '') | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
38 changes: 38 additions & 0 deletions
38
packages/app/src/cli/services/dev/migrate-admin-link-extension.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
import {LocalSource, RemoteSource} from '../context/identifiers.js' | ||
import {IdentifiersExtensions} from '../../models/app/identifiers.js' | ||
import {getExtensionIds, LocalRemoteSource} from '../context/id-matching.js' | ||
import {MAX_EXTENSION_HANDLE_LENGTH} from '../../models/extensions/schemas.js' | ||
import {slugify} from '@shopify/cli-kit/common/string' | ||
|
||
export function getAdminLinkExtensionsToMigrate( | ||
localSources: LocalSource[], | ||
remoteSources: RemoteSource[], | ||
identifiers: IdentifiersExtensions, | ||
) { | ||
const ids = getExtensionIds(localSources, identifiers) | ||
const localExtensionTypesToMigrate = ['admin_link'] | ||
const remoteExtensionTypesToMigrate = ['app_link', 'bulk_action'] | ||
const typesMap = new Map<string, string[]>() | ||
typesMap.set('admin_link', ['app_link', 'bulk_action']) | ||
|
||
const local = localSources.filter((source) => localExtensionTypesToMigrate.includes(source.type)) | ||
const remote = remoteSources.filter((source) => remoteExtensionTypesToMigrate.includes(source.type)) | ||
|
||
// Map remote sources by uuid and slugified title (the slugified title is used for matching with local folder) | ||
const remoteSourcesMap = new Map<string, RemoteSource>() | ||
remote.forEach((remoteSource) => { | ||
remoteSourcesMap.set(remoteSource.uuid, remoteSource) | ||
remoteSourcesMap.set(slugify(remoteSource.title.substring(0, MAX_EXTENSION_HANDLE_LENGTH)), remoteSource) | ||
}) | ||
|
||
return local.reduce<LocalRemoteSource[]>((accumulator, localSource) => { | ||
const localSourceId = ids[localSource.localIdentifier] ?? 'unknown' | ||
const remoteSource = remoteSourcesMap.get(localSourceId) || remoteSourcesMap.get(localSource.localIdentifier) | ||
Check warning on line 30 in packages/app/src/cli/services/dev/migrate-admin-link-extension.ts GitHub Actions / ESLint Report Analysispackages/app/src/cli/services/dev/migrate-admin-link-extension.ts#L30
|
||
const typeMatch = typesMap.get(localSource.type)?.includes(remoteSource?.type ?? 'undefined') | ||
|
||
if (remoteSource && typeMatch) { | ||
accumulator.push({local: localSource, remote: remoteSource}) | ||
} | ||
return accumulator | ||
}, []) | ||
} |