Skip to content
Draft
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
@@ -1,25 +1,25 @@
import spec from './app_config_branding.js'
import {placeholderAppConfiguration} from '../../app/app.test-data.js'
import {describe, expect, test} from 'vitest'

describe('branding', () => {
describe('transform', () => {
test('should return the transformed object', () => {
// Given
const object = {
test('transformLocalToRemote should be undefined', () => {
expect(spec.transformLocalToRemote).toBeUndefined()
})
})

describe('deployConfig', () => {
test('should preserve both name and handle in deploy payload', async () => {
const config = {
type: 'branding',
uid: 'branding',
path: '/test',
extensions: {},
name: 'my-app',
handle: 'my-app-handle',
}
const appConfigSpec = spec

// When
const result = appConfigSpec.transformLocalToRemote!(object, placeholderAppConfiguration)

// Then
expect(result).toMatchObject({
name: 'my-app',
app_handle: 'my-app-handle',
})
const result = await spec.deployConfig!(config as any, '', '', undefined)
expect(result).toEqual({name: 'my-app', handle: 'my-app-handle'})
})
})

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {TransformationConfig, createConfigExtensionSpecification} from '../specification.js'
import {configWithoutFirstClassFields, createConfigExtensionSpecification} from '../specification.js'
import {BaseSchemaWithoutHandle} from '../schemas.js'
import {zod} from '@shopify/cli-kit/node/schema'

Expand All @@ -13,17 +13,23 @@ const BrandingSchema = BaseSchemaWithoutHandle.extend({
.optional(),
})

const BrandingTransformConfig: TransformationConfig = {
name: 'name',
app_handle: 'handle',
}

export const BrandingSpecIdentifier = 'branding'

const appBrandingSpec = createConfigExtensionSpecification({
identifier: BrandingSpecIdentifier,
schema: BrandingSchema,
transformConfig: BrandingTransformConfig,
deployConfig: async (config) => {
// Use configWithoutFirstClassFields to strip type/handle/uid/path/extensions,
// then re-add handle since it's real branding data (not just a base field)
const stripped = configWithoutFirstClassFields(config)
const handle = (config as {handle?: string}).handle
if (handle === undefined) return stripped
return {...stripped, handle}
},
transformRemoteToLocal: (content: object) => ({
name: (content as {name?: string}).name,
handle: (content as {app_handle?: string}).app_handle,
}),

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reverse transform only reads app_handle; could drop handle if backend migrated

transformRemoteToLocal maps handle exclusively from content.app_handle. If the backend has migrated to return handle (matching the new deploy behavior), pulls/syncs will start producing handle: undefined and lose the value in local config.

Evidence:

handle: (content as {app_handle?: string}).app_handle,

Impact:

  • Local config may lose branding handle on pull/sync.
  • Causes local/remote drift and repeated diffs.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reverse transform returns explicit undefined keys, which may clobber existing config

The new transformRemoteToLocal always returns { name: ..., handle: ... } even when fields are missing, which can produce {name: undefined, handle: undefined}. Some merge strategies treat explicit undefined as an overwrite, potentially clearing existing local values.

Previously, reverse-transform logic only copied keys when present (e.g., if (content[key] !== undefined) result[key] = content[key]), avoiding clobbering.

Impact:

  • Pulling/syncing from partial remote responses could wipe locally configured name/handle.
  • Leads to configuration loss and hard-to-debug drift.

})

export default appBrandingSpec