From 7d1020aeff61332c9bfdc9d4677a53aee659d246 Mon Sep 17 00:00:00 2001 From: Ryan Bahan Date: Fri, 27 Feb 2026 17:51:52 -0700 Subject: [PATCH] Migrate app_access spec to deployConfig + transformRemoteToLocal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove transformConfig, set deployConfig (configWithoutFirstClassFields pass-through) and transformRemoteToLocal directly (flat scopes → access_scopes.scopes, redirect_url_allowlist → auth.redirect_urls, etc.). transformLocalToRemote is now undefined. Co-Authored-By: Claude Opus 4.6 (1M context) --- packages/app/src/cli/models/app/app.test.ts | 4 +- .../cli/models/extensions/specification.ts | 2 +- .../app_config_app_access.test.ts | 35 +--------------- .../specifications/app_config_app_access.ts | 40 ++++++++++++++----- 4 files changed, 34 insertions(+), 47 deletions(-) diff --git a/packages/app/src/cli/models/app/app.test.ts b/packages/app/src/cli/models/app/app.test.ts index d07d05fc7bc..3605f69777a 100644 --- a/packages/app/src/cli/models/app/app.test.ts +++ b/packages/app/src/cli/models/app/app.test.ts @@ -836,7 +836,7 @@ describe('manifest', () => { assets: appAccessModule.uid, target: appAccessModule.contextValue, config: expect.objectContaining({ - redirect_url_allowlist: ['https://example.com/auth/callback'], + auth: {redirect_urls: ['https://example.com/auth/callback']}, }), }, ], @@ -906,7 +906,7 @@ describe('manifest', () => { assets: appAccess.uid, target: appAccess.contextValue, config: expect.objectContaining({ - redirect_url_allowlist: ['https://new-url.io/auth/callback'], + auth: {redirect_urls: ['https://new-url.io/auth/callback']}, }), }, ], diff --git a/packages/app/src/cli/models/extensions/specification.ts b/packages/app/src/cli/models/extensions/specification.ts index 67e897c0b8c..d66474f9c43 100644 --- a/packages/app/src/cli/models/extensions/specification.ts +++ b/packages/app/src/cli/models/extensions/specification.ts @@ -22,7 +22,7 @@ export type ExtensionFeature = | 'localization' | 'generates_source_maps' -export interface TransformationConfig { +interface TransformationConfig { [key: string]: string } diff --git a/packages/app/src/cli/models/extensions/specifications/app_config_app_access.test.ts b/packages/app/src/cli/models/extensions/specifications/app_config_app_access.test.ts index 22ec0a97ecd..e3d11deabf7 100644 --- a/packages/app/src/cli/models/extensions/specifications/app_config_app_access.test.ts +++ b/packages/app/src/cli/models/extensions/specifications/app_config_app_access.test.ts @@ -1,41 +1,10 @@ import spec from './app_config_app_access.js' -import {placeholderAppConfiguration} from '../../app/app.test-data.js' import {describe, expect, test} from 'vitest' describe('app_config_app_access', () => { describe('transform', () => { - test('should return the transformed object', () => { - // Given - const object = { - access: { - admin: {direct_api_mode: 'online'}, - }, - access_scopes: { - scopes: 'read_products,write_products', - optional_scopes: ['read_customers'], - required_scopes: ['write_orders', 'read_inventory'], - use_legacy_install_flow: true, - }, - auth: { - redirect_urls: ['https://example.com/auth/callback'], - }, - } - const appAccessSpec = spec - - // When - const result = appAccessSpec.transformLocalToRemote!(object, placeholderAppConfiguration) - - // Then - expect(result).toMatchObject({ - access: { - admin: {direct_api_mode: 'online'}, - }, - scopes: 'read_products,write_products', - optional_scopes: ['read_customers'], - required_scopes: ['write_orders', 'read_inventory'], - use_legacy_install_flow: true, - redirect_url_allowlist: ['https://example.com/auth/callback'], - }) + test('transformLocalToRemote should be undefined', () => { + expect(spec.transformLocalToRemote).toBeUndefined() }) }) diff --git a/packages/app/src/cli/models/extensions/specifications/app_config_app_access.ts b/packages/app/src/cli/models/extensions/specifications/app_config_app_access.ts index b46603c58c2..b8b0aa3e800 100644 --- a/packages/app/src/cli/models/extensions/specifications/app_config_app_access.ts +++ b/packages/app/src/cli/models/extensions/specifications/app_config_app_access.ts @@ -1,5 +1,5 @@ import {validateUrl} from '../../app/validation/common.js' -import {TransformationConfig, createConfigExtensionSpecification} from '../specification.js' +import {configWithoutFirstClassFields, createConfigExtensionSpecification} from '../specification.js' import {BaseSchemaWithoutHandle} from '../schemas.js' import {normalizeDelimitedString} from '@shopify/cli-kit/common/string' import {zod} from '@shopify/cli-kit/node/schema' @@ -33,19 +33,37 @@ const AppAccessSchema = BaseSchemaWithoutHandle.extend({ export const AppAccessSpecIdentifier = 'app_access' -const AppAccessTransformConfig: TransformationConfig = { - access: 'access', - scopes: 'access_scopes.scopes', - required_scopes: 'access_scopes.required_scopes', - optional_scopes: 'access_scopes.optional_scopes', - use_legacy_install_flow: 'access_scopes.use_legacy_install_flow', - redirect_url_allowlist: 'auth.redirect_urls', -} - const appAccessSpec = createConfigExtensionSpecification({ identifier: AppAccessSpecIdentifier, schema: AppAccessSchema, - transformConfig: AppAccessTransformConfig, + deployConfig: async (config) => { + const {name, ...rest} = configWithoutFirstClassFields(config) + return rest + }, + transformRemoteToLocal: (remoteContent: object) => { + const remote = remoteContent as {[key: string]: unknown} + const result: {[key: string]: unknown} = {} + + if (remote.access !== undefined) { + result.access = remote.access + } + + const accessScopes: {[key: string]: unknown} = {} + if (remote.scopes !== undefined) accessScopes.scopes = remote.scopes + if (remote.required_scopes !== undefined) accessScopes.required_scopes = remote.required_scopes + if (remote.optional_scopes !== undefined) accessScopes.optional_scopes = remote.optional_scopes + if (remote.use_legacy_install_flow !== undefined) + accessScopes.use_legacy_install_flow = remote.use_legacy_install_flow + if (Object.keys(accessScopes).length > 0) { + result.access_scopes = accessScopes + } + + if (remote.redirect_url_allowlist !== undefined) { + result.auth = {redirect_urls: remote.redirect_url_allowlist} + } + + return result + }, getDevSessionUpdateMessages: async (config) => { const hasAccessModule = config.access_scopes !== undefined const isLegacyInstallFlow = config.access_scopes?.use_legacy_install_flow === true