googleapiconv.pp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574
  1. {$mode objfpc}
  2. {$h+}
  3. { $DEFINE USESYNAPSE}
  4. {$IFDEF VER2_6}
  5. {$DEFINE USESYNAPSE}
  6. {$ENDIF}
  7. program googleapiconv;
  8. uses
  9. custapp, classes, sysutils, fpjson, jsonparser, fpwebclient,
  10. {$IFDEF USESYNAPSE}
  11. ssl_openssl,
  12. synapsewebclient,
  13. {$ELSE}
  14. fphttpwebclient,
  15. {$ENDIF}
  16. googlediscoverytopas, googleservice, restbase, restcodegen;
  17. Const
  18. BaseDiscoveryURL = 'https://www.googleapis.com/discovery/v1/apis/';
  19. Type
  20. { TGoogleAPIConverter }
  21. { TAPIEntry }
  22. TAPIEntry = Class(TCollectionItem)
  23. private
  24. FAPIIcon: String;
  25. FAPIName: String;
  26. FAPIUnitName: String;
  27. FFileName: String;
  28. Public
  29. Property APIName : String Read FAPIName Write FAPIName;
  30. Property FileName : String Read FFileName Write FFileName;
  31. Property APIUnitName : String Read FAPIUnitName Write FAPIUnitName;
  32. Property APIIcon : String Read FAPIIcon Write FAPIIcon;
  33. end;
  34. { TAPIEntries }
  35. TAPIEntries = Class(TCollection)
  36. private
  37. function GetE(AIndex : Integer): TAPIEntry;
  38. Public
  39. Function AddEntry : TAPIEntry;
  40. Property Entries [AIndex : Integer] : TAPIEntry Read GetE; default;
  41. end;
  42. TGoogleAPIConverter = CLass(TCustomApplication)
  43. private
  44. FKeepJSON: Boolean;
  45. FVerbose: Boolean;
  46. procedure ConversionLog(Sender: TObject; LogType: TCodegenLogType; const Msg: String);
  47. procedure CreateFPMake(FileName: String; L: TAPIEntries);
  48. procedure DoAll(LocalFile, URL, OFN: String; AllVersions: Boolean);
  49. Procedure DoConversion(JS: TStream; AEntry : TAPIEntry) ;
  50. procedure DownloadIcon(APIEntry: TAPIentry);
  51. function GetList(LocalFile, URL: String; AllVersions: Boolean): TJSONObject;
  52. Function HttpGetJSON(Const URL : String; Response : TStream) : Boolean;
  53. procedure RegisterUnit(FileName: String; L: TAPIEntries);
  54. procedure Usage(Msg: String);
  55. Public
  56. Constructor Create(AOwner: TComponent); override;
  57. Destructor Destroy; override;
  58. Procedure DoRun; override;
  59. Property KeepJSON : Boolean Read FKeepJSON Write FKeepJSON;
  60. Property Verbose : Boolean Read FVerbose Write FVerbose;
  61. end;
  62. { TAPIEntries }
  63. function TAPIEntries.GetE(AIndex : Integer): TAPIEntry;
  64. begin
  65. Result:=Items[AIndex] as TAPIEntry;
  66. end;
  67. function TAPIEntries.AddEntry: TAPIEntry;
  68. begin
  69. Result:=Add as TAPIEntry;
  70. end;
  71. constructor TGoogleAPIConverter.Create(AOwner: TComponent);
  72. begin
  73. inherited Create(AOwner);
  74. StopOnException:=True;
  75. TDiscoveryJSONToPas.RegisterAllObjects;
  76. end;
  77. destructor TGoogleAPIConverter.Destroy;
  78. begin
  79. inherited Destroy;
  80. end;
  81. function TGoogleAPIConverter.HttpGetJSON(const URL: String; Response: TStream): Boolean;
  82. Var
  83. Webclient : TAbstractWebClient;
  84. Req: TWebClientRequest;
  85. Resp : TWebClientResponse;
  86. begin
  87. Result:=True;
  88. Req:=Nil;
  89. Resp:=Nil;
  90. {$IFDEF USESYNAPSE}
  91. WebClient:=TSynapseWebClient.Create(Self);
  92. {$ELSE}
  93. WebClient:=TFPHTTPWebClient.Create(Self);
  94. {$ENDIF}
  95. try
  96. Req:=WebClient.CreateRequest;
  97. Req.ResponseContent:=Response;
  98. Resp:=WebClient.ExecuteRequest('GET',URL,Req);
  99. Result:=(Resp<>Nil);
  100. finally
  101. Resp.Free;
  102. Req.Free;
  103. WebClient.Free;
  104. end;
  105. end;
  106. procedure TGoogleAPIConverter.Usage(Msg : String);
  107. begin
  108. If (Msg<>'') then
  109. Writeln('Error : ',Msg);
  110. Writeln('Usage : ',ExeName,' [options] [inputfile] [outputfile]');
  111. Writeln('Where options is one of: ');
  112. Writeln('-a --all Download and generate code for all preferred services.');
  113. Writeln('-A --All Download and generate code for all services.');
  114. Writeln('if one of these options is given, then:');
  115. Writeln(' a) The service name will be appended to the output filename.');
  116. Writeln(' b) The --input will be used as a json which lists the services.');
  117. Writeln('-b --baseclass=classname Class name to use as parent class for all classes.');
  118. Writeln('-b --baseclass=classname Class name to use as parent class for all classes.');
  119. Writeln('-m --fpmake=filename Generate fpmake program.');
  120. Writeln('-e --extraunits=units comma separated list of units to add to uses clause.');
  121. Writeln('-h --help this message');
  122. Writeln('-i --input=file input filename (overrides non-option inputfile)');
  123. Writeln('-I --icons Download service icon (size 16)');
  124. Writeln('-L --license=licensetext Set license text to be added to the top of the unit.');
  125. Writeln(' Use @filename to load license text from filename');
  126. Writeln('-o --output=file output filename (overrides non-option outputfile)');
  127. Writeln(' Default is to use input filename with extension .pp');
  128. Writeln('-p --classprefix=prefix Prefix to use in class names for all classes.');
  129. Writeln('-r --resourcesuffix=suffix Suffix to use for resource names. Default is Resource.');
  130. Writeln('-R --register=unit Register unit for Lazarus.');
  131. Writeln('-t --timestamp Add timestamp to generated unit.');
  132. Writeln('-u --url=URL URL to download the REST description from.');
  133. Writeln('-v --serviceversion=v Service version to download the REST description for.');
  134. Writeln('-V --verbose Write some diagnostic messages');
  135. Writeln('If the outputfilename is empty and cannot be determined, an error is returned');
  136. Halt(Ord(Msg<>''));
  137. end;
  138. function TGoogleAPIConverter.GetList(LocalFile, URL: String;
  139. AllVersions: Boolean): TJSONObject;
  140. Var
  141. D : TJSONData;
  142. S : TStream;
  143. begin
  144. if (LocalFile<>'') then
  145. S:=TFileStream.Create(LocalFile,fmOpenRead)
  146. else
  147. begin
  148. S:=TMemoryStream.Create;
  149. if (URL='') then
  150. URL:=BaseDiscoveryURL;
  151. If not AllVersions then
  152. URL:=URL+'?preferred=true';
  153. HTTPGetJSON(URL,S);
  154. S.Position:=0;
  155. end;
  156. try
  157. D:=GetJSON(S);
  158. if Not (D is TJSONObject) then
  159. begin
  160. D.Free;
  161. Raise Exception.CreateFmt('Source is not a valid JSON description',[LocalFile+URL]);
  162. end;
  163. Result:=D as TJSONObject;
  164. finally
  165. S.Free;
  166. end;
  167. end;
  168. procedure TGoogleAPIConverter.RegisterUnit(FileName :String; L : TAPIEntries);
  169. Var
  170. I : Integer;
  171. UN,N,V : String;
  172. begin
  173. UN:=ChangeFileext(ExtractFileName(FileName),'');
  174. With TStringList.Create do
  175. try
  176. Add(Format('unit %s;',[un]));
  177. Add('');
  178. Add('interface');
  179. Add('');
  180. Add('{$mode objfpc}{$h+}');
  181. Add('');
  182. Add('uses sysutils,classes;');
  183. Add('');
  184. Add('procedure register;');
  185. Add('');
  186. Add('implementation');
  187. Add('');
  188. Add('uses');
  189. if Hasoption('I','icon') then
  190. Add(' lazres,');
  191. Add(' restbase,');
  192. Add(' googleservice,');
  193. Add(' googlebase,');
  194. Add(' googleclient,');
  195. For I:=0 to L.Count-1 do
  196. begin
  197. N:=L[i].APIUnitName;
  198. if I<L.Count-1 then
  199. Add(' '+N+',')
  200. else
  201. Add(' '+N+';')
  202. end;
  203. Add('');
  204. Add('');
  205. Add('procedure register;');
  206. Add('');
  207. Add('begin');
  208. if Hasoption('I','icon') then
  209. Add('{$i '+un+'.inc}');
  210. Add(' RegisterComponents(''Google API'',[');
  211. Add(' TGoogleClient,');
  212. For I:=0 to L.Count-1 do
  213. begin
  214. N:=L[i].APIName;
  215. if I<L.Count-1 then
  216. Add(' '+N+',')
  217. else
  218. Add(' '+N)
  219. end;
  220. Add(' ]);');
  221. Add('end;');
  222. Add('');
  223. Add('end.');
  224. SaveToFile(FileName);
  225. finally
  226. Free;
  227. end;
  228. end;
  229. procedure TGoogleAPIConverter.ConversionLog(Sender: TObject;
  230. LogType: TCodegenLogType; const Msg: String);
  231. begin
  232. if Verbose then
  233. Writeln(StdErr,Msg);
  234. end;
  235. procedure TGoogleAPIConverter.CreateFPMake(FileName :String; L : TAPIEntries);
  236. Var
  237. I : Integer;
  238. UN,N,V : String;
  239. begin
  240. UN:=ChangeFileext(ExtractFileName(FileName),'');
  241. With TStringList.Create do
  242. try
  243. Add('program fpmake;');
  244. Add('');
  245. Add('{$mode objfpc}{$h+}');
  246. Add('');
  247. Add('uses sysutils,classes, fpmkunit;');
  248. Add('');
  249. Add('');
  250. Add('function StdDep(T : TTarget) : TTarget;');
  251. Add('begin');
  252. Add(' T.Dependencies.AddUnit(''googlebase'');');
  253. Add(' T.Dependencies.AddUnit(''googleservice'');');
  254. Add(' Result:=T;');
  255. Add('end;');
  256. Add('');
  257. Add('Procedure AddGoogle;');
  258. Add('');
  259. Add('Var');
  260. Add(' P : TPackage;');
  261. Add(' T : TTarget;');
  262. Add('');
  263. Add('begin');
  264. Add(' With Installer do');
  265. Add(' begin');
  266. Add(' P:=AddPackage(''googleapis'');');
  267. Add(' P.ShortName:=''googleap'';');
  268. Add(' T:=P.Targets.AddUnit(''googlebase.pp'');');
  269. Add(' T:=P.Targets.AddUnit(''googleclient.pp'');');
  270. Add(' T:=P.Targets.AddUnit(''googleservice.pp'');');
  271. Add(' T.Dependencies.AddUnit(''googleclient'');');
  272. Add(' T.Dependencies.AddUnit(''googlebase'');');
  273. For I:=0 to L.Count-1 do
  274. begin
  275. N:=L[i].APIUnitName;
  276. Add(Format(' T:=StdDep(P.Targets.AddUnit(''%s''));',[ExtractFileName(L[i].FAPIUnitName)]));
  277. end;
  278. Add(' end;');
  279. Add('end;');
  280. Add('');
  281. Add('{$ifndef ALLPACKAGES}');
  282. Add('begin');
  283. Add(' AddGoogle;');
  284. Add(' Installer.Run;');
  285. Add('end.');
  286. Add('{$endif ALLPACKAGES}');
  287. Add('');
  288. Add('procedure register;');
  289. Add('');
  290. Add('begin');
  291. SaveToFile(FileName);
  292. finally
  293. Free;
  294. end;
  295. end;
  296. procedure TGoogleAPIConverter.DoAll(LocalFile, URL, OFN : String; AllVersions : Boolean);
  297. Var
  298. D,O : TJSONObject;
  299. RS : TStringStream;
  300. A : TJSONArray;
  301. S : TJSONEnum;
  302. LFN,RU,E : String;
  303. UL : TAPIEntries;
  304. U : TAPIEntry;
  305. I : Integer;
  306. begin
  307. E:=ExtractFileExt(OFN);
  308. if (E='') then
  309. E:='.pp';
  310. UL:=Nil;
  311. D:=GetList(LocalFile,URL,ALlVersions);
  312. try
  313. UL:=TAPIEntries.Create(TAPIEntry);
  314. A:=D.Get('items',TJSONArray(Nil));
  315. For S in A do
  316. begin
  317. O:=S.Value as TJSONObject;
  318. if AllVersions or O.Get('preferred',false) then
  319. begin
  320. RU:=O.get('discoveryRestUrl');
  321. LFN:=O.get('name');
  322. if AllVersions then
  323. LFN:=LFN+'_'+StringReplace(O.get('version'),'.','',[rfReplaceAll]);
  324. if (OFN='') then
  325. LFN:=LFN+E
  326. else
  327. LFN:=ChangeFileExt(OFN,LFN+E);
  328. RS:=TStringStream.Create('');
  329. try
  330. if not HttpGetJSON(RU,RS) then
  331. Raise Exception.Create('Could not download rest description from URL: '+RU);
  332. ConversionLog(Self,cltInfo,Format('Converting service "%s" to unit: %s',[O.get('name'),LFN]));
  333. if KeepJSON then
  334. With TFIleStream.Create(ChangeFileExt(LFN,'.json'),fmCreate) do
  335. try
  336. CopyFrom(RS,0);
  337. finally
  338. Free;
  339. end;
  340. RS.Position:=0;
  341. U:=UL.AddEntry;
  342. U.FileName:=LFN;
  343. DoConversion(RS,U);
  344. finally
  345. RS.Free;
  346. end;
  347. end;
  348. end;
  349. if HasOption('R','register') then
  350. RegisterUnit(GetOptionValue('R','register'),UL);
  351. if HasOption('m','fpmake') then
  352. CreateFpMake(GetOptionValue('m','fpmake'),UL);
  353. if HasOption('I','icon') then
  354. For I:=0 to UL.Count-1 do
  355. DownloadIcon(UL[i]);
  356. finally
  357. UL.Free;
  358. D.Free;
  359. end;
  360. end;
  361. procedure TGoogleAPIConverter.DoRun;
  362. Const
  363. MyO : Array[1..19] of ansistring
  364. = ('help','input:','output:','extraunits:','baseclass:','classprefix:',
  365. 'url:','service:','serviceversion:','resourcesuffix:','license:',
  366. 'All','all','register','icon','fpmake:','timestamp','verbose','keepjson');
  367. Var
  368. O,NonOpts : TStrings;
  369. URL, S, IFN, OFN : AnsiString;
  370. JS : TStream;
  371. DoAllServices : Boolean;
  372. APIEntry : TAPIEntry;
  373. begin
  374. JS:=Nil;
  375. O:=Nil;
  376. NonOpts:=TStringList.Create;
  377. try
  378. O:=TStringList.Create;
  379. For S in MyO do O.Add(S);
  380. S:=Checkoptions('hi:o:e:b:p:u:s:v:r:L:aAR:Im:tVk',O,TStrings(Nil),NonOpts,True);
  381. if NonOpts.Count>0 then
  382. IFN:=NonOpts[0];
  383. if NonOpts.Count>1 then
  384. OFN:=NonOpts[1];
  385. finally
  386. O.Free;
  387. NonOpts.Free;
  388. end;
  389. FVerbose:=HasOption('V','verbose');
  390. FKeepJSON:=HasOption('k','keepjson');
  391. if (S<>'') or HasOption('h','help') then
  392. Usage(S);
  393. DoAllServices:=HasOption('a','all') or HasOption('A','All');
  394. if HasOption('i','input') then
  395. IFN:=GetOptionValue('i','input');
  396. if HasOption('o','output') then
  397. OFN:=GetOptionValue('o','output');
  398. if HasOption('u','url') then
  399. URL:=GetOptionValue('u','url')
  400. else if hasOption('s','service') then
  401. begin
  402. URL:=BaseDiscoveryURL+getOptionValue('s','service');
  403. if (pos('/',getOptionValue('s','service'))= 0) and
  404. HasOption('v','serviceversion') then
  405. URL:=URL+getOptionValue('v','serviceversion');
  406. if (URL[Length(URL)]<>'/') then
  407. URL:=URL+'/';
  408. URL:=URL+'rest';
  409. end;
  410. if (not DoAllServices) and (IFN='') and (URL='') then
  411. Usage('Need an input filename or URL');
  412. if (OFN='') then
  413. if (IFN<>'') then
  414. OFN:=ChangeFileExt(IFN,'.pp')
  415. else if getOptionValue('s','service')<>'' then
  416. OFN:='google'+getOptionValue('s','service')+'.pp';
  417. if (OFN='') and Not DoAllServices then
  418. Usage('Need an output filename');
  419. if DoAllServices then
  420. DoAll(IFN,URL,OFN,HasOption('A','All'))
  421. else
  422. begin
  423. APIEntry:=Nil;
  424. if (IFN='') and (URL<>'') then
  425. begin
  426. JS:=TMemoryStream.Create;
  427. if not HttpGetJSON(URL,JS) then
  428. Raise Exception.Create('Could not download from URL: '+URL);
  429. if KeepJSON then
  430. With TFIleStream.Create(ChangeFileExt(OFN,'.json'),fmCreate) do
  431. try
  432. CopyFrom(JS,0);
  433. finally
  434. Free;
  435. end;
  436. JS.POsition:=0;
  437. end
  438. else
  439. JS:=TFileStream.Create(IFN,fmOpenRead or fmShareDenyWrite);
  440. try
  441. APIEntry:=TAPIEntry.Create(Nil);
  442. try
  443. APIEntry.FileName:=OFN;
  444. DoConversion(JS,APIEntry);
  445. if HasOption('I','icon') then
  446. DownloadIcon(APIEntry);
  447. finally
  448. APIEntry.Free;
  449. end;
  450. finally
  451. JS.Free;
  452. end;
  453. end;
  454. Terminate;
  455. end;
  456. procedure TGoogleAPIConverter.DownloadIcon(APIEntry : TAPIentry);
  457. Var
  458. FN : String;
  459. FS : TFileStream;
  460. begin
  461. if (APIEntry.APIIcon<>'') then
  462. begin
  463. FN:=ExtractFilePath(APIEntry.FileName)+APIEntry.APIName+ExtractFileExt(APIEntry.APIIcon);
  464. FS:=TFileStream.Create(FN,fmCreate);
  465. try
  466. if HasOption('V','verbose') then
  467. Writeln(Format('Downloading icon %s to %s',[APIEntry.APIIcon,FN]));
  468. HttpGetJSON(APIEntry.APIIcon,FS);
  469. finally
  470. FS.Free;
  471. end;
  472. end;
  473. end;
  474. procedure TGoogleAPIConverter.DoConversion(JS: TStream; AEntry: TAPIEntry);
  475. Var
  476. L: String;
  477. O : TGoogleIcons;
  478. begin
  479. With TDiscoveryJSONToPas.Create(Nil) do
  480. try
  481. L:=GetOptionValue('L','license');
  482. if (L<>'') then
  483. begin
  484. if (L[1]<>'@') then
  485. LicenseText.Text:=L
  486. else
  487. begin
  488. Delete(L,1,1);
  489. LicenseText.LoadFromFile(L);
  490. end;
  491. end;
  492. OnLog:=@ConversionLog;
  493. ExtraUnits:=GetOptionValue('e','extraunits');
  494. if HasOption('b','baseclass') then
  495. BaseClassName:=GetOptionValue('b','baseclass');
  496. if HasOption('p','classprefix') then
  497. ClassPrefix:=GetOptionValue('p','classprefix');
  498. if HasOption('r','resourcesuffix') then
  499. ResourceSuffix:=GetOptionValue('r','resourcesuffix');
  500. AddTimeStamp:=HasOption('t','timestamp');
  501. LoadFromStream(JS);
  502. AEntry.APIUnitName:=ChangeFileExt(ExtractFileName(AEntry.FileName),'');
  503. AEntry.APIName:=APIClassName;
  504. O:=Description.icons;
  505. if Assigned(O) then
  506. AEntry.APIIcon:=O.x16;
  507. SaveToFile(AEntry.FileName);
  508. finally
  509. Free;
  510. end;
  511. end;
  512. Var
  513. Application : TGoogleAPIConverter;
  514. begin
  515. {$if declared(Heaptrc)}
  516. printleakedblock:=true;
  517. printfaultyblock:=true;
  518. add_tail:=true;
  519. SetHeapTraceOutput('heaptrc.txt');
  520. {$endif}
  521. Application:=TGoogleAPIConverter.Create(Nil);
  522. Application.Initialize;
  523. Application.Run;
  524. end.