/**
* Deployment Service
* Manages CI/CD deployment pipelines in Microsoft Fabric
*/
import * as vscode from 'vscode';
import { FabricApiService } from '../core/api/fabric-api-service';
export interface DeploymentPipeline {
id: string;
displayName: string;
description?: string;
stages: DeploymentStage[];
createdTime?: Date;
lastModifiedTime?: Date;
}
export interface DeploymentStage {
order: number;
displayName: string;
workspaceId?: string;
workspaceName?: string;
isPublic: boolean;
}
export interface DeploymentOperation {
id: string;
pipelineId: string;
sourceStageOrder: number;
targetStageOrder: number;
status: 'NotStarted' | 'InProgress' | 'Succeeded' | 'Failed' | 'Cancelled';
startTime?: Date;
endTime?: Date;
note?: string;
createdBy?: string;
}
export interface DeployableItem {
itemId: string;
itemType: string;
itemDisplayName: string;
sourceWorkspaceId: string;
isNew: boolean;
hasChanges: boolean;
conflictResolution?: 'KeepBoth' | 'KeepSource' | 'KeepTarget';
}
export interface DeploymentOptions {
allowCreateArtifact: boolean;
allowOverwriteArtifact: boolean;
allowOverwriteTargetArtifactLabel: boolean;
allowSkipTilesWithMissingPrerequisites: boolean;
allowPurgeData: boolean;
allowTakeOver: boolean;
}
export interface DeploymentRequest {
sourceStageOrder: number;
targetStageOrder?: number;
items?: { id: string; type: string }[];
options?: Partial<DeploymentOptions>;
note?: string;
}
export class DeploymentService {
private api: FabricApiService;
constructor() {
this.api = FabricApiService.getInstance();
}
async getPipelines(): Promise<DeploymentPipeline[]> {
try {
const result = await this.api.get<{ value: DeploymentPipeline[] }>(
'/deploymentPipelines'
);
return result?.value || [];
} catch (error) {
console.error('Failed to get deployment pipelines:', error);
return [];
}
}
async getPipeline(pipelineId: string): Promise<DeploymentPipeline | null> {
try {
return await this.api.get<DeploymentPipeline>(
`/deploymentPipelines/${pipelineId}`
);
} catch (error) {
console.error('Failed to get deployment pipeline:', error);
return null;
}
}
async createPipeline(name: string, description?: string): Promise<DeploymentPipeline | null> {
try {
return await this.api.post<DeploymentPipeline>(
'/deploymentPipelines',
{ displayName: name, description }
);
} catch (error) {
console.error('Failed to create deployment pipeline:', error);
return null;
}
}
async updatePipeline(pipelineId: string, updates: Partial<DeploymentPipeline>): Promise<DeploymentPipeline | null> {
try {
return await this.api.patch<DeploymentPipeline>(
`/deploymentPipelines/${pipelineId}`,
updates
);
} catch (error) {
console.error('Failed to update deployment pipeline:', error);
return null;
}
}
async deletePipeline(pipelineId: string): Promise<boolean> {
try {
await this.api.delete(`/deploymentPipelines/${pipelineId}`);
return true;
} catch (error) {
console.error('Failed to delete deployment pipeline:', error);
return false;
}
}
async getStages(pipelineId: string): Promise<DeploymentStage[]> {
try {
const result = await this.api.get<{ value: DeploymentStage[] }>(
`/deploymentPipelines/${pipelineId}/stages`
);
return result?.value || [];
} catch (error) {
console.error('Failed to get stages:', error);
return [];
}
}
async assignWorkspace(pipelineId: string, stageOrder: number, workspaceId: string): Promise<boolean> {
try {
await this.api.post(
`/deploymentPipelines/${pipelineId}/stages/${stageOrder}/assignWorkspace`,
{ workspaceId }
);
return true;
} catch (error) {
console.error('Failed to assign workspace:', error);
return false;
}
}
async unassignWorkspace(pipelineId: string, stageOrder: number): Promise<boolean> {
try {
await this.api.post(
`/deploymentPipelines/${pipelineId}/stages/${stageOrder}/unassignWorkspace`,
{}
);
return true;
} catch (error) {
console.error('Failed to unassign workspace:', error);
return false;
}
}
async getDeployableItems(pipelineId: string, sourceStageOrder: number, targetStageOrder: number): Promise<DeployableItem[]> {
try {
const result = await this.api.post<{ value: DeployableItem[] }>(
`/deploymentPipelines/${pipelineId}/stages/${sourceStageOrder}/getDeployableItems`,
{ targetStageOrder }
);
return result?.value || [];
} catch (error) {
console.error('Failed to get deployable items:', error);
return [];
}
}
async deploy(pipelineId: string, request: DeploymentRequest): Promise<DeploymentOperation | null> {
try {
return await this.api.post<DeploymentOperation>(
`/deploymentPipelines/${pipelineId}/deploy`,
request
);
} catch (error) {
console.error('Failed to deploy:', error);
return null;
}
}
async deployAll(pipelineId: string, sourceStageOrder: number, targetStageOrder: number, note?: string): Promise<DeploymentOperation | null> {
return this.deploy(pipelineId, {
sourceStageOrder,
targetStageOrder,
note,
options: {
allowCreateArtifact: true,
allowOverwriteArtifact: true
}
});
}
async deploySelected(
pipelineId: string,
sourceStageOrder: number,
targetStageOrder: number,
items: { id: string; type: string }[],
note?: string
): Promise<DeploymentOperation | null> {
return this.deploy(pipelineId, {
sourceStageOrder,
targetStageOrder,
items,
note,
options: {
allowCreateArtifact: true,
allowOverwriteArtifact: true
}
});
}
async getOperations(pipelineId: string): Promise<DeploymentOperation[]> {
try {
const result = await this.api.get<{ value: DeploymentOperation[] }>(
`/deploymentPipelines/${pipelineId}/operations`
);
return result?.value || [];
} catch (error) {
console.error('Failed to get operations:', error);
return [];
}
}
async getOperation(pipelineId: string, operationId: string): Promise<DeploymentOperation | null> {
try {
return await this.api.get<DeploymentOperation>(
`/deploymentPipelines/${pipelineId}/operations/${operationId}`
);
} catch (error) {
console.error('Failed to get operation:', error);
return null;
}
}
async addStage(pipelineId: string, stage: Partial<DeploymentStage>): Promise<DeploymentStage | null> {
try {
return await this.api.post<DeploymentStage>(
`/deploymentPipelines/${pipelineId}/stages`,
stage
);
} catch (error) {
console.error('Failed to add stage:', error);
return null;
}
}
async updateStage(pipelineId: string, stageOrder: number, updates: Partial<DeploymentStage>): Promise<DeploymentStage | null> {
try {
return await this.api.patch<DeploymentStage>(
`/deploymentPipelines/${pipelineId}/stages/${stageOrder}`,
updates
);
} catch (error) {
console.error('Failed to update stage:', error);
return null;
}
}
async removeStage(pipelineId: string, stageOrder: number): Promise<boolean> {
try {
await this.api.delete(`/deploymentPipelines/${pipelineId}/stages/${stageOrder}`);
return true;
} catch (error) {
console.error('Failed to remove stage:', error);
return false;
}
}
getStageStatusIcon(stage: DeploymentStage): string {
if (!stage.workspaceId) {
return '$(circle-outline)';
}
return stage.isPublic ? '$(globe)' : '$(lock)';
}
getOperationStatusIcon(status: string): string {
const icons: Record<string, string> = {
'NotStarted': '$(circle-outline)',
'InProgress': '$(sync~spin)',
'Succeeded': '$(pass)',
'Failed': '$(error)',
'Cancelled': '$(circle-slash)'
};
return icons[status] || '$(question)';
}
}