Skip to content

Commit c451f07

Browse files
authored
Merge pull request #2492 from trycompai/main
[comp] Production Deploy
2 parents 902b261 + 132aa95 commit c451f07

File tree

13 files changed

+1443
-25
lines changed

13 files changed

+1443
-25
lines changed

apps/api/src/integration-platform/controllers/task-integrations.controller.ts

Lines changed: 77 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,15 @@ import {
2020
getManifest,
2121
runAllChecks,
2222
type CheckRunResult,
23-
type OAuthConfig,
2423
} from '@trycompai/integration-platform';
2524
import { ConnectionRepository } from '../repositories/connection.repository';
2625
import { ProviderRepository } from '../repositories/provider.repository';
2726
import { CheckRunRepository } from '../repositories/check-run.repository';
2827
import { CredentialVaultService } from '../services/credential-vault.service';
2928
import { OAuthCredentialsService } from '../services/oauth-credentials.service';
29+
import { TaskIntegrationChecksService } from '../services/task-integration-checks.service';
3030
import { getStringValue, toStringCredentials } from '../utils/credential-utils';
31+
import { isCheckDisabledForTask } from '../utils/disabled-task-checks';
3132
import { db } from '@db';
3233
import type { Prisma } from '@db';
3334

@@ -39,6 +40,8 @@ interface TaskIntegrationCheck {
3940
checkName: string;
4041
checkDescription: string;
4142
isConnected: boolean;
43+
/** True when the check has been manually disconnected from this task. */
44+
isDisabledForTask: boolean;
4245
needsConfiguration: boolean;
4346
connectionId?: string;
4447
connectionStatus?: string;
@@ -56,6 +59,11 @@ interface RunCheckForTaskDto {
5659
checkId: string;
5760
}
5861

62+
interface ToggleCheckForTaskDto {
63+
connectionId: string;
64+
checkId: string;
65+
}
66+
5967
@Controller({ path: 'integrations/tasks', version: '1' })
6068
@ApiTags('Integrations')
6169
@UseGuards(HybridAuthGuard, PermissionGuard)
@@ -69,18 +77,22 @@ export class TaskIntegrationsController {
6977
private readonly checkRunRepository: CheckRunRepository,
7078
private readonly credentialVaultService: CredentialVaultService,
7179
private readonly oauthCredentialsService: OAuthCredentialsService,
80+
private readonly taskIntegrationChecksService: TaskIntegrationChecksService,
7281
) {}
7382

7483
/**
75-
* Get all integration checks that can auto-complete a specific task template
84+
* Get all integration checks that can auto-complete a specific task template.
85+
* When a specific `taskId` is also provided, per-task disable state is
86+
* resolved from the matching connection's metadata so the UI can show
87+
* which checks have been manually disconnected from that task.
7688
*/
7789
@Get('template/:templateId/checks')
7890
@RequirePermission('integration', 'read')
7991
async getChecksForTaskTemplate(
8092
@Param('templateId') templateId: string,
8193
@OrganizationId() organizationId: string,
94+
taskIdForDisableState?: string,
8295
): Promise<{ checks: TaskIntegrationCheck[] }> {
83-
8496
const manifests = getActiveManifests();
8597
const checks: TaskIntegrationCheck[] = [];
8698

@@ -136,6 +148,15 @@ export class TaskIntegrationsController {
136148
oauthConfigured = availability.available;
137149
}
138150

151+
const isDisabledForTask =
152+
!!taskIdForDisableState &&
153+
!!connection &&
154+
isCheckDisabledForTask(
155+
connection.metadata,
156+
taskIdForDisableState,
157+
check.id,
158+
);
159+
139160
checks.push({
140161
integrationId: manifest.id,
141162
integrationName: manifest.name,
@@ -144,6 +165,7 @@ export class TaskIntegrationsController {
144165
checkName: check.name,
145166
checkDescription: check.description,
146167
isConnected: !!connection && connection.status === 'active',
168+
isDisabledForTask,
147169
needsConfiguration,
148170
connectionId: connection?.id,
149171
connectionStatus: connection?.status,
@@ -169,7 +191,6 @@ export class TaskIntegrationsController {
169191
checks: TaskIntegrationCheck[];
170192
task: { id: string; title: string; templateId: string | null };
171193
}> {
172-
173194
// Get the task to find its template ID
174195
const task = await db.task.findUnique({
175196
where: { id: taskId, organizationId },
@@ -187,10 +208,11 @@ export class TaskIntegrationsController {
187208
};
188209
}
189210

190-
// Get checks for this template
211+
// Get checks for this template, annotated with per-task disable state
191212
const { checks } = await this.getChecksForTaskTemplate(
192213
task.taskTemplateId,
193214
organizationId,
215+
task.id,
194216
);
195217

196218
return {
@@ -215,7 +237,6 @@ export class TaskIntegrationsController {
215237
checkRunId?: string;
216238
taskStatus?: string | null;
217239
}> {
218-
219240
const { connectionId, checkId } = body;
220241

221242
// Verify task exists
@@ -240,6 +261,14 @@ export class TaskIntegrationsController {
240261
);
241262
}
242263

264+
// Reject runs for checks that have been disconnected from this task.
265+
if (isCheckDisabledForTask(connection.metadata, taskId, checkId)) {
266+
throw new HttpException(
267+
'This check is disconnected from the task. Reconnect it before running.',
268+
HttpStatus.BAD_REQUEST,
269+
);
270+
}
271+
243272
// Get provider and manifest
244273
const provider = await this.providerRepository.findById(
245274
connection.providerId,
@@ -493,6 +522,48 @@ export class TaskIntegrationsController {
493522
}
494523
}
495524

525+
/**
526+
* Disconnect a single integration check from a specific task.
527+
* Does not affect the connection itself or any other task that uses the
528+
* same check. Scheduled runs, manual runs, and the task detail UI will all
529+
* skip this (task, check) pair until it is reconnected.
530+
*/
531+
@Post(':taskId/checks/disconnect')
532+
@RequirePermission('integration', 'update')
533+
async disconnectCheckFromTask(
534+
@Param('taskId') taskId: string,
535+
@OrganizationId() organizationId: string,
536+
@Body() body: ToggleCheckForTaskDto,
537+
): Promise<{ success: true; disabled: true }> {
538+
await this.taskIntegrationChecksService.disconnectCheckFromTask({
539+
taskId,
540+
connectionId: body.connectionId,
541+
checkId: body.checkId,
542+
organizationId,
543+
});
544+
return { success: true, disabled: true };
545+
}
546+
547+
/**
548+
* Re-enable a previously disconnected integration check for a specific
549+
* task. Inverse of the disconnect endpoint.
550+
*/
551+
@Post(':taskId/checks/reconnect')
552+
@RequirePermission('integration', 'update')
553+
async reconnectCheckToTask(
554+
@Param('taskId') taskId: string,
555+
@OrganizationId() organizationId: string,
556+
@Body() body: ToggleCheckForTaskDto,
557+
): Promise<{ success: true; disabled: false }> {
558+
await this.taskIntegrationChecksService.reconnectCheckToTask({
559+
taskId,
560+
connectionId: body.connectionId,
561+
checkId: body.checkId,
562+
organizationId,
563+
});
564+
return { success: true, disabled: false };
565+
}
566+
496567
/**
497568
* Get check run history for a task
498569
*/
@@ -502,7 +573,6 @@ export class TaskIntegrationsController {
502573
@Param('taskId') taskId: string,
503574
@Query('limit') limit?: string,
504575
) {
505-
506576
const runs = await this.checkRunRepository.findByTask(
507577
taskId,
508578
limit ? parseInt(limit, 10) : 10,

apps/api/src/integration-platform/integration-platform.module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { AutoCheckRunnerService } from './services/auto-check-runner.service';
1717
import { ConnectionAuthTeardownService } from './services/connection-auth-teardown.service';
1818
import { OAuthTokenRevocationService } from './services/oauth-token-revocation.service';
1919
import { DynamicManifestLoaderService } from './services/dynamic-manifest-loader.service';
20+
import { TaskIntegrationChecksService } from './services/task-integration-checks.service';
2021
import { ProviderRepository } from './repositories/provider.repository';
2122
import { ConnectionRepository } from './repositories/connection.repository';
2223
import { CredentialRepository } from './repositories/credential.repository';
@@ -52,6 +53,7 @@ import { GenericEmployeeSyncService } from './services/generic-employee-sync.ser
5253
OAuthTokenRevocationService,
5354
ConnectionAuthTeardownService,
5455
DynamicManifestLoaderService,
56+
TaskIntegrationChecksService,
5557
IntegrationSyncLoggerService,
5658
GenericEmployeeSyncService,
5759
// Repositories

0 commit comments

Comments
 (0)