@@ -20,6 +20,7 @@ type ImportResources = {
2020 folders : AtLeast < Folder , "name" | "id" | "model" | "workspaceId" > [ ] ;
2121 httpRequests : AtLeast < HttpRequest , "name" | "id" | "model" | "workspaceId" > [ ] ;
2222} ;
23+ type OAuthVariableNames = { clientId : string ; clientSecret : string } ;
2324
2425const HTTP_METHODS = [ "delete" , "get" , "head" , "options" , "patch" , "post" , "put" , "trace" ] ;
2526const BODY_CONTENT_TYPE_PREFERENCE = [
@@ -62,6 +63,7 @@ export async function convertOpenApi(contents: string): Promise<ImportPluginResp
6263 folders : [ ] ,
6364 httpRequests : [ ] ,
6465 } ;
66+ const oauthVariablesByScheme = buildOAuthVariablesByScheme ( importState , spec ) ;
6567 const baseUrl = importBaseUrl ( spec ) ;
6668 const requestBaseUrl = baseUrl . length > 0 ? "${[baseUrl]}" : "" ;
6769
@@ -119,6 +121,7 @@ export async function convertOpenApi(contents: string): Promise<ImportPluginResp
119121 importState,
120122 method,
121123 operation,
124+ oauthVariablesByScheme,
122125 path : rawPath ,
123126 pathItem,
124127 pathParameters,
@@ -132,6 +135,31 @@ export async function convertOpenApi(contents: string): Promise<ImportPluginResp
132135 }
133136 }
134137
138+ if ( resources . httpRequests . some ( ( request ) => request . authenticationType === "oauth2" ) ) {
139+ let globalEnvironment = resources . environments [ 0 ] ;
140+ if ( globalEnvironment == null ) {
141+ globalEnvironment = {
142+ model : "environment" ,
143+ id : importState . generateId ( "environment" ) ,
144+ workspaceId : workspace . id ,
145+ name : "Global Variables" ,
146+ variables : [ ] ,
147+ parentModel : "workspace" ,
148+ parentId : null ,
149+ sortPriority : importState . nextSortPriority ( ) ,
150+ } ;
151+ resources . environments . push ( globalEnvironment ) ;
152+ }
153+
154+ const variableNames = new Set (
155+ [ ...oauthVariablesByScheme . values ( ) ] . flatMap ( ( { clientId, clientSecret } ) => [
156+ clientId ,
157+ clientSecret ,
158+ ] ) ,
159+ ) ;
160+ globalEnvironment . variables . push ( ...[ ...variableNames ] . map ( ( name ) => ( { name, value : "" } ) ) ) ;
161+ }
162+
135163 if ( resources . httpRequests . length === 0 ) return undefined ;
136164
137165 disambiguateNames ( resources . httpRequests , routeLabels ) ;
@@ -178,6 +206,7 @@ function importOperation({
178206 importState,
179207 method,
180208 operation,
209+ oauthVariablesByScheme,
181210 path,
182211 pathItem,
183212 pathParameters,
@@ -189,6 +218,7 @@ function importOperation({
189218 importState : ImportState ;
190219 method : string ;
191220 operation : UnknownRecord ;
221+ oauthVariablesByScheme : Map < string , OAuthVariableNames > ;
192222 path : string ;
193223 pathItem : UnknownRecord ;
194224 pathParameters : unknown [ ] ;
@@ -210,7 +240,12 @@ function importOperation({
210240 body . headers ,
211241 importAcceptHeader ( { importState, operation, spec } ) ,
212242 ) ;
213- const authentication = importAuthentication ( { importState, operation, spec } ) ;
243+ const authentication = importAuthentication ( {
244+ importState,
245+ oauthVariablesByScheme,
246+ operation,
247+ spec,
248+ } ) ;
214249
215250 // Built after everything else, so it can report the refs they left unresolved
216251 const description = importOperationDescription ( {
@@ -828,10 +863,12 @@ function inferSchemaType(schema: UnknownRecord): string {
828863
829864function importAuthentication ( {
830865 importState,
866+ oauthVariablesByScheme,
831867 operation,
832868 spec,
833869} : {
834870 importState : ImportState ;
871+ oauthVariablesByScheme : Map < string , OAuthVariableNames > ;
835872 operation : UnknownRecord ;
836873 spec : UnknownRecord ;
837874} ) : Pick < HttpRequest , "authentication" | "authenticationType" > {
@@ -849,7 +886,15 @@ function importAuthentication({
849886 const scheme = toRecord ( importState . resolve ( schemes [ schemeName ] ) ) ;
850887 const type = stringAt ( scheme , "type" ) ;
851888 if ( type === "oauth2" ) {
852- const oauth2 = importOAuth2 ( scheme , rawScopes ) ;
889+ const oauth2 = importOAuth2 (
890+ scheme ,
891+ rawScopes ,
892+ importBaseUrl ( spec ) ,
893+ oauthVariablesByScheme . get ( schemeName ) ?? {
894+ clientId : "oauth_client_id" ,
895+ clientSecret : "oauth_client_secret" ,
896+ } ,
897+ ) ;
853898 if ( oauth2 != null ) return oauth2 ;
854899 continue ;
855900 }
@@ -902,6 +947,8 @@ function importApiKey(scheme: UnknownRecord, schemeName: string): Record<string,
902947function importOAuth2 (
903948 scheme : UnknownRecord ,
904949 rawScopes : unknown ,
950+ baseUrl : string ,
951+ variableNames : OAuthVariableNames ,
905952) : Pick < HttpRequest , "authentication" | "authenticationType" > | null {
906953 const scope = toArray ( rawScopes )
907954 . filter ( ( s ) : s is string => typeof s === "string" )
@@ -929,24 +976,36 @@ function importOAuth2(
929976 }
930977
931978 for ( const { grantType, flow } of candidates ) {
932- const authorizationUrl = stringAt ( flow , "authorizationUrl" ) ;
933- const accessTokenUrl = stringAt ( flow , "tokenUrl" ) ;
979+ const authorizationUrl = resolveOAuthUrl ( stringAt ( flow , "authorizationUrl" ) , baseUrl ) ;
980+ const accessTokenUrl = resolveOAuthUrl ( stringAt ( flow , "tokenUrl" ) , baseUrl ) ;
934981 if ( authorizationUrl == null && accessTokenUrl == null ) continue ;
935982
936983 const grantPatch =
937984 grantType === "authorization_code"
938- ? { authorizationUrl, accessTokenUrl, clientSecret : "" }
985+ ? {
986+ authorizationUrl,
987+ accessTokenUrl,
988+ clientSecret : templateVariable ( variableNames . clientSecret ) ,
989+ }
939990 : grantType === "implicit"
940991 ? { authorizationUrl }
941992 : grantType === "password"
942- ? { accessTokenUrl, clientSecret : "" , username : "" , password : "" }
943- : { accessTokenUrl, clientSecret : "" } ;
993+ ? {
994+ accessTokenUrl,
995+ clientSecret : templateVariable ( variableNames . clientSecret ) ,
996+ username : "" ,
997+ password : "" ,
998+ }
999+ : {
1000+ accessTokenUrl,
1001+ clientSecret : templateVariable ( variableNames . clientSecret ) ,
1002+ } ;
9441003
9451004 return {
9461005 authenticationType : "oauth2" ,
9471006 authentication : {
9481007 grantType,
949- clientId : "" ,
1008+ clientId : templateVariable ( variableNames . clientId ) ,
9501009 headerPrefix : "Bearer" ,
9511010 ...( scope . length > 0 ? { scope } : { } ) ,
9521011 ...grantPatch ,
@@ -957,6 +1016,50 @@ function importOAuth2(
9571016 return null ;
9581017}
9591018
1019+ function resolveOAuthUrl ( value : string | undefined , baseUrl : string ) : string | undefined {
1020+ if ( value == null || baseUrl . length === 0 ) return value ;
1021+ try {
1022+ return new URL ( value , `${ trimTrailingSlashes ( baseUrl ) } /` ) . toString ( ) ;
1023+ } catch {
1024+ return value ;
1025+ }
1026+ }
1027+
1028+ function buildOAuthVariablesByScheme (
1029+ importState : ImportState ,
1030+ spec : UnknownRecord ,
1031+ ) : Map < string , OAuthVariableNames > {
1032+ const schemes = {
1033+ ...toRecord ( toRecord ( spec . components ) . securitySchemes ) ,
1034+ ...toRecord ( spec . securityDefinitions ) ,
1035+ } ;
1036+ const oauthSchemeNames = Object . entries ( schemes )
1037+ . filter ( ( [ , scheme ] ) => stringAt ( importState . resolve ( scheme ) , "type" ) === "oauth2" )
1038+ . map ( ( [ name ] ) => name ) ;
1039+ const usedPrefixes = new Set < string > ( ) ;
1040+
1041+ return new Map (
1042+ oauthSchemeNames . map ( ( schemeName ) => {
1043+ const basePrefix =
1044+ oauthSchemeNames . length === 1
1045+ ? "oauth"
1046+ : `oauth_${ schemeName . replaceAll ( / [ ^ a - z A - Z 0 - 9 _ ] + / g, "_" ) . replaceAll ( / ^ _ + | _ + $ / g, "" ) || "auth" } ` ;
1047+ let prefix = basePrefix ;
1048+ let suffix = 2 ;
1049+ while ( usedPrefixes . has ( prefix ) ) prefix = `${ basePrefix } _${ suffix ++ } ` ;
1050+ usedPrefixes . add ( prefix ) ;
1051+ return [
1052+ schemeName ,
1053+ { clientId : `${ prefix } _client_id` , clientSecret : `${ prefix } _client_secret` } ,
1054+ ] ;
1055+ } ) ,
1056+ ) ;
1057+ }
1058+
1059+ function templateVariable ( name : string ) : string {
1060+ return `\${[${ name } ]}` ;
1061+ }
1062+
9601063function mergeHeaders ( ...headerGroups : HttpRequestHeader [ ] [ ] ) : HttpRequestHeader [ ] {
9611064 const headers : HttpRequestHeader [ ] = [ ] ;
9621065 for ( const header of headerGroups . flat ( ) ) {
0 commit comments