googleapiconv.pp 17 KB

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