7381038452
## 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>
61 lines
2.3 KiB
TypeScript
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,
|
|
});
|
|
});
|
|
});
|