Add isConfigured to application registration in App admin panel (#20326)
## After Added "Configured" column in admin panel apps tab <img width="1171" height="611" alt="image" src="https://github.com/user-attachments/assets/e6850b39-5789-4ad2-b3df-192a28cb03e2" /> Add a banner to ask to configure the application <img width="1218" height="483" alt="image" src="https://github.com/user-attachments/assets/1491363c-3479-41ca-97b7-c7dc9e07ff16" />
This commit is contained in:
+18
@@ -0,0 +1,18 @@
|
||||
import { Context, Parent, ResolveField } from '@nestjs/graphql';
|
||||
|
||||
import { AdminResolver } from 'src/engine/api/graphql/graphql-config/decorators/admin-resolver.decorator';
|
||||
import { ApplicationRegistrationEntity } from 'src/engine/core-modules/application/application-registration/application-registration.entity';
|
||||
import { type IDataloaders } from 'src/engine/dataloaders/dataloader.interface';
|
||||
|
||||
@AdminResolver(() => ApplicationRegistrationEntity)
|
||||
export class AdminPanelApplicationRegistrationResolver {
|
||||
@ResolveField(() => Boolean)
|
||||
async isConfigured(
|
||||
@Parent() registration: ApplicationRegistrationEntity,
|
||||
@Context() context: { loaders: IDataloaders },
|
||||
): Promise<boolean> {
|
||||
return context.loaders.isConfiguredLoader.load({
|
||||
applicationRegistrationId: registration.id,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ import { Module } from '@nestjs/common';
|
||||
import { TerminusModule } from '@nestjs/terminus';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
|
||||
import { AdminPanelApplicationRegistrationResolver } from 'src/engine/core-modules/admin-panel/admin-panel-application-registration.resolver';
|
||||
import { AdminPanelHealthService } from 'src/engine/core-modules/admin-panel/admin-panel-health.service';
|
||||
import { AdminPanelQueueService } from 'src/engine/core-modules/admin-panel/admin-panel-queue.service';
|
||||
import { AdminPanelResolver } from 'src/engine/core-modules/admin-panel/admin-panel.resolver';
|
||||
@@ -78,6 +79,7 @@ import { PermissionsModule } from 'src/engine/metadata-modules/permissions/permi
|
||||
],
|
||||
providers: [
|
||||
AdminPanelResolver,
|
||||
AdminPanelApplicationRegistrationResolver,
|
||||
AdminPanelUserLookupService,
|
||||
AdminPanelStatisticsService,
|
||||
AdminPanelBillingService,
|
||||
|
||||
+10
-3
@@ -7,6 +7,7 @@ import {
|
||||
ApplicationRegistrationException,
|
||||
ApplicationRegistrationExceptionCode,
|
||||
} from 'src/engine/core-modules/application/application-registration/application-registration.exception';
|
||||
import { ApplicationRegistrationVariableService } from 'src/engine/core-modules/application/application-registration-variable/application-registration-variable.service';
|
||||
import { ApplicationRegistrationService } from 'src/engine/core-modules/application/application-registration/application-registration.service';
|
||||
import { MarketplaceCatalogSyncCronJob } from 'src/engine/core-modules/application/application-marketplace/crons/marketplace-catalog-sync.cron.job';
|
||||
import { MarketplaceAppDTO } from 'src/engine/core-modules/application/application-marketplace/dtos/marketplace-app.dto';
|
||||
@@ -22,6 +23,7 @@ export class MarketplaceQueryService {
|
||||
|
||||
constructor(
|
||||
private readonly applicationRegistrationService: ApplicationRegistrationService,
|
||||
private readonly applicationRegistrationVariableService: ApplicationRegistrationVariableService,
|
||||
@InjectMessageQueue(MessageQueue.cronQueue)
|
||||
private readonly messageQueueService: MessageQueueService,
|
||||
) {}
|
||||
@@ -46,9 +48,14 @@ export class MarketplaceQueryService {
|
||||
return [];
|
||||
}
|
||||
|
||||
return registrations.map((registration) =>
|
||||
this.toMarketplaceAppDTO(registration),
|
||||
);
|
||||
const configuredStatuses =
|
||||
await this.applicationRegistrationVariableService.isConfiguredBatch(
|
||||
registrations.map((registration) => registration.id),
|
||||
);
|
||||
|
||||
return registrations
|
||||
.filter((registration) => configuredStatuses.get(registration.id) ?? true)
|
||||
.map((registration) => this.toMarketplaceAppDTO(registration));
|
||||
}
|
||||
|
||||
async findMarketplaceAppDetail(
|
||||
|
||||
+40
@@ -175,6 +175,46 @@ export class ApplicationRegistrationVariableService {
|
||||
}
|
||||
}
|
||||
|
||||
async isConfiguredBatch(
|
||||
applicationRegistrationIds: string[],
|
||||
): Promise<Map<string, boolean>> {
|
||||
const variables = await this.variableRepository.find({
|
||||
where: { applicationRegistrationId: In(applicationRegistrationIds) },
|
||||
});
|
||||
|
||||
const variablesByRegistrationId = new Map<
|
||||
string,
|
||||
ApplicationRegistrationVariableEntity[]
|
||||
>();
|
||||
|
||||
for (const variable of variables) {
|
||||
const existing =
|
||||
variablesByRegistrationId.get(variable.applicationRegistrationId) ?? [];
|
||||
|
||||
existing.push(variable);
|
||||
variablesByRegistrationId.set(
|
||||
variable.applicationRegistrationId,
|
||||
existing,
|
||||
);
|
||||
}
|
||||
|
||||
const result = new Map<string, boolean>();
|
||||
|
||||
for (const id of applicationRegistrationIds) {
|
||||
const registrationVariables = variablesByRegistrationId.get(id) ?? [];
|
||||
const requiredVariables = registrationVariables.filter(
|
||||
(v) => v.isRequired,
|
||||
);
|
||||
|
||||
result.set(
|
||||
id,
|
||||
requiredVariables.every((v) => v.isFilled),
|
||||
);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private async assertRegistrationOwnedByWorkspace(
|
||||
registrationId: string,
|
||||
workspaceId: string,
|
||||
|
||||
+21
-2
@@ -1,11 +1,20 @@
|
||||
import { UseFilters, UseGuards, UsePipes } from '@nestjs/common';
|
||||
import { Args, Mutation, Query } from '@nestjs/graphql';
|
||||
import {
|
||||
Args,
|
||||
Context,
|
||||
Mutation,
|
||||
Parent,
|
||||
Query,
|
||||
ResolveField,
|
||||
} from '@nestjs/graphql';
|
||||
|
||||
import GraphQLUpload from 'graphql-upload/GraphQLUpload.mjs';
|
||||
import { PermissionFlagType } from 'twenty-shared/constants';
|
||||
import { FileFolder } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { type IDataloaders } from 'src/engine/dataloaders/dataloader.interface';
|
||||
|
||||
import type { FileUpload } from 'graphql-upload/processRequest.mjs';
|
||||
|
||||
import { MetadataResolver } from 'src/engine/api/graphql/graphql-config/decorators/metadata-resolver.decorator';
|
||||
@@ -47,7 +56,7 @@ import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard';
|
||||
import { streamToBuffer } from 'src/utils/stream-to-buffer';
|
||||
|
||||
@UsePipes(ResolverValidationPipe)
|
||||
@MetadataResolver()
|
||||
@MetadataResolver(() => ApplicationRegistrationEntity)
|
||||
@UseFilters(
|
||||
ApplicationRegistrationExceptionFilter,
|
||||
AuthGraphqlApiExceptionFilter,
|
||||
@@ -309,4 +318,14 @@ export class ApplicationRegistrationResolver {
|
||||
currentOwnerWorkspaceId: workspaceId,
|
||||
});
|
||||
}
|
||||
|
||||
@ResolveField(() => Boolean)
|
||||
async isConfigured(
|
||||
@Parent() registration: ApplicationRegistrationEntity,
|
||||
@Context() context: { loaders: IDataloaders },
|
||||
): Promise<boolean> {
|
||||
return context.loaders.isConfiguredLoader.load({
|
||||
applicationRegistrationId: registration.id,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user