Files
twenty/packages/twenty-server/test/integration/metadata/suites/application/failing-upload-application-server-version.integration-spec.ts
T
martmull 7381038452 Paginate admin panel app registrations list (#22734)
## Context

The `findAllApplicationRegistrations` query on the admin panel Apps page
(`/settings/admin-panel#apps`) loaded every application registration at
once, with search and filtering done client-side.

## Changes

**Server**
- `findAllApplicationRegistrations` now takes `limit` / `offset` /
`searchTerm` / `isPreInstalledOnly` args and returns a
`PaginatedApplicationRegistrations` object (`registrations`,
`totalCount`, `hasMore`), following the same pattern as `getQueueJobs`.
- `ApplicationRegistrationService.findAll` uses `findAndCount` with
`take`/`skip`, and moves the search (name, source package, universal
identifier via `ILIKE`) and the pre-installed filter into the SQL query,
mirroring how `getInstalledWorkspacesGlobal` filters installed
workspaces.

**Frontend**
- `SettingsAdminApps` passes the page, the debounced search term (300ms,
like the installed workspaces table), and the pre-installed toggle as
query variables instead of filtering client-side.
- Adds a Previous / Next pagination footer (25 per page) matching the
queue jobs table, shown only when there is more than one page.
- The "unconfigured first" ordering is kept within each page
(`isConfigured` is a dataloader-resolved field, so it can't be sorted in
SQL).

## Notes
- Regenerated `generated-admin/graphql.ts` follows in a subsequent
commit.

---
_Generated by [Claude
Code](https://claude.ai/code/session_015erumgPozkbNA3zPeKrrFW)_

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/22734?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->

---------

Co-authored-by: Weiko <corentin@twenty.com>
2026-07-13 14:07:11 +02:00

61 lines
2.3 KiB
TypeScript

import { expectOneNotInternalServerErrorSnapshot } from 'test/integration/graphql/utils/expect-one-not-internal-server-error-snapshot.util';
import { buildBaseManifest } from 'test/integration/metadata/suites/application/utils/build-base-manifest.util';
import { cleanupApplicationAndAppRegistration } from 'test/integration/metadata/suites/application/utils/cleanup-application-and-app-registration.util';
import { createAppTarball } from 'test/integration/metadata/suites/application/utils/create-app-tarball.util';
import { uploadAppTarball } from 'test/integration/metadata/suites/application/utils/upload-app-tarball.util';
import { scrubSemverVersions } from 'test/utils/scrub-semver-versions.util';
import { v4 as uuidv4 } from 'uuid';
// The upload flow runs cache-lock retries with real delays, so fake timers
// would hang it — mirror the other application suites.
jest.setTimeout(120000);
describe('Publish application is gated by the instance server version', () => {
const createdApplicationUniversalIdentifiers: string[] = [];
beforeAll(() => {
jest.useRealTimers();
});
afterAll(async () => {
for (const universalIdentifier of createdApplicationUniversalIdentifiers) {
await cleanupApplicationAndAppRegistration({
applicationUniversalIdentifier: universalIdentifier,
});
}
jest.useFakeTimers();
});
it('rejects publishing when the app requires a server version the instance does not satisfy', async () => {
const universalIdentifier = uuidv4();
const roleId = uuidv4();
const tarball = await createAppTarball({
'manifest.json': JSON.stringify(
buildBaseManifest({ appId: universalIdentifier, roleId }),
),
'package.json': JSON.stringify({
name: `test-server-version-gate-${universalIdentifier}`,
version: '1.0.0',
// Require a server version far above any real instance version so the
// instance-level compatibility check always rejects it.
engines: { twenty: '>=999.0.0' },
}),
});
createdApplicationUniversalIdentifiers.push(universalIdentifier);
const { errors } = await uploadAppTarball({
tarballBuffer: tarball,
universalIdentifier,
expectToFail: true,
});
expectOneNotInternalServerErrorSnapshot({
errors,
normalizeMessage: scrubSemverVersions,
});
});
});