Browse Source

* OpenAPI to pascal conversion tool

Michaël Van Canneyt 9 months ago
parent
commit
25954d39e4

+ 1 - 0
utils/fpmake_add.inc

@@ -20,3 +20,4 @@
   add_pas2js(ADirectory+IncludeTrailingPathDelimiter('pas2js'));
   add_ihxutil(ADirectory+IncludeTrailingPathDelimiter('ihxutil'));
   add_wasmbin(ADirectory+IncludeTrailingPathDelimiter('wasmbin'));
+  add_openapi2pas(ADirectory+IncludeTrailingPathDelimiter('openapi'));

+ 1 - 0
utils/fpmake_proc.inc

@@ -42,3 +42,4 @@
 
 {$include wasmbin/fpmake.pp}
 
+{$include openapi/fpmake.pp}

+ 2 - 0
utils/openapi/Makefile

@@ -0,0 +1,2 @@
+PACKAGE_NAME=openapi2pas
+include ../build/Makefile.pkg

+ 58 - 0
utils/openapi/fpmake.pp

@@ -0,0 +1,58 @@
+{$ifndef ALLPACKAGES}
+{$mode objfpc}{$H+}
+program fpmake;
+
+uses 
+{$ifdef unix}
+  cthreads,
+{$endif}
+  fpmkunit, sysutils;
+{$endif ALLPACKAGES}
+
+procedure add_openapi2pas(const ADirectory: string);
+
+Var
+  P : TPackage;
+  T : TTarget;
+  Bin2Obj : string;
+
+begin
+  With Installer do
+    begin
+    P:=AddPackage('utils-openapi2pas');
+    P.ShortName:='oapi2p';
+    P.Author := '<various>';
+    P.License := 'LGPL with modification';
+    P.HomepageURL := 'www.freepascal.org';
+    P.Email := '';
+    P.Description := 'Free Pascal OpenAPI to pascal code generation utility.';
+    P.NeedLibC:= false;
+
+    P.OSes:=AllOSes-[embedded,msdos,win16,go32v2,nativent,macosclassic,palmos,atari,zxspectrum,msxdos,amstradcpc,watcom,sinclairql,wasi,human68k,ps1];
+    if Defaults.CPU=jvm then
+      P.OSes := P.OSes - [java,android];
+
+    P.Dependencies.Add('fcl-base');
+    P.Dependencies.Add('fcl-json');
+    P.Dependencies.Add('fcl-openapi');
+    P.Dependencies.Add('fcl-jsonschema');
+
+    P.Directory:=ADirectory;
+    P.Version:='3.3.1';
+
+    P.Options.Add('-S2h');
+
+    T:=P.Targets.AddProgram('openapi2pas.pp');
+    end;
+end;
+
+{$ifndef ALLPACKAGES}
+begin
+  add_openapi2pas('');
+  Installer.Run;
+end.
+{$endif ALLPACKAGES}
+
+
+
+

+ 62 - 0
utils/openapi/openapi2pas.lpi

@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<CONFIG>
+  <ProjectOptions>
+    <Version Value="12"/>
+    <General>
+      <Flags>
+        <MainUnitHasCreateFormStatements Value="False"/>
+        <MainUnitHasTitleStatement Value="False"/>
+        <MainUnitHasScaledStatement Value="False"/>
+      </Flags>
+      <SessionStorage Value="InProjectDir"/>
+      <Title Value="OpenAPI to pascal code generator."/>
+      <UseAppBundle Value="False"/>
+      <ResourceType Value="res"/>
+    </General>
+    <BuildModes>
+      <Item Name="Default" Default="True"/>
+    </BuildModes>
+    <PublishOptions>
+      <Version Value="2"/>
+      <UseFileFilters Value="True"/>
+    </PublishOptions>
+    <RunParams>
+      <FormatVersion Value="2"/>
+    </RunParams>
+    <Units>
+      <Unit>
+        <Filename Value="openapi2pas.pp"/>
+        <IsPartOfProject Value="True"/>
+      </Unit>
+    </Units>
+  </ProjectOptions>
+  <CompilerOptions>
+    <Version Value="11"/>
+    <Target>
+      <Filename Value="openapi2pas"/>
+    </Target>
+    <SearchPaths>
+      <IncludeFiles Value="$(ProjOutDir)"/>
+      <OtherUnitFiles Value="../../packages/fcl-openapi/src;../../packages/fcl-jsonschema/src"/>
+      <UnitOutputDirectory Value="lib/$(TargetCPU)-$(TargetOS)"/>
+    </SearchPaths>
+    <Linking>
+      <Debugging>
+        <DebugInfoType Value="dsDwarf3"/>
+      </Debugging>
+    </Linking>
+  </CompilerOptions>
+  <Debugging>
+    <Exceptions>
+      <Item>
+        <Name Value="EAbort"/>
+      </Item>
+      <Item>
+        <Name Value="ECodetoolError"/>
+      </Item>
+      <Item>
+        <Name Value="EFOpenError"/>
+      </Item>
+    </Exceptions>
+  </Debugging>
+</CONFIG>

+ 201 - 0
utils/openapi/openapi2pas.pp

@@ -0,0 +1,201 @@
+{
+    This file is part of the Free Component Library
+    Copyright (c) 2024 by Michael Van Canneyt [email protected]
+
+    Open API to pascal code generator
+
+    See the file COPYING.FPC, included in this distribution,
+    for details about the copyright.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+ **********************************************************************}
+program openapi2pas;
+
+{$mode objfpc}{$H+}
+
+uses
+  Classes,
+  SysUtils,
+  CustApp,
+  fpopenapi.objects,
+  fpopenapi.reader,
+  fpopenapi.codegen;
+
+type
+
+  { TGenDTOApplication }
+
+  TGenDTOApplication = class(TCustomApplication)
+  private
+    FQuiet : Boolean;
+    FCodeGen : TOpenAPICodeGen;
+    FServiceMapFile,
+    FUUIDMapFile : String;
+  protected
+    Procedure DoLog(EventType : TEventType; const Msg : String); override;
+    procedure DoRun; override;
+    procedure ReadOpenAPi(const aInputFile: string; aApi: TOpenAPI);
+    procedure WriteAPI(aApi: TOpenAPI; const aOutputFile: String);
+    procedure WriteConfig(const aFileName: string);
+  public
+    constructor Create(TheOwner: TComponent); override;
+    destructor Destroy; override;
+    procedure Usage(const aMessage : string); virtual;
+  end;
+
+{ TGenDTOApplication }
+
+procedure TGenDTOApplication.WriteAPI(aApi: TOpenAPI; const aOutputFile : String) ;
+
+begin
+  FCodeGen.OnLog:=@DoLog;
+  FCodeGen.BaseOutputFileName:=aOutputFile;
+  FCodeGen.API:=aAPI;
+  if (FUUIDMapFile<>'') and FileExists(FUUIDMapFile) then
+    FCodeGen.UUIDMap.LoadFromFile(FUUIDMapFile);
+  if (FServiceMapFile<>'') then
+    FCodeGen.ServiceMap.LoadFromFile(FServiceMapFile);
+  FCodeGen.Execute;
+  if FUUIDMapFile<>'' then
+    FCodeGen.UUIDMap.SavetoFile(FUUIDMapFile);
+end;
+
+procedure TGenDTOApplication.ReadOpenAPi(const aInputFile : string; aApi: TOpenAPI);
+
+var
+  lReader : TOpenAPIReader;
+
+begin
+  lReader:=TOpenAPIReader.Create(Self);
+  try
+    lReader.ReadFromFile(aAPI,aInputFile);
+  finally
+    lReader.Free;
+  end;
+end;
+
+procedure TGenDTOApplication.DoLog(EventType: TEventType; const Msg: String);
+begin
+  if FQuiet then
+    exit;
+  Writeln(EventType,' : ',Msg);
+end;
+
+procedure TGenDTOApplication.WriteConfig(const aFileName : string);
+
+var
+  lDir : String;
+
+begin
+  lDir:=ExtractFilePath(aFileName);
+  if lDir<>'' then
+    If not ForceDirectories(lDir) then
+      begin
+      Writeln(StdErr,'Failed to create directory ',lDir);
+      Exit;
+      end;
+  Log(etInfo,'Writing config file to %s',[aFileName]);
+  FCodegen.SaveConfig(aFileName);
+end;
+
+procedure TGenDTOApplication.DoRun;
+
+const
+  shortOpts = 'hi:o:dequ:s:varcC:bnw:';
+  LongOpts : Array of string = ('help','input:','output:','delphi','uuid-map:','quiet','service-map','verbose-header','enumerated','async','server','client','config:','abstract','no-implementation','write-config:');
+
+var
+  lAPI : TOpenAPI;
+  lConfig, lOutputFile,lInputFile, ErrorMsg : String;
+
+begin
+  Terminate;
+  ErrorMsg:=CheckOptions(ShortOPts,LongOpts);
+  if (ErrorMsg<>'') or HasOption('h','help') then
+    begin
+    Usage(ErrorMsg);
+    Exit;
+    end;
+  lConfig:=GetOptionValue('C','config');
+  if (lConfig<>'') then
+    FCodeGen.LoadConfig(lConfig);
+  FCodeGen.DelphiCode:=HasOption('d','delphi');
+  FCodeGen.VerboseHeader:=HasOption('v','verbose-header');
+  FCodeGen.UseEnums:=HasOption('e','enumerated');
+  FCodeGen.AsyncService:=HasOption('a','async');
+  lInputFile:=GetOptionValue('i','input');
+  lOutputFile:=GetOptionValue('o','output');
+  FUUIDMapFile:=GetOptionValue('u','uuid-map');
+  FServiceMapFile:=GetOptionValue('s','service-map');
+  FCodeGen.GenerateServer:=HasOption('r','server');
+  FCodeGen.AbstractServiceCalls:=HasOption('b','abstract');
+  FCodeGen.SkipServerServiceImplementationModule:=HasOption('n','no-implementation');
+  FQuiet:=HasOption('q','quiet');
+  if HasOption('w','write-config') then
+    WriteConfig(GetOptionValue('w','write-config'))
+  else
+    begin
+    if lOutputFile='' then
+      lOutputFile:=ChangeFileExt(lInputFile,'');
+    lAPI:=TOpenAPI.Create;
+    try
+      ReadOpenAPi(lInputFile,lAPI);
+      WriteApi(lApi,lOutputFile);
+    finally
+      lApi.Free;
+    end;
+    end;
+end;
+
+constructor TGenDTOApplication.Create(TheOwner: TComponent);
+
+begin
+  inherited Create(TheOwner);
+  ExceptionExitCode:=1;
+  StopOnException:=True;
+  FCodeGen:=TOpenAPICodeGen.Create(Self);
+end;
+
+destructor TGenDTOApplication.Destroy;
+
+begin
+  FreeAndNil(FCodeGen);
+  inherited Destroy;
+end;
+
+procedure TGenDTOApplication.Usage(const aMessage: string);
+
+begin
+  writeln('Usage: ', ExeName, '[options]');
+  Writeln('Where options is one or more of:');
+  Writeln('-a --async             Generate asynchronous service calls.');
+  Writeln('-b --abstract          Split server in abstract handler and implementation modules (and units).');
+  Writeln('-c --client            Generate client-side service.');
+  Writeln('-C --config=FILE       Read config file with converter settings.');
+  Writeln('-d --delphi            Generate delphi code for DTO/Serializer/Service definitions.');
+  Writeln('-e --enumerated        Use enumerateds (default is to keep strings).');
+  Writeln('-h --help              This message.');
+  Writeln('-i --input=FILE        OpenAPI JSON File to use.');
+  Writeln('-n --no-implementation Skip generation of server service module (only useful when -b is used).');
+  Writeln('-o --output=FILE       Base filename for output.');
+  Writeln('-q --quiet             Be less verbose.');
+  Writeln('-s --service-map=FILE  Read service and method name mapping from file.');
+  Writeln('-u --uuid-map=FILE     Read (and write) a file with UUIDs for interfaces.');
+  Writeln('-v --verbose-header    Add OpenAPI description to unit header.');
+  Writeln('-w --write-config=FILE Write a configuration file with current settings and exit.');
+  ExitCode:=Ord(aMessage<>'');
+end;
+
+var
+  Application: TGenDTOApplication;
+
+begin
+  Application:=TGenDTOApplication.Create(nil);
+  Application.Title:='Generate DTO Application';
+  Application.Run;
+  Application.Free;
+end.
+