@open-mercato/core 0.6.8-develop.6992.1.00c90fecff → 0.6.8-develop.6998.1.d2bc46c56c
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/.turbo/turbo-build.log +1 -1
- package/dist/generated/entities/sync_run/index.js +2 -0
- package/dist/generated/entities/sync_run/index.js.map +2 -2
- package/dist/generated/entity-fields-registry.js +1 -0
- package/dist/generated/entity-fields-registry.js.map +2 -2
- package/dist/modules/auth/api/sidebar/preferences/route.js +2 -2
- package/dist/modules/auth/api/sidebar/preferences/route.js.map +1 -1
- package/dist/modules/auth/lib/backendChrome.js +3 -3
- package/dist/modules/auth/lib/backendChrome.js.map +1 -1
- package/dist/modules/auth/services/sidebarPreferencesService.js +7 -2
- package/dist/modules/auth/services/sidebarPreferencesService.js.map +2 -2
- package/dist/modules/data_sync/api/options.js +1 -0
- package/dist/modules/data_sync/api/options.js.map +2 -2
- package/dist/modules/data_sync/api/run.js +15 -1
- package/dist/modules/data_sync/api/run.js.map +2 -2
- package/dist/modules/data_sync/api/runs/[id]/retry.js +23 -1
- package/dist/modules/data_sync/api/runs/[id]/retry.js.map +2 -2
- package/dist/modules/data_sync/api/runs/[id]/route.js +1 -0
- package/dist/modules/data_sync/api/runs/[id]/route.js.map +2 -2
- package/dist/modules/data_sync/backend/data-sync/page.js +60 -17
- package/dist/modules/data_sync/backend/data-sync/page.js.map +2 -2
- package/dist/modules/data_sync/backend/data-sync/runs/[id]/page.js +34 -2
- package/dist/modules/data_sync/backend/data-sync/runs/[id]/page.js.map +2 -2
- package/dist/modules/data_sync/components/IntegrationScheduleTab.js +21 -15
- package/dist/modules/data_sync/components/IntegrationScheduleTab.js.map +2 -2
- package/dist/modules/data_sync/components/RunParameterFields.js +135 -0
- package/dist/modules/data_sync/components/RunParameterFields.js.map +7 -0
- package/dist/modules/data_sync/data/entities.js +3 -0
- package/dist/modules/data_sync/data/entities.js.map +2 -2
- package/dist/modules/data_sync/data/validators.js +4 -1
- package/dist/modules/data_sync/data/validators.js.map +2 -2
- package/dist/modules/data_sync/lib/run-parameters.js +108 -0
- package/dist/modules/data_sync/lib/run-parameters.js.map +7 -0
- package/dist/modules/data_sync/lib/start-run.js +1 -0
- package/dist/modules/data_sync/lib/start-run.js.map +2 -2
- package/dist/modules/data_sync/lib/sync-engine.js +4 -2
- package/dist/modules/data_sync/lib/sync-engine.js.map +2 -2
- package/dist/modules/data_sync/lib/sync-run-service.js +1 -0
- package/dist/modules/data_sync/lib/sync-run-service.js.map +2 -2
- package/dist/modules/data_sync/migrations/Migration20260810120000.js +13 -0
- package/dist/modules/data_sync/migrations/Migration20260810120000.js.map +7 -0
- package/dist/modules/data_sync/workers/sync-scheduled.js +23 -2
- package/dist/modules/data_sync/workers/sync-scheduled.js.map +2 -2
- package/dist/modules/query_index/di.js +125 -48
- package/dist/modules/query_index/di.js.map +2 -2
- package/dist/modules/sales/api/documents/factory.js +39 -1
- package/dist/modules/sales/api/documents/factory.js.map +2 -2
- package/dist/modules/sales/commands/documents.js +26 -5
- package/dist/modules/sales/commands/documents.js.map +2 -2
- package/generated/entities/sync_run/index.ts +1 -0
- package/generated/entity-fields-registry.ts +1 -0
- package/package.json +7 -7
- package/src/modules/auth/api/sidebar/preferences/route.ts +2 -2
- package/src/modules/auth/lib/backendChrome.tsx +2 -2
- package/src/modules/auth/services/sidebarPreferencesService.ts +22 -3
- package/src/modules/data_sync/AGENTS.md +83 -8
- package/src/modules/data_sync/api/options.ts +1 -0
- package/src/modules/data_sync/api/run.ts +17 -0
- package/src/modules/data_sync/api/runs/[id]/retry.ts +32 -1
- package/src/modules/data_sync/api/runs/[id]/route.ts +1 -0
- package/src/modules/data_sync/backend/data-sync/page.tsx +73 -17
- package/src/modules/data_sync/backend/data-sync/runs/[id]/page.tsx +62 -2
- package/src/modules/data_sync/components/IntegrationScheduleTab.tsx +28 -15
- package/src/modules/data_sync/components/RunParameterFields.tsx +229 -0
- package/src/modules/data_sync/data/entities.ts +4 -1
- package/src/modules/data_sync/data/validators.ts +3 -0
- package/src/modules/data_sync/i18n/de.json +10 -0
- package/src/modules/data_sync/i18n/en.json +10 -0
- package/src/modules/data_sync/i18n/es.json +10 -0
- package/src/modules/data_sync/i18n/ko.json +10 -0
- package/src/modules/data_sync/i18n/pl.json +10 -0
- package/src/modules/data_sync/lib/adapter.ts +88 -0
- package/src/modules/data_sync/lib/run-parameters.ts +196 -0
- package/src/modules/data_sync/lib/start-run.ts +2 -0
- package/src/modules/data_sync/lib/sync-engine.ts +5 -1
- package/src/modules/data_sync/lib/sync-run-service.ts +2 -0
- package/src/modules/data_sync/migrations/.snapshot-open-mercato.json +17 -1
- package/src/modules/data_sync/migrations/Migration20260810120000.ts +13 -0
- package/src/modules/data_sync/workers/sync-scheduled.ts +32 -1
- package/src/modules/query_index/di.ts +153 -56
- package/src/modules/sales/api/documents/factory.ts +62 -1
- package/src/modules/sales/commands/documents.ts +40 -4
- package/src/modules/sales/i18n/de.json +1 -0
- package/src/modules/sales/i18n/en.json +1 -0
- package/src/modules/sales/i18n/es.json +1 -0
- package/src/modules/sales/i18n/ko.json +1 -0
- package/src/modules/sales/i18n/pl.json +1 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/data_sync/api/run.ts"],
|
|
4
|
-
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { organizationScopeRequiredResponse, resolveActiveOrganizationId } from '@open-mercato/shared/lib/auth/organizationScope'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'\nimport { getIntegration } from '@open-mercato/shared/modules/integrations/types'\nimport type { ProgressService } from '../../progress/lib/progressService'\nimport type { IntegrationStateService } from '../../integrations/lib/state-service'\nimport type { SyncRunService } from '../lib/sync-run-service'\nimport { runSyncSchema } from '../data/validators'\nimport { startDataSyncRun } from '../lib/start-run'\nimport { getDataSyncAdapter } from '../lib/adapter-registry'\nimport { resolveStartCursor } from '../lib/start-cursor'\nimport {\n runCrudMutationGuardAfterSuccess,\n validateCrudMutationGuard,\n} from '@open-mercato/shared/lib/crud/mutation-guard'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('data_sync').child({ component: 'run' })\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['data_sync.run'] },\n}\n\nexport const openApi = {\n tags: ['DataSync'],\n summary: 'Start a data sync run',\n}\n\nexport async function POST(req: Request) {\n try {\n const auth = await getAuthFromRequest(req)\n if (!auth?.tenantId) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n const organizationId = resolveActiveOrganizationId(auth)\n if (!organizationId) {\n return organizationScopeRequiredResponse()\n }\n\n const payload = await readJsonSafe(req)\n const parsed = runSyncSchema.safeParse(payload)\n if (!parsed.success) {\n return NextResponse.json({ error: 'Invalid payload', details: parsed.error.flatten() }, { status: 422 })\n }\n\n const container = await createRequestContainer()\n const syncRunService = container.resolve('dataSyncRunService') as SyncRunService\n const progressService = container.resolve('progressService') as ProgressService\n const integrationStateService = container.resolve('integrationStateService') as IntegrationStateService\n\n const scope = {\n organizationId,\n tenantId: auth.tenantId,\n }\n\n const integration = getIntegration(parsed.data.integrationId)\n if (!integration?.providerKey) {\n return NextResponse.json({ error: 'Integration not found' }, { status: 404 })\n }\n\n const adapter = getDataSyncAdapter(integration.providerKey)\n if (!adapter) {\n return NextResponse.json({ error: 'No registered sync adapter for provider' }, { status: 404 })\n }\n if (adapter.runMode === 'provider') {\n return NextResponse.json(\n {\n error: 'This integration must be started from its provider-specific import flow.',\n settingsPath: `/backend/integrations/${encodeURIComponent(parsed.data.integrationId)}`,\n },\n { status: 422 },\n )\n }\n\n if (!adapter.supportedEntities.includes(parsed.data.entityType)) {\n return NextResponse.json({ error: 'Unsupported entity type for this integration' }, { status: 422 })\n }\n\n const integrationEnabled = await integrationStateService.isEnabled(parsed.data.integrationId, scope)\n if (!integrationEnabled) {\n return NextResponse.json({ error: 'Integration is disabled' }, { status: 409 })\n }\n\n const overlap = await syncRunService.findRunningOverlap(\n parsed.data.integrationId,\n parsed.data.entityType,\n parsed.data.direction,\n scope,\n )\n if (overlap) {\n return NextResponse.json({ error: 'A sync run is already in progress for this integration and entity direction' }, { status: 409 })\n }\n\n const guardResult = await validateCrudMutationGuard(container, {\n tenantId: auth.tenantId,\n organizationId: scope.organizationId,\n userId: auth.sub,\n resourceKind: 'data_sync.run',\n resourceId: parsed.data.integrationId,\n operation: 'custom',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: parsed.data,\n })\n if (guardResult && !guardResult.ok) {\n return NextResponse.json(guardResult.body, { status: guardResult.status })\n }\n\n const cursor = parsed.data.fullSync\n ? null\n : await resolveStartCursor({\n syncRunService,\n adapter,\n integrationId: parsed.data.integrationId,\n entityType: parsed.data.entityType,\n direction: parsed.data.direction,\n scope,\n })\n\n const { run, progressJob } = await startDataSyncRun({\n syncRunService,\n progressService,\n scope: {\n ...scope,\n userId: auth.sub,\n },\n input: {\n integrationId: parsed.data.integrationId,\n entityType: parsed.data.entityType,\n direction: parsed.data.direction,\n cursor,\n triggeredBy: parsed.data.triggeredBy ?? auth.sub,\n batchSize: parsed.data.batchSize,\n },\n })\n\n if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(container, {\n tenantId: auth.tenantId,\n organizationId: scope.organizationId,\n userId: auth.sub,\n resourceKind: 'data_sync.run',\n resourceId: run.id,\n operation: 'custom',\n requestMethod: req.method,\n requestHeaders: req.headers,\n metadata: guardResult.metadata ?? null,\n })\n }\n\n return NextResponse.json({ id: run.id, progressJobId: progressJob?.id ?? null }, { status: 201 })\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n const stack = error instanceof Error ? error.stack : undefined\n logger.error('Unhandled error starting sync run', { message, stack })\n return NextResponse.json(\n { error: 'Failed to start data sync run.' },\n { status: 500 },\n )\n }\n}\n"],
|
|
5
|
-
"mappings": "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
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { organizationScopeRequiredResponse, resolveActiveOrganizationId } from '@open-mercato/shared/lib/auth/organizationScope'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'\nimport { getIntegration } from '@open-mercato/shared/modules/integrations/types'\nimport type { ProgressService } from '../../progress/lib/progressService'\nimport type { IntegrationStateService } from '../../integrations/lib/state-service'\nimport type { SyncRunService } from '../lib/sync-run-service'\nimport { runSyncSchema } from '../data/validators'\nimport { startDataSyncRun } from '../lib/start-run'\nimport { getDataSyncAdapter } from '../lib/adapter-registry'\nimport { normalizeRunParameters } from '../lib/run-parameters'\nimport { resolveStartCursor } from '../lib/start-cursor'\nimport {\n runCrudMutationGuardAfterSuccess,\n validateCrudMutationGuard,\n} from '@open-mercato/shared/lib/crud/mutation-guard'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('data_sync').child({ component: 'run' })\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['data_sync.run'] },\n}\n\nexport const openApi = {\n tags: ['DataSync'],\n summary: 'Start a data sync run',\n}\n\nexport async function POST(req: Request) {\n try {\n const auth = await getAuthFromRequest(req)\n if (!auth?.tenantId) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n const organizationId = resolveActiveOrganizationId(auth)\n if (!organizationId) {\n return organizationScopeRequiredResponse()\n }\n\n const payload = await readJsonSafe(req)\n const parsed = runSyncSchema.safeParse(payload)\n if (!parsed.success) {\n return NextResponse.json({ error: 'Invalid payload', details: parsed.error.flatten() }, { status: 422 })\n }\n\n const container = await createRequestContainer()\n const syncRunService = container.resolve('dataSyncRunService') as SyncRunService\n const progressService = container.resolve('progressService') as ProgressService\n const integrationStateService = container.resolve('integrationStateService') as IntegrationStateService\n\n const scope = {\n organizationId,\n tenantId: auth.tenantId,\n }\n\n const integration = getIntegration(parsed.data.integrationId)\n if (!integration?.providerKey) {\n return NextResponse.json({ error: 'Integration not found' }, { status: 404 })\n }\n\n const adapter = getDataSyncAdapter(integration.providerKey)\n if (!adapter) {\n return NextResponse.json({ error: 'No registered sync adapter for provider' }, { status: 404 })\n }\n if (adapter.runMode === 'provider') {\n return NextResponse.json(\n {\n error: 'This integration must be started from its provider-specific import flow.',\n settingsPath: `/backend/integrations/${encodeURIComponent(parsed.data.integrationId)}`,\n },\n { status: 422 },\n )\n }\n\n if (!adapter.supportedEntities.includes(parsed.data.entityType)) {\n return NextResponse.json({ error: 'Unsupported entity type for this integration' }, { status: 422 })\n }\n\n const normalizedParameters = normalizeRunParameters(\n adapter.runParameters,\n parsed.data.direction,\n parsed.data.parameters,\n parsed.data.entityType,\n )\n if (!normalizedParameters.ok) {\n return NextResponse.json(\n { error: 'Invalid run parameters', details: { parameters: normalizedParameters.errors } },\n { status: 422 },\n )\n }\n\n const integrationEnabled = await integrationStateService.isEnabled(parsed.data.integrationId, scope)\n if (!integrationEnabled) {\n return NextResponse.json({ error: 'Integration is disabled' }, { status: 409 })\n }\n\n const overlap = await syncRunService.findRunningOverlap(\n parsed.data.integrationId,\n parsed.data.entityType,\n parsed.data.direction,\n scope,\n )\n if (overlap) {\n return NextResponse.json({ error: 'A sync run is already in progress for this integration and entity direction' }, { status: 409 })\n }\n\n const guardResult = await validateCrudMutationGuard(container, {\n tenantId: auth.tenantId,\n organizationId: scope.organizationId,\n userId: auth.sub,\n resourceKind: 'data_sync.run',\n resourceId: parsed.data.integrationId,\n operation: 'custom',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: parsed.data,\n })\n if (guardResult && !guardResult.ok) {\n return NextResponse.json(guardResult.body, { status: guardResult.status })\n }\n\n const cursor = parsed.data.fullSync\n ? null\n : await resolveStartCursor({\n syncRunService,\n adapter,\n integrationId: parsed.data.integrationId,\n entityType: parsed.data.entityType,\n direction: parsed.data.direction,\n scope,\n })\n\n const { run, progressJob } = await startDataSyncRun({\n syncRunService,\n progressService,\n scope: {\n ...scope,\n userId: auth.sub,\n },\n input: {\n integrationId: parsed.data.integrationId,\n entityType: parsed.data.entityType,\n direction: parsed.data.direction,\n cursor,\n triggeredBy: parsed.data.triggeredBy ?? auth.sub,\n batchSize: parsed.data.batchSize,\n parameters: Object.keys(normalizedParameters.values).length > 0\n ? normalizedParameters.values\n : null,\n },\n })\n\n if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(container, {\n tenantId: auth.tenantId,\n organizationId: scope.organizationId,\n userId: auth.sub,\n resourceKind: 'data_sync.run',\n resourceId: run.id,\n operation: 'custom',\n requestMethod: req.method,\n requestHeaders: req.headers,\n metadata: guardResult.metadata ?? null,\n })\n }\n\n return NextResponse.json({ id: run.id, progressJobId: progressJob?.id ?? null }, { status: 201 })\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n const stack = error instanceof Error ? error.stack : undefined\n logger.error('Unhandled error starting sync run', { message, stack })\n return NextResponse.json(\n { error: 'Failed to start data sync run.' },\n { status: 500 },\n )\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -6,6 +6,7 @@ import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
|
|
|
6
6
|
import { readJsonSafe } from "@open-mercato/shared/lib/http/readJsonSafe";
|
|
7
7
|
import { retrySyncSchema } from "../../../data/validators.js";
|
|
8
8
|
import { startDataSyncRun } from "../../../lib/start-run.js";
|
|
9
|
+
import { normalizeRunParameters } from "../../../lib/run-parameters.js";
|
|
9
10
|
import { resolveAdapterForIntegration, resolveStartCursor } from "../../../lib/start-cursor.js";
|
|
10
11
|
import {
|
|
11
12
|
runCrudMutationGuardAfterSuccess,
|
|
@@ -72,9 +73,29 @@ async function POST(req, ctx) {
|
|
|
72
73
|
if (guardResult && !guardResult.ok) {
|
|
73
74
|
return NextResponse.json(guardResult.body, { status: guardResult.status });
|
|
74
75
|
}
|
|
76
|
+
const retryAdapter = resolveAdapterForIntegration(previous.integrationId);
|
|
77
|
+
const normalizedParameters = normalizeRunParameters(
|
|
78
|
+
retryAdapter?.runParameters,
|
|
79
|
+
previous.direction,
|
|
80
|
+
previous.parameters ?? null,
|
|
81
|
+
previous.entityType
|
|
82
|
+
);
|
|
83
|
+
if (!normalizedParameters.ok) {
|
|
84
|
+
return NextResponse.json(
|
|
85
|
+
{
|
|
86
|
+
error: "Stored run parameters are no longer valid for this integration. Start a new run from the Data Sync dashboard.",
|
|
87
|
+
// Machine-readable so the dashboard can render the way out in the
|
|
88
|
+
// operator's language; the English sentence stays for non-UI callers.
|
|
89
|
+
code: "parametersStale",
|
|
90
|
+
details: { parameters: normalizedParameters.errors }
|
|
91
|
+
},
|
|
92
|
+
{ status: 422 }
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
const retryParameters = Object.keys(normalizedParameters.values).length > 0 ? normalizedParameters.values : null;
|
|
75
96
|
const cursor = parsedBody.data.fromBeginning ? null : previous.cursor ?? await resolveStartCursor({
|
|
76
97
|
syncRunService,
|
|
77
|
-
adapter:
|
|
98
|
+
adapter: retryAdapter,
|
|
78
99
|
integrationId: previous.integrationId,
|
|
79
100
|
entityType: previous.entityType,
|
|
80
101
|
direction: previous.direction,
|
|
@@ -94,6 +115,7 @@ async function POST(req, ctx) {
|
|
|
94
115
|
cursor,
|
|
95
116
|
triggeredBy: auth.sub,
|
|
96
117
|
batchSize: 100,
|
|
118
|
+
parameters: retryParameters,
|
|
97
119
|
progressJob: {
|
|
98
120
|
name: `Retry data sync ${previous.integrationId} \u2014 ${previous.entityType}`
|
|
99
121
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/modules/data_sync/api/runs/%5Bid%5D/retry.ts"],
|
|
4
|
-
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { organizationScopeRequiredResponse, resolveActiveOrganizationId } from '@open-mercato/shared/lib/auth/organizationScope'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'\nimport type { ProgressService } from '../../../../progress/lib/progressService'\nimport type { SyncRunService } from '../../../lib/sync-run-service'\nimport { retrySyncSchema } from '../../../data/validators'\nimport { startDataSyncRun } from '../../../lib/start-run'\nimport { resolveAdapterForIntegration, resolveStartCursor } from '../../../lib/start-cursor'\nimport {\n runCrudMutationGuardAfterSuccess,\n validateCrudMutationGuard,\n} from '@open-mercato/shared/lib/crud/mutation-guard'\n\nconst paramsSchema = z.object({ id: z.string().uuid() })\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['data_sync.run'] },\n}\n\nexport const openApi = {\n tags: ['DataSync'],\n summary: 'Retry a failed sync run',\n}\n\nexport async function POST(req: Request, ctx: { params?: Promise<{ id?: string }> | { id?: string } }) {\n const auth = await getAuthFromRequest(req)\n if (!auth?.tenantId) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n const organizationId = resolveActiveOrganizationId(auth)\n if (!organizationId) {\n return organizationScopeRequiredResponse()\n }\n\n const rawParams = (ctx.params && typeof (ctx.params as Promise<unknown>).then === 'function')\n ? await (ctx.params as Promise<{ id?: string }>)\n : (ctx.params as { id?: string } | undefined)\n\n const parsedParams = paramsSchema.safeParse(rawParams)\n if (!parsedParams.success) {\n return NextResponse.json({ error: 'Invalid run id' }, { status: 400 })\n }\n\n const payload = await readJsonSafe(req)\n const parsedBody = retrySyncSchema.safeParse(payload ?? {})\n if (!parsedBody.success) {\n return NextResponse.json({ error: 'Invalid payload', details: parsedBody.error.flatten() }, { status: 422 })\n }\n\n const container = await createRequestContainer()\n const syncRunService = container.resolve('dataSyncRunService') as SyncRunService\n const progressService = container.resolve('progressService') as ProgressService\n const scope = { organizationId, tenantId: auth.tenantId }\n\n const previous = await syncRunService.getRun(parsedParams.data.id, scope)\n if (!previous) {\n return NextResponse.json({ error: 'Run not found' }, { status: 404 })\n }\n if (previous.status !== 'failed' && previous.status !== 'cancelled') {\n return NextResponse.json({ error: 'Only failed or cancelled runs can be retried' }, { status: 409 })\n }\n\n const overlap = await syncRunService.findRunningOverlap(\n previous.integrationId,\n previous.entityType,\n previous.direction,\n scope,\n )\n if (overlap) {\n return NextResponse.json({ error: 'A sync run is already in progress for this integration and entity direction' }, { status: 409 })\n }\n\n const guardResult = await validateCrudMutationGuard(container, {\n tenantId: auth.tenantId,\n organizationId: scope.organizationId,\n userId: auth.sub,\n resourceKind: 'data_sync.run',\n resourceId: previous.id,\n operation: 'custom',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: { action: 'retry', ...parsedBody.data },\n })\n if (guardResult && !guardResult.ok) {\n return NextResponse.json(guardResult.body, { status: guardResult.status })\n }\n\n const cursor = parsedBody.data.fromBeginning\n ? null\n : previous.cursor ?? await resolveStartCursor({\n syncRunService,\n adapter:
|
|
5
|
-
"mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,SAAS;AAClB,SAAS,0BAA0B;AACnC,SAAS,mCAAmC,mCAAmC;AAC/E,SAAS,8BAA8B;AACvC,SAAS,oBAAoB;AAG7B,SAAS,uBAAuB;AAChC,SAAS,wBAAwB;AACjC,SAAS,8BAA8B,0BAA0B;AACjE;AAAA,EACE;AAAA,EACA;AAAA,OACK;AAEP,MAAM,eAAe,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;AAEhD,MAAM,WAAW;AAAA,EACtB,MAAM,EAAE,aAAa,MAAM,iBAAiB,CAAC,eAAe,EAAE;AAChE;AAEO,MAAM,UAAU;AAAA,EACrB,MAAM,CAAC,UAAU;AAAA,EACjB,SAAS;AACX;AAEA,eAAsB,KAAK,KAAc,KAA8D;AACrG,QAAM,OAAO,MAAM,mBAAmB,GAAG;AACzC,MAAI,CAAC,MAAM,UAAU;AACnB,WAAO,aAAa,KAAK,EAAE,OAAO,eAAe,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACrE;AACA,QAAM,iBAAiB,4BAA4B,IAAI;AACvD,MAAI,CAAC,gBAAgB;AACnB,WAAO,kCAAkC;AAAA,EAC3C;AAEA,QAAM,YAAa,IAAI,UAAU,OAAQ,IAAI,OAA4B,SAAS,aAC9E,MAAO,IAAI,SACV,IAAI;AAET,QAAM,eAAe,aAAa,UAAU,SAAS;AACrD,MAAI,CAAC,aAAa,SAAS;AACzB,WAAO,aAAa,KAAK,EAAE,OAAO,iBAAiB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACvE;AAEA,QAAM,UAAU,MAAM,aAAa,GAAG;AACtC,QAAM,aAAa,gBAAgB,UAAU,WAAW,CAAC,CAAC;AAC1D,MAAI,CAAC,WAAW,SAAS;AACvB,WAAO,aAAa,KAAK,EAAE,OAAO,mBAAmB,SAAS,WAAW,MAAM,QAAQ,EAAE,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EAC7G;AAEA,QAAM,YAAY,MAAM,uBAAuB;AAC/C,QAAM,iBAAiB,UAAU,QAAQ,oBAAoB;AAC7D,QAAM,kBAAkB,UAAU,QAAQ,iBAAiB;AAC3D,QAAM,QAAQ,EAAE,gBAAgB,UAAU,KAAK,SAAS;AAExD,QAAM,WAAW,MAAM,eAAe,OAAO,aAAa,KAAK,IAAI,KAAK;AACxE,MAAI,CAAC,UAAU;AACb,WAAO,aAAa,KAAK,EAAE,OAAO,gBAAgB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACtE;AACA,MAAI,SAAS,WAAW,YAAY,SAAS,WAAW,aAAa;AACnE,WAAO,aAAa,KAAK,EAAE,OAAO,+CAA+C,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACrG;AAEA,QAAM,UAAU,MAAM,eAAe;AAAA,IACnC,SAAS;AAAA,IACT,SAAS;AAAA,IACT,SAAS;AAAA,IACT;AAAA,EACF;AACA,MAAI,SAAS;AACX,WAAO,aAAa,KAAK,EAAE,OAAO,8EAA8E,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACpI;AAEA,QAAM,cAAc,MAAM,0BAA0B,WAAW;AAAA,IAC7D,UAAU,KAAK;AAAA,IACf,gBAAgB,MAAM;AAAA,IACtB,QAAQ,KAAK;AAAA,IACb,cAAc;AAAA,IACd,YAAY,SAAS;AAAA,IACrB,WAAW;AAAA,IACX,eAAe,IAAI;AAAA,IACnB,gBAAgB,IAAI;AAAA,IACpB,iBAAiB,EAAE,QAAQ,SAAS,GAAG,WAAW,KAAK;AAAA,EACzD,CAAC;AACD,MAAI,eAAe,CAAC,YAAY,IAAI;AAClC,WAAO,aAAa,KAAK,YAAY,MAAM,EAAE,QAAQ,YAAY,OAAO,CAAC;AAAA,EAC3E;
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { organizationScopeRequiredResponse, resolveActiveOrganizationId } from '@open-mercato/shared/lib/auth/organizationScope'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'\nimport type { ProgressService } from '../../../../progress/lib/progressService'\nimport type { SyncRunService } from '../../../lib/sync-run-service'\nimport { retrySyncSchema } from '../../../data/validators'\nimport { startDataSyncRun } from '../../../lib/start-run'\nimport { normalizeRunParameters } from '../../../lib/run-parameters'\nimport { resolveAdapterForIntegration, resolveStartCursor } from '../../../lib/start-cursor'\nimport {\n runCrudMutationGuardAfterSuccess,\n validateCrudMutationGuard,\n} from '@open-mercato/shared/lib/crud/mutation-guard'\n\nconst paramsSchema = z.object({ id: z.string().uuid() })\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['data_sync.run'] },\n}\n\nexport const openApi = {\n tags: ['DataSync'],\n summary: 'Retry a failed sync run',\n}\n\nexport async function POST(req: Request, ctx: { params?: Promise<{ id?: string }> | { id?: string } }) {\n const auth = await getAuthFromRequest(req)\n if (!auth?.tenantId) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n const organizationId = resolveActiveOrganizationId(auth)\n if (!organizationId) {\n return organizationScopeRequiredResponse()\n }\n\n const rawParams = (ctx.params && typeof (ctx.params as Promise<unknown>).then === 'function')\n ? await (ctx.params as Promise<{ id?: string }>)\n : (ctx.params as { id?: string } | undefined)\n\n const parsedParams = paramsSchema.safeParse(rawParams)\n if (!parsedParams.success) {\n return NextResponse.json({ error: 'Invalid run id' }, { status: 400 })\n }\n\n const payload = await readJsonSafe(req)\n const parsedBody = retrySyncSchema.safeParse(payload ?? {})\n if (!parsedBody.success) {\n return NextResponse.json({ error: 'Invalid payload', details: parsedBody.error.flatten() }, { status: 422 })\n }\n\n const container = await createRequestContainer()\n const syncRunService = container.resolve('dataSyncRunService') as SyncRunService\n const progressService = container.resolve('progressService') as ProgressService\n const scope = { organizationId, tenantId: auth.tenantId }\n\n const previous = await syncRunService.getRun(parsedParams.data.id, scope)\n if (!previous) {\n return NextResponse.json({ error: 'Run not found' }, { status: 404 })\n }\n if (previous.status !== 'failed' && previous.status !== 'cancelled') {\n return NextResponse.json({ error: 'Only failed or cancelled runs can be retried' }, { status: 409 })\n }\n\n const overlap = await syncRunService.findRunningOverlap(\n previous.integrationId,\n previous.entityType,\n previous.direction,\n scope,\n )\n if (overlap) {\n return NextResponse.json({ error: 'A sync run is already in progress for this integration and entity direction' }, { status: 409 })\n }\n\n const guardResult = await validateCrudMutationGuard(container, {\n tenantId: auth.tenantId,\n organizationId: scope.organizationId,\n userId: auth.sub,\n resourceKind: 'data_sync.run',\n resourceId: previous.id,\n operation: 'custom',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: { action: 'retry', ...parsedBody.data },\n })\n if (guardResult && !guardResult.ok) {\n return NextResponse.json(guardResult.body, { status: guardResult.status })\n }\n\n // A retry replays the stored parameters, but the adapter's declaration may\n // have moved on since the original run \u2014 a parameter dropped, a bound\n // tightened, a select option removed, a scope narrowed. Re-normalize against\n // the current declaration so an adapter never receives a set the run API\n // would reject today. Undeclared keys fall away silently; values that are now\n // invalid stop the retry, because the operator has no form here to fix them.\n const retryAdapter = resolveAdapterForIntegration(previous.integrationId)\n const normalizedParameters = normalizeRunParameters(\n retryAdapter?.runParameters,\n previous.direction,\n previous.parameters ?? null,\n previous.entityType,\n )\n if (!normalizedParameters.ok) {\n return NextResponse.json(\n {\n error: 'Stored run parameters are no longer valid for this integration. Start a new run from the Data Sync dashboard.',\n // Machine-readable so the dashboard can render the way out in the\n // operator's language; the English sentence stays for non-UI callers.\n code: 'parametersStale',\n details: { parameters: normalizedParameters.errors },\n },\n { status: 422 },\n )\n }\n const retryParameters = Object.keys(normalizedParameters.values).length > 0\n ? normalizedParameters.values\n : null\n\n const cursor = parsedBody.data.fromBeginning\n ? null\n : previous.cursor ?? await resolveStartCursor({\n syncRunService,\n adapter: retryAdapter,\n integrationId: previous.integrationId,\n entityType: previous.entityType,\n direction: previous.direction,\n scope,\n })\n\n const { run, progressJob } = await startDataSyncRun({\n syncRunService,\n progressService,\n scope: {\n ...scope,\n userId: auth.sub,\n },\n input: {\n integrationId: previous.integrationId,\n entityType: previous.entityType,\n direction: previous.direction,\n cursor,\n triggeredBy: auth.sub,\n batchSize: 100,\n parameters: retryParameters,\n progressJob: {\n name: `Retry data sync ${previous.integrationId} \u2014 ${previous.entityType}`,\n },\n },\n })\n\n if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(container, {\n tenantId: auth.tenantId,\n organizationId: scope.organizationId,\n userId: auth.sub,\n resourceKind: 'data_sync.run',\n resourceId: run.id,\n operation: 'custom',\n requestMethod: req.method,\n requestHeaders: req.headers,\n metadata: guardResult.metadata ?? null,\n })\n }\n\n return NextResponse.json({ id: run.id, progressJobId: progressJob?.id ?? null }, { status: 201 })\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -53,6 +53,7 @@ async function GET(req, ctx) {
|
|
|
53
53
|
batchesCompleted: run.batchesCompleted,
|
|
54
54
|
lastError: run.lastError ?? null,
|
|
55
55
|
progressJobId: run.progressJobId ?? null,
|
|
56
|
+
parameters: run.parameters ?? null,
|
|
56
57
|
progressJob: progressJob ? {
|
|
57
58
|
id: progressJob.id,
|
|
58
59
|
status: progressJob.status,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/modules/data_sync/api/runs/%5Bid%5D/route.ts"],
|
|
4
|
-
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { organizationScopeRequiredResponse, resolveActiveOrganizationId } from '@open-mercato/shared/lib/auth/organizationScope'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport type { ProgressService } from '../../../../progress/lib/progressService'\nimport type { SyncRunService } from '../../../lib/sync-run-service'\n\nconst paramsSchema = z.object({ id: z.string().uuid() })\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['data_sync.view'] },\n}\n\nexport const openApi = {\n tags: ['DataSync'],\n summary: 'Get sync run detail',\n}\n\nexport async function GET(req: Request, ctx: { params?: Promise<{ id?: string }> | { id?: string } }) {\n const auth = await getAuthFromRequest(req)\n if (!auth?.tenantId) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n const organizationId = resolveActiveOrganizationId(auth)\n if (!organizationId) {\n return organizationScopeRequiredResponse()\n }\n\n const rawParams = (ctx.params && typeof (ctx.params as Promise<unknown>).then === 'function')\n ? await (ctx.params as Promise<{ id?: string }>)\n : (ctx.params as { id?: string } | undefined)\n\n const parsed = paramsSchema.safeParse(rawParams)\n if (!parsed.success) {\n return NextResponse.json({ error: 'Invalid run id' }, { status: 400 })\n }\n\n const container = await createRequestContainer()\n const syncRunService = container.resolve('dataSyncRunService') as SyncRunService\n const progressService = container.resolve('progressService') as ProgressService\n const scope = { organizationId, tenantId: auth.tenantId }\n\n const run = await syncRunService.getRun(parsed.data.id, scope)\n if (!run) {\n return NextResponse.json({ error: 'Run not found' }, { status: 404 })\n }\n\n const progressJob = run.progressJobId\n ? await progressService.getJob(run.progressJobId, {\n tenantId: auth.tenantId,\n organizationId,\n userId: auth.sub,\n })\n : null\n\n return NextResponse.json({\n id: run.id,\n integrationId: run.integrationId,\n entityType: run.entityType,\n direction: run.direction,\n status: run.status,\n cursor: run.cursor ?? null,\n initialCursor: run.initialCursor ?? null,\n createdCount: run.createdCount,\n updatedCount: run.updatedCount,\n skippedCount: run.skippedCount,\n failedCount: run.failedCount,\n batchesCompleted: run.batchesCompleted,\n lastError: run.lastError ?? null,\n progressJobId: run.progressJobId ?? null,\n progressJob: progressJob\n ? {\n id: progressJob.id,\n status: progressJob.status,\n progressPercent: progressJob.progressPercent,\n processedCount: progressJob.processedCount,\n totalCount: progressJob.totalCount ?? null,\n etaSeconds: progressJob.etaSeconds ?? null,\n }\n : null,\n triggeredBy: run.triggeredBy ?? null,\n createdAt: run.createdAt.toISOString(),\n updatedAt: run.updatedAt.toISOString(),\n })\n}\n"],
|
|
5
|
-
"mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,SAAS;AAClB,SAAS,0BAA0B;AACnC,SAAS,mCAAmC,mCAAmC;AAC/E,SAAS,8BAA8B;AAIvC,MAAM,eAAe,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;AAEhD,MAAM,WAAW;AAAA,EACtB,KAAK,EAAE,aAAa,MAAM,iBAAiB,CAAC,gBAAgB,EAAE;AAChE;AAEO,MAAM,UAAU;AAAA,EACrB,MAAM,CAAC,UAAU;AAAA,EACjB,SAAS;AACX;AAEA,eAAsB,IAAI,KAAc,KAA8D;AACpG,QAAM,OAAO,MAAM,mBAAmB,GAAG;AACzC,MAAI,CAAC,MAAM,UAAU;AACnB,WAAO,aAAa,KAAK,EAAE,OAAO,eAAe,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACrE;AACA,QAAM,iBAAiB,4BAA4B,IAAI;AACvD,MAAI,CAAC,gBAAgB;AACnB,WAAO,kCAAkC;AAAA,EAC3C;AAEA,QAAM,YAAa,IAAI,UAAU,OAAQ,IAAI,OAA4B,SAAS,aAC9E,MAAO,IAAI,SACV,IAAI;AAET,QAAM,SAAS,aAAa,UAAU,SAAS;AAC/C,MAAI,CAAC,OAAO,SAAS;AACnB,WAAO,aAAa,KAAK,EAAE,OAAO,iBAAiB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACvE;AAEA,QAAM,YAAY,MAAM,uBAAuB;AAC/C,QAAM,iBAAiB,UAAU,QAAQ,oBAAoB;AAC7D,QAAM,kBAAkB,UAAU,QAAQ,iBAAiB;AAC3D,QAAM,QAAQ,EAAE,gBAAgB,UAAU,KAAK,SAAS;AAExD,QAAM,MAAM,MAAM,eAAe,OAAO,OAAO,KAAK,IAAI,KAAK;AAC7D,MAAI,CAAC,KAAK;AACR,WAAO,aAAa,KAAK,EAAE,OAAO,gBAAgB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACtE;AAEA,QAAM,cAAc,IAAI,gBACpB,MAAM,gBAAgB,OAAO,IAAI,eAAe;AAAA,IAChD,UAAU,KAAK;AAAA,IACf;AAAA,IACA,QAAQ,KAAK;AAAA,EACf,CAAC,IACC;AAEJ,SAAO,aAAa,KAAK;AAAA,IACvB,IAAI,IAAI;AAAA,IACR,eAAe,IAAI;AAAA,IACnB,YAAY,IAAI;AAAA,IAChB,WAAW,IAAI;AAAA,IACf,QAAQ,IAAI;AAAA,IACZ,QAAQ,IAAI,UAAU;AAAA,IACtB,eAAe,IAAI,iBAAiB;AAAA,IACpC,cAAc,IAAI;AAAA,IAClB,cAAc,IAAI;AAAA,IAClB,cAAc,IAAI;AAAA,IAClB,aAAa,IAAI;AAAA,IACjB,kBAAkB,IAAI;AAAA,IACtB,WAAW,IAAI,aAAa;AAAA,IAC5B,eAAe,IAAI,iBAAiB;AAAA,IACpC,aAAa,cACT;AAAA,MACA,IAAI,YAAY;AAAA,MAChB,QAAQ,YAAY;AAAA,MACpB,iBAAiB,YAAY;AAAA,MAC7B,gBAAgB,YAAY;AAAA,MAC5B,YAAY,YAAY,cAAc;AAAA,MACtC,YAAY,YAAY,cAAc;AAAA,IACxC,IACE;AAAA,IACJ,aAAa,IAAI,eAAe;AAAA,IAChC,WAAW,IAAI,UAAU,YAAY;AAAA,IACrC,WAAW,IAAI,UAAU,YAAY;AAAA,EACvC,CAAC;AACH;",
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { organizationScopeRequiredResponse, resolveActiveOrganizationId } from '@open-mercato/shared/lib/auth/organizationScope'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport type { ProgressService } from '../../../../progress/lib/progressService'\nimport type { SyncRunService } from '../../../lib/sync-run-service'\n\nconst paramsSchema = z.object({ id: z.string().uuid() })\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['data_sync.view'] },\n}\n\nexport const openApi = {\n tags: ['DataSync'],\n summary: 'Get sync run detail',\n}\n\nexport async function GET(req: Request, ctx: { params?: Promise<{ id?: string }> | { id?: string } }) {\n const auth = await getAuthFromRequest(req)\n if (!auth?.tenantId) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n const organizationId = resolveActiveOrganizationId(auth)\n if (!organizationId) {\n return organizationScopeRequiredResponse()\n }\n\n const rawParams = (ctx.params && typeof (ctx.params as Promise<unknown>).then === 'function')\n ? await (ctx.params as Promise<{ id?: string }>)\n : (ctx.params as { id?: string } | undefined)\n\n const parsed = paramsSchema.safeParse(rawParams)\n if (!parsed.success) {\n return NextResponse.json({ error: 'Invalid run id' }, { status: 400 })\n }\n\n const container = await createRequestContainer()\n const syncRunService = container.resolve('dataSyncRunService') as SyncRunService\n const progressService = container.resolve('progressService') as ProgressService\n const scope = { organizationId, tenantId: auth.tenantId }\n\n const run = await syncRunService.getRun(parsed.data.id, scope)\n if (!run) {\n return NextResponse.json({ error: 'Run not found' }, { status: 404 })\n }\n\n const progressJob = run.progressJobId\n ? await progressService.getJob(run.progressJobId, {\n tenantId: auth.tenantId,\n organizationId,\n userId: auth.sub,\n })\n : null\n\n return NextResponse.json({\n id: run.id,\n integrationId: run.integrationId,\n entityType: run.entityType,\n direction: run.direction,\n status: run.status,\n cursor: run.cursor ?? null,\n initialCursor: run.initialCursor ?? null,\n createdCount: run.createdCount,\n updatedCount: run.updatedCount,\n skippedCount: run.skippedCount,\n failedCount: run.failedCount,\n batchesCompleted: run.batchesCompleted,\n lastError: run.lastError ?? null,\n progressJobId: run.progressJobId ?? null,\n parameters: run.parameters ?? null,\n progressJob: progressJob\n ? {\n id: progressJob.id,\n status: progressJob.status,\n progressPercent: progressJob.progressPercent,\n processedCount: progressJob.processedCount,\n totalCount: progressJob.totalCount ?? null,\n etaSeconds: progressJob.etaSeconds ?? null,\n }\n : null,\n triggeredBy: run.triggeredBy ?? null,\n createdAt: run.createdAt.toISOString(),\n updatedAt: run.updatedAt.toISOString(),\n })\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,SAAS;AAClB,SAAS,0BAA0B;AACnC,SAAS,mCAAmC,mCAAmC;AAC/E,SAAS,8BAA8B;AAIvC,MAAM,eAAe,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;AAEhD,MAAM,WAAW;AAAA,EACtB,KAAK,EAAE,aAAa,MAAM,iBAAiB,CAAC,gBAAgB,EAAE;AAChE;AAEO,MAAM,UAAU;AAAA,EACrB,MAAM,CAAC,UAAU;AAAA,EACjB,SAAS;AACX;AAEA,eAAsB,IAAI,KAAc,KAA8D;AACpG,QAAM,OAAO,MAAM,mBAAmB,GAAG;AACzC,MAAI,CAAC,MAAM,UAAU;AACnB,WAAO,aAAa,KAAK,EAAE,OAAO,eAAe,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACrE;AACA,QAAM,iBAAiB,4BAA4B,IAAI;AACvD,MAAI,CAAC,gBAAgB;AACnB,WAAO,kCAAkC;AAAA,EAC3C;AAEA,QAAM,YAAa,IAAI,UAAU,OAAQ,IAAI,OAA4B,SAAS,aAC9E,MAAO,IAAI,SACV,IAAI;AAET,QAAM,SAAS,aAAa,UAAU,SAAS;AAC/C,MAAI,CAAC,OAAO,SAAS;AACnB,WAAO,aAAa,KAAK,EAAE,OAAO,iBAAiB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACvE;AAEA,QAAM,YAAY,MAAM,uBAAuB;AAC/C,QAAM,iBAAiB,UAAU,QAAQ,oBAAoB;AAC7D,QAAM,kBAAkB,UAAU,QAAQ,iBAAiB;AAC3D,QAAM,QAAQ,EAAE,gBAAgB,UAAU,KAAK,SAAS;AAExD,QAAM,MAAM,MAAM,eAAe,OAAO,OAAO,KAAK,IAAI,KAAK;AAC7D,MAAI,CAAC,KAAK;AACR,WAAO,aAAa,KAAK,EAAE,OAAO,gBAAgB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACtE;AAEA,QAAM,cAAc,IAAI,gBACpB,MAAM,gBAAgB,OAAO,IAAI,eAAe;AAAA,IAChD,UAAU,KAAK;AAAA,IACf;AAAA,IACA,QAAQ,KAAK;AAAA,EACf,CAAC,IACC;AAEJ,SAAO,aAAa,KAAK;AAAA,IACvB,IAAI,IAAI;AAAA,IACR,eAAe,IAAI;AAAA,IACnB,YAAY,IAAI;AAAA,IAChB,WAAW,IAAI;AAAA,IACf,QAAQ,IAAI;AAAA,IACZ,QAAQ,IAAI,UAAU;AAAA,IACtB,eAAe,IAAI,iBAAiB;AAAA,IACpC,cAAc,IAAI;AAAA,IAClB,cAAc,IAAI;AAAA,IAClB,cAAc,IAAI;AAAA,IAClB,aAAa,IAAI;AAAA,IACjB,kBAAkB,IAAI;AAAA,IACtB,WAAW,IAAI,aAAa;AAAA,IAC5B,eAAe,IAAI,iBAAiB;AAAA,IACpC,YAAY,IAAI,cAAc;AAAA,IAC9B,aAAa,cACT;AAAA,MACA,IAAI,YAAY;AAAA,MAChB,QAAQ,YAAY;AAAA,MACpB,iBAAiB,YAAY;AAAA,MAC7B,gBAAgB,YAAY;AAAA,MAC5B,YAAY,YAAY,cAAc;AAAA,MACtC,YAAY,YAAY,cAAc;AAAA,IACxC,IACE;AAAA,IACJ,aAAa,IAAI,eAAe;AAAA,IAChC,WAAW,IAAI,UAAU,YAAY;AAAA,IACrC,WAAW,IAAI,UAAU,YAAY;AAAA,EACvC,CAAC;AACH;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -43,6 +43,14 @@ import {
|
|
|
43
43
|
ShieldCheck
|
|
44
44
|
} from "lucide-react";
|
|
45
45
|
import { getSyncRunStatusVariant, getSyncSummaryVariant } from "../../lib/syncRunStatus.js";
|
|
46
|
+
import { getApplicableRunParameters } from "../../lib/run-parameters.js";
|
|
47
|
+
import {
|
|
48
|
+
RunParameterFields,
|
|
49
|
+
buildDefaultRunParameterValues,
|
|
50
|
+
buildRetryFailureMessage,
|
|
51
|
+
buildRunFailureMessage,
|
|
52
|
+
buildRunParametersPayload
|
|
53
|
+
} from "../../components/RunParameterFields.js";
|
|
46
54
|
const DEFAULT_TIMEZONE = Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC";
|
|
47
55
|
function formatEntityTypeLabel(entityType) {
|
|
48
56
|
return entityType.replace(/[_-]+/g, " ").replace(/\b\w/g, (letter) => letter.toUpperCase());
|
|
@@ -76,6 +84,7 @@ function SyncRunsDashboardPage() {
|
|
|
76
84
|
const [selectedDirection, setSelectedDirection] = React.useState("import");
|
|
77
85
|
const [batchSize, setBatchSize] = React.useState("100");
|
|
78
86
|
const [fullSync, setFullSync] = React.useState(false);
|
|
87
|
+
const [paramValues, setParamValues] = React.useState({});
|
|
79
88
|
const [scheduleEditor, setScheduleEditor] = React.useState(() => buildDefaultScheduleState(""));
|
|
80
89
|
const [isLoadingSchedule, setIsLoadingSchedule] = React.useState(false);
|
|
81
90
|
const [isSavingSchedule, setIsSavingSchedule] = React.useState(false);
|
|
@@ -155,6 +164,24 @@ function SyncRunsDashboardPage() {
|
|
|
155
164
|
() => selectedIntegration?.supportedEntities ?? [],
|
|
156
165
|
[selectedIntegration]
|
|
157
166
|
);
|
|
167
|
+
const runParameters = React.useMemo(
|
|
168
|
+
() => getApplicableRunParameters(
|
|
169
|
+
selectedIntegration?.runParameters,
|
|
170
|
+
selectedDirection,
|
|
171
|
+
// Pass the state through as-is. Before an entity is chosen it is '',
|
|
172
|
+
// which matches no `entityType` and so hides scoped parameters — the
|
|
173
|
+
// wanted outcome. Mapping '' to `undefined` would mean "skip entity
|
|
174
|
+
// scoping" and show every scoped parameter instead.
|
|
175
|
+
selectedEntityType
|
|
176
|
+
),
|
|
177
|
+
[selectedIntegration, selectedDirection, selectedEntityType]
|
|
178
|
+
);
|
|
179
|
+
React.useEffect(() => {
|
|
180
|
+
setParamValues(buildDefaultRunParameterValues(runParameters));
|
|
181
|
+
}, [runParameters]);
|
|
182
|
+
const updateParamValue = React.useCallback((key, value) => {
|
|
183
|
+
setParamValues((current) => ({ ...current, [key]: value }));
|
|
184
|
+
}, []);
|
|
158
185
|
React.useEffect(() => {
|
|
159
186
|
if (!selectedIntegration) {
|
|
160
187
|
setSelectedEntityType("");
|
|
@@ -235,7 +262,7 @@ function SyncRunsDashboardPage() {
|
|
|
235
262
|
flash(t("data_sync.runs.detail.retrySuccess"), "success");
|
|
236
263
|
setReloadToken((token) => token + 1);
|
|
237
264
|
} else {
|
|
238
|
-
flash(
|
|
265
|
+
flash(buildRetryFailureMessage(call.result, t), "error");
|
|
239
266
|
}
|
|
240
267
|
}, [t]);
|
|
241
268
|
const handleFiltersApply = React.useCallback((values) => {
|
|
@@ -257,27 +284,24 @@ function SyncRunsDashboardPage() {
|
|
|
257
284
|
flash(t("data_sync.dashboard.start.invalidBatchSize", "Batch size must be between 1 and 1000."), "error");
|
|
258
285
|
return;
|
|
259
286
|
}
|
|
287
|
+
const parameters = buildRunParametersPayload(runParameters, paramValues);
|
|
288
|
+
const requestBody = {
|
|
289
|
+
integrationId: selectedIntegration.integrationId,
|
|
290
|
+
entityType: selectedEntityType,
|
|
291
|
+
direction: selectedDirection,
|
|
292
|
+
batchSize: parsedBatchSize,
|
|
293
|
+
fullSync
|
|
294
|
+
};
|
|
295
|
+
if (runParameters.length > 0) requestBody.parameters = parameters;
|
|
260
296
|
try {
|
|
261
297
|
const call = await runMutation({
|
|
262
298
|
// optimistic-lock-exempt: starts a new sync run (create), not a concurrent record edit
|
|
263
299
|
operation: () => apiCall("/api/data_sync/run", {
|
|
264
300
|
method: "POST",
|
|
265
301
|
headers: { "Content-Type": "application/json" },
|
|
266
|
-
body: JSON.stringify(
|
|
267
|
-
integrationId: selectedIntegration.integrationId,
|
|
268
|
-
entityType: selectedEntityType,
|
|
269
|
-
direction: selectedDirection,
|
|
270
|
-
batchSize: parsedBatchSize,
|
|
271
|
-
fullSync
|
|
272
|
-
})
|
|
302
|
+
body: JSON.stringify(requestBody)
|
|
273
303
|
}, { fallback: null }),
|
|
274
|
-
mutationPayload:
|
|
275
|
-
integrationId: selectedIntegration.integrationId,
|
|
276
|
-
entityType: selectedEntityType,
|
|
277
|
-
direction: selectedDirection,
|
|
278
|
-
batchSize: parsedBatchSize,
|
|
279
|
-
fullSync
|
|
280
|
-
},
|
|
304
|
+
mutationPayload: requestBody,
|
|
281
305
|
context: {
|
|
282
306
|
operation: "create",
|
|
283
307
|
actionId: "start-sync-run",
|
|
@@ -285,7 +309,11 @@ function SyncRunsDashboardPage() {
|
|
|
285
309
|
}
|
|
286
310
|
});
|
|
287
311
|
if (!call.ok || !call.result?.id) {
|
|
288
|
-
flash(
|
|
312
|
+
flash(buildRunFailureMessage(
|
|
313
|
+
call.result,
|
|
314
|
+
t("data_sync.dashboard.start.error", "Failed to start sync run"),
|
|
315
|
+
t
|
|
316
|
+
), "error");
|
|
289
317
|
return;
|
|
290
318
|
}
|
|
291
319
|
flash(t("data_sync.dashboard.start.success", "Sync run started"), "success");
|
|
@@ -295,7 +323,7 @@ function SyncRunsDashboardPage() {
|
|
|
295
323
|
const message = error instanceof Error ? error.message : t("data_sync.dashboard.start.error", "Failed to start sync run");
|
|
296
324
|
flash(message, "error");
|
|
297
325
|
}
|
|
298
|
-
}, [batchSize, fullSync, router, runMutation, selectedDirection, selectedEntityType, selectedIntegration, t]);
|
|
326
|
+
}, [batchSize, fullSync, paramValues, router, runMutation, runParameters, selectedDirection, selectedEntityType, selectedIntegration, t]);
|
|
299
327
|
const handleSaveSchedule = React.useCallback(async () => {
|
|
300
328
|
if (!selectedIntegration || !selectedEntityType) return;
|
|
301
329
|
if (scheduleEditor.scheduleValue.trim().length === 0) {
|
|
@@ -623,6 +651,21 @@ function SyncRunsDashboardPage() {
|
|
|
623
651
|
/* @__PURE__ */ jsx(Switch, { checked: fullSync, onCheckedChange: setFullSync })
|
|
624
652
|
] }) })
|
|
625
653
|
] }),
|
|
654
|
+
runParameters.length > 0 ? /* @__PURE__ */ jsxs("div", { className: "mt-4 space-y-3", children: [
|
|
655
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
656
|
+
/* @__PURE__ */ jsx(Settings2, { className: "size-4 text-muted-foreground" }),
|
|
657
|
+
/* @__PURE__ */ jsx("h4", { className: "text-sm font-semibold", children: t("data_sync.dashboard.start.parameters", "Run parameters") })
|
|
658
|
+
] }),
|
|
659
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: t("data_sync.dashboard.start.parametersHelp", "Optional values this integration accepts for the manual run.") }),
|
|
660
|
+
/* @__PURE__ */ jsx(
|
|
661
|
+
RunParameterFields,
|
|
662
|
+
{
|
|
663
|
+
params: runParameters,
|
|
664
|
+
values: paramValues,
|
|
665
|
+
onChange: updateParamValue
|
|
666
|
+
}
|
|
667
|
+
)
|
|
668
|
+
] }) : null,
|
|
626
669
|
/* @__PURE__ */ jsxs("div", { className: "mt-4 flex flex-wrap items-center justify-between gap-3", children: [
|
|
627
670
|
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: t("data_sync.dashboard.start.runNowFootnote", "Manual runs show progress immediately and land on the run detail page after launch.") }),
|
|
628
671
|
/* @__PURE__ */ jsxs(
|