fpopenapi.pascaltypes.pp 47 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573
  1. {
  2. This file is part of the Free Component Library (FCL)
  3. Copyright (c) 2024 by Michael Van Canneyt ([email protected])
  4. Data Structures to generate pascal code based on OpenAPI data
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. unit fpopenapi.pascaltypes;
  12. {$mode ObjFPC}{$H+}
  13. interface
  14. uses
  15. {$IFDEF FPC_DOTTEDUNITS}
  16. System.Classes, System.SysUtils, System.Types, System.Contnrs,
  17. {$ELSE}
  18. Classes, SysUtils, Types, contnrs,
  19. {$ENDIF}
  20. fpjson.schema.types,
  21. fpjson.schema.schema,
  22. fpjson.schema.pascaltypes,
  23. fpopenapi.objects,
  24. fpopenapi.types;
  25. const
  26. ptAPIComponent = ptSchemaStruct;
  27. type
  28. EGenAPI = Class(Exception);
  29. TAPIService = Class;
  30. TAPIServiceMethod = Class;
  31. TAPIData = class;
  32. TAPIProperty = class(TPascalPropertyData);
  33. { TAPITypeData }
  34. TAPITypeData = Class(TPascalTypeData)
  35. private
  36. FBinaryData: Boolean;
  37. FContentType: String;
  38. FIndex: Integer;
  39. protected
  40. function CreateProperty(const aAPIName, aPascalName: string): TPascalPropertyData; override;
  41. public
  42. constructor CreateBinaryData(const aContentType : string); overload;
  43. function AddProperty(const aApiName, aPascalName: String): TAPIProperty; reintroduce;
  44. property BinaryData : Boolean Read FBinaryData Write FBinaryData;
  45. Property ContentType : String read FContentType Write FContentType;
  46. // Index in openAPI #components
  47. Property Index : Integer Read FIndex;
  48. end;
  49. { TAPIServiceMethod }
  50. { TAPIServiceMethodParam }
  51. TParamLocation = (plQuery,plPath,plHeader,plCookie);
  52. TAPIServiceMethodParam = Class(TObject)
  53. private
  54. FArgType: TPropertyType;
  55. FDefaultValue: String;
  56. FName: String;
  57. FOriginalName: String;
  58. FParameter: TParameter;
  59. FTypeName: String;
  60. function GetAsStringValue: String;
  61. function Getlocation: TParamLocation;
  62. Public
  63. Constructor create(aArgType : TPropertyType; aOriginalName : String; aName : String; aTypeName : String; aParam : TParameter);
  64. // Argument type
  65. Property ArgType : TPropertyType Read FArgType;
  66. // Assigned pascal name
  67. Property Name : String Read FName;
  68. // Original openAPI name. Can be empty
  69. Property OriginalName : String Read FOriginalName;
  70. // Pascal type name
  71. Property TypeName : String Read FTypeName;
  72. // The OpenAPI parameter
  73. Property Parameter : TParameter Read FParameter;
  74. // Location of this parameter
  75. Property Location : TParamLocation Read Getlocation;
  76. // Pascal expression to return the parameter value as a string
  77. Property AsStringValue : String Read GetAsStringValue;
  78. // Pascal expression with default value for this parameter
  79. Property DefaultValue : String Read FDefaultValue Write FDefaultValue;
  80. end;
  81. TAPIServiceMethod = Class(TObject)
  82. private
  83. FBodyType: TAPITypeData;
  84. FMethodName: String;
  85. FOperation: TApiOperation;
  86. FResultCallbackType: String;
  87. FResultDataType: TAPITypeData;
  88. FService: TAPIService;
  89. FPath : TPathItem;
  90. FParams : TFPObjectList;
  91. function GetParam(aIndex : Integer): TAPIServiceMethodParam;
  92. function GetParamCount: Integer;
  93. function GetRequestBodyType: String;
  94. function GetResultCallbackType: String;
  95. function GetResultType(AIndex: TNameType): String;
  96. protected
  97. // override this if you want to subclass the parameter
  98. function CreateParam(const aType: TPascaltype; const aOriginalName, aName, aTypeName: String; aParam: TParameter ): TAPIServiceMethodParam; virtual;
  99. Procedure SortParams;
  100. Public
  101. Constructor create (aOperation : TApiOperation; aPath : TPathItem; aService : TAPIService; const aMethod : String);
  102. Destructor Destroy; override;
  103. // Add a parameter
  104. function AddParam(const aType: TPascalType; const aOriginalName, aName, aTypeName: String; aParam: TParameter
  105. ): TAPIServiceMethodParam;
  106. // Find parameter methods by name.
  107. Function ParamByName(aOriginalName : String) : TAPIServiceMethodParam;
  108. // Does this class have parameters with location 'path'
  109. function HasPathParam: Boolean;
  110. // Does this class have parameters with location 'query'
  111. Function HasQueryParam : Boolean;
  112. // Does this method have parameters with default values ?
  113. Function HasOptionalParams : Boolean;
  114. // Pascal type of request body. May be empty.
  115. Property RequestBodyType : String Read GetRequestBodyType; deprecated;
  116. // Pascal type of request body. May be empty.
  117. Property RequestBodyDataType : TAPITypeData Read FBodyType Write FBodyType;
  118. // Result data type. Can be nil
  119. Property ResultDataType : TAPITypeData Read FResultDataType Write FResultDataType;
  120. // Result type. Can be empty
  121. Property ResultType : String index ntInterface Read GetResultType;
  122. // Component result class type.
  123. Property ResultClassType : String index ntImplementation Read GetResultType;
  124. // Component result Dto type
  125. Property ResultDtoType : String index ntPascal Read GetResultType;
  126. // Callback type for result.
  127. Property ResultCallBackType : String Read GetResultCallbackType write FResultCallBackType;
  128. // OpenAPI Operation for this method.
  129. Property Operation : TApiOperation Read FOperation;
  130. // Pascal name for the method.
  131. Property MethodName : String Read FMethodName;
  132. // Service this method belongs to.
  133. Property Service : TAPIService Read FService;
  134. // OpenAPI Path of this method.
  135. Property Path : TPathItem Read FPath;
  136. // indexed access to parameters
  137. Property Param[aIndex : Integer] : TAPIServiceMethodParam Read GetParam;
  138. // Number of parameters.
  139. Property ParamCount: Integer Read GetParamCount;
  140. end;
  141. TAPIService = Class(TObject)
  142. private
  143. FMethods : TFPObjectList;
  144. FNeedsAuthentication: Boolean;
  145. FServiceInterfaceName: string;
  146. FServiceName: string;
  147. FServiceParentInterface: String;
  148. FServiceProxyImplementationClassName: String;
  149. FServiceUUID: string;
  150. function GetMethod(aIndex : Integer): TAPIServiceMethod;
  151. function GetMethodCount: Integer;
  152. function GetServiceImplementationClassName: String;
  153. function GetServiceInterfaceName: string;
  154. function GetServiceUUID: string;
  155. protected
  156. // Override this if you wish to create a descendant of TAPIserviceMethod
  157. function CreateMethod(aOperation: TAPIOperation; aPath: TPathItem; const aName: String): TAPIserviceMethod; virtual;
  158. // Sort the methods on their name
  159. procedure SortMethods;
  160. public
  161. constructor create(const aServiceName : string);
  162. destructor destroy; override;
  163. // Add a method to the list of methods.
  164. Function AddMethod(const aName : String; aOperation : TAPIOperation; aPath : TPathItem) : TAPIserviceMethod;
  165. // Does this service need authentication ?
  166. Property NeedsAuthentication : Boolean read FNeedsAuthentication Write FNeedsAuthentication;
  167. // Pascal name of the service
  168. Property ServiceName : string Read FServiceName;
  169. // Interface name of the service.
  170. Property ServiceInterfaceName : string Read GetServiceInterfaceName Write FServiceInterfaceName;
  171. // Parent interface for the service interface
  172. Property ServiceParentInterface : String Read FServiceParentInterface Write FServiceParentInterface;
  173. // Service interface UUID
  174. Property ServiceUUID : string Read GetServiceUUID Write FServiceUUID;
  175. // Service interface implementation Class Name
  176. Property ServiceProxyImplementationClassName : String Read GetServiceImplementationClassName Write FServiceProxyImplementationClassName;
  177. // Indexed access to methods.
  178. Property Methods[aIndex : Integer]: TAPIServiceMethod Read GetMethod;
  179. // Number of methods.
  180. Property MethodCount : Integer Read GetMethodCount;
  181. end;
  182. { TAPIData }
  183. TAPIData = Class(TSchemaData)
  184. Private
  185. FInterfaceArrayType: String;
  186. FServiceNamePrefix: String;
  187. FServiceNameSuffix: String;
  188. FServiceParentInterface: String;
  189. FServices : TFPObjectList;
  190. FServiceOperationMap : TFPStringHashTable;
  191. FAPI : TOpenAPI;
  192. FStreamContentTypes: TStrings;
  193. FVoidResultCallbackType: String;
  194. function AllowOperation(aKeyword: TPathItemOperationKeyword; aOperation: TAPIOperation): boolean;
  195. function GetAPITypeCount: Integer;
  196. function GetService(aIndex : Integer): TAPIService;
  197. function GetServiceCount: Integer;
  198. function GetTypeData(aIndex : Integer): TAPITypeData;
  199. function GetVoidResultCallbackType: String;
  200. procedure SetStreamContentTypes(const aValue: TStrings);
  201. Protected
  202. //
  203. // Type management
  204. //
  205. procedure FinishAutoCreatedType(aName: string; aType: TPascalTypeData; lElementTypeData: TPascalTypeData); override;
  206. // Called after a new type is created.
  207. procedure ConfigType(aType: TAPITypeData); virtual;
  208. // Find requested name type in API types, based on openAPI name.
  209. function RawToNameType(const aName: string; aNameType: TNameType): string; virtual;
  210. // Check whether a type needs to be serialized (i.e. is sent to REST service)
  211. Function NeedsSerialize(aData : TAPITypeData) : Boolean; virtual;
  212. // Check whether a type needs to be de-serialized (i.e. is received from the REST service)
  213. Function NeedsDeSerialize(aData : TAPITypeData) : Boolean; virtual;
  214. // Is the request body application/json or no request body
  215. function IsRequestBodyApplicationJSON(aOperation: TAPIOperation): Boolean; virtual;
  216. // Is the response content application/json or no response content
  217. function IsResponseContentApplicationJSON(aOperation: TAPIOperation): boolean; virtual;
  218. // is the response content streamable ?
  219. function IsResponseContentStreamable(aOperation: TAPIOperation): boolean; virtual;
  220. // Is the request body application/json or no request body
  221. function IsRequestBodyStreamable(aOperation: TAPIOperation): Boolean; virtual;
  222. // Find streaming schema pascal type data. Create if needed
  223. function GetStreamTypeData(const aContentType : String) : TAPITypeData;
  224. // Service/Method management
  225. //
  226. // Generate the name of the service, based on URL/Operation. Takes into account the mapping.
  227. function GenerateServiceName(const aUrl : String; const aPath: TPathItem; aOperation: TAPIOperation): String;virtual;
  228. // Create a new service definition. Override this if you want to subclass it.
  229. function CreateService(const aName: String): TAPIService; virtual;
  230. // Configure a service
  231. procedure ConfigService(const aService: TAPIService); virtual;
  232. // Generate the name of the method, based on URL/Operation. Takes into account the mapping.
  233. function GenerateServiceMethodName(const aUrl : String; const aPath: TPathItem; aOperation: TAPIOperation): String; virtual;
  234. // Return the request body type. The application/json content type is used.
  235. function GetMethodRequestBodyType(aMethod: TAPIServiceMethod): TAPITypeData; virtual;
  236. // Return the method result type
  237. function GetMethodResultTypeData(aMethod: TAPIServiceMethod): TAPITypeData; virtual;
  238. // Return the method result type
  239. function GetMethodResultType(aMethod: TAPIServiceMethod; aNameType: TNameType): String; virtual; deprecated;
  240. // Return the method result callback name
  241. function GenerateMethodResultCallBackName(aMethod: TAPIServiceMethod): String; virtual;
  242. // Configure the service method. Called after it is created.
  243. procedure ConfigureServiceMethod(aService: TAPIService; aMethod: TAPIServiceMethod); virtual;
  244. // Add a parameter to a method.
  245. function AddServiceMethodParam(aService: TAPIservice; aMethod: TAPIServiceMethod; Idx: Integer; aParam: TParameterOrReference ): TAPIServiceMethodParam; virtual;
  246. // Check the input of various operations of a OpenAPI path item. Used in determining the need for serialization
  247. function CheckOperationsInput(aPath: TPathItem; aData: TAPITypeData): Boolean;
  248. // Check the output of various operations of a OpenAPI path item. Used in determining the need for deserialization
  249. function CheckOperationsOutput(aPath: TPathItem; aData: TAPITypeData): Boolean;
  250. // Check input/output for serialization
  251. procedure CheckInputOutput(aIncludeServer : Boolean);
  252. Public
  253. // Create. API must be alive while the data is kept alive.
  254. constructor Create(aAPI : TOpenAPI); reintroduce;
  255. // Destroy
  256. destructor Destroy; override;
  257. // Add a service
  258. function AddService(const aName: String) : TAPIService;
  259. // Create default type maps (integer,string etc.)
  260. Procedure CreateDefaultTypeMaps; virtual;
  261. // Create default API type maps (#components in openapi)
  262. Procedure CreateDefaultAPITypeMaps(aIncludeServer : Boolean);
  263. // Create service defs from paths. Call RecordMethodNameMap first)
  264. Procedure CreateServiceDefs; virtual;
  265. // Get schema element typename of aRef. For components, return requested name
  266. function GetRefSchemaTypeName(const aRef: String; aNameType : TNameType): string; virtual;
  267. // Create a new API type. You can override this to return a descendent class
  268. function CreatePascalType(aIndex: integer; aPascalType : TPascaltype; const aAPIName, aPascalName: String; aSchema: TJSONSchema): TAPITypeData; override;
  269. // Is the schema an API component ?
  270. Function IsAPIComponent(aSchema : TJSONSchema) : Boolean;
  271. // Is the schema an array of API components ?
  272. Function IsAPIComponentArray(aSchema : TJSONSchema) : Boolean;
  273. // Get the specified type name of a schema
  274. function GetSchemaTypeName(aSchema : TJSONSchema; aNameType : TNameType) : String;
  275. // Apply the UUIDs to the API types. Call after CreateDefaultAPITypeMaps
  276. procedure ApplyUUIDMap(aMap: TStrings);
  277. {
  278. Store the service/maps for operations. Call before CreateServiceDefs
  279. an entry is either
  280. operationID=ServiceName[.MethodName]
  281. or
  282. HTTPMethod.Path=ServiceName[.MethodName]
  283. }
  284. procedure RecordMethodNameMap(aMap: TStrings);
  285. // Index of service with given ServiceName. Return -1 if not found.
  286. function IndexOfService(const aName : String) : Integer;
  287. // Find service with given ServiceName. Can return Nil
  288. function FindService(const aName : String) : TAPIService;
  289. // Find service with given ServiceName. Raise exception if not found
  290. function GetServiceByName(const aName : String) : TAPIService;
  291. // Indexed access to services
  292. Property Services[aIndex :Integer] : TAPIService Read GetService;
  293. // Number of generated services
  294. Property ServiceCount : Integer Read GetServiceCount;
  295. // Return index of named API type (name as in OpenApi). Return -1 if not found.
  296. function IndexOfAPIType(const aName: String): integer;
  297. // Find API type by name (name as known in OpenApi). Return nil if not found.
  298. function FindApiType(const aName: String): TAPITypeData;
  299. // Find API type by name (name as known in OpenApi). Raise exception if not found.
  300. function GetAPIType(const aName: String): TAPITypeData;
  301. // #components Types by name
  302. Property ApiNamedTypes[aName : String] : TAPITypeData Read GetApiType;
  303. // #components Types by index
  304. Property APITypes[aIndex : Integer] : TAPITypeData Read GetTypeData;
  305. // #components Type count
  306. Property APITypeCount : Integer Read GetAPITypeCount;
  307. // The api we generate code for.
  308. Property API : TOpenAPI Read FAPI;
  309. // Prefix to use when generating service names (will still have I prepended for interface definition)
  310. Property ServiceNamePrefix : String Read FServiceNamePrefix Write FServiceNamePrefix;
  311. // Suffix to use when generating service names (will still have I prepended for interface definition)
  312. // By default, this is 'Service'
  313. Property ServiceNameSuffix : String Read FServiceNameSuffix Write FServiceNameSuffix;
  314. // Parent interface for services. Applied to all services
  315. Property ServiceParentInterface : String Read FServiceParentInterface Write FServiceParentInterface;
  316. // Void result type callback name
  317. Property VoidResultCallbackType : String Read GetVoidResultCallbackType Write FVoidResultCallbackType;
  318. // Name of generic Interface that implements an array
  319. Property InterfaceArrayType : String Read FInterfaceArrayType Write FInterfaceArrayType;
  320. // Stream content types: these content types will be streamed.
  321. Property StreamContentTypes : TStrings Read FStreamContentTypes Write SetStreamContentTypes;
  322. end;
  323. implementation
  324. {$IFDEF FPC_DOTTEDUNITS}
  325. uses System.StrUtils;
  326. {$ELSE}
  327. uses strutils;
  328. {$ENDIF}
  329. function PrettyPrint(S : String) : String;
  330. begin
  331. Result:=S;
  332. if Length(Result)>0 then
  333. Result[1]:=UpCase(Result[1]);
  334. end;
  335. { TAPITypeData }
  336. function CompareServiceName(Item1, Item2: Pointer): Integer;
  337. var
  338. lService1 : TAPIService absolute Item1;
  339. lService2 : TAPIService absolute Item2;
  340. begin
  341. Result:=CompareText(lService1.ServiceName,lService2.ServiceName);
  342. end;
  343. function CompareMethodName(Item1, Item2: Pointer): Integer;
  344. var
  345. lMethod1 : TAPIServiceMethod absolute Item1;
  346. lMethod2 : TAPIServiceMethod absolute Item2;
  347. begin
  348. Result:=CompareText(lMethod1.MethodName,lMethod2.MethodName);
  349. end;
  350. function CompareParamName(Item1, Item2: Pointer): Integer;
  351. var
  352. lParam1 : TAPIServiceMethodParam absolute Item1;
  353. lParam2 : TAPIServiceMethodParam absolute Item2;
  354. begin
  355. Result:=CompareText(lParam1.Name,lParam2.Name);
  356. end;
  357. { TAPIServiceMethodParam }
  358. function TAPIServiceMethodParam.Getlocation: TParamLocation;
  359. begin
  360. if FParameter.In_='query' then
  361. Result:=plQuery
  362. else
  363. Result:=plPath;
  364. end;
  365. function TAPIServiceMethodParam.GetAsStringValue: String;
  366. var
  367. lType : TSchemaSimpleType;
  368. begin
  369. lType:=TAPITypeData.ExtractFirstType(FParameter.Schema);
  370. case lType of
  371. sstInteger : Result:=Format('IntToStr(%s)',[Name]); // Also handles int64
  372. sstString : Result:=Name;
  373. sstNumber : Result:=Format('FloatToStr(%s,cRestFormatSettings)',[Name]);
  374. sstBoolean : Result:=Format('cRESTBooleans[%s]',[Name]);
  375. else
  376. Result:=Name;
  377. end;
  378. end;
  379. constructor TAPIServiceMethodParam.create(aArgType: TPropertyType; aOriginalName: String; aName: String; aTypeName: String;
  380. aParam: TParameter);
  381. begin
  382. FArgType:=aArgType;
  383. FOriginalName:=aOriginalName;
  384. FName:=aName;
  385. FTypeName:=aTypeName;
  386. FParameter:=aParam;
  387. end;
  388. { TAPIService }
  389. function TAPIService.GetMethod(aIndex : Integer): TAPIServiceMethod;
  390. begin
  391. Result:=TAPIServiceMethod(FMethods[aIndex]);
  392. end;
  393. function TAPIService.GetMethodCount: Integer;
  394. begin
  395. Result:=FMethods.Count;
  396. end;
  397. function TAPIService.GetServiceImplementationClassName: String;
  398. begin
  399. Result:=FServiceProxyImplementationClassName;
  400. if Result='' then
  401. Result:='T'+ServiceName+'Proxy';
  402. end;
  403. function TAPIService.GetServiceInterfaceName: string;
  404. begin
  405. Result:=FServiceInterfaceName;
  406. if Result='' then
  407. Result:='I'+ServiceName;
  408. end;
  409. function TAPIService.GetServiceUUID: string;
  410. begin
  411. if FServiceUUID='' then
  412. FServiceUUID:=TGUID.NewGuid.ToString(False);
  413. Result:=FServiceUUID;
  414. end;
  415. function TAPIService.CreateMethod(aOperation: TAPIOperation; aPath: TPathItem; const aName: String): TAPIserviceMethod;
  416. begin
  417. Result:=TAPIserviceMethod.Create(aOperation,aPath,Self,aName);
  418. end;
  419. procedure TAPIService.SortMethods;
  420. begin
  421. FMethods.Sort(@CompareMethodName);
  422. end;
  423. constructor TAPIService.create(const aServiceName: string);
  424. begin
  425. FMethods:=TFPObjectList.Create(True);
  426. FServiceName:=aServiceName;
  427. FNeedsAuthentication:=True;
  428. end;
  429. destructor TAPIService.destroy;
  430. begin
  431. FreeAndNil(FMethods);
  432. inherited destroy;
  433. end;
  434. function TAPIService.AddMethod(const aName: String; aOperation: TAPIOperation; aPath : TPathItem): TAPIserviceMethod;
  435. begin
  436. Result:=CreateMethod(aOperation,aPath,aName);
  437. FMethods.Add(Result);
  438. end;
  439. { TAPIServiceMethod }
  440. function TAPIServiceMethod.GetParam(aIndex : Integer): TAPIServiceMethodParam;
  441. begin
  442. Result:=TAPIServiceMethodParam(FParams[aIndex]);
  443. end;
  444. function TAPIServiceMethod.GetParamCount: Integer;
  445. begin
  446. Result:=FParams.Count;
  447. end;
  448. function TAPIServiceMethod.GetRequestBodyType: String;
  449. begin
  450. if assigned(FBodyType) then
  451. Result:=FBodyType.GetTypeName(ntPascal)
  452. else
  453. Result:='';
  454. end;
  455. function TAPIServiceMethod.GetResultCallbackType: String;
  456. begin
  457. Result:=FResultCallbackType;
  458. if Result='' then
  459. begin
  460. Result:=ResultType;
  461. if Result='' then
  462. Result:='VoidResult';
  463. Result:='T'+Result+'CallBack';
  464. end;
  465. end;
  466. function TAPIServiceMethod.GetResultType(AIndex: TNameType): String;
  467. begin
  468. if assigned(FResultDataType) then
  469. Result:=FResultDataType.GetTypeName(aIndex)
  470. else
  471. Result:='';
  472. end;
  473. procedure TAPIServiceMethod.SortParams;
  474. begin
  475. FParams.Sort(@CompareParamName);
  476. end;
  477. constructor TAPIServiceMethod.create(aOperation: TApiOperation;
  478. aPath: TPathItem; aService: TAPIService; const aMethod: String);
  479. begin
  480. FOperation:=aOperation;
  481. FService:=aService;
  482. FMethodName:=aMethod;
  483. FPath:=aPath;
  484. FParams:=TFPObjectList.Create(True);
  485. end;
  486. destructor TAPIServiceMethod.Destroy;
  487. begin
  488. FreeAndNil(FParams);
  489. inherited Destroy;
  490. end;
  491. function TAPIServiceMethod.CreateParam(const aType : TPascaltype; const aOriginalName,aName, aTypeName: String; aParam: TParameter): TAPIServiceMethodParam;
  492. begin
  493. Result:=TAPIServiceMethodParam.Create(aType,aOriginalName,aName,aTypeName,aParam);
  494. end;
  495. function TAPIServiceMethod.AddParam(const aType : TPascalType; const aOriginalName,aName, aTypeName: String; aParam: TParameter): TAPIServiceMethodParam;
  496. begin
  497. Result:=CreateParam(aType,aOriginalName,aName,aTypeName,aParam);
  498. FParams.Add(Result);
  499. end;
  500. function TAPIServiceMethod.ParamByName(aOriginalName: String): TAPIServiceMethodParam;
  501. var
  502. Idx : Integer;
  503. begin
  504. Idx:=ParamCount-1;
  505. While (Idx>=0) and Not SameText(Param[Idx].OriginalName,aOriginalName) do
  506. Dec(Idx);
  507. if Idx=-1 then
  508. Result:=Nil
  509. else
  510. Result:=Param[Idx];
  511. end;
  512. function TAPIServiceMethod.HasPathParam: Boolean;
  513. var
  514. I : Integer;
  515. begin
  516. Result:=False;
  517. For I:=0 to ParamCount-1 do
  518. if Param[i].Location=plPath then
  519. Exit(True);
  520. end;
  521. function TAPIServiceMethod.HasQueryParam: Boolean;
  522. var
  523. I : Integer;
  524. begin
  525. Result:=False;
  526. For I:=0 to ParamCount-1 do
  527. if Param[i].Location=plQuery then
  528. Exit(True);
  529. end;
  530. function TAPIServiceMethod.HasOptionalParams: Boolean;
  531. var
  532. I : Integer;
  533. begin
  534. Result:=False;
  535. For I:=0 to ParamCount-1 do
  536. if (Param[i].DefaultValue<>'') then
  537. Exit(True);
  538. end;
  539. { TAPIData }
  540. function TAPIData.GetTypeData(aIndex : Integer): TAPITypeData;
  541. begin
  542. Result:=TAPITypeData(Inherited Types[aIndex]);
  543. end;
  544. function TAPIData.GetVoidResultCallbackType: String;
  545. begin
  546. Result:=FVoidResultCallbackType;
  547. if Result='' then
  548. Result:='TVoidResultCallBack';
  549. end;
  550. procedure TAPIData.SetStreamContentTypes(const aValue: TStrings);
  551. begin
  552. if FStreamContentTypes=aValue then Exit;
  553. FStreamContentTypes.Assign(aValue);
  554. end;
  555. function TAPIData.CreatePascalType(aIndex: integer; aPascalType: TPascaltype; const aAPIName, aPascalName: String;
  556. aSchema: TJSONSchema): TAPITypeData;
  557. begin
  558. Result:=TAPITypeData.Create(aIndex,aPascalType,aAPIName,aPascalName,aSchema);
  559. end;
  560. function TAPIData.GetAPITypeCount: Integer;
  561. begin
  562. Result:=TypeCount;
  563. end;
  564. function TAPIData.GetService(aIndex : Integer): TAPIService;
  565. begin
  566. Result:=TAPIService(FServices[aIndex]);
  567. end;
  568. function TAPIData.GetServiceCount: Integer;
  569. begin
  570. Result:=FServices.Count;
  571. end;
  572. function TAPIData.IndexOfAPIType(const aName : String): integer;
  573. begin
  574. Result:=IndexOfSchemaType(aName);
  575. end;
  576. function TAPIData.FindApiType(const aName: String): TAPITypeData;
  577. begin
  578. Result:=FindSchemaTypeData(aName) as TAPITypeData;
  579. end;
  580. function TAPIData.GetAPIType(const aName : String): TAPITypeData;
  581. begin
  582. Result:=FindAPIType(aName);
  583. if Result=Nil then
  584. Raise EListError.CreateFmt('Unknown type: %s',[aName]);
  585. end;
  586. function TAPIData.CreateService(const aName: String): TAPIService;
  587. begin
  588. Result:=TAPIService.Create(aName);
  589. end;
  590. procedure TAPIData.ConfigService(const aService : TAPIService);
  591. begin
  592. aService.ServiceParentInterface:=Self.ServiceParentInterface;
  593. end;
  594. function TAPIData.AddService(const aName: String): TAPIService;
  595. begin
  596. Result:=CreateService(aName);
  597. FServices.Add(Result);
  598. end;
  599. constructor TAPIData.Create(aAPI: TOpenAPI);
  600. begin
  601. Inherited Create;
  602. FStreamContentTypes:=TStringList.Create;
  603. FStreamContentTypes.Add('application/octet-stream');
  604. FStreamContentTypes.Add('*/*');
  605. FServices:=TFPObjectList.Create(True);
  606. FServiceOperationMap:=TFPStringHashTable.Create;
  607. FAPI:=aAPI;
  608. FServiceNameSuffix:='Service';
  609. FServiceNamePrefix:='';
  610. end;
  611. destructor TAPIData.Destroy;
  612. begin
  613. FreeAndNil(FStreamContentTypes);
  614. FreeAndNil(FServices);
  615. FreeAndNil(FServiceOperationMap);
  616. inherited Destroy;
  617. end;
  618. procedure TAPIData.CreateDefaultTypeMaps;
  619. begin
  620. DefineStandardPascalTypes;
  621. end;
  622. procedure TAPIData.ConfigType(aType :TAPITypeData);
  623. begin
  624. if aType.Pascaltype in [ptAnonStruct,ptSchemaStruct] then
  625. begin
  626. aType.InterfaceName:=EscapeKeyWord(Sanitize(InterfaceTypePrefix+aType.SchemaName));
  627. aType.InterfaceUUID:=TGUID.NewGUID.ToString(False);
  628. end;
  629. end;
  630. procedure TAPIData.ApplyUUIDMap(aMap : TStrings);
  631. var
  632. I : Integer;
  633. N,V : String;
  634. lData : TAPITypeData;
  635. lService : TAPIService;
  636. begin
  637. if aMap.Count=0 then exit;
  638. For I:=0 to aMap.Count-1 do
  639. begin
  640. aMap.GetNameValue(I,N,V);
  641. lData:=FindApiType(N);
  642. if assigned(lData) then
  643. lData.InterfaceUUID:=V
  644. else
  645. begin
  646. lService:=FindService(N);
  647. if assigned(lService) then
  648. lService.ServiceUUID:=V;
  649. end;
  650. end;
  651. end;
  652. procedure TAPIData.RecordMethodNameMap(aMap: TStrings);
  653. var
  654. I : Integer;
  655. N,V : String;
  656. begin
  657. if aMap.Count=0 then exit;
  658. For I:=0 to aMap.Count-1 do
  659. begin
  660. aMap.GetNameValue(I,N,V);
  661. if (N<>'') and (V<>'') then
  662. FServiceOperationMap.Add(N,V);
  663. end;
  664. end;
  665. function TAPIData.IndexOfService(const aName: String): Integer;
  666. begin
  667. Result:=FServices.Count-1;
  668. While (Result>=0) and not SameText(aName,GetService(Result).ServiceName) do
  669. Dec(Result);
  670. end;
  671. function TAPIData.FindService(const aName: String): TAPIService;
  672. var
  673. Idx : Integer;
  674. begin
  675. Result:=nil;
  676. Idx:=IndexOfService(aName);
  677. if Idx>=0 then
  678. Result:=GetService(Idx);
  679. end;
  680. function TAPIData.GetServiceByName(const aName: String): TAPIService;
  681. begin
  682. Result:=FindService(aName);
  683. if Result=Nil then
  684. Raise EGenAPI.CreateFmt('Unknown service: %s',[aName]);
  685. end;
  686. procedure TAPIData.CheckInputOutput(aIncludeServer: Boolean);
  687. var
  688. I: Integer;
  689. lData : TAPITypeData;
  690. lSerTypes : TSerializeTypes;
  691. begin
  692. for I:=0 to TypeCount-1 do
  693. begin
  694. lSerTypes:=[];
  695. lData:=APITypes[i];
  696. if aIncludeServer then
  697. begin
  698. if NeedsSerialize(lData) or NeedsDeserialize(lData) then
  699. lSerTypes:=[stSerialize,stDeSerialize]
  700. end
  701. else
  702. begin
  703. if NeedsSerialize(lData) then
  704. Include(lSerTypes,stSerialize);
  705. if NeedsDeserialize(lData) then
  706. Include(lSerTypes,stDeSerialize);
  707. end;
  708. lData.SerializeTypes:=lSerTypes;
  709. DoLog(etInfo,'%s needs serialize: %s, deserialize: %s',[lData.SchemaName,BoolToStr(stSerialize in lSerTypes,True),BoolToStr(stDeSerialize in lSerTypes,True)]);
  710. end;
  711. end;
  712. procedure TAPIData.CreateDefaultAPITypeMaps(aIncludeServer : Boolean);
  713. Procedure AddProperties(aType : TAPITypeData);
  714. var
  715. I : Integer;
  716. begin
  717. for I:=0 to aType.Schema.Properties.Count-1 do
  718. AddTypeProperty(aType,aType.Schema.Properties[i]);
  719. aType.SortProperties;
  720. end;
  721. var
  722. I : Integer;
  723. lName,lTypeName : String;
  724. lSchema : TJsonSchema;
  725. lData : TAPITypeData;
  726. lType : TSchemaSimpleType;
  727. begin
  728. For I:=0 to FAPI.Components.Schemas.Count-1 Do
  729. begin
  730. lName:=FAPI.Components.Schemas.Names[I];
  731. lSchema:=FAPI.Components.Schemas.Schemas[lName];
  732. lType:=lSchema.Validations.GetFirstType;
  733. if (lType in [sstObject,sstString]) then
  734. begin
  735. lTypeName:=EscapeKeyWord(ObjectTypePrefix+Sanitize(lName)+ObjectTypeSuffix);
  736. case lType of
  737. sstObject : lData:=CreatePascalType(I,ptSchemaStruct,lName,lTypeName,lSchema);
  738. sstString :
  739. begin
  740. lData:=CreatePascalType(I,ptString,lName,lTypeName,lSchema);
  741. end;
  742. end;
  743. ConfigType(lData);
  744. AddType(lName,lData);
  745. AddToTypeMap(lName,lData);
  746. end;
  747. end;
  748. // We do this here, so all API type references can be resolved
  749. For I:=0 to APITypeCount-1 do
  750. AddProperties(APITypes[i]);
  751. // Finally, sort
  752. CheckDependencies;
  753. SortTypes;
  754. CheckInputOutput(aIncludeServer);
  755. end;
  756. function TAPIData.GenerateServiceName(const aUrl: String; const aPath: TPathItem;
  757. aOperation: TAPIOperation): String;
  758. function CleanIdentifier(S : String) : String;
  759. begin
  760. Result:=StringReplace(S,' ','',[rfReplaceAll]);
  761. Result:=Sanitize(Result);
  762. end;
  763. {
  764. the maps contain ServiceName[.MethodName]
  765. We use ServiceName if there is an entry in the map.
  766. if there is no entry in the map and there is 1 tag, we take the name of the tag.
  767. if there is no tag, we take the first component of the URL path.
  768. }
  769. var
  770. S,lTag,lFullName : String;
  771. lStrings : TStringDynArray;
  772. begin
  773. Result:='';
  774. // We need to take into account the full map first, as is done for the method.
  775. if aOperation.OperationID<>'' then
  776. lFullName:=FServiceOperationMap.Items[aOperation.OperationID]
  777. else
  778. lFullName:=FServiceOperationMap.Items[aOperation.PathComponent+'.'+aURL];
  779. if (lFullName='') and (aOperation.Tags.Count=1) then
  780. lFullName:=ServiceNamePrefix+CleanIdentifier(aOperation.Tags[0])+ServiceNameSuffix;
  781. if (lFullName='') then
  782. begin
  783. lStrings:=SplitString(aURL,'/');
  784. // First non-empty
  785. For S in lStrings do
  786. if (Result='') and (S<>'') then
  787. Result:=CleanIdentifier(S);
  788. Result:=ServiceNamePrefix+PrettyPrint(Result)+ServiceNameSuffix;
  789. end
  790. else
  791. begin
  792. lStrings:=SplitString(lFullName,'.');
  793. Result:=LStrings[0];
  794. end;
  795. if (aOperation.OperationID<>'') and (lFullName='') then
  796. begin
  797. S:=aOperation.OperationID;
  798. DoLog(etWarning,'No mapping for %s: (Tag= "%s"), Generated: %s=%s.%s',[S,lTag,S,Result,S]);
  799. end;
  800. end;
  801. function TAPIData.GenerateServiceMethodName(const aUrl: String;
  802. const aPath: TPathItem; aOperation: TAPIOperation): String;
  803. (*
  804. the maps contain ServiceName[.MethodName]
  805. 1. if there is a method name in either map: we use that.
  806. 2. if there is no method name in either map:
  807. a. if there is an operation ID we use it as the method name.
  808. b. if there is no operation ID, we use the operation HTTP method together with the url except the first path component.
  809. Parameters are reduced to their names.
  810. get /users/contacts/{Id} -> servicename "users" method "get_contacts_Id"
  811. *)
  812. var
  813. S,lFullName : String;
  814. lStrings : TStringDynArray;
  815. I,J : Integer;
  816. begin
  817. Result:='';
  818. if aOperation.OperationID<>'' then
  819. lFullName:=FServiceOperationMap.Items[aOperation.OperationID]
  820. else
  821. lFullName:=FServiceOperationMap.Items[aOperation.PathComponent+'.'+aURL];
  822. if lFullName='' then
  823. begin
  824. Result:=aOperation.OperationID;
  825. if Result='' then
  826. begin
  827. lStrings:=SplitString(aURL,'/');
  828. Result:=aOperation.PathComponent;
  829. for I:=1 to Length(lStrings)-1 do
  830. begin
  831. S:=lStrings[i];
  832. S:=StringReplace(S,'{','',[rfReplaceAll]);
  833. S:=StringReplace(S,'}','',[rfReplaceAll]);
  834. S:=Sanitize(S);
  835. Result:=Result+'_'+S;
  836. end;
  837. end;
  838. end
  839. else
  840. begin
  841. lStrings:=SplitString(lFullName,'.');
  842. Result:=LStrings[1];
  843. end;
  844. Result:=PrettyPrint(Result);
  845. end;
  846. function TAPIData.AddServiceMethodParam(aService: TAPIservice; aMethod : TAPIServiceMethod; Idx : Integer; aParam : TParameterOrReference) : TAPIServiceMethodParam;
  847. var
  848. lOriginalName,lName,lTypeName : string;
  849. lTypeData : TPascaltypeData;
  850. lType : TPascalType;
  851. begin
  852. if aParam.HasReference then
  853. begin
  854. lTypeName:=GetRefSchemaTypeName(aParam.Reference.Ref,ntPascal);
  855. lName:='';
  856. lOriginalName:='';
  857. lType:=ptSchemaStruct;
  858. end
  859. else
  860. begin
  861. lTypeData:=GetSchemaTypeData(Nil,aParam.Schema,False);
  862. lTypeName:=lTypeData.GetTypeName(ntPascal);
  863. lType:=lTypeData.Pascaltype;
  864. lOriginalName:=aParam.Name;
  865. lName:='a'+PrettyPrint(lOriginalName);
  866. end;
  867. if lName='' then
  868. lName:=Format('aParam%d',[Idx]);
  869. Result:=aMethod.AddParam(lType,lOriginalName,lName,lTypeName,aParam);
  870. if aParam.Schema.MetaData.HasKeywordData(jskDefault) then
  871. begin
  872. Result.DefaultValue:=aParam.Schema.MetaData.DefaultValue.AsString;
  873. if lType=ptString then
  874. Result.DefaultValue:=''''+StringReplace(Result.DefaultValue,'''','''''',[rfReplaceAll])+'''';
  875. end;
  876. end;
  877. function TAPIData.IsResponseContentApplicationJSON(aOperation : TAPIOperation) : boolean;
  878. var
  879. lResponse : TResponse;
  880. lMedia : TMediaType;
  881. begin
  882. if aOperation.Responses.Count=0 then
  883. Result:=True
  884. else
  885. begin
  886. lResponse:=aOperation.Responses.ResponseByindex[0];
  887. lMedia:=lResponse.Content.MediaTypes['application/json'];
  888. Result:=lMedia<>nil;
  889. end;
  890. end;
  891. function TAPIData.GenerateMethodResultCallBackName(aMethod : TAPIServiceMethod) : String;
  892. var
  893. lResponse: TResponse;
  894. lMedia : TMediaType;
  895. begin
  896. if AMethod.Operation.Responses.Count=0 then
  897. Result:=VoidResultCallbackType
  898. else
  899. begin
  900. lResponse:=AMethod.Operation.Responses.ResponseByindex[0];
  901. lMedia:=lResponse.Content.MediaTypes['application/json'];
  902. if (lMedia.Schema.Ref<>'') then
  903. Result:=Format('%sResultCallBack',[GetRefSchemaTypeName(lMedia.Schema.Ref,ntPascal)])
  904. else if (lMedia.Schema.Validations.Types=[]) then
  905. Result:=VoidResultCallbackType
  906. else
  907. Result:=GetSchemaTypeName(lMedia.Schema,ntPascal);
  908. end;
  909. end;
  910. function TAPIData.GetMethodResultTypeData(aMethod: TAPIServiceMethod): TAPITypeData;
  911. var
  912. lResponse: TResponse;
  913. lMedia : TMediaType;
  914. S : String;
  915. begin
  916. if AMethod.Operation.Responses.Count>0 then
  917. begin
  918. lResponse:=AMethod.Operation.Responses.ResponseByindex[0];
  919. lMedia:=lResponse.Content.MediaTypes['application/json'];
  920. if lMedia=Nil then
  921. begin
  922. // Check if we must stream
  923. For S in StreamContentTypes do
  924. begin
  925. lMedia:=lResponse.Content.MediaTypes[S];
  926. if lMedia<>nil then
  927. break;
  928. end;
  929. if lMedia=nil then
  930. Raise EGenAPI.CreateFmt('No application/json response media type for %s.%s',[aMethod.Service.ServiceName,aMethod.MethodName]);
  931. Result:=GetStreamTypeData(S);
  932. end
  933. else
  934. Result:=GetSchemaTypeData(Nil,lMedia.Schema,True) as TAPITypeData;
  935. end
  936. else
  937. Result:=Nil; // FindApiType('boolean');
  938. end;
  939. function TAPIData.GetMethodResultType(aMethod : TAPIServiceMethod; aNameType : TNameType) : String;
  940. var
  941. lData : TAPITypeData;
  942. begin
  943. lData:=aMethod.ResultDataType;
  944. if not assigned(lData) then
  945. Raise EGenAPI.CreateFmt('No result type %s.%s',[aMethod.Service.ServiceName,aMethod.MethodName]);
  946. Result:=lData.GetTypeName(aNameType);
  947. end;
  948. function TAPIData.IsRequestBodyApplicationJSON(aOperation : TAPIOperation) : Boolean;
  949. var
  950. lMedia : TMediaType;
  951. begin
  952. Result:=False;
  953. if Not aOperation.HasKeyWord(okRequestBody) then
  954. exit(True);
  955. if aOperation.RequestBody.HasReference then
  956. // We have a definition
  957. Result:=GetRefSchemaTypeName(aOperation.RequestBody.Reference.Ref,ntPascal)<>''
  958. else
  959. begin
  960. lMedia:=aOperation.RequestBody.Content['application/json'];
  961. Result:=lMedia<>Nil;
  962. end;
  963. end;
  964. function TAPIData.GetMethodRequestBodyType(aMethod: TAPIServiceMethod): TAPITypeData;
  965. var
  966. lMedia : TMediaType;
  967. i : Integer;
  968. S : String;
  969. begin
  970. Result:=Nil;
  971. if Not aMethod.Operation.HasKeyWord(okRequestBody) then
  972. exit;
  973. if aMethod.Operation.RequestBody.HasReference then
  974. Result:=TAPITypeData(GetPascalTypeDataFromRef(aMethod.Operation.RequestBody.Reference.Ref))
  975. else
  976. begin
  977. lMedia:=aMethod.Operation.RequestBody.Content['application/json'];
  978. if lMedia<>Nil then
  979. Result:=TAPITypeData(GetSchemaTypeData(Nil,lMedia.Schema,True))
  980. else
  981. begin
  982. I:=0;
  983. While (lMedia=Nil) and (I<StreamContentTypes.Count) do
  984. begin
  985. S:=StreamContentTypes[I];
  986. lMedia:=aMethod.Operation.RequestBody.Content[S];
  987. end;
  988. Result:=TAPITypeData.CreateBinaryData(S);
  989. end;
  990. end;
  991. if Result=Nil then
  992. with aMethod do
  993. Raise EGenAPI.CreateFmt('Unknown result type for method %s.%s: %s',[Service.ServiceName,MethodName,Operation.RequestBody.Reference.Ref]);
  994. end;
  995. procedure TAPIData.ConfigureServiceMethod(aService : TAPIService; aMethod : TAPIServiceMethod);
  996. begin
  997. aMethod.ResultDataType:=GetMethodResultTypeData(aMethod);
  998. aMethod.RequestBodyDataType:=GetMethodRequestBodyType(aMethod);
  999. end;
  1000. function TAPIData.IsResponseContentStreamable(aOperation : TAPIOperation) : boolean;
  1001. var
  1002. i : Integer;
  1003. lResponse : TResponse;
  1004. lMedia : TMediaType;
  1005. begin
  1006. if aOperation.Responses.Count=0 then
  1007. Result:=False
  1008. else
  1009. begin
  1010. lResponse:=aOperation.Responses.ResponseByindex[0];
  1011. I:=0;
  1012. lMedia:=Nil;
  1013. While (lMedia=Nil) and (I<StreamContentTypes.Count) do
  1014. begin
  1015. lMedia:=lResponse.Content.MediaTypes[StreamContentTypes[i]];
  1016. inc(i);
  1017. end;
  1018. Result:=lMedia<>nil;
  1019. end;
  1020. end;
  1021. function TAPIData.IsRequestBodyStreamable(aOperation: TAPIOperation): Boolean;
  1022. var
  1023. lMedia : TMediaType;
  1024. I : Integer;
  1025. begin
  1026. Result:=False;
  1027. if Not aOperation.HasKeyWord(okRequestBody) then
  1028. exit(True);
  1029. if aOperation.RequestBody.HasReference then
  1030. // We have a definition
  1031. Result:=GetRefSchemaTypeName(aOperation.RequestBody.Reference.Ref,ntPascal)<>''
  1032. else
  1033. begin
  1034. lMedia:=Nil;
  1035. I:=0;
  1036. While (I<StreamContentTypes.Count) and (lMedia=Nil) do
  1037. lMedia:=aOperation.RequestBody.Content[StreamContentTypes[i]];
  1038. Result:=lMedia<>Nil;
  1039. end;
  1040. end;
  1041. function TAPIData.GetStreamTypeData(const aContentType: String): TAPITypeData;
  1042. var
  1043. I : Integer;
  1044. S : String;
  1045. begin
  1046. I:=0;
  1047. Result:=Nil;
  1048. While (Result=Nil) and (I<APITypeCount) do
  1049. begin
  1050. Result:=APITypes[i];
  1051. if not (Result.BinaryData and (Result.ContentType=aContentType)) then
  1052. Result:=Nil;
  1053. Inc(I);
  1054. end;
  1055. if Result=Nil then
  1056. begin
  1057. Result:=TAPITypeData.CreateBinaryData(aContentType);
  1058. S:=ObjectTypePrefix+Sanitize(aContentType)+'StreamData';
  1059. AddType(S,Result);
  1060. end;
  1061. end;
  1062. function TAPIData.AllowOperation(aKeyword: TPathItemOperationKeyword; aOperation: TAPIOperation): boolean;
  1063. begin
  1064. Result:=True;
  1065. Result:=IsResponseContentApplicationJSON(aOperation)
  1066. or IsResponseContentStreamable(aOperation);
  1067. if (aKeyword in [pkPost,pkPut,pkPatch]) then
  1068. Result:=IsRequestBodyApplicationJSON(aOperation)
  1069. or IsRequestBodyStreamable(aOperation);
  1070. end;
  1071. procedure TAPIData.CreateServiceDefs;
  1072. var
  1073. I,J : Integer;
  1074. lPath: TPathItem;
  1075. lURL : String;
  1076. lOperation : TAPIOperation;
  1077. lKeyword : TPathItemOperationKeyword;
  1078. lServiceName,lMethodName : String;
  1079. lService : TAPIService;
  1080. lMethod : TAPIServiceMethod;
  1081. lMap : String;
  1082. begin
  1083. for I:=0 to FAPI.Paths.Count-1 do
  1084. begin
  1085. lPath:=FAPI.Paths.PathByIndex[I];
  1086. lURL:=FAPI.Paths.Names[I];
  1087. for lKeyword in TPathItemOperationKeyword do
  1088. begin
  1089. lOperation:=lPath.GetOperation(lKeyword);
  1090. if assigned(lOperation) and AllowOperation(lKeyword,lOperation) then
  1091. begin
  1092. lServiceName:=GenerateServiceName(lUrl,lPath,lOperation);
  1093. lService:=FindService(lServiceName);
  1094. if lService=Nil then
  1095. begin
  1096. lService:=AddService(lServiceName);
  1097. ConfigService(lService);
  1098. end;
  1099. lMethodName:=GenerateServiceMethodName(lUrl,lPath,lOperation);
  1100. lMethod:=lService.AddMethod(lMethodName,lOperation,lPath);
  1101. ConfigureServiceMethod(lService,lMethod);
  1102. if lOperation.HasKeyWord(okParameters) then
  1103. for J:=0 to lOperation.Parameters.Count-1 do
  1104. AddServiceMethodParam(lService,lMethod,j,lOperation.Parameters[j]);
  1105. if lOperation.OperationId='' then
  1106. lMap:=lOperation.PathComponent+'.'+lPath.PathComponent
  1107. else
  1108. lMap:=lOperation.OperationID;
  1109. doLog(etInfo,'Map %s on %s.%s',[lMap,lService.ServiceName,lMethod.MethodName]);
  1110. end;
  1111. end;
  1112. end;
  1113. FServices.Sort(@CompareServiceName);
  1114. For I:=0 to ServiceCount-1 do
  1115. begin
  1116. Services[i].SortMethods;
  1117. For J:=0 to Services[i].MethodCount-1 do
  1118. Services[i].Methods[J].SortParams;
  1119. end;
  1120. end;
  1121. function TAPIData.IsAPIComponent(aSchema: TJSONSchema): Boolean;
  1122. begin
  1123. Result:=(aSchema.Ref<>'') and (GetRefSchemaTypeName(aSchema.Ref,ntSchema)<>'');
  1124. end;
  1125. function TAPIData.IsAPIComponentArray(aSchema: TJSONSchema): Boolean;
  1126. begin
  1127. Result:=GetSchemaType(aSchema)=sstArray;
  1128. if Result then
  1129. Result:=(aSchema.Items.Count>0) and IsAPIComponent(aSchema.Items[0]);
  1130. end;
  1131. function TAPIData.GetRefSchemaTypeName(const aRef: String; aNameType: TNameType): string;
  1132. const
  1133. ComponentsRef = '#/components/schemas/';
  1134. var
  1135. lLen : Integer;
  1136. lName : string;
  1137. begin
  1138. if Pos(ComponentsRef,aRef)=1 then
  1139. begin
  1140. lLen:=Length(ComponentsRef);
  1141. lName:=Copy(aRef,lLen+1,Length(aRef)-lLen);
  1142. if aNameType=ntSchema then
  1143. Result:=lName
  1144. else
  1145. Result:=RawToNameType(lName,aNameType);
  1146. end
  1147. else
  1148. Result:='';
  1149. end;
  1150. function TAPIData.CheckOperationsInput(aPath : TPathItem; aData: TAPITypeData): Boolean;
  1151. function CheckOperation(aOperation : TAPIOperation) : Boolean;
  1152. var
  1153. lRef,lName : String;
  1154. lMediaType : TMediaType;
  1155. lInputType : TAPITypeData;
  1156. begin
  1157. Result:=False;
  1158. if aOperation=Nil then
  1159. exit;
  1160. if not aOperation.HasKeyWord(okRequestBody) then
  1161. exit;
  1162. if aOperation.RequestBody.HasReference then
  1163. lRef:=aOperation.RequestBody.Reference.ref
  1164. else
  1165. begin
  1166. if not aOperation.RequestBody.HasKeyWord(rbkContent) then
  1167. exit;
  1168. lMediaType:=aOperation.RequestBody.Content.MediaTypes['application/json'];
  1169. if assigned(lMediaType) and assigned(lMediaType.Schema) then
  1170. lRef:=lMediaType.Schema.Ref
  1171. end;
  1172. if lRef<>'' then
  1173. begin
  1174. lRef:=GetRefSchemaTypeName(lRef,ntSchema);
  1175. lName:=aData.SchemaName;
  1176. if lRef=lName then
  1177. Exit(True);
  1178. lInputType:=GetAPIType(lRef);
  1179. if Assigned(lInputType) and (lInputType.DependsOn(aData,True)<>dtNone) then
  1180. Exit(True);
  1181. end;
  1182. end;
  1183. var
  1184. aKeyword : TPathItemOperationKeyword;
  1185. begin
  1186. Result:=False;
  1187. For aKeyword in TPathItemOperationKeyword do
  1188. if CheckOperation(aPath.GetOperation(aKeyword)) then
  1189. Exit(True);
  1190. end;
  1191. function TAPIData.CheckOperationsOutput(aPath : TPathItem; aData: TAPITypeData): Boolean;
  1192. function CheckResponse(aResponse : TResponse) : Boolean;
  1193. var
  1194. lMediaType : TMediaType;
  1195. lName,lRef : String;
  1196. lInputType : TAPITypeData;
  1197. begin
  1198. Result:=False;
  1199. lMediaType:=aResponse.Content.MediaTypes['application/json'];
  1200. if Not (assigned(lMediaType) and assigned(lMediaType.Schema)) then
  1201. exit;
  1202. lRef:=lMediaType.Schema.Ref;
  1203. if lRef='' then
  1204. exit;
  1205. lRef:=GetRefSchemaTypeName(lRef,ntSchema);
  1206. lName:=aData.SchemaName;
  1207. if lRef=lName then
  1208. Exit(True);
  1209. lInputType:=GetAPIType(lRef);
  1210. if Assigned(lInputType) and (lInputType.DependsOn(aData,True)<>dtNone) then
  1211. Exit(True);
  1212. end;
  1213. function CheckOperation(aOperation : TAPIOperation) : Boolean;
  1214. var
  1215. I : Integer;
  1216. begin
  1217. Result:=False;
  1218. if not Assigned(aOperation) then
  1219. exit;
  1220. if not aOperation.HasKeyWord(okResponses) then
  1221. exit;
  1222. For I:=0 to aOperation.Responses.Count-1 do
  1223. If CheckResponse(aOperation.Responses.ResponseByIndex[I]) then
  1224. Exit(True);
  1225. end;
  1226. var
  1227. aKeyword : TPathItemOperationKeyword;
  1228. begin
  1229. Result:=False;
  1230. For aKeyword in TPathItemOperationKeyword do
  1231. if CheckOperation(aPath.GetOperation(aKeyword)) then
  1232. Exit(True);
  1233. end;
  1234. function TAPIData.NeedsSerialize(aData: TAPITypeData): Boolean;
  1235. var
  1236. lRef,lName : String;
  1237. Itm : TPathItem;
  1238. lParam: TParameterOrReference;
  1239. lParamType : TAPITypeData;
  1240. I,J : Integer;
  1241. begin
  1242. Result:=False;
  1243. lName:=aData.SchemaName;
  1244. For I:=0 to FAPI.Paths.Count-1 do
  1245. begin
  1246. Itm:=FAPI.Paths.PathByIndex[I];
  1247. for J:=0 to Itm.Parameters.Count-1 do
  1248. begin
  1249. lParam:=itm.Parameters[j];
  1250. if (lParam.HasReference) then
  1251. lRef:=lParam.Reference.ref
  1252. else if Assigned(lParam.Schema) then
  1253. lRef:=lParam.Schema.Ref;
  1254. if lRef<>'' then
  1255. begin
  1256. lRef:=GetRefSchemaTypeName(lRef,ntSchema);
  1257. if lRef=lName then
  1258. Exit(True);
  1259. lParamType:=GetAPIType(lRef);
  1260. if Assigned(lParamType) and (lParamType.DependsOn(aData,True)<>dtNone) then
  1261. Exit(True);
  1262. end;
  1263. end;
  1264. If CheckOperationsInput(Itm,aData) then
  1265. exit(True);
  1266. end;
  1267. end;
  1268. function TAPIData.NeedsDeSerialize(aData: TAPITypeData): Boolean;
  1269. var
  1270. Itm : TPathItem;
  1271. I : Integer;
  1272. begin
  1273. Result:=False;
  1274. For I:=0 to FAPI.Paths.Count-1 do
  1275. begin
  1276. Itm:=FAPI.Paths.PathByIndex[I];
  1277. if CheckOperationsOutput(Itm,aData) then
  1278. Exit(True);
  1279. end;
  1280. end;
  1281. function TAPIData.RawToNameType(const aName : string; aNameType: TNameType) : string;
  1282. var
  1283. lType : TAPITypeData;
  1284. begin
  1285. lType:=FindApiType(aName);
  1286. if Assigned(lType) then
  1287. Result:=lType.GetTypeName(aNameType)
  1288. else
  1289. Result:=aName;
  1290. end;
  1291. procedure TAPIData.FinishAutoCreatedType(aName: string; aType: TPascalTypeData; lElementTypeData: TPascalTypeData);
  1292. begin
  1293. if aType.Pascaltype=ptArray then
  1294. begin
  1295. if InterfaceArrayType<>'' then
  1296. aType.InterfaceName:=Format('%s<%s>',[InterfaceArrayType,lElementTypeData.InterfaceName])
  1297. else
  1298. aType.InterfaceName:=lElementTypeData.InterfaceName+ArrayTypeSuffix;
  1299. aType.ImplementationName:=aType.PascalName;
  1300. end;
  1301. Inherited;
  1302. end;
  1303. function TAPIData.GetSchemaTypeName(aSchema: TJSONSchema; aNameType: TNameType): String;
  1304. var
  1305. {
  1306. lTmp,elType : String;
  1307. lType : TPascalType;
  1308. }
  1309. lData : TPascalTypeData;
  1310. begin
  1311. lData:=GetSchemaTypeData(Nil,aSchema,False);
  1312. if assigned(lData) then
  1313. Result:=lData.GetTypeName(aNameType)
  1314. else
  1315. Raise Exception.CreateFmt('No name for schema %s',[aSchema.Name]);
  1316. (*
  1317. lType:=SchemaTypeToPascalType(aSchema,Result);
  1318. if lType in [ptInteger,ptInt64,ptBoolean,ptFloat32,ptFloat64,ptString] then
  1319. begin
  1320. lTmp:=Self.TypeMap[Result];
  1321. if lTmp<>'' then
  1322. Result:=lTmp;
  1323. end
  1324. else if lType=ptArray then
  1325. begin
  1326. if aNameType=ntInterface then
  1327. begin
  1328. ElType:=GetSchemaTypeName(aSchema.Items[0],aNametype);
  1329. Result:=Format(InterfaceArrayType,[elType])
  1330. end
  1331. else
  1332. if DelphiTypes then
  1333. Result:='TArray<'+GetSchemaTypeName(aSchema.Items[0],aNametype)+'>'
  1334. else
  1335. Result:='Array of '+GetSchemaTypeName(aSchema.Items[0],aNametype);
  1336. end;
  1337. end;
  1338. *)
  1339. end;
  1340. function TAPITypeData.CreateProperty(const aAPIName, aPascalName: string): TPascalPropertyData;
  1341. begin
  1342. Result:=TAPIProperty.Create(aAPIName,aPascalName);
  1343. end;
  1344. constructor TAPITypeData.CreateBinaryData(const aContentType: string);
  1345. begin
  1346. Inherited Create(0,ptUnknown,'','TStream',Nil);
  1347. FContentType:=aContentType;
  1348. FBinaryData:=True;
  1349. end;
  1350. function TAPITypeData.AddProperty(const aApiName, aPascalName: String): TAPIProperty;
  1351. begin
  1352. Result:=(Inherited AddProperty(aApiName,aPascalName)) as TAPIProperty;
  1353. end;
  1354. end.