@@ -46,6 +46,105 @@ const ConvertedSpec = Schema.Struct({
4646
4747const decodeConvertedSpec = Schema . decodeUnknownSync ( Schema . fromJsonString ( ConvertedSpec ) ) ;
4848
49+ const OAUTH2_URL = "https://www.googleapis.com/discovery/v1/apis/oauth2/v2/rest" ;
50+ const OAUTH2_USERINFO_SCOPES = [
51+ "openid" ,
52+ "https://www.googleapis.com/auth/userinfo.email" ,
53+ "https://www.googleapis.com/auth/userinfo.profile" ,
54+ ] as const ;
55+
56+ const oauth2DiscoveryDoc = {
57+ name : "oauth2" ,
58+ version : "v2" ,
59+ title : "Google OAuth2 API" ,
60+ rootUrl : "https://www.googleapis.com/" ,
61+ servicePath : "" ,
62+ auth : {
63+ oauth2 : {
64+ scopes : {
65+ openid : { description : "Associate you with your personal info on Google" } ,
66+ "https://www.googleapis.com/auth/userinfo.email" : {
67+ description : "See your primary Google Account email address" ,
68+ } ,
69+ "https://www.googleapis.com/auth/userinfo.profile" : {
70+ description : "See your personal info" ,
71+ } ,
72+ } ,
73+ } ,
74+ } ,
75+ methods : {
76+ tokeninfo : {
77+ id : "oauth2.tokeninfo" ,
78+ httpMethod : "POST" ,
79+ path : "oauth2/v2/tokeninfo" ,
80+ parameters : {
81+ access_token : {
82+ location : "query" ,
83+ type : "string" ,
84+ } ,
85+ } ,
86+ response : { $ref : "Tokeninfo" } ,
87+ } ,
88+ } ,
89+ resources : {
90+ userinfo : {
91+ methods : {
92+ get : {
93+ id : "oauth2.userinfo.get" ,
94+ httpMethod : "GET" ,
95+ path : "oauth2/v2/userinfo" ,
96+ scopes : OAUTH2_USERINFO_SCOPES ,
97+ response : { $ref : "Userinfo" } ,
98+ } ,
99+ } ,
100+ resources : {
101+ v2 : {
102+ resources : {
103+ me : {
104+ methods : {
105+ get : {
106+ id : "oauth2.userinfo.v2.me.get" ,
107+ httpMethod : "GET" ,
108+ path : "userinfo/v2/me" ,
109+ scopes : OAUTH2_USERINFO_SCOPES ,
110+ response : { $ref : "Userinfo" } ,
111+ } ,
112+ } ,
113+ } ,
114+ } ,
115+ } ,
116+ } ,
117+ } ,
118+ } ,
119+ schemas : {
120+ Tokeninfo : {
121+ id : "Tokeninfo" ,
122+ type : "object" ,
123+ properties : {
124+ audience : { type : "string" } ,
125+ scope : { type : "string" } ,
126+ } ,
127+ } ,
128+ Userinfo : {
129+ id : "Userinfo" ,
130+ type : "object" ,
131+ properties : {
132+ email : { type : "string" } ,
133+ family_name : { type : "string" } ,
134+ gender : { type : "string" } ,
135+ given_name : { type : "string" } ,
136+ hd : { type : "string" } ,
137+ id : { type : "string" } ,
138+ link : { type : "string" } ,
139+ locale : { type : "string" } ,
140+ name : { type : "string" } ,
141+ picture : { type : "string" } ,
142+ verified_email : { type : "boolean" } ,
143+ } ,
144+ } ,
145+ } ,
146+ } ;
147+
49148it ( "accepts only supported HTTPS Google Discovery endpoints" , ( ) => {
50149 expect (
51150 normalizeGoogleDiscoveryUrl ( "https://www.googleapis.com/discovery/v1/apis/gmail/v1/rest/" ) ,
@@ -294,6 +393,46 @@ it.effect("converts Google Discovery documents into Executor-preserving OpenAPI
294393 } ) ,
295394) ;
296395
396+ it . effect ( "converts Google OAuth2 v2 top-level and aliased userinfo methods" , ( ) =>
397+ Effect . gen ( function * ( ) {
398+ const result = yield * convertGoogleDiscoveryToOpenApi ( {
399+ discoveryUrl : OAUTH2_URL ,
400+ // @effect -diagnostics-next-line preferSchemaOverJson:off
401+ documentText : JSON . stringify ( oauth2DiscoveryDoc ) ,
402+ } ) ;
403+
404+ const spec = decodeConvertedSpec ( result . specText ) ;
405+ const userinfo = spec . paths [ "/oauth2/v2/userinfo" ] ?. get ;
406+ const userinfoMe = spec . paths [ "/userinfo/v2/me" ] ?. get ;
407+ const tokeninfo = spec . paths [ "/oauth2/v2/tokeninfo" ] ?. post ;
408+
409+ expect ( userinfo ) . toMatchObject ( {
410+ operationId : "userinfo.get" ,
411+ "x-executor-toolPath" : "userinfo.get" ,
412+ "x-google-scopes" : [ ...OAUTH2_USERINFO_SCOPES ] ,
413+ } ) ;
414+ expect ( userinfo ?. security ) . toEqual ( [ { googleOAuth2 : [ ...OAUTH2_USERINFO_SCOPES ] } ] ) ;
415+ expect ( userinfo ?. responses ) . toMatchObject ( {
416+ "200" : {
417+ content : {
418+ "application/json" : {
419+ schema : { $ref : "#/components/schemas/Userinfo" } ,
420+ } ,
421+ } ,
422+ } ,
423+ } ) ;
424+ expect ( userinfoMe ) . toMatchObject ( {
425+ operationId : "userinfo.v2.me.get" ,
426+ "x-executor-toolPath" : "userinfo.v2.me.get" ,
427+ "x-google-scopes" : [ ...OAUTH2_USERINFO_SCOPES ] ,
428+ } ) ;
429+ expect ( tokeninfo ) . toMatchObject ( {
430+ operationId : "tokeninfo" ,
431+ "x-executor-toolPath" : "tokeninfo" ,
432+ } ) ;
433+ } ) ,
434+ ) ;
435+
297436it . effect ( "marks Google Discovery media-download methods as binary responses" , ( ) =>
298437 Effect . gen ( function * ( ) {
299438 const result = yield * convertGoogleDiscoveryToOpenApi ( {
0 commit comments