@@ -4,28 +4,20 @@ import type {
44 PostHogAPIClient ,
55 SignalSourceConfig ,
66} from "@posthog/api-client/posthog-client" ;
7+ import {
8+ EXTERNAL_INBOX_SOURCES ,
9+ type SignalRecordKind ,
10+ sourceNeedsFullRefresh ,
11+ type ToggleableSourceProduct ,
12+ } from "@posthog/shared" ;
713import type { SignalUserAutonomyConfig } from "@posthog/shared/domain-types" ;
814import { injectable } from "inversify" ;
915
10- export interface SignalSourceValues {
11- session_replay : boolean ;
12- error_tracking : boolean ;
13- github : boolean ;
14- linear : boolean ;
15- jira : boolean ;
16- zendesk : boolean ;
17- conversations : boolean ;
18- pganalyze : boolean ;
19- }
20-
21- export type SignalSourceProduct = keyof SignalSourceValues ;
22-
23- export type WarehouseSourceProduct =
24- | "github"
25- | "linear"
26- | "jira"
27- | "zendesk"
28- | "pganalyze" ;
16+ export type SignalSourceProduct = ToggleableSourceProduct ;
17+ export type SignalSourceValues = Record < SignalSourceProduct , boolean > ;
18+ // Any warehouse source can be a setup target; membership in DATA_WAREHOUSE_SOURCES is the
19+ // runtime narrowing, so this stays a broad alias rather than a hand-kept union.
20+ export type WarehouseSourceProduct = SignalSourceProduct ;
2921
3022export interface SignalSourceState {
3123 requiresSetup : boolean ;
@@ -37,20 +29,16 @@ export interface ToggleSourceResult {
3729 isFirstConnection : boolean ;
3830}
3931
40- type SourceProduct = SignalSourceConfig [ "source_product" ] ;
4132type SourceType = SignalSourceConfig [ "source_type" ] ;
4233
43- const SOURCE_TYPE_MAP : Record <
44- Exclude < SourceProduct , "error_tracking" | "llm_analytics" | "signals_scout" > ,
45- SourceType
46- > = {
34+ // Non-warehouse toggles are hard-wired; warehouse sources are derived from the shared
35+ // EXTERNAL_INBOX_SOURCES registry (kept in sync with the UI hook).
36+ const SOURCE_TYPE_MAP : Partial < Record < SignalSourceProduct , SourceType > > = {
4737 session_replay : "session_analysis_cluster" ,
48- github : "issue" ,
49- linear : "issue" ,
50- jira : "issue" ,
51- zendesk : "ticket" ,
5238 conversations : "ticket" ,
53- pganalyze : "issue" ,
39+ ...Object . fromEntries (
40+ EXTERNAL_INBOX_SOURCES . map ( ( s ) => [ s . product , s . recordKind ] ) ,
41+ ) ,
5442} ;
5543
5644const ERROR_TRACKING_SOURCE_TYPES : SourceType [ ] = [
@@ -60,30 +48,27 @@ const ERROR_TRACKING_SOURCE_TYPES: SourceType[] = [
6048] ;
6149
6250const DATA_WAREHOUSE_SOURCES : Record <
63- WarehouseSourceProduct ,
64- { dwSourceType : string ; requiredTable : string }
65- > = {
66- github : { dwSourceType : "Github" , requiredTable : "issues" } ,
67- linear : { dwSourceType : "Linear" , requiredTable : "issues" } ,
68- jira : { dwSourceType : "Jira" , requiredTable : "issues" } ,
69- zendesk : { dwSourceType : "Zendesk" , requiredTable : "tickets" } ,
70- pganalyze : { dwSourceType : "PgAnalyze" , requiredTable : "issues" } ,
71- } ;
51+ string ,
52+ { dwSourceType : string ; requiredTable : string ; recordKind : SourceType }
53+ > = Object . fromEntries (
54+ EXTERNAL_INBOX_SOURCES . map ( ( s ) => [
55+ s . product ,
56+ {
57+ dwSourceType : s . dwSourceType ,
58+ requiredTable : s . requiredTables [ 0 ] ,
59+ recordKind : s . recordKind ,
60+ } ,
61+ ] ) ,
62+ ) ;
7263
7364const ALL_SOURCE_PRODUCTS : SignalSourceProduct [ ] = [
7465 "session_replay" ,
7566 "error_tracking" ,
76- "github" ,
77- "linear" ,
78- "jira" ,
79- "zendesk" ,
8067 "conversations" ,
81- "pganalyze" ,
68+ ... EXTERNAL_INBOX_SOURCES . map ( ( s ) => s . product as SignalSourceProduct ) ,
8269] ;
8370
84- function isWarehouseSource (
85- product : SignalSourceProduct ,
86- ) : product is WarehouseSourceProduct {
71+ function isWarehouseSource ( product : SignalSourceProduct ) : boolean {
8772 return product in DATA_WAREHOUSE_SOURCES ;
8873}
8974
@@ -106,16 +91,9 @@ function findExternalSource(
10691export function computeSourceValues (
10792 configs : SignalSourceConfig [ ] | undefined ,
10893) : SignalSourceValues {
109- const result : SignalSourceValues = {
110- session_replay : false ,
111- error_tracking : false ,
112- github : false ,
113- linear : false ,
114- jira : false ,
115- zendesk : false ,
116- conversations : false ,
117- pganalyze : false ,
118- } ;
94+ const result = Object . fromEntries (
95+ ALL_SOURCE_PRODUCTS . map ( ( p ) => [ p , false ] ) ,
96+ ) as SignalSourceValues ;
11997 if ( ! configs ?. length ) {
12098 return result ;
12199 }
@@ -199,11 +177,9 @@ export class SignalSourceService {
199177 return ;
200178 }
201179
202- const issuesFullReplication =
203- ( product === "github" || product === "linear" || product === "jira" ) &&
204- dwConfig . requiredTable === "issues" ;
205-
206- if ( issuesFullReplication ) {
180+ // Issue-like records (issues, findings, feedback, reviews) mutate after creation, so
181+ // they need full-refresh sync; tickets are append-only.
182+ if ( sourceNeedsFullRefresh ( dwConfig . recordKind as SignalRecordKind ) ) {
207183 const needsUpdate =
208184 ! requiredSchema . should_sync ||
209185 requiredSchema . sync_type !== "full_refresh" ;
@@ -319,20 +295,15 @@ export class SignalSourceService {
319295 configs : SignalSourceConfig [ ] | undefined ,
320296 ) : Promise < void > {
321297 const existing = configs ?. find ( ( c ) => c . source_product === product ) ;
298+ const sourceType = SOURCE_TYPE_MAP [ product ] ;
322299 if ( existing ) {
323300 await client . updateSignalSourceConfig ( projectId , existing . id , {
324301 enabled,
325302 } ) ;
326- } else if ( enabled ) {
303+ } else if ( enabled && sourceType ) {
327304 await client . createSignalSourceConfig ( projectId , {
328305 source_product : product ,
329- source_type :
330- SOURCE_TYPE_MAP [
331- product as Exclude <
332- SourceProduct ,
333- "error_tracking" | "llm_analytics" | "signals_scout"
334- >
335- ] ,
306+ source_type : sourceType ,
336307 enabled : true ,
337308 } ) ;
338309 }
@@ -345,13 +316,14 @@ export class SignalSourceService {
345316 configs : SignalSourceConfig [ ] | undefined ,
346317 ) : Promise < ToggleSourceResult > {
347318 const existing = configs ?. find ( ( c ) => c . source_product === product ) ;
348- if ( ! existing ) {
319+ const sourceType = SOURCE_TYPE_MAP [ product ] ;
320+ if ( ! existing && sourceType ) {
349321 await client . createSignalSourceConfig ( projectId , {
350322 source_product : product ,
351- source_type : SOURCE_TYPE_MAP [ product ] ,
323+ source_type : sourceType ,
352324 enabled : true ,
353325 } ) ;
354- } else if ( ! existing . enabled ) {
326+ } else if ( existing && ! existing . enabled ) {
355327 await client . updateSignalSourceConfig ( projectId , existing . id , {
356328 enabled : true ,
357329 } ) ;
0 commit comments