Add marketplace catalog synchronization to admin panel (#22260)

## After

<img width="1476" height="658" alt="image"
src="https://github.com/user-attachments/assets/25a042dd-eabf-4b71-9872-d3b627104634"
/>


## Summary
This PR adds the ability for admins to manually synchronize the
marketplace application catalog from the admin panel. It introduces a
new mutation endpoint and UI controls to trigger catalog synchronization
with user feedback via snackbar notifications.

## Key Changes
- **Frontend (SettingsAdminApps component)**:
  - Added `useSnackBar` hook for user feedback on sync success/failure
- Imported `useMutation` from Apollo Client to handle the sync operation
  - Added `IconRefresh` and `Button` imports for the sync UI control
- Created `handleSyncCatalog` function that triggers the mutation,
refetches app registrations, and displays appropriate snackbar messages
- Added a new "General" section with a "Synchronize catalog" button
above the existing app registrations table
  - Button shows loading state and is disabled while sync is in progress

- **Backend (AdminPanelResolver)**:
- Added `syncMarketplaceCatalog` mutation that queues a
`MarketplaceCatalogSyncCronJob` via the message queue
- Uses `@InjectMessageQueue` decorator to inject the cron queue service
- Includes job deduplication via `id: 'marketplace-catalog-sync'` to
prevent multiple pending sync jobs
  - Protected with `@UseGuards(AdminPanelGuard)` for admin-only access

- **GraphQL Schema**:
  - Added `SyncMarketplaceCatalog` mutation type definition
  - Generated corresponding TypeScript types and mutation document

- **New Files**:
  - Created `syncMarketplaceCatalog.ts` GraphQL mutation document

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/22260?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. -->
This commit is contained in:
martmull
2026-06-29 14:58:34 +02:00
committed by GitHub
parent 66edd96213
commit d1854bc7e9
5 changed files with 129 additions and 63 deletions
@@ -65,6 +65,7 @@ import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/service
import { PreventNestToAutoLogGraphqlErrorsFilter } from 'src/engine/core-modules/graphql/filters/prevent-nest-to-auto-log-graphql-errors.filter';
import { ResolverValidationPipe } from 'src/engine/core-modules/graphql/pipes/resolver-validation.pipe';
import { UserInputError } from 'src/engine/core-modules/graphql/utils/graphql-errors.util';
import { MarketplaceCatalogSyncCronJob } from 'src/engine/core-modules/application/application-marketplace/crons/marketplace-catalog-sync.cron.job';
import { InjectMessageQueue } from 'src/engine/core-modules/message-queue/decorators/message-queue.decorator';
import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.constants';
import { MessageQueueService } from 'src/engine/core-modules/message-queue/services/message-queue.service';
@@ -139,8 +140,10 @@ export class AdminPanelResolver {
private readonly upgradeStatusService: UpgradeStatusService,
@InjectRepository(WorkspaceEntity)
private readonly workspaceRepository: Repository<WorkspaceEntity>,
@InjectMessageQueue(MessageQueue.cronQueue)
private readonly cronQueueService: MessageQueueService,
@InjectMessageQueue(MessageQueue.workspaceQueue)
private readonly messageQueueService: MessageQueueService,
private readonly workspaceQueueService: MessageQueueService,
) {}
@UseGuards(AdminPanelOrImpersonateGuard)
@@ -479,6 +482,18 @@ export class AdminPanelResolver {
return this.applicationRegistrationService.findAll();
}
@UseGuards(AdminPanelGuard)
@Mutation(() => Boolean)
async syncMarketplaceCatalog(): Promise<boolean> {
await this.cronQueueService.add(
MarketplaceCatalogSyncCronJob.name,
{},
{ id: 'marketplace-catalog-sync' }, // Avoids triggering multiple pending jobs
);
return true;
}
@UseGuards(AdminPanelGuard)
@Mutation(() => ApplicationRegistrationEntity)
async updateAdminApplicationRegistration(
@@ -503,7 +518,7 @@ export class AdminPanelResolver {
);
}
await this.messageQueueService.add<BackfillApplicationInstallationJobData>(
await this.workspaceQueueService.add<BackfillApplicationInstallationJobData>(
BACKFILL_APPLICATION_INSTALLATION_JOB_NAME,
{ applicationRegistrationId },
{