Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP channel config extension #4898

Draft
wants to merge 8 commits into
base: main
Choose a base branch
from
7 changes: 7 additions & 0 deletions packages/app/src/cli/commands/app/import-extensions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {buildTomlObject as buildFlowTomlObject} from '../../services/flow/extens
import {buildTomlObject as buildAdminLinkTomlObject} from '../../services/admin-link/extension-to-toml.js'
import {buildTomlObject as buildMarketingActivityTomlObject} from '../../services/marketing_activity/extension-to-toml.js'
import {buildTomlObject as buildSubscriptionLinkTomlObject} from '../../services/subscription_link/extension-to-toml.js'
import {buildTomlObject as buildChannelConfigTomlObject} from '../../services/channel_config/extension-to-toml.js'
import {ExtensionRegistration} from '../../api/graphql/all_app_extension_registrations.js'
import {appFlags} from '../../flags.js'
import {importExtensions} from '../../services/import-extensions.js'
Expand Down Expand Up @@ -58,6 +59,12 @@ const getMigrationChoices = (): MigrationChoice[] => [
extensionTypes: ['app_link', 'bulk_action'],
buildTomlObject: buildAdminLinkTomlObject,
},
{
label: 'Sales Channel Extensions',
value: 'sales channel',
extensionTypes: ['channel_config'],
buildTomlObject: buildChannelConfigTomlObject,
},
]

export default class ImportExtensions extends AppCommand {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import checkoutSpec from './specifications/checkout_ui_extension.js'
import flowActionSpecification from './specifications/flow_action.js'
import flowTemplateSpec from './specifications/flow_template.js'
import flowTriggerSpecification from './specifications/flow_trigger.js'
import functionSpec from './specifications/function.js'
import paymentExtensionSpec from './specifications/payments_app_extension.js'
import posUISpec from './specifications/pos_ui_extension.js'
import productSubscriptionSpec from './specifications/product_subscription.js'
Expand Down Expand Up @@ -67,7 +66,6 @@ function loadSpecifications() {
flowActionSpecification,
flowTemplateSpec,
flowTriggerSpecification,
functionSpec,
paymentExtensionSpec,
posUISpec,
productSubscriptionSpec,
Expand Down
23 changes: 21 additions & 2 deletions packages/app/src/cli/prompts/init/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {renderSelectPrompt} from '@shopify/cli-kit/node/ui'
export interface InitOptions {
template?: string
flavor?: string
channel?: string
}

interface InitOutput {
Expand Down Expand Up @@ -31,8 +32,23 @@ interface Template {
// Eventually this list should be taken from a remote location
// That way we don't have to update the CLI every time we add a template
export const templates = {
channel: {
// Forked Private Repo
url: 'https://github.com/Shopify/app-channel-template-remix',
// url: 'https://github.com/Shopify/shopify-app-template-remix', // Public Repo
label: 'Build a sales channel app',
visible: true,
branches: {
prompt: 'For your sales channel app template, which language do you want?',
options: {
javascript: {branch: 'javascript', label: 'JavaScript'},
typescript: {branch: 'main', label: 'TypeScript'},
},
},
} as Template,
remix: {
url: 'https://github.com/Shopify/shopify-app-template-remix',
// change to our connect app repo
label: 'Build a Remix app (recommended)',
visible: true,
branches: {
Expand Down Expand Up @@ -62,11 +78,12 @@ type PredefinedTemplate = keyof typeof templates
const allTemplates = Object.keys(templates) as Readonly<PredefinedTemplate[]>
export const visibleTemplates = allTemplates.filter((key) => templates[key].visible) as Readonly<PredefinedTemplate[]>

const templateOptionsInOrder = ['remix', 'none'] as const
const templateOptionsInOrder = ['remix', 'none', 'channel'] as const

const init = async (options: InitOptions): Promise<InitOutput> => {
let template = options.template
const flavor = options.flavor
const channel = options.channel

const defaults = {
template: templates.remix.url,
Expand Down Expand Up @@ -98,7 +115,9 @@ const init = async (options: InitOptions): Promise<InitOutput> => {
const selectedTemplate = templates[answers.templateType]
selectedUrl = selectedTemplate.url

if (selectedTemplate.branches) {
if (channel) {
branch = 'main'
} else if (selectedTemplate.branches) {
if (flavor) {
branch = selectedTemplate.branches.options[flavor]?.branch
} else {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
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 ChannelConfigExtensionConfig {
channel_definition_handle: string
max_listing_variants: number
publication_status_listing_level: string
channel_configs: {
handle: string
channel_definition_handle: string
max_listing_variants: number
publication_status_level: string
markets: {
handle: string
taxonomy_name: string
taxonomy_id: number
product_schema: string
languages: string[]
}[]
}[]
product_schema: {
handle: string
schema_fields: {
handle: string
resource_type: string
default_source_path: string
required_data_type: string
enforcement: string
can_overwrite: boolean
display: {
display_name: string
display_domain: string
display_order: number
}
}[]
}[]
}

/**
* Given a channel_config 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 config: ChannelConfigExtensionConfig = JSON.parse(versionConfig)

const localExtensionRepresentation = {
extensions: [
{
type: 'channel_config',
name: extension.title,
handle: slugify(extension.title.substring(0, MAX_EXTENSION_HANDLE_LENGTH)),
channel_configs: config.channel_configs,
product_schema: config.product_schema,
},
],
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return encodeToml(localExtensionRepresentation as any)
}
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,8 @@ export interface AppVersionIdentifiers {
}

export function allDeveloperPlatformClients(): DeveloperPlatformClient[] {
const clients: DeveloperPlatformClient[] = [new PartnersClient()]
const clients: DeveloperPlatformClient[] = []
if (!isTruthy(process.env.DISABLE_PARTNERS_CLIENT)) clients.push(new PartnersClient())
if (isTruthy(process.env.USE_APP_MANAGEMENT_API)) clients.push(new AppManagementClient())
return clients
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,6 @@ import {UserInfo} from '../../api/graphql/business-platform-destinations/generat
import {ensureAuthenticatedAppManagement, ensureAuthenticatedBusinessPlatform} from '@shopify/cli-kit/node/session'
import {isUnitTest} from '@shopify/cli-kit/node/context/local'
import {AbortError, BugError} from '@shopify/cli-kit/node/error'
import {fetch} from '@shopify/cli-kit/node/http'
import {appManagementRequestDoc} from '@shopify/cli-kit/node/api/app-management'
import {appDevRequest} from '@shopify/cli-kit/node/api/app-dev'
import {
Expand All @@ -115,6 +114,7 @@ import {CLI_KIT_VERSION} from '@shopify/cli-kit/common/version'
import {versionSatisfies} from '@shopify/cli-kit/node/node-package-manager'
import {outputDebug} from '@shopify/cli-kit/node/output'
import {developerDashboardFqdn} from '@shopify/cli-kit/node/context/fqdn'
import {readFile} from 'fs/promises'

const TEMPLATE_JSON_URL = 'https://cdn.shopify.com/static/cli/extensions/templates.json'

Expand Down Expand Up @@ -307,16 +307,21 @@ export class AppManagementClient implements DeveloperPlatformClient {
}

async templateSpecifications({organizationId}: MinimalAppIdentifiers): Promise<ExtensionTemplate[]> {
let response
let templates: GatedExtensionTemplate[]
const jsonPath = process.env.TEMPLATE_JSON_PATH
try {
response = await fetch(TEMPLATE_JSON_URL)
templates = await (response.json() as Promise<GatedExtensionTemplate[]>)
if (jsonPath) {
const templateFile = await readFile(jsonPath, 'utf8')
templates = JSON.parse(templateFile) as GatedExtensionTemplate[]
} else {
const response = await fetch(TEMPLATE_JSON_URL)
templates = await (response.json() as Promise<GatedExtensionTemplate[]>)
}
} catch (_e) {
throw new AbortError(
[
'Failed to fetch extension templates from',
{link: {url: TEMPLATE_JSON_URL}},
{link: {url: jsonPath ? jsonPath : TEMPLATE_JSON_URL}},
{char: '.'},
'This likely means a problem with your internet connection.',
],
Expand Down
Loading