Skip to content

Commit 2538360

Browse files
committed
fix(cli): include authorization from root steps in childIngestionSources
Root steps (matched via ingestionSourceId) that carry authorization metadata are now included in childIngestionSources as minimal entries (id, name, authorization only) to avoid increasing config file size. This allows downstream consumers like docs-v2 to generate complete authorization documentation.
1 parent bb89b52 commit 2538360

2 files changed

Lines changed: 123 additions & 6 deletions

File tree

packages/integration-sdk-cli/src/commands/generate-ingestion-sources-config.test.ts

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,4 +190,96 @@ describe('#generateIngestionSourcesConfig', () => {
190190
ingestionSourcesConfig[INGESTION_SOURCE_IDS.TEST_SOURCE],
191191
).toBeUndefined();
192192
});
193+
194+
it('should include only authorization from matched root steps', () => {
195+
const integrationSteps: IntegrationStep<IntegrationInstanceConfig>[] = [
196+
{
197+
id: 'fetch-repos',
198+
name: 'Fetch Repos',
199+
entities: [
200+
{
201+
resourceName: 'Github Repo',
202+
_type: 'github_repo',
203+
_class: ['CodeRepo'],
204+
},
205+
],
206+
relationships: [],
207+
dependsOn: ['fetch-account'],
208+
ingestionSourceId: INGESTION_SOURCE_IDS.FETCH_REPOS,
209+
authorization: {
210+
permissions: ['repo:read'],
211+
endpoints: ['https://api.example.com/repos'],
212+
},
213+
executionHandler: jest.fn(),
214+
},
215+
{
216+
id: 'fetch-issues',
217+
name: 'Fetch Issues',
218+
entities: [
219+
{
220+
resourceName: 'GitHub Issue',
221+
_type: 'github_issue',
222+
_class: ['Issue'],
223+
},
224+
],
225+
relationships: [],
226+
dependsOn: ['fetch-repos'],
227+
executionHandler: jest.fn(),
228+
},
229+
];
230+
const ingestionSourcesConfig = generateIngestionSourcesConfig(
231+
ingestionConfig,
232+
integrationSteps,
233+
);
234+
const children =
235+
ingestionSourcesConfig[INGESTION_SOURCE_IDS.FETCH_REPOS]
236+
.childIngestionSources!;
237+
// Root step entry only has id, name, and authorization (no entities/relationships)
238+
const rootEntry = children.find((c) => c.id === 'fetch-repos');
239+
expect(rootEntry).toEqual({
240+
id: 'fetch-repos',
241+
name: 'Fetch Repos',
242+
authorization: {
243+
permissions: ['repo:read'],
244+
endpoints: ['https://api.example.com/repos'],
245+
},
246+
});
247+
// Dependent step still has full metadata
248+
const dependentEntry = children.find((c) => c.id === 'fetch-issues');
249+
expect(dependentEntry).toEqual(
250+
expect.objectContaining({
251+
id: 'fetch-issues',
252+
entities: expect.any(Array),
253+
}),
254+
);
255+
});
256+
257+
it('should not include root steps without authorization', () => {
258+
const integrationSteps: IntegrationStep<IntegrationInstanceConfig>[] = [
259+
{
260+
id: 'fetch-vulnerability-alerts',
261+
name: 'Fetch Vulnerability Alerts',
262+
entities: [
263+
{
264+
resourceName: 'GitHub Vulnerability Alerts',
265+
_type: 'github_finding',
266+
_class: ['Finding'],
267+
},
268+
],
269+
relationships: [],
270+
dependsOn: ['fetch-repos'],
271+
ingestionSourceId: INGESTION_SOURCE_IDS.FINDING_ALERTS,
272+
executionHandler: jest.fn(),
273+
},
274+
];
275+
const ingestionSourcesConfig = generateIngestionSourcesConfig(
276+
ingestionConfig,
277+
integrationSteps,
278+
);
279+
// No authorization on the step, so it's not added as a root entry — only dependents (none here)
280+
expect(
281+
ingestionSourcesConfig[INGESTION_SOURCE_IDS.FINDING_ALERTS]
282+
.childIngestionSources,
283+
).toBeEmpty();
284+
});
193285
});

packages/integration-sdk-cli/src/commands/generate-ingestion-sources-config.ts

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,17 @@ export function generateIngestionSourcesConfigCommand() {
6767
});
6868
}
6969

70+
type ChildIngestionSource =
71+
| StepMetadata
72+
| (Pick<StepMetadata, 'id' | 'name'> & {
73+
authorization?: StepMetadata['authorization'];
74+
});
75+
7076
export type EnhancedIntegrationIngestionConfigFieldMap = Record<
7177
IntegrationSourceId,
72-
IntegrationIngestionConfigField & { childIngestionSources?: StepMetadata[] }
78+
IntegrationIngestionConfigField & {
79+
childIngestionSources?: ChildIngestionSource[];
80+
}
7381
>;
7482

7583
/**
@@ -102,18 +110,35 @@ export function generateIngestionSourcesConfig<
102110
return;
103111
}
104112
// Get the dependent steps for the given matchedIntegrationStepIds
105-
const childIngestionSources = integrationSteps.filter((step) =>
113+
const dependentSteps = integrationSteps.filter((step) =>
106114
step.dependsOn?.some((value) =>
107115
matchedIntegrationStepIds.includes(value),
108116
),
109117
);
118+
// Extract authorization from matched root steps as minimal entries
119+
const rootStepAuthorizations = integrationSteps
120+
.filter((step) => step.ingestionSourceId === key && step.authorization)
121+
.map(({ id, name, authorization }) => ({
122+
id,
123+
name,
124+
authorization,
125+
}));
126+
// Combine root step authorizations with dependent steps, deduplicating by id
127+
const seenIds = new Set<string>();
128+
const childIngestionSources = [
129+
...rootStepAuthorizations,
130+
...dependentSteps.map(
131+
({ executionHandler, ...keepAttrs }) => keepAttrs,
132+
),
133+
].filter((step) => {
134+
if (seenIds.has(step.id)) return false;
135+
seenIds.add(step.id);
136+
return true;
137+
});
110138
// Generate ingestionConfig with the childIngestionSources
111139
newIngestionConfig[key] = {
112140
...ingestionConfig[key],
113-
// Drop execution handler from returned object
114-
childIngestionSources: childIngestionSources.map(
115-
({ executionHandler, ...keepAttrs }) => keepAttrs,
116-
),
141+
childIngestionSources,
117142
};
118143
} else {
119144
log.warn(`The key ${key} does not exist in the ingestionConfig`);

0 commit comments

Comments
 (0)