import { ApiManagementModels } from "@azure/arm-apimanagement" ; import { HttpOperationResponse , RequestPrepareOptions , ServiceClient } from "@azure/ms-rest-js" ; import { ProgressLocation , window } from "vscode" ; import { appendExtensionUserAgent , createGenericClient } from "vscode-azureextensionui" ; import { openApiAcceptHeader , openApiExport , openApiSchema , showSavePromptConfigKey , swaggerAcceptHeader , swaggerExport , swaggerSchema } from "../../../constants" ; import { localize } from "../../../localize" ; import { IOpenApiImportObject } from "../../../openApi/OpenApiImportObject" ; import { OpenApiParser } from "../../../openApi/OpenApiParser" ; import { processError } from "../../../utils/errorUtil" ; import { nonNullProp } from "../../../utils/nonNull" ; import { ApiTreeItem } from "../../ApiTreeItem" ; import { Editor } from "../Editor" ; export class OpenApiEditor extends Editor < ApiTreeItem > { constructor ( ) { super ( showSavePromptConfigKey ) ; } public async getData ( context ) { try { const schemas = await context . root . client . apiSchema . listByApi ( context . root . resourceGroupName , context . root . serviceName , context . root . apiName ) ; let exportFormat = openApiExport ; let exportAcceptHeader = openApiAcceptHeader ; if ( schemas . length > 0 ) { const openApiSchemaSupported = schemas . find ( ( s ) => s . contentType === openApiSchema ) ; if ( openApiSchemaSupported === undefined ) { const swaggerSchemaSupported = schemas . find ( ( s ) => s . contentType === swaggerSchema ) ; if ( swaggerSchemaSupported !== undefined ) { exportFormat = swaggerExport ; exportAcceptHeader = swaggerAcceptHeader ; } else { throw Error ( localize ( "unSupportedSchema" , `'${ context . root . apiName }' does not support OpenAPI 2.0 or OpenAPI 3.0 schema.` ) ) ; } } } const responseDocument = await this . requestOpenAPIDocument ( context , exportFormat , exportAcceptHeader ) ; const sourceDocument = await this . processDocument ( context , responseDocument ) ; return JSON . stringify ( sourceDocument , null , "\t" ) ; } catch ( error ) { throw new Error ( processError ( error , localize ( "getOpenAPIDocumentFailed" , `Failed to retriev OpenAPI document for API ${ context . root . apiName }.` ) ) ) ; } } public async updateData ( context , data ) { let openApiDocument ; try { const documentJson = JSON . parse ( data ) ; const openApiparser = new OpenApiParser ( ) ; openApiDocument = await openApiparser . parse ( documentJson ) ; if ( context . apiContract . serviceUrl !== null && context . apiContract . serviceUrl !== undefined ) { openApiparser . updateBackend ( openApiDocument . sourceDocument , nonNullProp ( context . apiContract , 'serviceUrl' ) ) ; } const swaggerJson = JSON . stringify ( openApiDocument . sourceDocument ) ; const payload = { format : openApiDocument . importFormat , value : swaggerJson , path : context . apiContract . path } ; return window . withProgress ( { location : ProgressLocation . Notification , title : localize ( "updatingAPI" , `Applying changes to API '${ context . root . apiName }' in API Management instance ${ context . root . serviceName }...` ) , cancellable : false } , async ( ) => context . root . client . api . createOrUpdate ( context . root . resourceGroupName , context . root . serviceName , context . root . apiName , payload ) ) . then ( async ( ) => { window . showInformationMessage ( localize ( "updateOpenApiSucceded" , `Changes to API '${ context . apiContract . name }' were succefully uploaded to cloud.` ) ) ; return this . getData ( context ) ; } ) ; } catch ( error ) { throw new Error ( processError ( error , localize ( "updateOpenApiFailed" , `Changes to the OpenAPI document could not be uploaded to cloud.` ) ) ) ; } } public async getDiffFilename ( context ) { return `${ context . root . serviceName }-${ context . root . apiName }-openapi.json` ; } public async getFilename ( context ) { return `${ context . root . serviceName }-${ context . root . apiName }-openapi-tempFile.json` ; } public async getSize ( ) { throw new Error ( localize ( "" , "Method not implemented." ) ) ; } public async getSaveConfirmationText ( context ) { return localize ( "" , `Saving will update the API '${ context . apiContract . name }'.` ) ; } private async requestOpenAPIDocument ( context , exportFormat , exportAcceptHeader ) { const client = await createGenericClient ( context . root . credentials ) ; const options = { method : "GET" , url : this . buildAPIExportUrl ( context , exportFormat ) } ; options . headers = { Accept : exportAcceptHeader , 'User-Agent' : appendExtensionUserAgent ( ) } ; const result = await client . sendRequest ( options ) ; return < string > result . bodyAsText ; } private buildAPIExportUrl ( context , exportFormat ) { let url = `${ context . root . environment . resourceManagerEndpointUrl }/subscriptions/${ context . root . subscriptionId }/resourceGroups/${ context . root . resourceGroupName }/providers/Microsoft.ApiManagement/service/${ context . root . serviceName }/apis/${ context . root . apiName }` ; url = `${ url }?export=true&format=${ exportFormat }&api-version=2019-01-01` ; return url ; } private async processDocument ( context , swaggerDocument ) { const openApiparser = new OpenApiParser ( ) ; const swagger = JSON . parse ( swaggerDocument ) ; const importDocument = await openApiparser . parse ( swagger ) ; const sourceDocument = importDocument . sourceDocument ; let basePath = importDocument . basePath !== undefined ? importDocument . basePath : "" ; if ( context . apiContract . apiVersionSet && context . apiContract . apiVersionSet . versioningScheme === "Segment" && sourceDocument . basePath !== undefined ) { const versionSegment = `/${ context . apiContract . apiVersion }` ; if ( basePath . endsWith ( versionSegment ) ) { basePath = sourceDocument . basePath . replace ( versionSegment , "" ) ; } } const service = await context . root . client . apiManagementService . get ( context . root . resourceGroupName , context . root . serviceName ) ; openApiparser . updateBasePath ( sourceDocument , basePath , nonNullProp ( service , "gatewayUrl" ) ) ; return sourceDocument ; } }