1
+ import TelemetryReporter from "@vscode/extension-telemetry" ;
2
+ import * as path from "path" ;
3
+ import * as vscode from "vscode" ;
4
+
5
+ import { extensionId , treeViewFocusCommand , treeViewId } from "../../constants" ;
6
+ import { DependenciesViewProvider } from "../../dependenciesViewProvider" ;
7
+ import { GenerationType , KiotaGenerationLanguage , KiotaPluginType } from "../../enums" ;
8
+ import { ExtensionSettings , getExtensionSettings } from "../../extensionSettings" ;
9
+ import { generateClient } from "../../generateClient" ;
10
+ import { GeneratedOutputState } from "../../GeneratedOutputState" ;
11
+ import { generatePlugin } from "../../generatePlugin" ;
12
+ import { getLanguageInformation , getLanguageInformationForDescription } from "../../getLanguageInformation" ;
13
+ import { setGenerationConfiguration } from "../../handlers/configurationHandler" ;
14
+ import { clearDeepLinkParams , getDeepLinkParams } from "../../handlers/deepLinkParamsHandler" ;
15
+ import { setWorkspaceGenerationType } from "../../handlers/workspaceGenerationTypeHandler" ;
16
+ import { ConsumerOperation , generationLanguageToString , getLogEntriesForLevel , KiotaLogEntry , LogLevel } from "../../kiotaInterop" ;
17
+ import { OpenApiTreeProvider } from "../../openApiTreeProvider" ;
18
+ import { GenerateState , generateSteps } from "../../steps" ;
19
+ import { getSanitizedString , getWorkspaceJsonDirectory , parseGenerationLanguage , parseGenerationType , parsePluginType , updateTreeViewIcons } from "../../util" ;
20
+ import { isDeeplinkEnabled , transformToGenerationConfig } from "../../utilities/deep-linking" ;
21
+ import { exportLogsAndShowErrors } from "../../utilities/logging" ;
22
+ import { showUpgradeWarningMessage } from "../../utilities/messaging" ;
23
+ import { Command } from "../Command" ;
24
+ import { checkForSuccess , displayGenerationResults } from "./generation-util" ;
25
+
26
+ export class GenerateClientCommand extends Command {
27
+ private _openApiTreeProvider : OpenApiTreeProvider ;
28
+ private _context : vscode . ExtensionContext ;
29
+ private _dependenciesViewProvider : DependenciesViewProvider ;
30
+
31
+ constructor ( openApiTreeProvider : OpenApiTreeProvider , context : vscode . ExtensionContext , dependenciesViewProvider : DependenciesViewProvider ) {
32
+ super ( ) ;
33
+ this . _openApiTreeProvider = openApiTreeProvider ;
34
+ this . _context = context ;
35
+ this . _dependenciesViewProvider = dependenciesViewProvider ;
36
+ }
37
+
38
+ public getName ( ) : string {
39
+ return `${ treeViewId } .generateClient` ;
40
+ }
41
+
42
+ public async execute ( ) : Promise < void > {
43
+ const deepLinkParams = getDeepLinkParams ( ) ;
44
+ const selectedPaths = this . _openApiTreeProvider . getSelectedPaths ( ) ;
45
+ if ( selectedPaths . length === 0 ) {
46
+ await vscode . window . showErrorMessage (
47
+ vscode . l10n . t ( "No endpoints selected, select endpoints first" )
48
+ ) ;
49
+ return ;
50
+ }
51
+
52
+ let languagesInformation = await getLanguageInformation ( this . _context ) ;
53
+ let availableStateInfo : Partial < GenerateState > ;
54
+ if ( isDeeplinkEnabled ( deepLinkParams ) ) {
55
+ if ( ! deepLinkParams . name && this . _openApiTreeProvider . apiTitle ) {
56
+ deepLinkParams . name = getSanitizedString ( this . _openApiTreeProvider . apiTitle ) ;
57
+ }
58
+ availableStateInfo = transformToGenerationConfig ( deepLinkParams ) ;
59
+ } else {
60
+ const pluginName = getSanitizedString ( this . _openApiTreeProvider . apiTitle ) ;
61
+ availableStateInfo = {
62
+ clientClassName : this . _openApiTreeProvider . clientClassName ,
63
+ clientNamespaceName : this . _openApiTreeProvider . clientNamespaceName ,
64
+ language : this . _openApiTreeProvider . language ,
65
+ outputPath : this . _openApiTreeProvider . outputPath ,
66
+ pluginName
67
+ } ;
68
+ }
69
+ let config = await generateSteps (
70
+ availableStateInfo ,
71
+ languagesInformation ,
72
+ deepLinkParams
73
+ ) ;
74
+ setGenerationConfiguration ( config ) ;
75
+ const generationType = parseGenerationType ( config . generationType ) ;
76
+ const outputPath = typeof config . outputPath === "string"
77
+ ? config . outputPath
78
+ : "./output" ;
79
+ await showUpgradeWarningMessage ( outputPath , this . _context ) ;
80
+ if ( ! this . _openApiTreeProvider . descriptionUrl ) {
81
+ await vscode . window . showErrorMessage (
82
+ vscode . l10n . t ( "No description found, select a description first" )
83
+ ) ;
84
+ return ;
85
+ }
86
+
87
+ const settings = getExtensionSettings ( extensionId ) ;
88
+ setWorkspaceGenerationType ( config . generationType as string ) ;
89
+ let result ;
90
+ switch ( generationType ) {
91
+ case GenerationType . Client :
92
+ result = await this . generateClientAndRefreshUI ( config , settings , outputPath , selectedPaths ) ;
93
+ break ;
94
+ case GenerationType . Plugin :
95
+ result = await this . generatePluginAndRefreshUI ( config , settings , outputPath , selectedPaths ) ;
96
+ break ;
97
+ case GenerationType . ApiManifest :
98
+ result = await this . generateManifestAndRefreshUI ( config , settings , outputPath , selectedPaths ) ;
99
+ break ;
100
+ default :
101
+ await vscode . window . showErrorMessage (
102
+ vscode . l10n . t ( "Invalid generation type" )
103
+ ) ;
104
+ return ;
105
+ }
106
+ if ( result && getLogEntriesForLevel ( result , LogLevel . critical , LogLevel . error ) . length === 0 ) {
107
+ // Save state before opening the new window
108
+ const outputState = {
109
+ outputPath,
110
+ config,
111
+ clientClassName : config . clientClassName || config . pluginName
112
+ } ;
113
+ void this . _context . workspaceState . update ( 'generatedOutput' , outputState as GeneratedOutputState ) ;
114
+
115
+ const pathOfSpec = path . join ( outputPath , `${ outputState . clientClassName ?. toLowerCase ( ) } -openapi.yml` ) ;
116
+ const pathPluginManifest = path . join ( outputPath , `${ outputState . clientClassName ?. toLowerCase ( ) } -apiplugin.json` ) ;
117
+ if ( deepLinkParams . source ?. toLowerCase ( ) === 'ttk' ) {
118
+ try {
119
+ await vscode . commands . executeCommand (
120
+ 'fx-extension.createprojectfromkiota' ,
121
+ [
122
+ pathOfSpec ,
123
+ pathPluginManifest ,
124
+ deepLinkParams . ttkContext ? deepLinkParams . ttkContext : undefined
125
+ ]
126
+ ) ;
127
+ this . _openApiTreeProvider . closeDescription ( ) ;
128
+ await updateTreeViewIcons ( treeViewId , false ) ;
129
+ } catch ( error ) {
130
+ const reporter = new TelemetryReporter ( this . _context . extension . packageJSON . telemetryInstrumentationKey ) ;
131
+ reporter . sendTelemetryEvent ( "DeepLinked fx-extension.createprojectfromkiota" , {
132
+ "error" : JSON . stringify ( error )
133
+ } ) ;
134
+ }
135
+ } else {
136
+ if ( ! vscode . workspace . workspaceFolders || vscode . workspace . workspaceFolders . length === 0 ) {
137
+ await vscode . commands . executeCommand ( 'vscode.openFolder' , vscode . Uri . file ( config . workingDirectory ?? getWorkspaceJsonDirectory ( ) ) , true ) ;
138
+ } else {
139
+ await displayGenerationResults ( this . _openApiTreeProvider , config ) ;
140
+ }
141
+ }
142
+
143
+ clearDeepLinkParams ( ) ; // Clear the state after the generation
144
+ }
145
+ }
146
+
147
+ private async generateManifestAndRefreshUI ( config : Partial < GenerateState > , settings : ExtensionSettings , outputPath : string , selectedPaths : string [ ] ) : Promise < KiotaLogEntry [ ] | undefined > {
148
+ const pluginTypes = KiotaPluginType . ApiManifest ;
149
+ const result = await vscode . window . withProgress ( {
150
+ location : vscode . ProgressLocation . Notification ,
151
+ cancellable : false ,
152
+ title : vscode . l10n . t ( "Generating manifest..." )
153
+ } , async ( progress , _ ) => {
154
+ const start = performance . now ( ) ;
155
+ const result = await generatePlugin (
156
+ this . _context ,
157
+ this . _openApiTreeProvider . descriptionUrl ,
158
+ outputPath ,
159
+ [ pluginTypes ] ,
160
+ selectedPaths ,
161
+ [ ] ,
162
+ typeof config . pluginName === "string"
163
+ ? config . pluginName
164
+ : "ApiClient" ,
165
+ settings . clearCache ,
166
+ settings . cleanOutput ,
167
+ settings . disableValidationRules ,
168
+ ConsumerOperation . Add ,
169
+ config . workingDirectory
170
+ ) ;
171
+ const duration = performance . now ( ) - start ;
172
+ const errorsCount = result ? getLogEntriesForLevel ( result , LogLevel . critical , LogLevel . error ) . length : 0 ;
173
+ const reporter = new TelemetryReporter ( this . _context . extension . packageJSON . telemetryInstrumentationKey ) ;
174
+ reporter . sendRawTelemetryEvent ( `${ extensionId } .generateManifest.completed` , {
175
+ "pluginType" : pluginTypes . toString ( ) ,
176
+ "errorsCount" : errorsCount . toString ( ) ,
177
+ } , {
178
+ "duration" : duration ,
179
+ } ) ;
180
+ return result ;
181
+ } ) ;
182
+ if ( result ) {
183
+ const isSuccess = await checkForSuccess ( result ) ;
184
+ if ( ! isSuccess ) {
185
+ await exportLogsAndShowErrors ( result ) ;
186
+ }
187
+ void vscode . window . showInformationMessage ( vscode . l10n . t ( 'Generation completed successfully.' ) ) ;
188
+ }
189
+ return result ;
190
+ }
191
+ private async generatePluginAndRefreshUI ( config : Partial < GenerateState > , settings : ExtensionSettings , outputPath : string , selectedPaths : string [ ] ) : Promise < KiotaLogEntry [ ] | undefined > {
192
+ const pluginTypes = Array . isArray ( config . pluginTypes ) ? parsePluginType ( config . pluginTypes ) : [ KiotaPluginType . ApiPlugin ] ;
193
+ const result = await vscode . window . withProgress ( {
194
+ location : vscode . ProgressLocation . Notification ,
195
+ cancellable : false ,
196
+ title : vscode . l10n . t ( "Generating plugin..." )
197
+ } , async ( progress , _ ) => {
198
+ const start = performance . now ( ) ;
199
+ const result = await generatePlugin (
200
+ this . _context ,
201
+ this . _openApiTreeProvider . descriptionUrl ,
202
+ outputPath ,
203
+ pluginTypes ,
204
+ selectedPaths ,
205
+ [ ] ,
206
+ typeof config . pluginName === "string"
207
+ ? config . pluginName
208
+ : "ApiClient" ,
209
+ settings . clearCache ,
210
+ settings . cleanOutput ,
211
+ settings . disableValidationRules ,
212
+ ConsumerOperation . Add ,
213
+ config . workingDirectory
214
+ ) ;
215
+ const duration = performance . now ( ) - start ;
216
+ const errorsCount = result ? getLogEntriesForLevel ( result , LogLevel . critical , LogLevel . error ) . length : 0 ;
217
+ const reporter = new TelemetryReporter ( this . _context . extension . packageJSON . telemetryInstrumentationKey ) ;
218
+ reporter . sendRawTelemetryEvent ( `${ extensionId } .generatePlugin.completed` , {
219
+ "pluginType" : pluginTypes . toString ( ) ,
220
+ "errorsCount" : errorsCount . toString ( ) ,
221
+ } , {
222
+ "duration" : duration ,
223
+ } ) ;
224
+ return result ;
225
+ } ) ;
226
+ if ( result ) {
227
+ const isSuccess = await checkForSuccess ( result ) ;
228
+ if ( ! isSuccess ) {
229
+ await exportLogsAndShowErrors ( result ) ;
230
+ }
231
+ const deepLinkParams = getDeepLinkParams ( ) ;
232
+ const isttkIntegration = deepLinkParams . source ?. toLowerCase ( ) === 'ttk' ;
233
+ if ( ! isttkIntegration ) {
234
+ void vscode . window . showInformationMessage ( vscode . l10n . t ( 'Plugin generated successfully.' ) ) ;
235
+ }
236
+ }
237
+ return result ;
238
+ }
239
+ private async generateClientAndRefreshUI ( config : Partial < GenerateState > , settings : ExtensionSettings , outputPath : string , selectedPaths : string [ ] ) : Promise < KiotaLogEntry [ ] | undefined > {
240
+ const language =
241
+ typeof config . language === "string"
242
+ ? parseGenerationLanguage ( config . language )
243
+ : KiotaGenerationLanguage . CSharp ;
244
+ const result = await vscode . window . withProgress ( {
245
+ location : vscode . ProgressLocation . Notification ,
246
+ cancellable : false ,
247
+ title : vscode . l10n . t ( "Generating client..." )
248
+ } , async ( progress , _ ) => {
249
+ const start = performance . now ( ) ;
250
+ const result = await generateClient (
251
+ this . _context ,
252
+ this . _openApiTreeProvider . descriptionUrl ,
253
+ outputPath ,
254
+ language ,
255
+ selectedPaths ,
256
+ [ ] ,
257
+ typeof config . clientClassName === "string"
258
+ ? config . clientClassName
259
+ : "ApiClient" ,
260
+ typeof config . clientNamespaceName === "string"
261
+ ? config . clientNamespaceName
262
+ : "ApiSdk" ,
263
+ settings . backingStore ,
264
+ settings . clearCache ,
265
+ settings . cleanOutput ,
266
+ settings . excludeBackwardCompatible ,
267
+ settings . disableValidationRules ,
268
+ settings . languagesSerializationConfiguration [ language ] . serializers ,
269
+ settings . languagesSerializationConfiguration [ language ] . deserializers ,
270
+ settings . structuredMimeTypes ,
271
+ settings . includeAdditionalData ,
272
+ ConsumerOperation . Add ,
273
+ config . workingDirectory
274
+ ) ;
275
+ const duration = performance . now ( ) - start ;
276
+ const errorsCount = result ? getLogEntriesForLevel ( result , LogLevel . critical , LogLevel . error ) . length : 0 ;
277
+ const reporter = new TelemetryReporter ( this . _context . extension . packageJSON . telemetryInstrumentationKey ) ;
278
+ reporter . sendRawTelemetryEvent ( `${ extensionId } .generateClient.completed` , {
279
+ "language" : generationLanguageToString ( language ) ,
280
+ "errorsCount" : errorsCount . toString ( ) ,
281
+ } , {
282
+ "duration" : duration ,
283
+ } ) ;
284
+ return result ;
285
+ } ) ;
286
+
287
+ let languagesInformation = await getLanguageInformationForDescription (
288
+ this . _context ,
289
+ this . _openApiTreeProvider . descriptionUrl ,
290
+ settings . clearCache
291
+ ) ;
292
+ if ( languagesInformation ) {
293
+ this . _dependenciesViewProvider . update ( languagesInformation , language ) ;
294
+ await vscode . commands . executeCommand ( treeViewFocusCommand ) ;
295
+ }
296
+ if ( result ) {
297
+ const isSuccess = await checkForSuccess ( result ) ;
298
+ if ( ! isSuccess ) {
299
+ await exportLogsAndShowErrors ( result ) ;
300
+ }
301
+ void vscode . window . showInformationMessage ( vscode . l10n . t ( 'Generation completed successfully.' ) ) ;
302
+ }
303
+ return result ;
304
+ }
305
+
306
+ }
0 commit comments