twenty-app-intake 0.3.0 → 0.4.0

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.
Files changed (43) hide show
  1. package/README.md +292 -7
  2. package/manifest.json +389 -15
  3. package/package.json +1 -1
  4. package/src/front-components/settings-panel.mjs +7 -7
  5. package/src/front-components/settings-panel.mjs.map +2 -2
  6. package/src/front-components/settings-panel.tsx +49 -0
  7. package/src/logic-functions/check-silence.mjs +263 -0
  8. package/src/logic-functions/check-silence.mjs.map +7 -0
  9. package/src/logic-functions/check-silence.ts +52 -0
  10. package/src/logic-functions/contract.mjs +507 -0
  11. package/src/logic-functions/contract.mjs.map +7 -0
  12. package/src/logic-functions/contract.ts +172 -0
  13. package/src/logic-functions/health.mjs +125 -5
  14. package/src/logic-functions/health.mjs.map +3 -3
  15. package/src/logic-functions/health.ts +41 -5
  16. package/src/logic-functions/quarantine-discard.mjs +380 -0
  17. package/src/logic-functions/quarantine-discard.mjs.map +7 -0
  18. package/src/logic-functions/quarantine-discard.ts +65 -0
  19. package/src/logic-functions/quarantine-list.mjs +211 -0
  20. package/src/logic-functions/quarantine-list.mjs.map +7 -0
  21. package/src/logic-functions/quarantine-list.ts +93 -0
  22. package/src/logic-functions/quarantine-release.mjs +1974 -0
  23. package/src/logic-functions/quarantine-release.mjs.map +7 -0
  24. package/src/logic-functions/quarantine-release.ts +94 -0
  25. package/src/logic-functions/register.mjs +34 -1
  26. package/src/logic-functions/register.mjs.map +2 -2
  27. package/src/logic-functions/replay-bulk.mjs +2040 -0
  28. package/src/logic-functions/replay-bulk.mjs.map +7 -0
  29. package/src/logic-functions/replay-bulk.ts +83 -0
  30. package/src/logic-functions/replay-log.mjs +1987 -0
  31. package/src/logic-functions/replay-log.mjs.map +7 -0
  32. package/src/logic-functions/replay-log.ts +54 -0
  33. package/src/logic-functions/retry.mjs +1001 -248
  34. package/src/logic-functions/retry.mjs.map +4 -4
  35. package/src/logic-functions/retry.ts +45 -68
  36. package/src/logic-functions/test-ingest.mjs +616 -28
  37. package/src/logic-functions/test-ingest.mjs.map +4 -4
  38. package/src/logic-functions/test-ingest.ts +132 -28
  39. package/src/logic-functions/webhook.mjs +965 -209
  40. package/src/logic-functions/webhook.mjs.map +4 -4
  41. package/src/logic-functions/webhook.ts +23 -48
  42. package/src/post-install.mjs +34 -1
  43. package/src/post-install.mjs.map +2 -2
@@ -1,112 +1,89 @@
1
1
  import { defineLogicFunction, type RoutePayload } from 'twenty-sdk/define';
2
- import { CoreApiClient } from 'twenty-client-sdk/core';
3
- import { MetadataApiClient } from 'twenty-client-sdk/metadata';
4
2
  import { IDS } from '../constants/universal-identifiers';
5
- import { createPipeline } from '../lib/pipeline-factory';
6
- import type { PipelineContext } from '../lib/pipeline';
7
- import { coreApi } from '../lib/rest-client';
3
+ import { loadLog, slugForSource } from '../lib/replay';
4
+ import { runIngestion, successBody } from '../lib/ingest';
8
5
 
6
+ /**
7
+ * Re-run a failed ingestion.
8
+ *
9
+ * Deliberately narrower than replay: this refuses a log that already succeeded,
10
+ * so an operator clicking Retry on a working record cannot create a second one.
11
+ * Use `/replay` for a log that succeeded but needs re-mapping.
12
+ */
9
13
  const handler = async (params: RoutePayload) => {
10
- const logId = params.pathParameters?.['logId'];
11
- if (!logId) {
12
- return { statusCode: 400, body: { error: 'Missing logId in URL' } };
13
- }
14
+ const startedAt = Date.now();
14
15
 
15
- // Fetch the existing log
16
- const logResult = await coreApi.get<{
17
- data: {
18
- intakeLog: {
19
- id: string;
20
- status: string;
21
- rawPayload: string | null;
22
- intakeSourceId: string | null;
23
- error: string | null;
24
- };
25
- };
26
- }>(`/intakeLogs/${logId}`).catch(() => null);
16
+ const logId = params.pathParameters?.['logId'];
17
+ if (!logId) return { statusCode: 400, body: { error: 'Missing logId in URL' } };
27
18
 
28
- const log = logResult?.data?.intakeLog;
19
+ const log = await loadLog(logId);
20
+ if (!log) return { statusCode: 404, body: { error: `IntakeLog ${logId} not found` } };
29
21
 
30
- if (!log) {
31
- return { statusCode: 404, body: { error: `IntakeLog ${logId} not found` } };
22
+ if (log.status === 'SUCCESS') {
23
+ return {
24
+ statusCode: 400,
25
+ body: {
26
+ error: 'This ingestion already succeeded — no retry needed. Use POST /s/intake/logs/:logId/replay to re-run it through the current rules.',
27
+ status: log.status,
28
+ },
29
+ };
32
30
  }
33
31
 
34
- if (log.status === 'SUCCESS') {
32
+ if (log.status === 'QUARANTINED') {
35
33
  return {
36
34
  statusCode: 400,
37
- body: { error: 'This ingestion already succeeded — no retry needed', status: log.status },
35
+ body: {
36
+ error: 'This payload is quarantined. Use POST /s/intake/quarantine/:logId/release to let it through.',
37
+ status: log.status,
38
+ },
38
39
  };
39
40
  }
40
41
 
41
42
  if (!log.rawPayload) {
42
43
  return {
43
44
  statusCode: 400,
44
- body: { error: 'No raw payload stored on this log — retry requires rawPayload field (logs created in v0.2.0+)' },
45
+ body: {
46
+ error: 'No raw payload stored on this log — retry needs one. Check INTAKE_RAW_PAYLOAD_RETENTION and INTAKE_RAW_PAYLOAD_MAX_BYTES.',
47
+ code: 'NO_STORED_PAYLOAD',
48
+ },
45
49
  };
46
50
  }
47
51
 
48
52
  let rawPayload: Record<string, unknown>;
49
53
  try {
50
- rawPayload = JSON.parse(log.rawPayload);
54
+ rawPayload = JSON.parse(log.rawPayload) as Record<string, unknown>;
51
55
  } catch {
52
56
  return { statusCode: 400, body: { error: 'rawPayload is not valid JSON' } };
53
57
  }
54
58
 
55
- // We need a sourceSlug to run the pipeline — get it from the source record
56
- let sourceSlug = 'unknown';
57
- if (log.intakeSourceId) {
58
- const srcResult = await coreApi.get<{ data: { intakeSource: { slug: string } } }>(
59
- `/intakeSources/${log.intakeSourceId}`,
60
- ).catch(() => null);
61
- sourceSlug = srcResult?.data?.intakeSource?.slug ?? 'unknown';
59
+ const sourceSlug = await slugForSource(log.intakeSourceId);
60
+ if (!sourceSlug) {
61
+ return { statusCode: 400, body: { error: 'The source for this log no longer exists' } };
62
62
  }
63
63
 
64
- const startedAt = Date.now();
65
- const coreClient = new CoreApiClient();
66
- const metadataClient = new MetadataApiClient();
67
- const pipeline = createPipeline(coreClient, metadataClient);
68
-
69
- const ctx: PipelineContext = {
64
+ const outcome = await runIngestion({
70
65
  sourceSlug,
71
- rawBody: undefined, // HMAC skipped on retry — source already verified originally
72
66
  rawPayload,
73
- headers: {},
74
- startedAt,
75
- skipDedup: true, // Critical — bypass idempotency so the payload re-runs
76
- fieldsCreated: 0,
77
- fieldsMatched: 0,
78
- warnings: [],
79
- events: [],
80
- };
67
+ // The signature was verified when the payload first arrived and there is no
68
+ // body to re-verify against; the caller here is already authenticated.
69
+ skipDedup: true,
70
+ replayOfLogId: log.id,
71
+ });
81
72
 
82
- const result = await pipeline.run(ctx);
83
-
84
- if (!result.ok) {
73
+ if (!outcome.ok) {
85
74
  return {
86
75
  statusCode: 500,
87
76
  body: {
88
- error: `Retry failed: ${result.error.message}`,
89
- code: result.error.code,
77
+ error: `Retry failed: ${outcome.error.message}`,
78
+ code: outcome.error.code,
90
79
  originalLogId: logId,
91
80
  },
92
81
  };
93
82
  }
94
83
 
95
- const { value: finalCtx } = result;
96
-
97
84
  return {
98
85
  statusCode: 200,
99
- body: {
100
- success: true,
101
- originalLogId: logId,
102
- recordId: finalCtx.personId ?? finalCtx.companyId,
103
- recordType: finalCtx.recordType,
104
- personId: finalCtx.personId,
105
- companyId: finalCtx.companyId,
106
- fieldsCreated: finalCtx.fieldsCreated,
107
- processingMs: Date.now() - startedAt,
108
- warnings: finalCtx.warnings,
109
- },
86
+ body: { originalLogId: logId, ...successBody(outcome.ctx, startedAt) },
110
87
  };
111
88
  };
112
89