Skip to content

Commit 20b4129

Browse files
authored
Merge pull request #6267 from Shopify/08-15-fix_webhooks_appearing_as_updated_all_the_time
Fix webhooks appearing as updated all the time
2 parents 59e0cb7 + f94a82d commit 20b4129

File tree

2 files changed

+146
-0
lines changed

2 files changed

+146
-0
lines changed

packages/app/src/cli/services/context/breakdown-extensions.test.ts

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1679,6 +1679,139 @@ describe('configExtensionsIdentifiersBreakdown', () => {
16791679
deletedFieldNames: [],
16801680
})
16811681
})
1682+
1683+
test('webhook subscriptions are properly sorted by URI and topics', async () => {
1684+
// Given
1685+
const unsortedWebhookModules: AppModuleVersion[] = [
1686+
{
1687+
registrationId: 'W_1',
1688+
registrationUuid: 'UUID_W_1',
1689+
registrationTitle: 'Webhook 1',
1690+
type: 'webhook_subscription',
1691+
config: {
1692+
// Unsorted topics
1693+
topics: ['products/update', 'products/create'],
1694+
uri: 'https://myapp.com/webhooks/products',
1695+
},
1696+
specification: {
1697+
identifier: 'webhook_subscription',
1698+
name: 'Webhook Subscription',
1699+
experience: 'extension',
1700+
options: {
1701+
managementExperience: 'cli',
1702+
},
1703+
},
1704+
},
1705+
{
1706+
registrationId: 'W_2',
1707+
registrationUuid: 'UUID_W_2',
1708+
registrationTitle: 'Webhook 2',
1709+
type: 'webhook_subscription',
1710+
config: {
1711+
topics: ['orders/create'],
1712+
uri: 'https://myapp.com/webhooks/orders',
1713+
},
1714+
specification: {
1715+
identifier: 'webhook_subscription',
1716+
name: 'Webhook Subscription',
1717+
experience: 'extension',
1718+
options: {
1719+
managementExperience: 'cli',
1720+
},
1721+
},
1722+
},
1723+
{
1724+
registrationId: 'W_4',
1725+
registrationUuid: 'UUID_W_4',
1726+
registrationTitle: 'Webhook 4',
1727+
type: 'webhook_subscription',
1728+
config: {
1729+
topics: ['products/delete'],
1730+
// Same URI as W_1
1731+
uri: 'https://myapp.com/webhooks/products',
1732+
},
1733+
specification: {
1734+
identifier: 'webhook_subscription',
1735+
name: 'Webhook Subscription',
1736+
experience: 'extension',
1737+
options: {
1738+
managementExperience: 'cli',
1739+
},
1740+
},
1741+
},
1742+
{
1743+
registrationId: 'W_3',
1744+
registrationUuid: 'UUID_W_3',
1745+
registrationTitle: 'Webhook 3',
1746+
type: 'webhook_subscription',
1747+
config: {
1748+
// Unsorted topics
1749+
topics: ['customers/create', 'customers/update'],
1750+
uri: 'https://myapp.com/webhooks/customers',
1751+
},
1752+
specification: {
1753+
identifier: 'webhook_subscription',
1754+
name: 'Webhook Subscription',
1755+
experience: 'extension',
1756+
options: {
1757+
managementExperience: 'cli',
1758+
},
1759+
},
1760+
},
1761+
]
1762+
1763+
const activeVersion = {appModuleVersions: [...unsortedWebhookModules, APP_URL_SPEC, API_VERSION_SPEC]}
1764+
const developerPlatformClient: DeveloperPlatformClient = testDeveloperPlatformClient({
1765+
activeAppVersion: (_app: MinimalAppIdentifiers) => Promise.resolve(activeVersion),
1766+
})
1767+
1768+
const appConfiguration = {
1769+
...APP_CONFIGURATION,
1770+
webhooks: {
1771+
api_version: '2023-04',
1772+
// Subscriptions in a different order than remote
1773+
subscriptions: [
1774+
{
1775+
topics: ['products/delete'],
1776+
uri: 'https://myapp.com/webhooks/products',
1777+
},
1778+
{
1779+
topics: ['orders/create'],
1780+
uri: 'https://myapp.com/webhooks/orders',
1781+
},
1782+
{
1783+
// Different order from remote
1784+
topics: ['customers/update', 'customers/create'],
1785+
uri: 'https://myapp.com/webhooks/customers',
1786+
},
1787+
{
1788+
// Different order from remote
1789+
topics: ['products/create', 'products/update'],
1790+
uri: 'https://myapp.com/webhooks/products',
1791+
},
1792+
],
1793+
},
1794+
}
1795+
1796+
// When
1797+
const result = await configExtensionsIdentifiersBreakdown({
1798+
developerPlatformClient,
1799+
apiKey: 'apiKey',
1800+
remoteApp: testOrganizationApp(),
1801+
localApp: await LOCAL_APP([], appConfiguration),
1802+
release: true,
1803+
})
1804+
1805+
// Then
1806+
// Even though the webhook subscriptions are in different orders and have unsorted topics,
1807+
// they should be considered the same after normalization
1808+
expect(result).toEqual({
1809+
existingFieldNames: ['webhooks', 'application_url'],
1810+
existingUpdatedFieldNames: [],
1811+
newFieldNames: ['name', 'embedded'],
1812+
deletedFieldNames: [],
1813+
})
1814+
})
16821815
})
16831816
describe('deploy with release using local configuration when there is no remote app version', () => {
16841817
test('all local configuration will be returned in the new list', async () => {

packages/app/src/cli/services/context/breakdown-extensions.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,19 @@ async function resolveRemoteConfigExtensionIdentifiersBreakdown(
214214
}
215215
return subscription
216216
})
217+
218+
// Sort webhook subscriptions by uri & topics in both baseline and remote config
219+
// Ensuring that deepCompare will not fail if the order is different
220+
baselineConfig.webhooks.subscriptions.sort((webhookA, webhookB) => {
221+
const keyA = webhookA.uri + (webhookA.topics?.sort().join(',') ?? '')
222+
const keyB = webhookB.uri + (webhookB.topics?.sort().join(',') ?? '')
223+
return keyA.localeCompare(keyB)
224+
})
225+
remoteConfig.webhooks?.subscriptions?.sort((webhookA, webhookB) => {
226+
const keyA = webhookA.uri + (webhookA.topics?.sort().join(',') ?? '')
227+
const keyB = webhookB.uri + (webhookB.topics?.sort().join(',') ?? '')
228+
return keyA.localeCompare(keyB)
229+
})
217230
}
218231

219232
const diffConfigContent = buildDiffConfigContent(baselineConfig, remoteConfig, app.configSchema)

0 commit comments

Comments
 (0)