@memberjunction/server 6.1.0-edge.2 → 6.1.0-edge.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +180 -4
- package/dist/agentSessions/SessionManager.d.ts +19 -5
- package/dist/agentSessions/SessionManager.d.ts.map +1 -1
- package/dist/agentSessions/SessionManager.js +27 -9
- package/dist/agentSessions/SessionManager.js.map +1 -1
- package/dist/auth/AuthProviderCatalogRouter.d.ts +31 -0
- package/dist/auth/AuthProviderCatalogRouter.d.ts.map +1 -0
- package/dist/auth/AuthProviderCatalogRouter.js +69 -0
- package/dist/auth/AuthProviderCatalogRouter.js.map +1 -0
- package/dist/auth/AuthProviderEngine.d.ts +96 -0
- package/dist/auth/AuthProviderEngine.d.ts.map +1 -0
- package/dist/auth/AuthProviderEngine.js +245 -0
- package/dist/auth/AuthProviderEngine.js.map +1 -0
- package/dist/auth/index.d.ts +12 -0
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +57 -13
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/initializeProviders.d.ts +64 -1
- package/dist/auth/initializeProviders.d.ts.map +1 -1
- package/dist/auth/initializeProviders.js +117 -5
- package/dist/auth/initializeProviders.js.map +1 -1
- package/dist/auth/scopeLimitedPrincipal.d.ts +39 -0
- package/dist/auth/scopeLimitedPrincipal.d.ts.map +1 -0
- package/dist/auth/scopeLimitedPrincipal.js +49 -0
- package/dist/auth/scopeLimitedPrincipal.js.map +1 -0
- package/dist/config.d.ts +89 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +52 -35
- package/dist/config.js.map +1 -1
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +21 -1
- package/dist/context.js.map +1 -1
- package/dist/entitySubclasses/MJEntityPermissionEntityServer.server.d.ts.map +1 -1
- package/dist/entitySubclasses/MJEntityPermissionEntityServer.server.js +8 -7
- package/dist/entitySubclasses/MJEntityPermissionEntityServer.server.js.map +1 -1
- package/dist/generated/generated.d.ts +619 -1597
- package/dist/generated/generated.d.ts.map +1 -1
- package/dist/generated/generated.js +3341 -17021
- package/dist/generated/generated.js.map +1 -1
- package/dist/generic/ResolverBase.d.ts +28 -1
- package/dist/generic/ResolverBase.d.ts.map +1 -1
- package/dist/generic/ResolverBase.js +57 -8
- package/dist/generic/ResolverBase.js.map +1 -1
- package/dist/generic/RunViewResolver.d.ts +4 -0
- package/dist/generic/RunViewResolver.d.ts.map +1 -1
- package/dist/generic/RunViewResolver.js +31 -0
- package/dist/generic/RunViewResolver.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +147 -14
- package/dist/index.js.map +1 -1
- package/dist/integration/CustomColumnPromoter.d.ts +79 -15
- package/dist/integration/CustomColumnPromoter.d.ts.map +1 -1
- package/dist/integration/CustomColumnPromoter.js +472 -97
- package/dist/integration/CustomColumnPromoter.js.map +1 -1
- package/dist/integration/InactiveRowWarnings.d.ts +48 -0
- package/dist/integration/InactiveRowWarnings.d.ts.map +1 -0
- package/dist/integration/InactiveRowWarnings.js +57 -0
- package/dist/integration/InactiveRowWarnings.js.map +1 -0
- package/dist/integration/RSUProgressBridge.d.ts +83 -0
- package/dist/integration/RSUProgressBridge.d.ts.map +1 -0
- package/dist/integration/RSUProgressBridge.js +230 -0
- package/dist/integration/RSUProgressBridge.js.map +1 -0
- package/dist/integration/SchemaRefreshLaunch.d.ts +62 -0
- package/dist/integration/SchemaRefreshLaunch.d.ts.map +1 -0
- package/dist/integration/SchemaRefreshLaunch.js +67 -0
- package/dist/integration/SchemaRefreshLaunch.js.map +1 -0
- package/dist/providerConfigUnits.d.ts +20 -0
- package/dist/providerConfigUnits.d.ts.map +1 -0
- package/dist/providerConfigUnits.js +22 -0
- package/dist/providerConfigUnits.js.map +1 -0
- package/dist/resolvers/AdhocQueryResolver.d.ts +4 -0
- package/dist/resolvers/AdhocQueryResolver.d.ts.map +1 -1
- package/dist/resolvers/AdhocQueryResolver.js +20 -6
- package/dist/resolvers/AdhocQueryResolver.js.map +1 -1
- package/dist/resolvers/FileResolver.d.ts +34 -4
- package/dist/resolvers/FileResolver.d.ts.map +1 -1
- package/dist/resolvers/FileResolver.js +218 -28
- package/dist/resolvers/FileResolver.js.map +1 -1
- package/dist/resolvers/IdentityClaimRedemptionResolver.d.ts +37 -0
- package/dist/resolvers/IdentityClaimRedemptionResolver.d.ts.map +1 -0
- package/dist/resolvers/IdentityClaimRedemptionResolver.js +220 -0
- package/dist/resolvers/IdentityClaimRedemptionResolver.js.map +1 -0
- package/dist/resolvers/IntegrationDiscoveryResolver.d.ts +108 -3
- package/dist/resolvers/IntegrationDiscoveryResolver.d.ts.map +1 -1
- package/dist/resolvers/IntegrationDiscoveryResolver.js +374 -87
- package/dist/resolvers/IntegrationDiscoveryResolver.js.map +1 -1
- package/dist/resolvers/QueryResolver.d.ts +5 -4
- package/dist/resolvers/QueryResolver.d.ts.map +1 -1
- package/dist/resolvers/QueryResolver.js +29 -14
- package/dist/resolvers/QueryResolver.js.map +1 -1
- package/dist/resolvers/QuerySystemUserResolver.d.ts +12 -3
- package/dist/resolvers/QuerySystemUserResolver.d.ts.map +1 -1
- package/dist/resolvers/QuerySystemUserResolver.js +33 -14
- package/dist/resolvers/QuerySystemUserResolver.js.map +1 -1
- package/dist/resolvers/RunAIAgentResolver.d.ts +3 -1
- package/dist/resolvers/RunAIAgentResolver.d.ts.map +1 -1
- package/dist/resolvers/RunAIAgentResolver.js +20 -7
- package/dist/resolvers/RunAIAgentResolver.js.map +1 -1
- package/dist/resolvers/TaskGraphFrameResolver.d.ts +18 -0
- package/dist/resolvers/TaskGraphFrameResolver.d.ts.map +1 -1
- package/dist/resolvers/TaskGraphFrameResolver.js +75 -0
- package/dist/resolvers/TaskGraphFrameResolver.js.map +1 -1
- package/dist/resolvers/UserResolver.d.ts +19 -17
- package/dist/resolvers/UserResolver.d.ts.map +1 -1
- package/dist/resolvers/UserResolver.js +66 -67
- package/dist/resolvers/UserResolver.js.map +1 -1
- package/dist/resolvers/currentUserRoles.d.ts +19 -0
- package/dist/resolvers/currentUserRoles.d.ts.map +1 -0
- package/dist/resolvers/currentUserRoles.js +36 -0
- package/dist/resolvers/currentUserRoles.js.map +1 -0
- package/dist/resolvers/meetingRecordingRegistration.d.ts.map +1 -1
- package/dist/resolvers/meetingRecordingRegistration.js +3 -6
- package/dist/resolvers/meetingRecordingRegistration.js.map +1 -1
- package/dist/rest/MediaAccessKeys.d.ts +17 -0
- package/dist/rest/MediaAccessKeys.d.ts.map +1 -1
- package/dist/rest/MediaAccessKeys.js +42 -1
- package/dist/rest/MediaAccessKeys.js.map +1 -1
- package/dist/rest/MediaStreamHandler.d.ts +3 -2
- package/dist/rest/MediaStreamHandler.d.ts.map +1 -1
- package/dist/rest/MediaStreamHandler.js +83 -2
- package/dist/rest/MediaStreamHandler.js.map +1 -1
- package/dist/rest/OAuthCallbackHandler.d.ts +49 -1
- package/dist/rest/OAuthCallbackHandler.d.ts.map +1 -1
- package/dist/rest/OAuthCallbackHandler.js +129 -43
- package/dist/rest/OAuthCallbackHandler.js.map +1 -1
- package/dist/rest/UploadTokenManager.d.ts +90 -0
- package/dist/rest/UploadTokenManager.d.ts.map +1 -0
- package/dist/rest/UploadTokenManager.js +156 -0
- package/dist/rest/UploadTokenManager.js.map +1 -0
- package/dist/services/IntegrationSyncWorkerService.d.ts +48 -0
- package/dist/services/IntegrationSyncWorkerService.d.ts.map +1 -0
- package/dist/services/IntegrationSyncWorkerService.js +132 -0
- package/dist/services/IntegrationSyncWorkerService.js.map +1 -0
- package/dist/services/StartTaskGraphDispatcher.d.ts +0 -10
- package/dist/services/StartTaskGraphDispatcher.d.ts.map +1 -1
- package/dist/services/StartTaskGraphDispatcher.js +21 -1
- package/dist/services/StartTaskGraphDispatcher.js.map +1 -1
- package/dist/services/TaskGraphAgentRunner.d.ts +7 -0
- package/dist/services/TaskGraphAgentRunner.d.ts.map +1 -1
- package/dist/services/TaskGraphAgentRunner.js +23 -0
- package/dist/services/TaskGraphAgentRunner.js.map +1 -1
- package/dist/services/TaskGraphContinuationDeliverer.d.ts.map +1 -1
- package/dist/services/TaskGraphContinuationDeliverer.js +12 -1
- package/dist/services/TaskGraphContinuationDeliverer.js.map +1 -1
- package/dist/types.d.ts +14 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +92 -92
- package/src/__tests__/APIKeyScopeAuth.test.ts +475 -0
- package/src/__tests__/AdhocQueryResolver.scopeGuard.test.ts +84 -0
- package/src/__tests__/AuthProviderEngine.test.ts +278 -0
- package/src/__tests__/CustomColumnPromoterOnePass.test.ts +387 -0
- package/src/__tests__/EntityCRUDHandler.test.ts +378 -0
- package/src/__tests__/InactiveRowWarnings.test.ts +87 -0
- package/src/__tests__/IntegrationSyncWorkerService.test.ts +275 -0
- package/src/__tests__/OAuthCallbackHandler.openRedirect.test.ts +117 -0
- package/src/__tests__/OAuthCallbackHandler.xss.test.ts +4 -1
- package/src/__tests__/RESTEndpointHandler.test.ts +672 -0
- package/src/__tests__/RSUProgressBridge.test.ts +250 -0
- package/src/__tests__/ReactivateRefreshMessage.test.ts +85 -0
- package/src/__tests__/RealtimeBridgeResolver.test.ts +10 -2
- package/src/__tests__/ResolverBase.filterEscaping.test.ts +147 -0
- package/src/__tests__/SchemaRefreshLaunch.test.ts +114 -0
- package/src/__tests__/SessionManager.test.ts +55 -0
- package/src/__tests__/UserResolver.currentUser.test.ts +87 -0
- package/src/__tests__/ViewOperationsHandler.test.ts +277 -0
- package/src/__tests__/config-units.test.ts +74 -0
- package/src/__tests__/initializeProviders.refresh.test.ts +146 -0
- package/src/__tests__/mjapi-bootstrap.test.ts +0 -6
- package/src/__tests__/newUsers.test.ts +726 -0
- package/src/__tests__/uploadTokenManager.test.ts +159 -0
- package/src/agentSessions/SessionManager.ts +27 -9
- package/src/auth/AuthProviderCatalogRouter.ts +76 -0
- package/src/auth/AuthProviderEngine.ts +253 -0
- package/src/auth/index.ts +60 -15
- package/src/auth/initializeProviders.ts +128 -7
- package/src/auth/scopeLimitedPrincipal.ts +50 -0
- package/src/config.ts +55 -36
- package/src/context.ts +26 -2
- package/src/entitySubclasses/MJEntityPermissionEntityServer.server.ts +8 -7
- package/src/generated/generated.ts +2347 -10551
- package/src/generic/ResolverBase.ts +67 -8
- package/src/generic/RunViewResolver.ts +31 -0
- package/src/index.ts +165 -16
- package/src/integration/CustomColumnPromoter.ts +520 -98
- package/src/integration/InactiveRowWarnings.ts +85 -0
- package/src/integration/RSUProgressBridge.ts +248 -0
- package/src/integration/SchemaRefreshLaunch.ts +93 -0
- package/src/providerConfigUnits.ts +21 -0
- package/src/resolvers/AdhocQueryResolver.ts +21 -6
- package/src/resolvers/FileResolver.ts +200 -40
- package/src/resolvers/IdentityClaimRedemptionResolver.ts +184 -0
- package/src/resolvers/IntegrationDiscoveryResolver.ts +390 -71
- package/src/resolvers/QueryResolver.ts +25 -11
- package/src/resolvers/QuerySystemUserResolver.ts +28 -13
- package/src/resolvers/RunAIAgentResolver.ts +21 -6
- package/src/resolvers/TaskGraphFrameResolver.ts +67 -0
- package/src/resolvers/UserResolver.ts +99 -60
- package/src/resolvers/currentUserRoles.ts +47 -0
- package/src/resolvers/meetingRecordingRegistration.ts +3 -7
- package/src/rest/MediaAccessKeys.ts +47 -1
- package/src/rest/MediaStreamHandler.ts +98 -3
- package/src/rest/OAuthCallbackHandler.ts +149 -41
- package/src/rest/UploadTokenManager.ts +211 -0
- package/src/services/IntegrationSyncWorkerService.ts +141 -0
- package/src/services/StartTaskGraphDispatcher.ts +21 -1
- package/src/services/TaskGraphAgentRunner.ts +23 -0
- package/src/services/TaskGraphContinuationDeliverer.ts +11 -1
- package/src/types.ts +14 -0
|
@@ -19,12 +19,15 @@ import { UUIDsEqual } from "@memberjunction/global";
|
|
|
19
19
|
import { CronExpressionHelper } from "@memberjunction/scheduling-engine";
|
|
20
20
|
import { ConnectorFactory, IntegrationEngine, IntegrationSchemaSync, decideSchemaLimitViolations, IntegrationConnectorCreationPipeline, IntegrationActionGenerator } from "@memberjunction/integration-engine";
|
|
21
21
|
import { IntegrationEngineBase } from "@memberjunction/integration-engine-base";
|
|
22
|
+
import { buildIntegrationLLMPKCallback } from "@memberjunction/core-entities-server";
|
|
22
23
|
import { SchemaBuilder, TypeMapper, SchemaEvolution } from "@memberjunction/integration-schema-builder";
|
|
23
24
|
import { RuntimeSchemaManager } from "@memberjunction/schema-engine";
|
|
24
|
-
import { IntegrationProgressReader } from "@memberjunction/integration-progress-artifacts";
|
|
25
|
+
import { IntegrationProgressEmitter, IntegrationProgressReader } from "@memberjunction/integration-progress-artifacts";
|
|
25
26
|
import { ResolverBase } from "../generic/ResolverBase.js";
|
|
26
27
|
import { IntegrationCustomColumnPromoter } from "../integration/CustomColumnPromoter.js";
|
|
27
28
|
import { ComputeCascadeRemovalSet, ComputeRemovedDependencyWarnings, DisableUnselectedEntityMaps, ReenableFieldMapsForEntityMap, ResetPullWatermarks, SetEntityMapEnabled } from "../integration/EntityMapLifecycle.js";
|
|
29
|
+
import { ComputeInactiveRowWarnings } from "../integration/InactiveRowWarnings.js";
|
|
30
|
+
import { BuildCreateConnectionMessage, BuildDetachedRefreshMessage, BuildReactivateMessage, BuildUpdateConnectionMessage } from "../integration/SchemaRefreshLaunch.js";
|
|
28
31
|
import { RequireSystemUser } from "../directives/RequireSystemUser.js";
|
|
29
32
|
import { UserCache } from "@memberjunction/generic-database-provider";
|
|
30
33
|
// ─── RSU Pipeline Output Types ──────────────────────────────────────────────
|
|
@@ -1074,6 +1077,18 @@ __decorate([
|
|
|
1074
1077
|
Field(),
|
|
1075
1078
|
__metadata("design:type", String)
|
|
1076
1079
|
], CreateConnectionPipelineSummary.prototype, "RunID", void 0);
|
|
1080
|
+
__decorate([
|
|
1081
|
+
Field(),
|
|
1082
|
+
__metadata("design:type", Boolean)
|
|
1083
|
+
], CreateConnectionPipelineSummary.prototype, "InProgress", void 0);
|
|
1084
|
+
__decorate([
|
|
1085
|
+
Field(),
|
|
1086
|
+
__metadata("design:type", Boolean)
|
|
1087
|
+
], CreateConnectionPipelineSummary.prototype, "Succeeded", void 0);
|
|
1088
|
+
__decorate([
|
|
1089
|
+
Field({ nullable: true }),
|
|
1090
|
+
__metadata("design:type", String)
|
|
1091
|
+
], CreateConnectionPipelineSummary.prototype, "FailureMessage", void 0);
|
|
1077
1092
|
__decorate([
|
|
1078
1093
|
Field(),
|
|
1079
1094
|
__metadata("design:type", Number)
|
|
@@ -1181,6 +1196,10 @@ __decorate([
|
|
|
1181
1196
|
Field(() => Int, { nullable: true, description: 'Time budget (ms) for stage-2 streaming field discovery before it stops and uses what it gathered.' }),
|
|
1182
1197
|
__metadata("design:type", Number)
|
|
1183
1198
|
], IntegrationSyncConfigInput.prototype, "DiscoveryTimeBudgetMs", void 0);
|
|
1199
|
+
__decorate([
|
|
1200
|
+
Field(() => Int, { nullable: true, description: 'Per-page FetchChanges timeout (ms). Raise it for a connector that fans out one request per parent record, whose page time scales with BatchSize. Wins over the connector\'s own FetchChangesTimeoutMs; unset = the connector\'s value, else the framework default (30000).' }),
|
|
1201
|
+
__metadata("design:type", Number)
|
|
1202
|
+
], IntegrationSyncConfigInput.prototype, "FetchTimeoutMs", void 0);
|
|
1184
1203
|
__decorate([
|
|
1185
1204
|
Field(() => Int, { nullable: true, description: 'Batch size for stage-2 streaming field discovery (records per FetchChanges page). Default 500.' }),
|
|
1186
1205
|
__metadata("design:type", Number)
|
|
@@ -1234,6 +1253,10 @@ __decorate([
|
|
|
1234
1253
|
Field(() => Int, { nullable: true }),
|
|
1235
1254
|
__metadata("design:type", Number)
|
|
1236
1255
|
], IntegrationSyncConfigOutput.prototype, "DiscoveryTimeBudgetMs", void 0);
|
|
1256
|
+
__decorate([
|
|
1257
|
+
Field(() => Int, { nullable: true }),
|
|
1258
|
+
__metadata("design:type", Number)
|
|
1259
|
+
], IntegrationSyncConfigOutput.prototype, "FetchTimeoutMs", void 0);
|
|
1237
1260
|
__decorate([
|
|
1238
1261
|
Field(() => Int, { nullable: true }),
|
|
1239
1262
|
__metadata("design:type", Number)
|
|
@@ -2083,7 +2106,7 @@ __decorate([
|
|
|
2083
2106
|
IntegrationRunEventsOutput = __decorate([
|
|
2084
2107
|
ObjectType()
|
|
2085
2108
|
], IntegrationRunEventsOutput);
|
|
2086
|
-
// Sync progress is now tracked inside IntegrationEngine itself via IntegrationEngine.
|
|
2109
|
+
// Sync progress is now tracked inside IntegrationEngine itself via IntegrationEngine.GetSyncProgressAsync()
|
|
2087
2110
|
let ConnectionSummaryOutput = class ConnectionSummaryOutput {
|
|
2088
2111
|
};
|
|
2089
2112
|
__decorate([
|
|
@@ -3310,7 +3333,7 @@ let IntegrationDiscoveryResolver = class IntegrationDiscoveryResolver extends Re
|
|
|
3310
3333
|
* - Foreign-key relationships are reconstructed from
|
|
3311
3334
|
* `RelatedIntegrationObjectID` lookups against the same cache.
|
|
3312
3335
|
*/
|
|
3313
|
-
buildSourceSchemaFromPersistedRows(integrationID, requestedNames) {
|
|
3336
|
+
buildSourceSchemaFromPersistedRows(integrationID, requestedNames, warningsOut) {
|
|
3314
3337
|
const engine = IntegrationEngineBase.Instance;
|
|
3315
3338
|
// ACTIVE-only materialization: an object/field a given tenant doesn't expose is marked
|
|
3316
3339
|
// Status='Inactive' by discovery (the phantom-skip), and MUST NOT be materialized — creating
|
|
@@ -3327,11 +3350,17 @@ let IntegrationDiscoveryResolver = class IntegrationDiscoveryResolver extends Re
|
|
|
3327
3350
|
for (const io of ios)
|
|
3328
3351
|
ioByID.set(io.ID, io.Name);
|
|
3329
3352
|
const result = { Objects: [] };
|
|
3353
|
+
const fieldsByObjectName = {};
|
|
3330
3354
|
for (const io of ios) {
|
|
3331
3355
|
if (filter && !filter.has(io.Name.toLowerCase()))
|
|
3332
3356
|
continue;
|
|
3333
3357
|
// Active fields only — an inactive (source-absent / deactivated) field is not materialized.
|
|
3334
|
-
const
|
|
3358
|
+
const allFields = engine.GetIntegrationObjectFields(io.ID);
|
|
3359
|
+
const iofs = allFields.filter(iof => iof.Status === 'Active');
|
|
3360
|
+
// Remember what this object declared, active or not — the caller's warning collector
|
|
3361
|
+
// turns the difference into the one message that explains a column that never appeared.
|
|
3362
|
+
if (warningsOut)
|
|
3363
|
+
fieldsByObjectName[io.Name] = allFields.map(f => ({ Name: f.Name, Status: f.Status }));
|
|
3335
3364
|
const fields = iofs.map(iof => {
|
|
3336
3365
|
const targetIOName = iof.RelatedIntegrationObjectID
|
|
3337
3366
|
? ioByID.get(iof.RelatedIntegrationObjectID) ?? null
|
|
@@ -3370,11 +3399,46 @@ let IntegrationDiscoveryResolver = class IntegrationDiscoveryResolver extends Re
|
|
|
3370
3399
|
IncrementalWatermarkField: io.IncrementalWatermarkField ?? undefined,
|
|
3371
3400
|
});
|
|
3372
3401
|
}
|
|
3402
|
+
// Declared-but-inactive rows this rebuild left out. The caller decides what to do with the
|
|
3403
|
+
// strings (the apply path puts them on its Warnings); computing them costs nothing when no
|
|
3404
|
+
// collector was passed, since the field lists above are only gathered then.
|
|
3405
|
+
if (warningsOut) {
|
|
3406
|
+
warningsOut.push(...ComputeInactiveRowWarnings({
|
|
3407
|
+
RequestedNames: requestedNames ?? null,
|
|
3408
|
+
AllObjects: engine.GetIntegrationObjectsByIntegrationID(integrationID)
|
|
3409
|
+
.map(io => ({ Name: io.Name, Status: io.Status })),
|
|
3410
|
+
FieldsByObjectName: fieldsByObjectName,
|
|
3411
|
+
}));
|
|
3412
|
+
}
|
|
3373
3413
|
return result;
|
|
3374
3414
|
}
|
|
3375
|
-
async runSchemaRefreshPipeline(companyIntegrationID, user, provider, universalPKConvention) {
|
|
3415
|
+
async runSchemaRefreshPipeline(companyIntegrationID, user, provider, universalPKConvention, runID) {
|
|
3416
|
+
// Sync lock (#3656). This pipeline rewrites the very IO/IOF rows, field
|
|
3417
|
+
// maps and DDL a sync reads, so it must not overlap a sync or another
|
|
3418
|
+
// maintenance operation for the same connection. The two other
|
|
3419
|
+
// pipeline call sites — IntegrationRefreshConnectorSchema and
|
|
3420
|
+
// IntegrationSchemaEvolution — already take this lock inline; this is
|
|
3421
|
+
// the third and fourth (create/update/reactivate) finally taking it too.
|
|
3422
|
+
if (!IntegrationEngine.AcquireMaintenanceLock(companyIntegrationID, 'schema refresh')) {
|
|
3423
|
+
throw new Error('Schema refresh not started: a sync or another maintenance operation is currently running for this connection. Retry after it completes.');
|
|
3424
|
+
}
|
|
3425
|
+
try {
|
|
3426
|
+
return await this.runSchemaRefreshPipelineLocked(companyIntegrationID, user, provider, universalPKConvention, runID);
|
|
3427
|
+
}
|
|
3428
|
+
finally {
|
|
3429
|
+
IntegrationEngine.ReleaseMaintenanceLock(companyIntegrationID);
|
|
3430
|
+
}
|
|
3431
|
+
}
|
|
3432
|
+
/** The body of {@link runSchemaRefreshPipeline}, run with the maintenance lock held. */
|
|
3433
|
+
async runSchemaRefreshPipelineLocked(companyIntegrationID, user, provider, universalPKConvention, runID) {
|
|
3376
3434
|
const { connector, companyIntegration } = await this.resolveConnector(companyIntegrationID, user, provider);
|
|
3377
3435
|
const pipeline = new IntegrationConnectorCreationPipeline();
|
|
3436
|
+
// SoftPKClassifier's LLM tier (universal → naming → statistical → LLM →
|
|
3437
|
+
// synthetic) only runs when this callback is supplied. It used to be
|
|
3438
|
+
// wired by the CompanyIntegration save hook; with that hook gone
|
|
3439
|
+
// (#3738) it is wired here, so the create/update/reactivate paths keep
|
|
3440
|
+
// the same PK-inference quality they had before.
|
|
3441
|
+
const llmInference = await buildIntegrationLLMPKCallback(user);
|
|
3378
3442
|
const runOpts = {
|
|
3379
3443
|
Connector: connector,
|
|
3380
3444
|
CompanyIntegration: companyIntegration,
|
|
@@ -3383,6 +3447,10 @@ let IntegrationDiscoveryResolver = class IntegrationDiscoveryResolver extends Re
|
|
|
3383
3447
|
UniversalPKConvention: universalPKConvention || undefined,
|
|
3384
3448
|
ConsoleMirror: true,
|
|
3385
3449
|
TriggerType: 'Manual',
|
|
3450
|
+
LLMInference: llmInference ?? undefined,
|
|
3451
|
+
// Caller-supplied runID: lets the detached path hand a tailable ID back to the client
|
|
3452
|
+
// BEFORE the pipeline has done any work. Omitted ⇒ the pipeline generates its own.
|
|
3453
|
+
RunID: runID,
|
|
3386
3454
|
};
|
|
3387
3455
|
const result = await pipeline.Run(runOpts);
|
|
3388
3456
|
// Refresh in-memory caches so downstream queries (object picker,
|
|
@@ -3391,6 +3459,9 @@ let IntegrationDiscoveryResolver = class IntegrationDiscoveryResolver extends Re
|
|
|
3391
3459
|
await IntegrationEngine.Instance.Config(true, user, provider);
|
|
3392
3460
|
return {
|
|
3393
3461
|
RunID: result.RunID,
|
|
3462
|
+
InProgress: false,
|
|
3463
|
+
Succeeded: result.Success,
|
|
3464
|
+
FailureMessage: result.FailureMessage,
|
|
3394
3465
|
ObjectsCreated: result.PersistResult?.ObjectsCreated ?? 0,
|
|
3395
3466
|
ObjectsUpdated: result.PersistResult?.ObjectsUpdated ?? 0,
|
|
3396
3467
|
FieldsCreated: result.PersistResult?.FieldsCreated ?? 0,
|
|
@@ -3406,6 +3477,88 @@ let IntegrationDiscoveryResolver = class IntegrationDiscoveryResolver extends Re
|
|
|
3406
3477
|
})),
|
|
3407
3478
|
};
|
|
3408
3479
|
}
|
|
3480
|
+
/**
|
|
3481
|
+
* Launches the schema-refresh pipeline WITHOUT awaiting it and returns a summary carrying the
|
|
3482
|
+
* tailable `RunID` and `InProgress: true`.
|
|
3483
|
+
*
|
|
3484
|
+
* Why: the pipeline is a live vendor introspect — minutes on a large catalog (HubSpot: 130
|
|
3485
|
+
* objects). Awaiting it inline holds the create/update mutation open for that entire time, so the
|
|
3486
|
+
* connection wizard's Finish button sits on an indeterminate spinner with no way to tell progress
|
|
3487
|
+
* from a hang. The pipeline already writes a complete, durable, per-run event stream keyed by its
|
|
3488
|
+
* runID; the only thing missing was handing that ID to the caller BEFORE the work starts. Hence
|
|
3489
|
+
* the caller-supplied runID.
|
|
3490
|
+
*
|
|
3491
|
+
* Trade-off the caller opts into: the mutation no longer reports what the refresh found (counts
|
|
3492
|
+
* are placeholder zeros) and a refresh failure no longer surfaces in the mutation's Message — both
|
|
3493
|
+
* live on the run stream instead. That is why `awaitSchemaRefresh` defaults to true.
|
|
3494
|
+
*
|
|
3495
|
+
* ON CREATE this is the only refresh that runs. #3738 removed the activation hook that used to
|
|
3496
|
+
* fire the same pipeline inside `Save()`, awaited — which had made the create mutation pay a full
|
|
3497
|
+
* introspect before ever reaching this method, and had run it BEFORE the connection test, so a
|
|
3498
|
+
* rejected connection was rolled back with its discovered schema already written.
|
|
3499
|
+
*
|
|
3500
|
+
* Belt and braces: if the launch below IS coalesced onto some other run for this connection (a
|
|
3501
|
+
* concurrent refresh, a repeat click), the pipeline publishes a terminal alias run under `runID`
|
|
3502
|
+
* naming the run that served it — so the ID handed back here is tailable in every case.
|
|
3503
|
+
*/
|
|
3504
|
+
startSchemaRefreshPipelineDetached(companyIntegrationID, user, provider, universalPKConvention) {
|
|
3505
|
+
const runID = IntegrationProgressEmitter.newRunID('connector');
|
|
3506
|
+
// Deliberately not awaited. Once the pipeline is running it records every outcome on this
|
|
3507
|
+
// run's artifact stream under `runID` itself — but it can also throw BEFORE it ever
|
|
3508
|
+
// constructs its emitter (connector-driver resolution, a missing Integration row), and in
|
|
3509
|
+
// the blocking path that throw surfaced in the mutation's Message. Detached, the caller has
|
|
3510
|
+
// already been handed `runID` and has nothing but the stream to watch, so a pre-emitter
|
|
3511
|
+
// throw must be published onto that stream — otherwise the run never appears at all and a
|
|
3512
|
+
// tailer waits forever on a run ID that will never produce an event.
|
|
3513
|
+
void this.runSchemaRefreshPipeline(companyIntegrationID, user, provider, universalPKConvention, runID)
|
|
3514
|
+
.catch(err => this.publishDetachedLaunchFailure(runID, companyIntegrationID, err));
|
|
3515
|
+
return {
|
|
3516
|
+
RunID: runID,
|
|
3517
|
+
InProgress: true,
|
|
3518
|
+
// Not known yet — the run has only just been launched.
|
|
3519
|
+
Succeeded: false,
|
|
3520
|
+
ObjectsCreated: 0,
|
|
3521
|
+
ObjectsUpdated: 0,
|
|
3522
|
+
FieldsCreated: 0,
|
|
3523
|
+
FieldsUpdated: 0,
|
|
3524
|
+
UnresolvedObjects: [],
|
|
3525
|
+
PKVerdicts: [],
|
|
3526
|
+
};
|
|
3527
|
+
}
|
|
3528
|
+
/**
|
|
3529
|
+
* Terminates a detached run that failed BEFORE the pipeline could open its own artifact stream.
|
|
3530
|
+
*
|
|
3531
|
+
* The pipeline constructs its emitter inside `Run()`, so anything that throws on the way there —
|
|
3532
|
+
* connector-driver resolution (`No connector registered for driver class "X"`), a missing
|
|
3533
|
+
* Integration row, a CompanyIntegration that vanished — produces no run directory at all. In the
|
|
3534
|
+
* blocking path that throw surfaced in the mutation's Message. Detached, the caller has already
|
|
3535
|
+
* been handed the run ID and has only the stream to watch, so the failure has to be published
|
|
3536
|
+
* there or the run is invisible forever.
|
|
3537
|
+
*
|
|
3538
|
+
* Only ever called on the rejection path: if the pipeline got far enough to build its own emitter
|
|
3539
|
+
* it terminates its own run, and a pipeline that completed never reaches here.
|
|
3540
|
+
*/
|
|
3541
|
+
publishDetachedLaunchFailure(runID, companyIntegrationID, err) {
|
|
3542
|
+
const message = this.formatError(err);
|
|
3543
|
+
LogError(`Detached schema refresh (run ${runID}) failed: ${message}`);
|
|
3544
|
+
try {
|
|
3545
|
+
const emitter = new IntegrationProgressEmitter({
|
|
3546
|
+
runID,
|
|
3547
|
+
runKind: 'ConnectorCreation',
|
|
3548
|
+
companyIntegrationID,
|
|
3549
|
+
triggerType: 'Manual',
|
|
3550
|
+
startedAt: new Date().toISOString(),
|
|
3551
|
+
});
|
|
3552
|
+
emitter.runStart('Detached schema refresh launch');
|
|
3553
|
+
emitter.stageError('Launch', message, { code: 'schema-refresh-launch-failed' });
|
|
3554
|
+
void emitter.fail(`Schema refresh could not start: ${message}`, 'schema-refresh-launch-failed')
|
|
3555
|
+
.catch(e => LogError(`Detached schema refresh (run ${runID}): failure artifact write failed — ${e}`));
|
|
3556
|
+
}
|
|
3557
|
+
catch (e) {
|
|
3558
|
+
// Progress reporting must never mask the original failure, which is already logged above.
|
|
3559
|
+
LogError(`Detached schema refresh (run ${runID}): could not open failure artifact — ${e}`);
|
|
3560
|
+
}
|
|
3561
|
+
}
|
|
3409
3562
|
/**
|
|
3410
3563
|
* Rolls back a freshly created connection by deleting both the CompanyIntegration and Credential records.
|
|
3411
3564
|
*/
|
|
@@ -3556,7 +3709,7 @@ let IntegrationDiscoveryResolver = class IntegrationDiscoveryResolver extends Re
|
|
|
3556
3709
|
/**
|
|
3557
3710
|
* Creates a CompanyIntegration with a linked Credential entity for encrypted credential storage.
|
|
3558
3711
|
*/
|
|
3559
|
-
async IntegrationCreateConnection(input, testConnection, runSchemaRefresh, universalPKConvention, ctx) {
|
|
3712
|
+
async IntegrationCreateConnection(input, testConnection, runSchemaRefresh, universalPKConvention, awaitSchemaRefresh, ctx) {
|
|
3560
3713
|
try {
|
|
3561
3714
|
const user = this.getAuthenticatedUser(ctx);
|
|
3562
3715
|
const md = GetReadWriteProvider(ctx.providers, { allowFallbackToReadOnly: true });
|
|
@@ -3579,12 +3732,20 @@ let IntegrationDiscoveryResolver = class IntegrationDiscoveryResolver extends Re
|
|
|
3579
3732
|
ci.IntegrationID = input.IntegrationID;
|
|
3580
3733
|
ci.CompanyID = input.CompanyID;
|
|
3581
3734
|
ci.CredentialID = credentialID;
|
|
3582
|
-
|
|
3735
|
+
// Created INACTIVE on purpose (#3738). A connection is not marked
|
|
3736
|
+
// active until its credential has had a chance to prove itself in
|
|
3737
|
+
// step 3 below — so nothing downstream ever observes an active row
|
|
3738
|
+
// backed by a password that is about to be rejected and rolled back.
|
|
3739
|
+
ci.IsActive = false;
|
|
3583
3740
|
ci.Name = input.CredentialName; // Name is required on CompanyIntegration
|
|
3584
3741
|
if (input.ExternalSystemID)
|
|
3585
3742
|
ci.ExternalSystemID = input.ExternalSystemID;
|
|
3586
3743
|
if (input.Configuration)
|
|
3587
3744
|
ci.Configuration = input.Configuration;
|
|
3745
|
+
// Whether the refresh runs detached (this resolver owns it and returns a tailable RunID)
|
|
3746
|
+
// or inline below. No save-side refresh exists to coordinate with any more — #3738 removed
|
|
3747
|
+
// the activation hook, so every refresh on this path is one this method asked for.
|
|
3748
|
+
const ownSchemaRefresh = runSchemaRefresh && !awaitSchemaRefresh;
|
|
3588
3749
|
const saved = await ci.Save();
|
|
3589
3750
|
if (!saved) {
|
|
3590
3751
|
const validationErrors = ci.LatestResult?.Message || 'Unknown validation error';
|
|
@@ -3607,12 +3768,27 @@ let IntegrationDiscoveryResolver = class IntegrationDiscoveryResolver extends Re
|
|
|
3607
3768
|
testPassed = true;
|
|
3608
3769
|
testMessage = testResult.Message;
|
|
3609
3770
|
}
|
|
3771
|
+
// 3b. Credential has proven itself (or the caller declined a test,
|
|
3772
|
+
// which is the pre-existing default) — activate. Ordering matters:
|
|
3773
|
+
// this save is what makes the connection usable, and it must not
|
|
3774
|
+
// happen while the credential is still unverified.
|
|
3775
|
+
ci.IsActive = true;
|
|
3776
|
+
if (!await ci.Save()) {
|
|
3777
|
+
await this.rollbackCreatedConnection(ci, credential);
|
|
3778
|
+
return {
|
|
3779
|
+
Success: false,
|
|
3780
|
+
Message: `Failed to activate CompanyIntegration: ${ci.LatestResult?.Message || 'Unknown error'}`,
|
|
3781
|
+
};
|
|
3782
|
+
}
|
|
3610
3783
|
// 4. Auto-run schema refresh pipeline (intermittent server-side period).
|
|
3611
3784
|
// Fires whenever runSchemaRefresh=true, regardless of whether the
|
|
3612
3785
|
// caller also asked for a test. The wizard may have tested separately
|
|
3613
3786
|
// and just be hitting Create to save.
|
|
3614
3787
|
let schemaRefreshSummary;
|
|
3615
|
-
if (
|
|
3788
|
+
if (ownSchemaRefresh) {
|
|
3789
|
+
schemaRefreshSummary = this.startSchemaRefreshPipelineDetached(ci.ID, user, md, universalPKConvention);
|
|
3790
|
+
}
|
|
3791
|
+
else if (runSchemaRefresh) {
|
|
3616
3792
|
try {
|
|
3617
3793
|
const refreshResult = await this.runSchemaRefreshPipeline(ci.ID, user, md, universalPKConvention);
|
|
3618
3794
|
schemaRefreshSummary = refreshResult;
|
|
@@ -3626,9 +3802,7 @@ let IntegrationDiscoveryResolver = class IntegrationDiscoveryResolver extends Re
|
|
|
3626
3802
|
if (testConnection || schemaRefreshSummary) {
|
|
3627
3803
|
return {
|
|
3628
3804
|
Success: true,
|
|
3629
|
-
Message: schemaRefreshSummary
|
|
3630
|
-
? `Connection created${testConnection ? ', test passed' : ''}, schema refresh: ${schemaRefreshSummary.ObjectsCreated} created, ${schemaRefreshSummary.ObjectsUpdated} updated, ${schemaRefreshSummary.UnresolvedObjects.length} PK-unresolved`
|
|
3631
|
-
: 'Connection created and test passed',
|
|
3805
|
+
Message: BuildCreateConnectionMessage(testConnection, schemaRefreshSummary),
|
|
3632
3806
|
CompanyIntegrationID: ci.ID,
|
|
3633
3807
|
CredentialID: credentialID,
|
|
3634
3808
|
ConnectionTestSuccess: testPassed,
|
|
@@ -3651,7 +3825,7 @@ let IntegrationDiscoveryResolver = class IntegrationDiscoveryResolver extends Re
|
|
|
3651
3825
|
/**
|
|
3652
3826
|
* Updates credential values and/or configuration on an existing CompanyIntegration.
|
|
3653
3827
|
*/
|
|
3654
|
-
async IntegrationUpdateConnection(companyIntegrationID, credentialValues, configuration, externalSystemID, testConnection, runSchemaRefresh, universalPKConvention, ctx) {
|
|
3828
|
+
async IntegrationUpdateConnection(companyIntegrationID, credentialValues, configuration, externalSystemID, testConnection, runSchemaRefresh, universalPKConvention, awaitSchemaRefresh, ctx) {
|
|
3655
3829
|
try {
|
|
3656
3830
|
const user = this.getAuthenticatedUser(ctx);
|
|
3657
3831
|
const md = GetReadWriteProvider(ctx.providers, { allowFallbackToReadOnly: true });
|
|
@@ -3700,13 +3874,14 @@ let IntegrationDiscoveryResolver = class IntegrationDiscoveryResolver extends Re
|
|
|
3700
3874
|
// Fires whenever runSchemaRefresh=true, regardless of whether the
|
|
3701
3875
|
// caller also asked for a test — the wizard may have tested separately
|
|
3702
3876
|
// already and is just hitting Update to save edits.
|
|
3877
|
+
if (runSchemaRefresh && !awaitSchemaRefresh) {
|
|
3878
|
+
const detached = this.startSchemaRefreshPipelineDetached(companyIntegrationID, user, md, universalPKConvention);
|
|
3879
|
+
return { Success: true, Message: BuildDetachedRefreshMessage(detached.RunID) };
|
|
3880
|
+
}
|
|
3703
3881
|
if (runSchemaRefresh) {
|
|
3704
3882
|
try {
|
|
3705
3883
|
const refreshResult = await this.runSchemaRefreshPipeline(companyIntegrationID, user, md, universalPKConvention);
|
|
3706
|
-
return {
|
|
3707
|
-
Success: true,
|
|
3708
|
-
Message: `Updated, schema refresh: ${refreshResult.ObjectsCreated} created, ${refreshResult.ObjectsUpdated} updated, ${refreshResult.UnresolvedObjects.length} PK-unresolved`,
|
|
3709
|
-
};
|
|
3884
|
+
return { Success: true, Message: BuildUpdateConnectionMessage(refreshResult) };
|
|
3710
3885
|
}
|
|
3711
3886
|
catch (refreshErr) {
|
|
3712
3887
|
LogError(`IntegrationUpdateConnection: pipeline error — ${refreshErr}`);
|
|
@@ -3751,6 +3926,7 @@ let IntegrationDiscoveryResolver = class IntegrationDiscoveryResolver extends Re
|
|
|
3751
3926
|
set('crossLayerPipeline', config.CrossLayerPipeline);
|
|
3752
3927
|
set('partitionReconcile', config.PartitionReconcile);
|
|
3753
3928
|
set('discoveryTimeBudgetMs', config.DiscoveryTimeBudgetMs);
|
|
3929
|
+
set('fetchTimeoutMs', config.FetchTimeoutMs);
|
|
3754
3930
|
set('discoveryBatchSize', config.DiscoveryBatchSize);
|
|
3755
3931
|
set('discoveryMaxRecords', config.DiscoveryMaxRecords);
|
|
3756
3932
|
set('deactivateAbsent', config.DeactivateAbsent);
|
|
@@ -3812,6 +3988,7 @@ let IntegrationDiscoveryResolver = class IntegrationDiscoveryResolver extends Re
|
|
|
3812
3988
|
CrossLayerPipeline: bool(cfg.crossLayerPipeline),
|
|
3813
3989
|
PartitionReconcile: bool(cfg.partitionReconcile),
|
|
3814
3990
|
DiscoveryTimeBudgetMs: num(cfg.discoveryTimeBudgetMs),
|
|
3991
|
+
FetchTimeoutMs: num(cfg.fetchTimeoutMs),
|
|
3815
3992
|
DiscoveryBatchSize: num(cfg.discoveryBatchSize),
|
|
3816
3993
|
DiscoveryMaxRecords: num(cfg.discoveryMaxRecords),
|
|
3817
3994
|
DeactivateAbsent: bool(cfg.deactivateAbsent),
|
|
@@ -3907,8 +4084,45 @@ let IntegrationDiscoveryResolver = class IntegrationDiscoveryResolver extends Re
|
|
|
3907
4084
|
}
|
|
3908
4085
|
/**
|
|
3909
4086
|
* Reactivates a previously deactivated CompanyIntegration by setting IsActive=true.
|
|
4087
|
+
*
|
|
4088
|
+
* Before #3738 this mutation inherited a full schema refresh from the
|
|
4089
|
+
* CompanyIntegration save hook, with no way to decline it. The refresh is
|
|
4090
|
+
* now this resolver's own, explicit step — visible in the API, suppressible
|
|
4091
|
+
* with `runSchemaRefresh: false`, and (since it gained `awaitSchemaRefresh`)
|
|
4092
|
+
* no longer something the caller has to sit through.
|
|
4093
|
+
*
|
|
4094
|
+
* WHY REACTIVATION NO LONGER SCANS THE SOURCE BY DEFAULT. Resuming a
|
|
4095
|
+
* connection and rescanning its schema are separate decisions that this
|
|
4096
|
+
* mutation used to fuse. A one-click resume would spend minutes of a
|
|
4097
|
+
* vendor's rate budget on an introspect nobody asked for, and the catalog is
|
|
4098
|
+
* usually exactly as current as it was when the connection was paused.
|
|
4099
|
+
* `IntegrationRefreshConnectorSchema` is the operation for "rescan now", and
|
|
4100
|
+
* a caller that genuinely wants both passes `runSchemaRefresh: true`.
|
|
4101
|
+
*
|
|
4102
|
+
* WHY THAT REFRESH, WHEN REQUESTED, DEFAULTS TO DETACHED AND ITS SIBLINGS DO
|
|
4103
|
+
* NOT. Create and Update default to `awaitSchemaRefresh: true` because the
|
|
4104
|
+
* caller is in a wizard, waiting on a form, and the counts ARE the answer
|
|
4105
|
+
* they asked for. Reactivate is a one-click toggle on a connection row, and
|
|
4106
|
+
* — unlike its siblings — its entire durable effect is already committed by
|
|
4107
|
+
* the time any refresh begins: the `Save()` above returned. Blocking the
|
|
4108
|
+
* response for the minutes a live introspect takes cannot make that
|
|
4109
|
+
* reactivation any more true; it can only misreport it.
|
|
4110
|
+
*
|
|
4111
|
+
* And it did. The transport, not this resolver, is what breaks: a held
|
|
4112
|
+
* request dies at the load balancer's fixed ceiling, which the app cannot
|
|
4113
|
+
* configure and which differs by front end, and the client then shows a
|
|
4114
|
+
* resumed connection as a failure. Note the shape of that bug — the two failure paths BELOW are both
|
|
4115
|
+
* handled correctly, returning `Success: true` with the refresh problem
|
|
4116
|
+
* appended, precisely because reactivation already happened. The gateway is
|
|
4117
|
+
* the one caller of this mutation that never reaches them.
|
|
4118
|
+
*
|
|
4119
|
+
* So the default here optimises for the operation actually being requested
|
|
4120
|
+
* (resume this connection) rather than for the operation that happens to be
|
|
4121
|
+
* expensive (rescan the source). The refresh still runs — detached, on a
|
|
4122
|
+
* durable, tailable run stream — and `awaitSchemaRefresh: true` restores the
|
|
4123
|
+
* blocking behaviour for a caller that genuinely wants the counts inline.
|
|
3910
4124
|
*/
|
|
3911
|
-
async IntegrationReactivateConnection(companyIntegrationID, ctx) {
|
|
4125
|
+
async IntegrationReactivateConnection(companyIntegrationID, runSchemaRefresh, awaitSchemaRefresh, ctx) {
|
|
3912
4126
|
try {
|
|
3913
4127
|
const user = this.getAuthenticatedUser(ctx);
|
|
3914
4128
|
const md = GetReadWriteProvider(ctx.providers, { allowFallbackToReadOnly: true });
|
|
@@ -3919,7 +4133,24 @@ let IntegrationDiscoveryResolver = class IntegrationDiscoveryResolver extends Re
|
|
|
3919
4133
|
ci.IsActive = true;
|
|
3920
4134
|
if (!await ci.Save())
|
|
3921
4135
|
return { Success: false, Message: `Failed to reactivate: ${ci.LatestResult?.Message ?? 'Unknown error'}` };
|
|
3922
|
-
|
|
4136
|
+
if (runSchemaRefresh && !awaitSchemaRefresh) {
|
|
4137
|
+
const detached = this.startSchemaRefreshPipelineDetached(companyIntegrationID, user, md);
|
|
4138
|
+
return { Success: true, Message: BuildReactivateMessage(detached) };
|
|
4139
|
+
}
|
|
4140
|
+
if (runSchemaRefresh) {
|
|
4141
|
+
try {
|
|
4142
|
+
const refreshResult = await this.runSchemaRefreshPipeline(companyIntegrationID, user, md);
|
|
4143
|
+
return { Success: true, Message: BuildReactivateMessage(refreshResult) };
|
|
4144
|
+
}
|
|
4145
|
+
catch (refreshErr) {
|
|
4146
|
+
// Refresh failure does NOT undo the reactivation — the
|
|
4147
|
+
// connection is active, only discovery failed, and the
|
|
4148
|
+
// operator can re-run IntegrationRefreshConnectorSchema.
|
|
4149
|
+
LogError(`IntegrationReactivateConnection: pipeline error — ${refreshErr}`);
|
|
4150
|
+
return { Success: true, Message: `Reactivated (schema refresh failed: ${this.formatError(refreshErr)})` };
|
|
4151
|
+
}
|
|
4152
|
+
}
|
|
4153
|
+
return { Success: true, Message: BuildReactivateMessage(undefined) };
|
|
3923
4154
|
}
|
|
3924
4155
|
catch (e) {
|
|
3925
4156
|
LogError(`IntegrationReactivateConnection error: ${e}`);
|
|
@@ -4050,11 +4281,17 @@ let IntegrationDiscoveryResolver = class IntegrationDiscoveryResolver extends Re
|
|
|
4050
4281
|
// the Phase 0 v5.39.x Save hook. Fall back to live IntrospectSchema only
|
|
4051
4282
|
// for direct-API callers bypassing the wizard (empty IO cache).
|
|
4052
4283
|
const requestedNames = new Set(objects.map(o => o.SourceObjectName));
|
|
4053
|
-
|
|
4284
|
+
// Declared rows this apply will leave out (deactivated objects/fields) — surfaced on the
|
|
4285
|
+
// mutation's Warnings so a column that never appears has a stated reason.
|
|
4286
|
+
const inactiveWarnings = [];
|
|
4287
|
+
let sourceSchema = this.buildSourceSchemaFromPersistedRows(companyIntegration.IntegrationID, Array.from(requestedNames), inactiveWarnings);
|
|
4054
4288
|
if (sourceSchema.Objects.length === 0) {
|
|
4055
4289
|
LogError(`[IntegrationApplySchema] Persisted IO cache empty for ${companyIntegration.Integration}; falling back to live introspect.`);
|
|
4056
4290
|
const introspect = connector.IntrospectSchema.bind(connector);
|
|
4057
4291
|
sourceSchema = await introspect(companyIntegration, user);
|
|
4292
|
+
// The persisted rows are not what gets materialized on this path, so anything they
|
|
4293
|
+
// said about deactivated rows describes a schema this apply is not using.
|
|
4294
|
+
inactiveWarnings.length = 0;
|
|
4058
4295
|
}
|
|
4059
4296
|
else {
|
|
4060
4297
|
console.log(`[IntegrationApplySchema] Reusing ${sourceSchema.Objects.length} persisted IOs for ${companyIntegration.Integration} ` +
|
|
@@ -4083,6 +4320,8 @@ let IntegrationDiscoveryResolver = class IntegrationDiscoveryResolver extends Re
|
|
|
4083
4320
|
SkipGitCommit: skipGitCommit,
|
|
4084
4321
|
SkipRestart: skipRestart,
|
|
4085
4322
|
});
|
|
4323
|
+
if (inactiveWarnings.length > 0)
|
|
4324
|
+
SchemaOutput.Warnings.unshift(...inactiveWarnings);
|
|
4086
4325
|
return {
|
|
4087
4326
|
Success: PipelineResult.Success,
|
|
4088
4327
|
Message: PipelineResult.Success
|
|
@@ -4266,11 +4505,7 @@ let IntegrationDiscoveryResolver = class IntegrationDiscoveryResolver extends Re
|
|
|
4266
4505
|
// Step 3: Build schema and RSU pipeline input
|
|
4267
4506
|
const { schemaOutput, rsuInput } = await this.buildSchemaForConnector(input.CompanyIntegrationID, objects, validatedPlatform, user, skipGitCommit, skipRestart, provider);
|
|
4268
4507
|
// Step 4: Inject integration post-restart payload into RSU input.
|
|
4269
|
-
|
|
4270
|
-
const rsuWorkDir = process.env.RSU_WORK_DIR || process.cwd();
|
|
4271
|
-
const pendingWorkDir = join(rsuWorkDir, '.rsu_pending');
|
|
4272
|
-
const pendingFilePath = join(pendingWorkDir, `${Date.now()}.json`);
|
|
4273
|
-
// Build per-object field map for pending file (null = all fields).
|
|
4508
|
+
// Build per-object field map for the pending payload (null = all fields).
|
|
4274
4509
|
// resolved.sourceObjects is order-aligned with resolvedNames after the
|
|
4275
4510
|
// resolveSourceObjectsToNames refactor — pair them directly instead
|
|
4276
4511
|
// of looking up by ID (which broke for name-only selections).
|
|
@@ -4300,13 +4535,12 @@ let IntegrationDiscoveryResolver = class IntegrationDiscoveryResolver extends Re
|
|
|
4300
4535
|
ScheduleTimezone: input.ScheduleTimezone,
|
|
4301
4536
|
StartSync: input.StartSync,
|
|
4302
4537
|
FullSync: effectiveFullSync,
|
|
4303
|
-
SyncScope: input.SyncScope
|
|
4538
|
+
SyncScope: input.SyncScope === 'all' ? 'all' : 'created',
|
|
4304
4539
|
UnselectedAction: (input.UnselectedAction ?? 'disable'),
|
|
4305
4540
|
CreatedAt: new Date().toISOString(),
|
|
4306
4541
|
};
|
|
4307
|
-
rsuInput.
|
|
4308
|
-
|
|
4309
|
-
];
|
|
4542
|
+
rsuInput.PendingWork = [pendingPayload];
|
|
4543
|
+
rsuInput.ContextUser = user;
|
|
4310
4544
|
// Step 5: Run pipeline (restart kills process at the end).
|
|
4311
4545
|
// Retrying variant — an install should not die because the migration hit a dropped
|
|
4312
4546
|
// connection or a transient lock; it only replays while nothing has been applied.
|
|
@@ -4316,12 +4550,9 @@ let IntegrationDiscoveryResolver = class IntegrationDiscoveryResolver extends Re
|
|
|
4316
4550
|
const pipelineSteps = batchResult.Results[0]?.Steps.map((s) => ({
|
|
4317
4551
|
Name: s.Name, Status: s.Status, DurationMs: s.DurationMs, Message: s.Message, StepIndex: s.StepIndex, StepTotal: s.StepTotal,
|
|
4318
4552
|
}));
|
|
4319
|
-
//
|
|
4553
|
+
// Pending work is registered only for migrations that succeeded, so a
|
|
4554
|
+
// failed pipeline has nothing to clean up.
|
|
4320
4555
|
if (!migrationSucceeded) {
|
|
4321
|
-
try {
|
|
4322
|
-
(await import('node:fs')).unlinkSync(pendingFilePath);
|
|
4323
|
-
}
|
|
4324
|
-
catch { /* may not exist */ }
|
|
4325
4556
|
return {
|
|
4326
4557
|
Success: false,
|
|
4327
4558
|
Message: `Pipeline failed: ${batchResult.Results[0]?.ErrorMessage ?? 'unknown error'}`,
|
|
@@ -4365,10 +4596,10 @@ let IntegrationDiscoveryResolver = class IntegrationDiscoveryResolver extends Re
|
|
|
4365
4596
|
console.warn(`[Integration] Schedule creation failed: ${schedErr}`);
|
|
4366
4597
|
}
|
|
4367
4598
|
}
|
|
4368
|
-
|
|
4369
|
-
|
|
4599
|
+
// The work just happened inline (no restart), so close out its durable rows.
|
|
4600
|
+
for (const pendingWorkID of batchResult.Results[0]?.PendingWorkIDs ?? []) {
|
|
4601
|
+
await rsm.CompletePendingWork(pendingWorkID, user);
|
|
4370
4602
|
}
|
|
4371
|
-
catch { /* already consumed */ }
|
|
4372
4603
|
return {
|
|
4373
4604
|
Success: true,
|
|
4374
4605
|
Message: `Applied ${objects.length} object(s) — ${entityMapsCreated.length} entity maps created${syncRunID ? ', sync started' : ''}${scheduledJobID ? ', schedule created' : ''}`,
|
|
@@ -4672,16 +4903,22 @@ let IntegrationDiscoveryResolver = class IntegrationDiscoveryResolver extends Re
|
|
|
4672
4903
|
// Intacct AND silently dropped selections when the second pass returned
|
|
4673
4904
|
// fewer objects than the first (rate limits, transient errors).
|
|
4674
4905
|
let sourceSchema;
|
|
4906
|
+
// Declared rows this apply will silently leave out (deactivated objects/fields). Collected
|
|
4907
|
+
// during the rebuild and surfaced on the apply's Warnings, alongside the schema-limit ones.
|
|
4908
|
+
const inactiveWarnings = [];
|
|
4675
4909
|
if (prefetchedSourceSchema) {
|
|
4676
4910
|
sourceSchema = prefetchedSourceSchema;
|
|
4677
4911
|
}
|
|
4678
4912
|
else {
|
|
4679
4913
|
const requestedNamesForReuse = objects.map(o => o.SourceObjectName);
|
|
4680
|
-
sourceSchema = this.buildSourceSchemaFromPersistedRows(companyIntegration.IntegrationID, requestedNamesForReuse);
|
|
4914
|
+
sourceSchema = this.buildSourceSchemaFromPersistedRows(companyIntegration.IntegrationID, requestedNamesForReuse, inactiveWarnings);
|
|
4681
4915
|
if (sourceSchema.Objects.length === 0) {
|
|
4682
4916
|
LogError(`[buildSchemaForConnector] Persisted IO cache empty for ${companyIntegration.Integration}; falling back to live introspect.`);
|
|
4683
4917
|
const introspect = connector.IntrospectSchema.bind(connector);
|
|
4684
4918
|
sourceSchema = await introspect(companyIntegration, user);
|
|
4919
|
+
// The persisted rows are not what gets materialized on this path, so anything they
|
|
4920
|
+
// said about deactivated rows describes a schema this apply is not using.
|
|
4921
|
+
inactiveWarnings.length = 0;
|
|
4685
4922
|
}
|
|
4686
4923
|
else {
|
|
4687
4924
|
console.log(`[buildSchemaForConnector] Reusing ${sourceSchema.Objects.length} persisted IOs for ${companyIntegration.Integration} ` +
|
|
@@ -4718,9 +4955,12 @@ let IntegrationDiscoveryResolver = class IntegrationDiscoveryResolver extends Re
|
|
|
4718
4955
|
};
|
|
4719
4956
|
const builder = new SchemaBuilder();
|
|
4720
4957
|
const schemaOutput = builder.BuildSchema(input);
|
|
4721
|
-
// Surface the auto-disabled over-wide objects (if any) in the apply's Warnings → CLI output
|
|
4958
|
+
// Surface the auto-disabled over-wide objects (if any) in the apply's Warnings → CLI output,
|
|
4959
|
+
// together with the declared-but-deactivated rows this apply left out.
|
|
4722
4960
|
if (limitWarnings.length > 0)
|
|
4723
4961
|
schemaOutput.Warnings.unshift(...limitWarnings);
|
|
4962
|
+
if (inactiveWarnings.length > 0)
|
|
4963
|
+
schemaOutput.Warnings.unshift(...inactiveWarnings);
|
|
4724
4964
|
if (schemaOutput.Errors.length > 0) {
|
|
4725
4965
|
throw new Error(`Schema generation failed: ${schemaOutput.Errors.join('; ')}`);
|
|
4726
4966
|
}
|
|
@@ -4849,14 +5089,56 @@ let IntegrationDiscoveryResolver = class IntegrationDiscoveryResolver extends Re
|
|
|
4849
5089
|
return { Success: false, Message: this.formatError(e) };
|
|
4850
5090
|
}
|
|
4851
5091
|
}
|
|
5092
|
+
/**
|
|
5093
|
+
* Enqueue a sync for worker execution (PR 1 item 8) instead of running it in this
|
|
5094
|
+
* process. Creates the run row with `Status='Queued'` and returns its ID immediately —
|
|
5095
|
+
* so the caller gets a real, trackable RunID with no fire-and-forget polling window,
|
|
5096
|
+
* and the sync survives this process going away before it starts.
|
|
5097
|
+
*
|
|
5098
|
+
* Requires a process running the integration sync worker (`integrationSyncWorker.enabled`);
|
|
5099
|
+
* without one, rows accumulate in the queue unexecuted.
|
|
5100
|
+
*/
|
|
5101
|
+
async IntegrationEnqueueSync(companyIntegrationID, fullSync, entityMapIDs, syncDirection, ctx) {
|
|
5102
|
+
try {
|
|
5103
|
+
const user = this.getAuthenticatedUser(ctx);
|
|
5104
|
+
const provider = GetReadWriteProvider(ctx.providers);
|
|
5105
|
+
await IntegrationEngine.Instance.Config(false, user, provider);
|
|
5106
|
+
// Same upfront gates as IntegrationStartSync — refusing here beats queueing work
|
|
5107
|
+
// that the engine will only refuse later, in another process, out of the caller's sight.
|
|
5108
|
+
const ciCheck = await provider.GetEntityObject('MJ: Company Integrations', user);
|
|
5109
|
+
if (await ciCheck.InnerLoad(CompositeKey.FromID(companyIntegrationID)) && ciCheck.IsActive === false) {
|
|
5110
|
+
return { Success: false, Message: 'Connector is deactivated (IsActive=false); sync not enqueued' };
|
|
5111
|
+
}
|
|
5112
|
+
const maintenance = IntegrationEngine.GetMaintenanceLock(companyIntegrationID);
|
|
5113
|
+
if (maintenance) {
|
|
5114
|
+
return { Success: false, Message: `Sync not enqueued: ${maintenance.Reason} is in progress for this connection (since ${maintenance.AcquiredAt.toISOString()}). Retry after it completes.` };
|
|
5115
|
+
}
|
|
5116
|
+
const syncOptions = {};
|
|
5117
|
+
if (fullSync)
|
|
5118
|
+
syncOptions.FullSync = true;
|
|
5119
|
+
if (entityMapIDs?.length)
|
|
5120
|
+
syncOptions.EntityMapIDs = entityMapIDs;
|
|
5121
|
+
if (syncDirection)
|
|
5122
|
+
syncOptions.SyncDirection = syncDirection;
|
|
5123
|
+
const runID = await IntegrationEngine.Instance.EnqueueSync(companyIntegrationID, user, 'Manual', Object.keys(syncOptions).length > 0 ? syncOptions : undefined, provider);
|
|
5124
|
+
return { Success: true, Message: 'Sync enqueued', RunID: runID };
|
|
5125
|
+
}
|
|
5126
|
+
catch (e) {
|
|
5127
|
+
LogError(`IntegrationEnqueueSync error: ${e}`);
|
|
5128
|
+
return { Success: false, Message: this.formatError(e) };
|
|
5129
|
+
}
|
|
5130
|
+
}
|
|
4852
5131
|
/**
|
|
4853
5132
|
* Cancels a running sync by marking its status as Cancelled.
|
|
4854
5133
|
*/
|
|
4855
5134
|
async IntegrationCancelSync(companyIntegrationID, ctx) {
|
|
4856
5135
|
try {
|
|
4857
|
-
this.getAuthenticatedUser(ctx);
|
|
4858
|
-
|
|
4859
|
-
|
|
5136
|
+
const user = this.getAuthenticatedUser(ctx);
|
|
5137
|
+
const provider = GetReadWriteProvider(ctx.providers);
|
|
5138
|
+
// DB-backed cancel (durable runs): stamps CancelRequestedAt on the live run row, so
|
|
5139
|
+
// the request reaches the owning process wherever it is — this server, another API
|
|
5140
|
+
// node, or a worker — via its heartbeat/boundary checks. No in-memory signal.
|
|
5141
|
+
const cancelled = await IntegrationEngine.CancelSyncAsync(companyIntegrationID, user, provider);
|
|
4860
5142
|
if (!cancelled) {
|
|
4861
5143
|
return { Success: false, Message: 'No active sync found for this connector' };
|
|
4862
5144
|
}
|
|
@@ -5395,8 +5677,11 @@ let IntegrationDiscoveryResolver = class IntegrationDiscoveryResolver extends Re
|
|
|
5395
5677
|
}
|
|
5396
5678
|
async IntegrationGetSyncProgress(companyIntegrationID, ctx) {
|
|
5397
5679
|
try {
|
|
5398
|
-
this.getAuthenticatedUser(ctx);
|
|
5399
|
-
const
|
|
5680
|
+
const user = this.getAuthenticatedUser(ctx);
|
|
5681
|
+
const provider = GetReadOnlyProvider(ctx.providers, { allowFallbackToReadWrite: true });
|
|
5682
|
+
// DB-backed progress (durable runs): reads ProgressJSON from the live run row, so
|
|
5683
|
+
// progress is visible from ANY process — not just the one executing the sync.
|
|
5684
|
+
const syncProgress = await IntegrationEngine.GetSyncProgressAsync(companyIntegrationID, user, provider);
|
|
5400
5685
|
if (syncProgress) {
|
|
5401
5686
|
return {
|
|
5402
5687
|
Success: true,
|
|
@@ -5840,16 +6125,12 @@ let IntegrationDiscoveryResolver = class IntegrationDiscoveryResolver extends Re
|
|
|
5840
6125
|
return obj;
|
|
5841
6126
|
});
|
|
5842
6127
|
const { schemaOutput, rsuInput } = await this.buildSchemaForConnector(connInput.CompanyIntegrationID, objects, validatedPlatform, user, skipGitCommit, skipRestart, provider, sourceSchema);
|
|
5843
|
-
// Build per-object field map for pending
|
|
6128
|
+
// Build per-object field map for the pending payload
|
|
5844
6129
|
const sourceObjectFields = {};
|
|
5845
6130
|
for (const name of resolvedNames) {
|
|
5846
6131
|
sourceObjectFields[name] = fieldsByName.get(name.toLowerCase()) ?? null;
|
|
5847
6132
|
}
|
|
5848
6133
|
// Inject post-restart pending work payload
|
|
5849
|
-
const { join } = await import('node:path');
|
|
5850
|
-
const rsuWorkDir = process.env.RSU_WORK_DIR || process.cwd();
|
|
5851
|
-
const pendingWorkDir = join(rsuWorkDir, '.rsu_pending');
|
|
5852
|
-
const pendingFilePath = join(pendingWorkDir, `${Date.now()}_${connInput.CompanyIntegrationID}.json`);
|
|
5853
6134
|
const pendingPayload = {
|
|
5854
6135
|
CompanyIntegrationID: connInput.CompanyIntegrationID,
|
|
5855
6136
|
SourceObjectNames: resolvedNames,
|
|
@@ -5859,15 +6140,15 @@ let IntegrationDiscoveryResolver = class IntegrationDiscoveryResolver extends Re
|
|
|
5859
6140
|
ScheduleTimezone: connInput.ScheduleTimezone,
|
|
5860
6141
|
StartSync: input.StartSync,
|
|
5861
6142
|
FullSync: input.FullSync ?? false,
|
|
5862
|
-
SyncScope: input.SyncScope
|
|
5863
|
-
|
|
5864
|
-
|
|
6143
|
+
SyncScope: input.SyncScope === 'all' ? 'all' : 'created',
|
|
6144
|
+
// Unrecognized directions fall back to undefined = "use the entity map's own SyncDirection"
|
|
6145
|
+
SyncDirection: input.SyncDirection && isValidSyncDirection(input.SyncDirection) ? input.SyncDirection : undefined,
|
|
6146
|
+
ScheduleSyncDirection: input.ScheduleSyncDirection && isValidSyncDirection(input.ScheduleSyncDirection) ? input.ScheduleSyncDirection : undefined,
|
|
5865
6147
|
UnselectedAction: (input.UnselectedAction ?? 'disable'),
|
|
5866
6148
|
CreatedAt: new Date().toISOString(),
|
|
5867
6149
|
};
|
|
5868
|
-
rsuInput.
|
|
5869
|
-
|
|
5870
|
-
];
|
|
6150
|
+
rsuInput.PendingWork = [pendingPayload];
|
|
6151
|
+
rsuInput.ContextUser = user;
|
|
5871
6152
|
return {
|
|
5872
6153
|
connInput,
|
|
5873
6154
|
connector,
|
|
@@ -5876,7 +6157,6 @@ let IntegrationDiscoveryResolver = class IntegrationDiscoveryResolver extends Re
|
|
|
5876
6157
|
objects,
|
|
5877
6158
|
schemaOutput,
|
|
5878
6159
|
rsuInput,
|
|
5879
|
-
pendingFilePath,
|
|
5880
6160
|
};
|
|
5881
6161
|
}));
|
|
5882
6162
|
// Separate successes and failures
|
|
@@ -5927,11 +6207,8 @@ let IntegrationDiscoveryResolver = class IntegrationDiscoveryResolver extends Re
|
|
|
5927
6207
|
Message: pipelineResult?.ErrorMessage ?? 'Pipeline failed',
|
|
5928
6208
|
Warnings: build.schemaOutput.Warnings.length > 0 ? build.schemaOutput.Warnings : undefined,
|
|
5929
6209
|
});
|
|
5930
|
-
//
|
|
5931
|
-
|
|
5932
|
-
(await import('node:fs')).unlinkSync(build.pendingFilePath);
|
|
5933
|
-
}
|
|
5934
|
-
catch { /* may not exist */ }
|
|
6210
|
+
// No pending-work cleanup needed — rows are registered only for
|
|
6211
|
+
// migrations that succeeded.
|
|
5935
6212
|
continue;
|
|
5936
6213
|
}
|
|
5937
6214
|
const connResult = {
|
|
@@ -5967,11 +6244,10 @@ let IntegrationDiscoveryResolver = class IntegrationDiscoveryResolver extends Re
|
|
|
5967
6244
|
if (scheduleResult)
|
|
5968
6245
|
connResult.ScheduledJobID = scheduleResult;
|
|
5969
6246
|
}
|
|
5970
|
-
//
|
|
5971
|
-
|
|
5972
|
-
|
|
6247
|
+
// The work just happened inline (no restart), so close out its durable rows.
|
|
6248
|
+
for (const pendingWorkID of pipelineResult.PendingWorkIDs ?? []) {
|
|
6249
|
+
await rsm.CompletePendingWork(pendingWorkID, user);
|
|
5973
6250
|
}
|
|
5974
|
-
catch { /* already consumed */ }
|
|
5975
6251
|
connResult.Message = `Applied ${build.objects.length} object(s) — ${entityMapsCreated.length} entity maps created${syncRunID ? ', sync started' : ''}`;
|
|
5976
6252
|
}
|
|
5977
6253
|
connectorResults.push(connResult);
|
|
@@ -6476,26 +6752,21 @@ let IntegrationDiscoveryResolver = class IntegrationDiscoveryResolver extends Re
|
|
|
6476
6752
|
// ApplyAll uses. default: created DISABLED (user enables after the refresh);
|
|
6477
6753
|
// autoEnableNewObjects opts in. Removal was already handled in Phase 3 → 'ignore'.
|
|
6478
6754
|
if (newObjects.length > 0 && !skipRestart) {
|
|
6479
|
-
|
|
6480
|
-
|
|
6481
|
-
const pendingFilePath = join(rsuWorkDir, '.rsu_pending', `${Date.now()}-evolution.json`);
|
|
6482
|
-
rsuInput.PostRestartFiles = [
|
|
6483
|
-
...(rsuInput.PostRestartFiles ?? []),
|
|
6755
|
+
rsuInput.PendingWork = [
|
|
6756
|
+
...(rsuInput.PendingWork ?? []),
|
|
6484
6757
|
{
|
|
6485
|
-
|
|
6486
|
-
|
|
6487
|
-
|
|
6488
|
-
|
|
6489
|
-
|
|
6490
|
-
|
|
6491
|
-
|
|
6492
|
-
|
|
6493
|
-
|
|
6494
|
-
CreateDisabled: !autoEnableNewObjects,
|
|
6495
|
-
CreatedAt: new Date().toISOString(),
|
|
6496
|
-
}, null, 2),
|
|
6758
|
+
CompanyIntegrationID: companyIntegrationID,
|
|
6759
|
+
SourceObjectNames: newObjects,
|
|
6760
|
+
SourceObjectFields: Object.fromEntries(newObjects.map(n => [n, null])),
|
|
6761
|
+
SchemaName: schemaName,
|
|
6762
|
+
StartSync: false,
|
|
6763
|
+
SyncScope: 'created',
|
|
6764
|
+
UnselectedAction: 'ignore',
|
|
6765
|
+
CreateDisabled: !autoEnableNewObjects,
|
|
6766
|
+
CreatedAt: new Date().toISOString(),
|
|
6497
6767
|
},
|
|
6498
6768
|
];
|
|
6769
|
+
rsuInput.ContextUser = user;
|
|
6499
6770
|
}
|
|
6500
6771
|
const rsm = RuntimeSchemaManager.Instance;
|
|
6501
6772
|
const batchResult = await rsm.RunPipelineBatch([rsuInput]);
|
|
@@ -6753,9 +7024,10 @@ __decorate([
|
|
|
6753
7024
|
__param(1, Arg("testConnection", () => Boolean, { defaultValue: false })),
|
|
6754
7025
|
__param(2, Arg("runSchemaRefresh", () => Boolean, { defaultValue: true, description: "When true (default) and TestConnection succeeds, automatically runs IntegrationConnectorCreationPipeline (live introspect → persist Declared/Discovered/Custom → SoftPKClassifier). The intermittent server-side work the wizard's Forward step represents." })),
|
|
6755
7026
|
__param(3, Arg("universalPKConvention", { nullable: true, description: "Optional vendor-wide PK hint (e.g. 'id' for HubSpot). Improves SoftPKClassifier convergence." })),
|
|
6756
|
-
__param(4,
|
|
7027
|
+
__param(4, Arg("awaitSchemaRefresh", () => Boolean, { defaultValue: true, description: "When false, the schema refresh is launched detached and this mutation returns immediately with SchemaRefresh.RunID + InProgress=true — tail that run instead of blocking on a minutes-long live introspect. The introspect happens exactly once either way, and always AFTER the connection test, so a failed test rolls back a connection that left no discovered schema behind. Default true preserves the blocking behaviour (counts returned inline)." })),
|
|
7028
|
+
__param(5, Ctx()),
|
|
6757
7029
|
__metadata("design:type", Function),
|
|
6758
|
-
__metadata("design:paramtypes", [CreateConnectionInput, Boolean, Boolean, String, Object]),
|
|
7030
|
+
__metadata("design:paramtypes", [CreateConnectionInput, Boolean, Boolean, String, Boolean, Object]),
|
|
6759
7031
|
__metadata("design:returntype", Promise)
|
|
6760
7032
|
], IntegrationDiscoveryResolver.prototype, "IntegrationCreateConnection", null);
|
|
6761
7033
|
__decorate([
|
|
@@ -6767,9 +7039,10 @@ __decorate([
|
|
|
6767
7039
|
__param(4, Arg("testConnection", () => Boolean, { defaultValue: false })),
|
|
6768
7040
|
__param(5, Arg("runSchemaRefresh", () => Boolean, { defaultValue: true, description: "When true (default) and TestConnection succeeds, automatically runs IntegrationConnectorCreationPipeline. Same intermittent server-side step as the create flow." })),
|
|
6769
7041
|
__param(6, Arg("universalPKConvention", { nullable: true, description: "Optional vendor-wide PK hint (e.g. 'id' for HubSpot)" })),
|
|
6770
|
-
__param(7,
|
|
7042
|
+
__param(7, Arg("awaitSchemaRefresh", () => Boolean, { defaultValue: true, description: "When false, the schema refresh is launched detached and this mutation returns immediately with the run ID to tail instead of blocking on a minutes-long live introspect. Default true preserves the blocking behaviour." })),
|
|
7043
|
+
__param(8, Ctx()),
|
|
6771
7044
|
__metadata("design:type", Function),
|
|
6772
|
-
__metadata("design:paramtypes", [String, String, String, String, Boolean, Boolean, String, Object]),
|
|
7045
|
+
__metadata("design:paramtypes", [String, String, String, String, Boolean, Boolean, String, Boolean, Object]),
|
|
6773
7046
|
__metadata("design:returntype", Promise)
|
|
6774
7047
|
], IntegrationDiscoveryResolver.prototype, "IntegrationUpdateConnection", null);
|
|
6775
7048
|
__decorate([
|
|
@@ -6819,9 +7092,11 @@ __decorate([
|
|
|
6819
7092
|
__decorate([
|
|
6820
7093
|
Mutation(() => MutationResultOutput),
|
|
6821
7094
|
__param(0, Arg("companyIntegrationID")),
|
|
6822
|
-
__param(1,
|
|
7095
|
+
__param(1, Arg("runSchemaRefresh", () => Boolean, { defaultValue: false, description: "When false (default), reactivation ONLY reactivates — no live scan of the source. Resuming a connection and rescanning its schema are separate decisions, and a resume should not spend minutes of a vendor's rate budget nobody asked for; run IntegrationRefreshConnectorSchema when the catalog actually needs refreshing. Pass true to re-run IntegrationConnectorCreationPipeline as part of the reactivation." })),
|
|
7096
|
+
__param(2, Arg("awaitSchemaRefresh", () => Boolean, { defaultValue: false, description: "When false (default), the schema refresh is launched detached and this mutation returns as soon as the connection is active, naming the run to tail. Reactivation is already committed at that point, so blocking on a minutes-long live introspect can only delay — or, at the load balancer's fixed request ceiling, misreport — an operation that has already succeeded. Pass true to block until the refresh finishes and get its counts in the message." })),
|
|
7097
|
+
__param(3, Ctx()),
|
|
6823
7098
|
__metadata("design:type", Function),
|
|
6824
|
-
__metadata("design:paramtypes", [String, Object]),
|
|
7099
|
+
__metadata("design:paramtypes", [String, Boolean, Boolean, Object]),
|
|
6825
7100
|
__metadata("design:returntype", Promise)
|
|
6826
7101
|
], IntegrationDiscoveryResolver.prototype, "IntegrationReactivateConnection", null);
|
|
6827
7102
|
__decorate([
|
|
@@ -6883,6 +7158,18 @@ __decorate([
|
|
|
6883
7158
|
__metadata("design:paramtypes", [String, String, Boolean, Array, String, Object]),
|
|
6884
7159
|
__metadata("design:returntype", Promise)
|
|
6885
7160
|
], IntegrationDiscoveryResolver.prototype, "IntegrationStartSync", null);
|
|
7161
|
+
__decorate([
|
|
7162
|
+
Mutation(() => StartSyncOutput),
|
|
7163
|
+
RequireSystemUser(),
|
|
7164
|
+
__param(0, Arg("companyIntegrationID")),
|
|
7165
|
+
__param(1, Arg("fullSync", () => Boolean, { defaultValue: false, description: 'If true, ignores watermarks and re-fetches all records from the source' })),
|
|
7166
|
+
__param(2, Arg("entityMapIDs", () => [String], { nullable: true, description: 'Optional: sync only these entity maps. If omitted, syncs all maps for the connector.' })),
|
|
7167
|
+
__param(3, Arg("syncDirection", () => String, { nullable: true, description: 'Override sync direction: Pull | Push | Bidirectional. If omitted, each entity map\'s own SyncDirection is used.' })),
|
|
7168
|
+
__param(4, Ctx()),
|
|
7169
|
+
__metadata("design:type", Function),
|
|
7170
|
+
__metadata("design:paramtypes", [String, Boolean, Array, String, Object]),
|
|
7171
|
+
__metadata("design:returntype", Promise)
|
|
7172
|
+
], IntegrationDiscoveryResolver.prototype, "IntegrationEnqueueSync", null);
|
|
6886
7173
|
__decorate([
|
|
6887
7174
|
Mutation(() => MutationResultOutput),
|
|
6888
7175
|
__param(0, Arg("companyIntegrationID")),
|