Skip to content

Commit 9ea97b4

Browse files
committed
fix(apps): include app_builder_id in the missing-URL warning
app_builder_id is present whenever this warning fires, except for a narrow DB invariant violation in the backend's upload no-op path (documented in app-builder-code's service.go) that shouldn't occur in normal operation. Surfacing it alongside the display name gives an unambiguous reference: disambiguates if multiple apps share a name, and is directly usable by anyone who already knows their org's domain. Extracted the message construction into buildMissingAppUrlWarning to avoid duplicating the ID-suffix logic across the upload and release call sites. Added a test for the edge case where app_builder_id is also absent, confirming the suffix is omitted rather than printing something broken.
1 parent 16354b3 commit 9ea97b4

2 files changed

Lines changed: 56 additions & 10 deletions

File tree

packages/plugins/apps/src/upload.test.ts

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -300,17 +300,40 @@ describe('Apps Plugin - upload', () => {
300300
// But it also must not go silent — surfaced as a warning so it's visible in CI
301301
// output (see handle-upload.ts, which logs and aggregates `warnings` without
302302
// failing the build, unlike `errors`). Names the app by its display name (not
303-
// context.identifier, which is an opaque hash — see identifier.ts) so there's a
304-
// concrete next step (find it in the apps list), not just a generic warning.
303+
// context.identifier, which is an opaque hash — see identifier.ts) and includes
304+
// the app_builder_id, so there's a concrete, unambiguous next step (find it in
305+
// the apps list, disambiguated by ID if names collide), not just a generic warning.
305306
expect(warnings).toHaveLength(1);
306307
expect(warnings[0]).toContain('Could not resolve the App Builder URL');
307308
expect(warnings[0]).toContain(context.name);
309+
expect(warnings[0]).toContain('builder123');
308310

309311
// Reset — other tests in this file rely on getDDEnvValueMock's default
310312
// (undefined) behavior and beforeEach doesn't reset this particular mock.
311313
getDDEnvValueMock.mockReset();
312314
});
313315

316+
test('Should omit the app ID suffix when app_builder_id is also absent', async () => {
317+
getDDEnvValueMock.mockImplementation((key) =>
318+
key === 'APPS_PUBLISH' ? 'false' : undefined,
319+
);
320+
// Matches the backend's own no-op-path edge case (app-builder-code's
321+
// TestAppBuilderURL_EmptyAppBuilderID) — both app_builder_id and
322+
// app_builder_url absent, not just the URL.
323+
doAuthenticatedRequestMock.mockResolvedValueOnce({
324+
version_id: 'v123',
325+
application_id: 'app123',
326+
} as any);
327+
328+
const { warnings } = await uploadArchive(archive, context, logger);
329+
330+
expect(warnings).toHaveLength(1);
331+
expect(warnings[0]).toContain(context.name);
332+
expect(warnings[0]).not.toContain('app ID');
333+
334+
getDDEnvValueMock.mockReset();
335+
});
336+
314337
test('Should upload archive using the supplied request function', async () => {
315338
const doUploadAuthenticatedRequestMock = jest.fn().mockResolvedValue({
316339
version_id: 'v123',
@@ -419,8 +442,9 @@ describe('Apps Plugin - upload', () => {
419442
})
420443
// The backend couldn't resolve the org's app URL for this release — a real,
421444
// designed-for degradation path (e.g. a transient org-lookup failure), not a
422-
// hypothetical. The release itself still succeeded.
423-
.mockResolvedValueOnce({});
445+
// hypothetical. The release itself still succeeded. app_builder_id is still
446+
// present, though — it's set from the DB independently of the URL lookup.
447+
.mockResolvedValueOnce({ app_builder_id: 'builder123' });
424448

425449
const { errors, warnings } = await uploadArchive(archive, context, logger);
426450

@@ -433,10 +457,11 @@ describe('Apps Plugin - upload', () => {
433457
expect(releaseLog?.[0]).toContain('to live.');
434458
expect(releaseLog?.[0]).not.toContain('\n');
435459
// Surfaced as a warning rather than a blank/malformed log line — names the app
436-
// by its display name so there's a concrete next step (find it in the apps list).
460+
// by its display name and includes the app_builder_id for unambiguous lookup.
437461
expect(warnings).toHaveLength(1);
438462
expect(warnings[0]).toContain('Could not resolve the App Builder URL');
439463
expect(warnings[0]).toContain(context.name);
464+
expect(warnings[0]).toContain('builder123');
440465
});
441466

442467
test.each(['false', '0', 'False', 'FALSE', 'off', 'no'])(

packages/plugins/apps/src/upload.ts

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,19 @@ export const getReleaseUrl = (site: string, appId: string) => {
4141
return `https://api.${site}/${APPS_API_PATH}/${appId}/release/live`;
4242
};
4343

44+
// Builds the warning shown when the backend couldn't resolve an org's App Builder URL.
45+
// Names the app (context.name — always human-readable, unlike context.identifier's opaque
46+
// hash) and its app_builder_id when present, which disambiguates same-named apps and lets
47+
// anyone who knows their org's domain construct the link directly.
48+
const buildMissingAppUrlWarning = (
49+
action: 'upload' | 'release',
50+
name: string,
51+
appBuilderId?: string,
52+
) => {
53+
const idSuffix = appBuilderId ? ` (app ID: ${appBuilderId})` : '';
54+
return `Could not resolve the App Builder URL for this ${action} — find "${name}"${idSuffix} in your App Builder apps list to view it.`;
55+
};
56+
4457
export const getData =
4558
(archivePath: string, defaultHeaders: Record<string, string> = {}, name: string) =>
4659
async (): Promise<DataResponse> => {
@@ -126,10 +139,14 @@ Would have uploaded ${summary}`,
126139
} else {
127140
// The backend couldn't resolve this org's App Builder URL (e.g. a transient
128141
// lookup failure) — the upload itself still succeeded, so this doesn't fail the
129-
// build. Point at the apps list by name (not context.identifier, which is an
130-
// opaque hash in the default case — see identifier.ts's buildIdentifier — and
131-
// not something anyone would recognize or search for).
132-
const message = `Could not resolve the App Builder URL for this upload — find "${context.name}" in your App Builder apps list to view it.`;
142+
// build. Uses context.name, not context.identifier, since the latter is an
143+
// opaque hash in the default case (see identifier.ts's buildIdentifier) — not
144+
// something anyone would recognize or search for.
145+
const message = buildMissingAppUrlWarning(
146+
'upload',
147+
context.name,
148+
response.app_builder_id,
149+
);
133150
warnings.push(message);
134151
log.warn(message);
135152
}
@@ -164,7 +181,11 @@ Would have uploaded ${summary}`,
164181
);
165182
} else {
166183
log.info(`Published uploaded version ${bold(response.version_id)} to live.`);
167-
const message = `Could not resolve the App Builder URL for this release — find "${context.name}" in your App Builder apps list to view it.`;
184+
const message = buildMissingAppUrlWarning(
185+
'release',
186+
context.name,
187+
releaseResponse.app_builder_id,
188+
);
168189
warnings.push(message);
169190
log.warn(message);
170191
}

0 commit comments

Comments
 (0)