@@ -25,7 +25,11 @@ import { CloudSecurityQueryService } from './cloud-security-query.service';
2525import { CloudSecurityLegacyService } from './cloud-security-legacy.service' ;
2626import { logCloudSecurityActivity } from './cloud-security-audit' ;
2727import { CloudSecurityActivityService } from './cloud-security-activity.service' ;
28- import { GCPSecurityService } from './providers/gcp-security.service' ;
28+ import {
29+ GCPSecurityService ,
30+ type GcpSetupStep ,
31+ type GcpSetupStepId ,
32+ } from './providers/gcp-security.service' ;
2933import { AzureSecurityService } from './providers/azure-security.service' ;
3034
3135@Controller ( { path : 'cloud-security' , version : '1' } )
@@ -226,55 +230,119 @@ export class CloudSecurityController {
226230 @OrganizationId ( ) organizationId : string ,
227231 ) {
228232 try {
229- const connection = await this . cloudSecurityService . getConnectionForDetect (
230- connectionId ,
231- organizationId ,
232- ) ;
233+ const context = await this . resolveGcpSetupContext ( connectionId , organizationId ) ;
233234
234- const credentials = connection . credentials as Record < string , unknown > ;
235- const accessToken = credentials ?. access_token as string ;
236- if ( ! accessToken ) {
237- throw new Error ( 'No access token found. Reconnect the GCP integration.' ) ;
238- }
235+ const result = await this . gcpSecurityService . autoSetup ( {
236+ accessToken : context . accessToken ,
237+ organizationId : context . organizationId ?? '' ,
238+ projectId : context . projectId ,
239+ } ) ;
239240
240- const variables = ( connection . variables ?? { } ) as Record < string , unknown > ;
241- const gcpOrgId = variables . organization_id as string | undefined ;
242-
243- // Auto-detect org if not set
244- let orgId = gcpOrgId ;
245- if ( ! orgId ) {
246- const orgs = await this . gcpSecurityService . detectOrganizations ( accessToken ) ;
247- if ( orgs . length > 0 ) {
248- orgId = orgs [ 0 ] . id ;
249- await this . cloudSecurityService . saveConnectionVariable ( connectionId , 'organization_id' , orgId , organizationId ) ;
250- }
251- }
241+ return {
242+ ... result ,
243+ steps : this . withGcpResolveActions ( result . steps , connectionId ) ,
244+ organizationId : context . organizationId ,
245+ projectId : context . projectId ,
246+ } ;
247+ } catch ( error ) {
248+ const message =
249+ error instanceof Error ? error . message : 'GCP setup failed' ;
250+ throw new HttpException ( message , HttpStatus . BAD_REQUEST ) ;
251+ }
252+ }
252253
253- // Auto-detect project if not known
254- const projects = await this . gcpSecurityService . detectProjects ( accessToken ) ;
255- const projectId = projects [ 0 ] ?. id ;
256- if ( ! projectId ) {
257- throw new Error ( 'No GCP projects found. Ensure your account has access to at least one project.' ) ;
254+ @Post ( 'setup-gcp/:connectionId/resolve-step' )
255+ @UseGuards ( HybridAuthGuard , PermissionGuard )
256+ @RequirePermission ( 'integration' , 'update' )
257+ async resolveGcpSetupStep (
258+ @Param ( 'connectionId' ) connectionId : string ,
259+ @Body ( ) body : { stepId : GcpSetupStepId } ,
260+ @OrganizationId ( ) organizationId : string ,
261+ ) {
262+ try {
263+ if ( ! body ?. stepId ) {
264+ throw new Error ( 'stepId is required' ) ;
258265 }
259266
260- const result = await this . gcpSecurityService . autoSetup ( {
261- accessToken,
262- organizationId : orgId ?? '' ,
263- projectId,
267+ const context = await this . resolveGcpSetupContext ( connectionId , organizationId ) ;
268+ const result = await this . gcpSecurityService . resolveSetupStep ( {
269+ stepId : body . stepId ,
270+ accessToken : context . accessToken ,
271+ organizationId : context . organizationId ?? '' ,
272+ projectId : context . projectId ,
264273 } ) ;
265274
266275 return {
267- ...result ,
268- organizationId : orgId ,
269- projectId,
276+ email : result . email ,
277+ step : this . withGcpResolveActions ( [ result . step ] , connectionId ) [ 0 ] ,
278+ organizationId : context . organizationId ,
279+ projectId : context . projectId ,
270280 } ;
271281 } catch ( error ) {
272282 const message =
273- error instanceof Error ? error . message : 'GCP setup failed ' ;
283+ error instanceof Error ? error . message : 'Failed to resolve setup step ' ;
274284 throw new HttpException ( message , HttpStatus . BAD_REQUEST ) ;
275285 }
276286 }
277287
288+ private withGcpResolveActions ( steps : GcpSetupStep [ ] , connectionId : string ) : GcpSetupStep [ ] {
289+ return steps . map ( ( step ) => {
290+ if ( step . success ) return step ;
291+ return {
292+ ...step ,
293+ resolveAction : {
294+ label : 'Resolve this' ,
295+ method : 'POST' ,
296+ endpoint : `/v1/cloud-security/setup-gcp/${ connectionId } /resolve-step` ,
297+ body : { stepId : step . id } ,
298+ } ,
299+ } ;
300+ } ) ;
301+ }
302+
303+ private async resolveGcpSetupContext ( connectionId : string , organizationId : string ) {
304+ const connection = await this . cloudSecurityService . getConnectionForDetect (
305+ connectionId ,
306+ organizationId ,
307+ ) ;
308+
309+ const credentials = connection . credentials as Record < string , unknown > ;
310+ const accessToken = credentials ?. access_token as string ;
311+ if ( ! accessToken ) {
312+ throw new Error ( 'No access token found. Reconnect the GCP integration.' ) ;
313+ }
314+
315+ const variables = ( connection . variables ?? { } ) as Record < string , unknown > ;
316+ let gcpOrgId = variables . organization_id as string | undefined ;
317+
318+ if ( ! gcpOrgId ) {
319+ const orgs = await this . gcpSecurityService . detectOrganizations ( accessToken ) ;
320+ if ( orgs . length > 0 ) {
321+ gcpOrgId = orgs [ 0 ] . id ;
322+ await this . cloudSecurityService . saveConnectionVariable (
323+ connectionId ,
324+ 'organization_id' ,
325+ gcpOrgId ,
326+ organizationId ,
327+ ) ;
328+ }
329+ }
330+
331+ const projects = await this . gcpSecurityService . detectProjects ( accessToken ) ;
332+ const projectId = projects [ 0 ] ?. id ;
333+ if ( ! projectId ) {
334+ throw new Error (
335+ 'No GCP projects found. Ensure your account has access to at least one project.' ,
336+ ) ;
337+ }
338+
339+ return {
340+ accessToken,
341+ organizationId : gcpOrgId ,
342+ projectId,
343+ } ;
344+ }
345+
278346 @Post ( 'setup-azure/:connectionId' )
279347 @UseGuards ( HybridAuthGuard , PermissionGuard )
280348 @RequirePermission ( 'integration' , 'update' )
0 commit comments