fpopenapi.pascaltypes.pp 48 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612
  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. Case FArgType of
  370. ptInteger,
  371. ptInt64: Result:=Format('IntToStr(%s)',[Name]); // Also handles int64
  372. ptString : Result:=Name;
  373. ptDateTime : Result:=Format('DateToISO8601(%s)',[Name]);
  374. ptFloat32,
  375. ptFloat64 : Result:=Format('FloatToStr(%s,cRestFormatSettings)',[Name]);
  376. ptBoolean : Result:=Format('cRESTBooleans[%s]',[Name]);
  377. else
  378. Result:=Name;
  379. end;
  380. end;
  381. constructor TAPIServiceMethodParam.create(aArgType: TPropertyType; aOriginalName: String; aName: String; aTypeName: String;
  382. aParam: TParameter);
  383. begin
  384. FArgType:=aArgType;
  385. FOriginalName:=aOriginalName;
  386. FName:=aName;
  387. FTypeName:=aTypeName;
  388. FParameter:=aParam;
  389. end;
  390. { TAPIService }
  391. function TAPIService.GetMethod(aIndex : Integer): TAPIServiceMethod;
  392. begin
  393. Result:=TAPIServiceMethod(FMethods[aIndex]);
  394. end;
  395. function TAPIService.GetMethodCount: Integer;
  396. begin
  397. Result:=FMethods.Count;
  398. end;
  399. function TAPIService.GetServiceImplementationClassName: String;
  400. begin
  401. Result:=FServiceProxyImplementationClassName;
  402. if Result='' then
  403. Result:='T'+ServiceName+'Proxy';
  404. end;
  405. function TAPIService.GetServiceInterfaceName: string;
  406. begin
  407. Result:=FServiceInterfaceName;
  408. if Result='' then
  409. Result:='I'+ServiceName;
  410. end;
  411. function TAPIService.GetServiceUUID: string;
  412. begin
  413. if FServiceUUID='' then
  414. FServiceUUID:=TGUID.NewGuid.ToString(False);
  415. Result:=FServiceUUID;
  416. end;
  417. function TAPIService.CreateMethod(aOperation: TAPIOperation; aPath: TPathItem; const aName: String): TAPIserviceMethod;
  418. begin
  419. Result:=TAPIserviceMethod.Create(aOperation,aPath,Self,aName);
  420. end;
  421. procedure TAPIService.SortMethods;
  422. begin
  423. FMethods.Sort(@CompareMethodName);
  424. end;
  425. constructor TAPIService.create(const aServiceName: string);
  426. begin
  427. FMethods:=TFPObjectList.Create(True);
  428. FServiceName:=aServiceName;
  429. FNeedsAuthentication:=True;
  430. end;
  431. destructor TAPIService.destroy;
  432. begin
  433. FreeAndNil(FMethods);
  434. inherited destroy;
  435. end;
  436. function TAPIService.AddMethod(const aName: String; aOperation: TAPIOperation; aPath : TPathItem): TAPIserviceMethod;
  437. begin
  438. Result:=CreateMethod(aOperation,aPath,aName);
  439. FMethods.Add(Result);
  440. end;
  441. { TAPIServiceMethod }
  442. function TAPIServiceMethod.GetParam(aIndex : Integer): TAPIServiceMethodParam;
  443. begin
  444. Result:=TAPIServiceMethodParam(FParams[aIndex]);
  445. end;
  446. function TAPIServiceMethod.GetParamCount: Integer;
  447. begin
  448. Result:=FParams.Count;
  449. end;
  450. function TAPIServiceMethod.GetRequestBodyType: String;
  451. begin
  452. if assigned(FBodyType) then
  453. Result:=FBodyType.GetTypeName(ntPascal)
  454. else
  455. Result:='';
  456. end;
  457. function TAPIServiceMethod.GetResultCallbackType: String;
  458. begin
  459. Result:=FResultCallbackType;
  460. if Result='' then
  461. begin
  462. Result:=ResultType;
  463. if Result='' then
  464. Result:='VoidResult';
  465. Result:='T'+Result+'CallBack';
  466. end;
  467. end;
  468. function TAPIServiceMethod.GetResultType(AIndex: TNameType): String;
  469. begin
  470. if assigned(FResultDataType) then
  471. Result:=FResultDataType.GetTypeName(aIndex)
  472. else
  473. Result:='';
  474. end;
  475. procedure TAPIServiceMethod.SortParams;
  476. begin
  477. FParams.Sort(@CompareParamName);
  478. end;
  479. constructor TAPIServiceMethod.create(aOperation: TApiOperation;
  480. aPath: TPathItem; aService: TAPIService; const aMethod: String);
  481. begin
  482. FOperation:=aOperation;
  483. FService:=aService;
  484. FMethodName:=aMethod;
  485. FPath:=aPath;
  486. FParams:=TFPObjectList.Create(True);
  487. end;
  488. destructor TAPIServiceMethod.Destroy;
  489. begin
  490. FreeAndNil(FParams);
  491. inherited Destroy;
  492. end;
  493. function TAPIServiceMethod.CreateParam(const aType : TPascaltype; const aOriginalName,aName, aTypeName: String; aParam: TParameter): TAPIServiceMethodParam;
  494. begin
  495. Result:=TAPIServiceMethodParam.Create(aType,aOriginalName,aName,aTypeName,aParam);
  496. end;
  497. function TAPIServiceMethod.AddParam(const aType : TPascalType; const aOriginalName,aName, aTypeName: String; aParam: TParameter): TAPIServiceMethodParam;
  498. begin
  499. Result:=CreateParam(aType,aOriginalName,aName,aTypeName,aParam);
  500. FParams.Add(Result);
  501. end;
  502. function TAPIServiceMethod.ParamByName(aOriginalName: String): TAPIServiceMethodParam;
  503. var
  504. Idx : Integer;
  505. begin
  506. Idx:=ParamCount-1;
  507. While (Idx>=0) and Not SameText(Param[Idx].OriginalName,aOriginalName) do
  508. Dec(Idx);
  509. if Idx=-1 then
  510. Result:=Nil
  511. else
  512. Result:=Param[Idx];
  513. end;
  514. function TAPIServiceMethod.HasPathParam: Boolean;
  515. var
  516. I : Integer;
  517. begin
  518. Result:=False;
  519. For I:=0 to ParamCount-1 do
  520. if Param[i].Location=plPath then
  521. Exit(True);
  522. end;
  523. function TAPIServiceMethod.HasQueryParam: Boolean;
  524. var
  525. I : Integer;
  526. begin
  527. Result:=False;
  528. For I:=0 to ParamCount-1 do
  529. if Param[i].Location=plQuery then
  530. Exit(True);
  531. end;
  532. function TAPIServiceMethod.HasOptionalParams: Boolean;
  533. var
  534. I : Integer;
  535. begin
  536. Result:=False;
  537. For I:=0 to ParamCount-1 do
  538. if (Param[i].DefaultValue<>'') then
  539. Exit(True);
  540. end;
  541. { TAPIData }
  542. function TAPIData.GetTypeData(aIndex : Integer): TAPITypeData;
  543. begin
  544. Result:=TAPITypeData(Inherited Types[aIndex]);
  545. end;
  546. function TAPIData.GetVoidResultCallbackType: String;
  547. begin
  548. Result:=FVoidResultCallbackType;
  549. if Result='' then
  550. Result:='TVoidResultCallBack';
  551. end;
  552. procedure TAPIData.SetStreamContentTypes(const aValue: TStrings);
  553. begin
  554. if FStreamContentTypes=aValue then Exit;
  555. FStreamContentTypes.Assign(aValue);
  556. end;
  557. function TAPIData.CreatePascalType(aIndex: integer; aPascalType: TPascaltype; const aAPIName, aPascalName: String;
  558. aSchema: TJSONSchema): TAPITypeData;
  559. begin
  560. Result:=TAPITypeData.Create(aIndex,aPascalType,aAPIName,aPascalName,aSchema);
  561. end;
  562. function TAPIData.GetAPITypeCount: Integer;
  563. begin
  564. Result:=TypeCount;
  565. end;
  566. function TAPIData.GetService(aIndex : Integer): TAPIService;
  567. begin
  568. Result:=TAPIService(FServices[aIndex]);
  569. end;
  570. function TAPIData.GetServiceCount: Integer;
  571. begin
  572. Result:=FServices.Count;
  573. end;
  574. function TAPIData.IndexOfAPIType(const aName : String): integer;
  575. begin
  576. Result:=IndexOfSchemaType(aName);
  577. end;
  578. function TAPIData.FindApiType(const aName: String): TAPITypeData;
  579. begin
  580. Result:=FindSchemaTypeData(aName) as TAPITypeData;
  581. end;
  582. function TAPIData.GetAPIType(const aName : String): TAPITypeData;
  583. begin
  584. Result:=FindAPIType(aName);
  585. if Result=Nil then
  586. Raise EListError.CreateFmt('Unknown type: %s',[aName]);
  587. end;
  588. function TAPIData.CreateService(const aName: String): TAPIService;
  589. begin
  590. Result:=TAPIService.Create(aName);
  591. end;
  592. procedure TAPIData.ConfigService(const aService : TAPIService);
  593. begin
  594. aService.ServiceParentInterface:=Self.ServiceParentInterface;
  595. end;
  596. function TAPIData.AddService(const aName: String): TAPIService;
  597. begin
  598. Result:=CreateService(aName);
  599. FServices.Add(Result);
  600. end;
  601. constructor TAPIData.Create(aAPI: TOpenAPI);
  602. begin
  603. Inherited Create;
  604. FStreamContentTypes:=TStringList.Create;
  605. FStreamContentTypes.Add('application/octet-stream');
  606. FStreamContentTypes.Add('*/*');
  607. FServices:=TFPObjectList.Create(True);
  608. FServiceOperationMap:=TFPStringHashTable.Create;
  609. FAPI:=aAPI;
  610. FServiceNameSuffix:='Service';
  611. FServiceNamePrefix:='';
  612. end;
  613. destructor TAPIData.Destroy;
  614. begin
  615. FreeAndNil(FStreamContentTypes);
  616. FreeAndNil(FServices);
  617. FreeAndNil(FServiceOperationMap);
  618. inherited Destroy;
  619. end;
  620. procedure TAPIData.CreateDefaultTypeMaps;
  621. begin
  622. DefineStandardPascalTypes;
  623. end;
  624. procedure TAPIData.ConfigType(aType :TAPITypeData);
  625. begin
  626. if aType.Pascaltype in [ptAnonStruct,ptSchemaStruct] then
  627. begin
  628. aType.InterfaceName:=EscapeKeyWord(Sanitize(InterfaceTypePrefix+aType.SchemaName));
  629. aType.InterfaceUUID:=TGUID.NewGUID.ToString(False);
  630. end;
  631. end;
  632. procedure TAPIData.ApplyUUIDMap(aMap : TStrings);
  633. var
  634. I : Integer;
  635. N,V : String;
  636. lData : TAPITypeData;
  637. lService : TAPIService;
  638. begin
  639. if aMap.Count=0 then exit;
  640. For I:=0 to aMap.Count-1 do
  641. begin
  642. aMap.GetNameValue(I,N,V);
  643. lData:=FindApiType(N);
  644. if assigned(lData) then
  645. lData.InterfaceUUID:=V
  646. else
  647. begin
  648. lService:=FindService(N);
  649. if assigned(lService) then
  650. lService.ServiceUUID:=V;
  651. end;
  652. end;
  653. end;
  654. procedure TAPIData.RecordMethodNameMap(aMap: TStrings);
  655. var
  656. I : Integer;
  657. N,V : String;
  658. begin
  659. if aMap.Count=0 then exit;
  660. For I:=0 to aMap.Count-1 do
  661. begin
  662. aMap.GetNameValue(I,N,V);
  663. if (N<>'') and (V<>'') then
  664. FServiceOperationMap.Add(N,V);
  665. end;
  666. end;
  667. function TAPIData.IndexOfService(const aName: String): Integer;
  668. begin
  669. Result:=FServices.Count-1;
  670. While (Result>=0) and not SameText(aName,GetService(Result).ServiceName) do
  671. Dec(Result);
  672. end;
  673. function TAPIData.FindService(const aName: String): TAPIService;
  674. var
  675. Idx : Integer;
  676. begin
  677. Result:=nil;
  678. Idx:=IndexOfService(aName);
  679. if Idx>=0 then
  680. Result:=GetService(Idx);
  681. end;
  682. function TAPIData.GetServiceByName(const aName: String): TAPIService;
  683. begin
  684. Result:=FindService(aName);
  685. if Result=Nil then
  686. Raise EGenAPI.CreateFmt('Unknown service: %s',[aName]);
  687. end;
  688. procedure TAPIData.CheckInputOutput(aIncludeServer: Boolean);
  689. var
  690. I: Integer;
  691. lData : TAPITypeData;
  692. lSerTypes : TSerializeTypes;
  693. begin
  694. for I:=0 to TypeCount-1 do
  695. begin
  696. lSerTypes:=[];
  697. lData:=APITypes[i];
  698. if aIncludeServer then
  699. begin
  700. if NeedsSerialize(lData) or NeedsDeserialize(lData) then
  701. lSerTypes:=[stSerialize,stDeSerialize]
  702. end
  703. else
  704. begin
  705. if NeedsSerialize(lData) then
  706. Include(lSerTypes,stSerialize);
  707. if NeedsDeserialize(lData) then
  708. Include(lSerTypes,stDeSerialize);
  709. end;
  710. lData.SerializeTypes:=lData.SerializeTypes+lSerTypes;
  711. DoLog(etInfo,'%s needs serialize: %s, deserialize: %s',[lData.SchemaName,BoolToStr(stSerialize in lSerTypes,True),BoolToStr(stDeSerialize in lSerTypes,True)]);
  712. if (lData.Pascaltype=ptArray) and Assigned(lData.ElementTypeData) then
  713. lData.ElementTypeData.SerializeTypes:=lData.ElementTypeData.SerializeTypes+lSerTypes;
  714. end;
  715. end;
  716. procedure TAPIData.CreateDefaultAPITypeMaps(aIncludeServer : Boolean);
  717. Procedure AddProperties(aType : TAPITypeData);
  718. var
  719. I : Integer;
  720. begin
  721. for I:=0 to aType.Schema.Properties.Count-1 do
  722. AddTypeProperty(aType,aType.Schema.Properties[i]);
  723. aType.SortProperties;
  724. end;
  725. var
  726. I : Integer;
  727. lName,lTypeName : String;
  728. lSchema : TJsonSchema;
  729. lData : TAPITypeData;
  730. lType : TSchemaSimpleType;
  731. begin
  732. For I:=0 to FAPI.Components.Schemas.Count-1 Do
  733. begin
  734. lName:=FAPI.Components.Schemas.Names[I];
  735. lSchema:=FAPI.Components.Schemas.Schemas[lName];
  736. lType:=lSchema.Validations.GetFirstType;
  737. if (lType in [sstArray,sstObject,sstInteger,sstString]) then
  738. begin
  739. lTypeName:=EscapeKeyWord(ObjectTypePrefix+Sanitize(lName)+ObjectTypeSuffix);
  740. case lType of
  741. sstObject :
  742. lData:=CreatePascalType(I,ptSchemaStruct,lName,lTypeName,lSchema);
  743. sstString :
  744. begin
  745. lData:=CreatePascalType(I,ptString,lName,lTypeName,lSchema);
  746. end;
  747. sstInteger:
  748. lData:=CreatePascalType(I,ptInteger,lName,lTypeName,lSchema);
  749. sstArray:
  750. lData:=CreatePascalType(I,ptArray,lName,lTypeName,lSchema);
  751. end;
  752. ConfigType(lData);
  753. AddType(lName,lData);
  754. AddToTypeMap(lName,lData);
  755. end;
  756. end;
  757. For I:=0 to FAPI.Components.Parameters.Count-1 Do
  758. begin
  759. lName:=FAPI.Components.Parameters.Names[I];
  760. if FAPI.Components.Parameters.ParameterOrReferences[lName].HasReference then
  761. Continue;
  762. lSchema:=FAPI.Components.Parameters.ParameterOrReferences[lName].Schema;
  763. if assigned(lSchema) then
  764. lType:=lSchema.Validations.GetFirstType;
  765. if (lType in [sstArray,sstObject,sstInteger,sstString]) then
  766. begin
  767. lTypeName:=EscapeKeyWord(ObjectTypePrefix+Sanitize(lName)+ObjectTypeSuffix);
  768. case lType of
  769. sstObject :
  770. lData:=CreatePascalType(I,ptSchemaStruct,lName,lTypeName,lSchema);
  771. sstString :
  772. begin
  773. lData:=CreatePascalType(I,ptString,lName,lTypeName,lSchema);
  774. end;
  775. sstInteger:
  776. lData:=CreatePascalType(I,ptInteger,lName,lTypeName,lSchema);
  777. sstArray:
  778. lData:=CreatePascalType(I,ptArray,lName,lTypeName,lSchema);
  779. end;
  780. ConfigType(lData);
  781. AddType(lName,lData);
  782. AddToTypeMap(lName,lData);
  783. end;
  784. end;
  785. // We do this here, so all API type references can be resolved
  786. For I:=0 to APITypeCount-1 do
  787. AddProperties(APITypes[i]);
  788. // Finally, sort
  789. CheckDependencies;
  790. SortTypes;
  791. CheckInputOutput(aIncludeServer);
  792. end;
  793. function TAPIData.GenerateServiceName(const aUrl: String; const aPath: TPathItem;
  794. aOperation: TAPIOperation): String;
  795. function CleanIdentifier(S : String) : String;
  796. begin
  797. Result:=StringReplace(S,' ','',[rfReplaceAll]);
  798. Result:=Sanitize(Result);
  799. end;
  800. {
  801. the maps contain ServiceName[.MethodName]
  802. We use ServiceName if there is an entry in the map.
  803. if there is no entry in the map and there is 1 tag, we take the name of the tag.
  804. if there is no tag, we take the first component of the URL path.
  805. }
  806. var
  807. S,lTag,lFullName : String;
  808. lStrings : TStringDynArray;
  809. begin
  810. Result:='';
  811. // We need to take into account the full map first, as is done for the method.
  812. if aOperation.OperationID<>'' then
  813. lFullName:=FServiceOperationMap.Items[aOperation.OperationID]
  814. else
  815. lFullName:=FServiceOperationMap.Items[aOperation.PathComponent+'.'+aURL];
  816. if (lFullName='') and (aOperation.Tags.Count=1) then
  817. lFullName:=ServiceNamePrefix+CleanIdentifier(aOperation.Tags[0])+ServiceNameSuffix;
  818. if (lFullName='') then
  819. begin
  820. lStrings:=SplitString(aURL,'/');
  821. // First non-empty
  822. For S in lStrings do
  823. if (Result='') and (S<>'') then
  824. Result:=CleanIdentifier(S);
  825. Result:=ServiceNamePrefix+PrettyPrint(Result)+ServiceNameSuffix;
  826. end
  827. else
  828. begin
  829. lStrings:=SplitString(lFullName,'.');
  830. Result:=LStrings[0];
  831. end;
  832. if (aOperation.OperationID<>'') and (lFullName='') then
  833. begin
  834. S:=aOperation.OperationID;
  835. DoLog(etWarning,'No mapping for %s: (Tag= "%s"), Generated: %s=%s.%s',[S,lTag,S,Result,S]);
  836. end;
  837. end;
  838. function TAPIData.GenerateServiceMethodName(const aUrl: String;
  839. const aPath: TPathItem; aOperation: TAPIOperation): String;
  840. (*
  841. the maps contain ServiceName[.MethodName]
  842. 1. if there is a method name in either map: we use that.
  843. 2. if there is no method name in either map:
  844. a. if there is an operation ID we use it as the method name.
  845. b. if there is no operation ID, we use the operation HTTP method together with the url except the first path component.
  846. Parameters are reduced to their names.
  847. get /users/contacts/{Id} -> servicename "users" method "get_contacts_Id"
  848. *)
  849. var
  850. S,lFullName : String;
  851. lStrings : TStringDynArray;
  852. I,J : Integer;
  853. begin
  854. Result:='';
  855. if aOperation.OperationID<>'' then
  856. lFullName:=FServiceOperationMap.Items[aOperation.OperationID]
  857. else
  858. lFullName:=FServiceOperationMap.Items[aOperation.PathComponent+'.'+aURL];
  859. if lFullName='' then
  860. begin
  861. Result:=aOperation.OperationID;
  862. if Result='' then
  863. begin
  864. lStrings:=SplitString(aURL,'/');
  865. Result:=aOperation.PathComponent;
  866. for I:=1 to Length(lStrings)-1 do
  867. begin
  868. S:=lStrings[i];
  869. S:=StringReplace(S,'{','',[rfReplaceAll]);
  870. S:=StringReplace(S,'}','',[rfReplaceAll]);
  871. S:=Sanitize(S);
  872. Result:=Result+'_'+S;
  873. end;
  874. end;
  875. end
  876. else
  877. begin
  878. lStrings:=SplitString(lFullName,'.');
  879. Result:=LStrings[1];
  880. end;
  881. Result:=PrettyPrint(Result);
  882. end;
  883. function TAPIData.AddServiceMethodParam(aService: TAPIservice; aMethod : TAPIServiceMethod; Idx : Integer; aParam : TParameterOrReference) : TAPIServiceMethodParam;
  884. var
  885. lOriginalName,lName,lTypeName : string;
  886. lTypeData : TPascaltypeData;
  887. lType : TPascalType;
  888. begin
  889. if aParam.HasReference then
  890. begin
  891. lTypeName:=GetRefSchemaTypeName(aParam.Reference.Ref,ntPascal);
  892. lName:='';
  893. lOriginalName:='';
  894. lType:=ptSchemaStruct;
  895. end
  896. else
  897. begin
  898. lTypeData:=GetSchemaTypeData(Nil,aParam.Schema,False);
  899. lTypeName:=lTypeData.GetTypeName(ntPascal);
  900. lType:=lTypeData.Pascaltype;
  901. lOriginalName:=aParam.Name;
  902. lName:='a'+PrettyPrint(lOriginalName);
  903. end;
  904. if lName='' then
  905. lName:=Format('aParam%d',[Idx]);
  906. Result:=aMethod.AddParam(lType,lOriginalName,lName,lTypeName,aParam);
  907. if aParam.Schema.MetaData.HasKeywordData(jskDefault) then
  908. begin
  909. Result.DefaultValue:=aParam.Schema.MetaData.DefaultValue.AsString;
  910. if lType=ptString then
  911. Result.DefaultValue:=''''+StringReplace(Result.DefaultValue,'''','''''',[rfReplaceAll])+'''';
  912. end;
  913. end;
  914. function TAPIData.IsResponseContentApplicationJSON(aOperation : TAPIOperation) : boolean;
  915. var
  916. lResponse : TResponse;
  917. lMedia : TMediaType;
  918. begin
  919. if aOperation.Responses.Count=0 then
  920. Result:=True
  921. else
  922. begin
  923. lResponse:=aOperation.Responses.ResponseByindex[0];
  924. lMedia:=lResponse.Content.MediaTypes['application/json'];
  925. Result:=lMedia<>nil;
  926. end;
  927. end;
  928. function TAPIData.GenerateMethodResultCallBackName(aMethod : TAPIServiceMethod) : String;
  929. var
  930. lResponse: TResponse;
  931. lMedia : TMediaType;
  932. begin
  933. if AMethod.Operation.Responses.Count=0 then
  934. Result:=VoidResultCallbackType
  935. else
  936. begin
  937. lResponse:=AMethod.Operation.Responses.ResponseByindex[0];
  938. lMedia:=lResponse.Content.MediaTypes['application/json'];
  939. if (lMedia.Schema.Ref<>'') then
  940. Result:=Format('%sResultCallBack',[GetRefSchemaTypeName(lMedia.Schema.Ref,ntPascal)])
  941. else if (lMedia.Schema.Validations.Types=[]) then
  942. Result:=VoidResultCallbackType
  943. else
  944. Result:=GetSchemaTypeName(lMedia.Schema,ntPascal);
  945. end;
  946. end;
  947. function TAPIData.GetMethodResultTypeData(aMethod: TAPIServiceMethod): TAPITypeData;
  948. var
  949. lResponse: TResponse;
  950. lMedia : TMediaType;
  951. S : String;
  952. begin
  953. Result:=Nil;
  954. if AMethod.Operation.Responses.Count>0 then
  955. begin
  956. lResponse:=AMethod.Operation.Responses.ResponseByindex[0];
  957. if lResponse.Content.Count<>0 then
  958. begin
  959. lMedia:=lResponse.Content.MediaTypes['application/json'];
  960. if lMedia=Nil then
  961. begin
  962. // Check if we must stream
  963. For S in StreamContentTypes do
  964. begin
  965. lMedia:=lResponse.Content.MediaTypes[S];
  966. if lMedia<>nil then
  967. break;
  968. end;
  969. if lMedia=nil then
  970. Raise EGenAPI.CreateFmt('No application/json response media type for %s.%s',[aMethod.Service.ServiceName,aMethod.MethodName]);
  971. Result:=GetStreamTypeData(S);
  972. end;
  973. Result:=GetSchemaTypeData(Nil,lMedia.Schema,True) as TAPITypeData;
  974. end;
  975. end;
  976. end;
  977. function TAPIData.GetMethodResultType(aMethod : TAPIServiceMethod; aNameType : TNameType) : String;
  978. var
  979. lData : TAPITypeData;
  980. begin
  981. lData:=aMethod.ResultDataType;
  982. if not assigned(lData) then
  983. Raise EGenAPI.CreateFmt('No result type %s.%s',[aMethod.Service.ServiceName,aMethod.MethodName]);
  984. Result:=lData.GetTypeName(aNameType);
  985. end;
  986. function TAPIData.IsRequestBodyApplicationJSON(aOperation : TAPIOperation) : Boolean;
  987. var
  988. lMedia : TMediaType;
  989. begin
  990. Result:=False;
  991. if Not aOperation.HasKeyWord(okRequestBody) then
  992. exit(True);
  993. if aOperation.RequestBody.HasReference then
  994. // We have a definition
  995. Result:=GetRefSchemaTypeName(aOperation.RequestBody.Reference.Ref,ntPascal)<>''
  996. else
  997. begin
  998. lMedia:=aOperation.RequestBody.Content['application/json'];
  999. Result:=lMedia<>Nil;
  1000. end;
  1001. end;
  1002. function TAPIData.GetMethodRequestBodyType(aMethod: TAPIServiceMethod): TAPITypeData;
  1003. var
  1004. lMedia : TMediaType;
  1005. i : Integer;
  1006. S : String;
  1007. begin
  1008. Result:=Nil;
  1009. if Not aMethod.Operation.HasKeyWord(okRequestBody) then
  1010. exit;
  1011. if aMethod.Operation.RequestBody.HasReference then
  1012. Result:=TAPITypeData(GetPascalTypeDataFromRef(aMethod.Operation.RequestBody.Reference.Ref))
  1013. else
  1014. begin
  1015. lMedia:=aMethod.Operation.RequestBody.Content['application/json'];
  1016. if lMedia<>Nil then
  1017. Result:=TAPITypeData(GetSchemaTypeData(Nil,lMedia.Schema,True))
  1018. else
  1019. begin
  1020. I:=0;
  1021. While (lMedia=Nil) and (I<StreamContentTypes.Count) do
  1022. begin
  1023. S:=StreamContentTypes[I];
  1024. lMedia:=aMethod.Operation.RequestBody.Content[S];
  1025. end;
  1026. Result:=TAPITypeData.CreateBinaryData(S);
  1027. end;
  1028. end;
  1029. if Result=Nil then
  1030. with aMethod do
  1031. Raise EGenAPI.CreateFmt('Unknown result type for method %s.%s: %s',[Service.ServiceName,MethodName,Operation.RequestBody.Reference.Ref]);
  1032. end;
  1033. procedure TAPIData.ConfigureServiceMethod(aService : TAPIService; aMethod : TAPIServiceMethod);
  1034. begin
  1035. aMethod.ResultDataType:=GetMethodResultTypeData(aMethod);
  1036. aMethod.RequestBodyDataType:=GetMethodRequestBodyType(aMethod);
  1037. end;
  1038. function TAPIData.IsResponseContentStreamable(aOperation : TAPIOperation) : boolean;
  1039. var
  1040. i : Integer;
  1041. lResponse : TResponse;
  1042. lMedia : TMediaType;
  1043. begin
  1044. if aOperation.Responses.Count=0 then
  1045. Result:=False
  1046. else
  1047. begin
  1048. lResponse:=aOperation.Responses.ResponseByindex[0];
  1049. I:=0;
  1050. lMedia:=Nil;
  1051. While (lMedia=Nil) and (I<StreamContentTypes.Count) do
  1052. begin
  1053. lMedia:=lResponse.Content.MediaTypes[StreamContentTypes[i]];
  1054. inc(i);
  1055. end;
  1056. Result:=lMedia<>nil;
  1057. end;
  1058. end;
  1059. function TAPIData.IsRequestBodyStreamable(aOperation: TAPIOperation): Boolean;
  1060. var
  1061. lMedia : TMediaType;
  1062. I : Integer;
  1063. begin
  1064. Result:=False;
  1065. if Not aOperation.HasKeyWord(okRequestBody) then
  1066. exit(True);
  1067. if aOperation.RequestBody.HasReference then
  1068. // We have a definition
  1069. Result:=GetRefSchemaTypeName(aOperation.RequestBody.Reference.Ref,ntPascal)<>''
  1070. else
  1071. begin
  1072. lMedia:=Nil;
  1073. I:=0;
  1074. While (I<StreamContentTypes.Count) and (lMedia=Nil) do
  1075. lMedia:=aOperation.RequestBody.Content[StreamContentTypes[i]];
  1076. Result:=lMedia<>Nil;
  1077. end;
  1078. end;
  1079. function TAPIData.GetStreamTypeData(const aContentType: String): TAPITypeData;
  1080. var
  1081. I : Integer;
  1082. S : String;
  1083. begin
  1084. I:=0;
  1085. Result:=Nil;
  1086. While (Result=Nil) and (I<APITypeCount) do
  1087. begin
  1088. Result:=APITypes[i];
  1089. if not (Result.BinaryData and (Result.ContentType=aContentType)) then
  1090. Result:=Nil;
  1091. Inc(I);
  1092. end;
  1093. if Result=Nil then
  1094. begin
  1095. Result:=TAPITypeData.CreateBinaryData(aContentType);
  1096. S:=ObjectTypePrefix+Sanitize(aContentType)+'StreamData';
  1097. AddType(S,Result);
  1098. end;
  1099. end;
  1100. function TAPIData.AllowOperation(aKeyword: TPathItemOperationKeyword; aOperation: TAPIOperation): boolean;
  1101. begin
  1102. Result:=True;
  1103. Result:=IsResponseContentApplicationJSON(aOperation)
  1104. or IsResponseContentStreamable(aOperation);
  1105. if (aKeyword in [pkPost,pkPut,pkPatch]) then
  1106. Result:=IsRequestBodyApplicationJSON(aOperation)
  1107. or IsRequestBodyStreamable(aOperation);
  1108. end;
  1109. procedure TAPIData.CreateServiceDefs;
  1110. var
  1111. I,J : Integer;
  1112. lPath: TPathItem;
  1113. lURL : String;
  1114. lOperation : TAPIOperation;
  1115. lKeyword : TPathItemOperationKeyword;
  1116. lServiceName,lMethodName : String;
  1117. lService : TAPIService;
  1118. lMethod : TAPIServiceMethod;
  1119. lMap : String;
  1120. begin
  1121. for I:=0 to FAPI.Paths.Count-1 do
  1122. begin
  1123. lPath:=FAPI.Paths.PathByIndex[I];
  1124. lURL:=FAPI.Paths.Names[I];
  1125. for lKeyword in TPathItemOperationKeyword do
  1126. begin
  1127. lOperation:=lPath.GetOperation(lKeyword);
  1128. if assigned(lOperation) and AllowOperation(lKeyword,lOperation) then
  1129. begin
  1130. lServiceName:=GenerateServiceName(lUrl,lPath,lOperation);
  1131. lService:=FindService(lServiceName);
  1132. if lService=Nil then
  1133. begin
  1134. lService:=AddService(lServiceName);
  1135. ConfigService(lService);
  1136. end;
  1137. lMethodName:=GenerateServiceMethodName(lUrl,lPath,lOperation);
  1138. lMethod:=lService.AddMethod(lMethodName,lOperation,lPath);
  1139. ConfigureServiceMethod(lService,lMethod);
  1140. if lOperation.HasKeyWord(okParameters) then
  1141. for J:=0 to lOperation.Parameters.Count-1 do
  1142. AddServiceMethodParam(lService,lMethod,j,lOperation.Parameters[j]);
  1143. if lOperation.OperationId='' then
  1144. lMap:=lOperation.PathComponent+'.'+lPath.PathComponent
  1145. else
  1146. lMap:=lOperation.OperationID;
  1147. doLog(etInfo,'Map %s on %s.%s',[lMap,lService.ServiceName,lMethod.MethodName]);
  1148. end;
  1149. end;
  1150. end;
  1151. FServices.Sort(@CompareServiceName);
  1152. For I:=0 to ServiceCount-1 do
  1153. begin
  1154. Services[i].SortMethods;
  1155. For J:=0 to Services[i].MethodCount-1 do
  1156. Services[i].Methods[J].SortParams;
  1157. end;
  1158. end;
  1159. function TAPIData.IsAPIComponent(aSchema: TJSONSchema): Boolean;
  1160. begin
  1161. Result:=(aSchema.Ref<>'') and (GetRefSchemaTypeName(aSchema.Ref,ntSchema)<>'');
  1162. end;
  1163. function TAPIData.IsAPIComponentArray(aSchema: TJSONSchema): Boolean;
  1164. begin
  1165. Result:=GetSchemaType(aSchema)=sstArray;
  1166. if Result then
  1167. Result:=(aSchema.Items.Count>0) and IsAPIComponent(aSchema.Items[0]);
  1168. end;
  1169. function TAPIData.GetRefSchemaTypeName(const aRef: String; aNameType: TNameType): string;
  1170. const
  1171. ComponentsRef = '#/components/schemas/';
  1172. var
  1173. lLen : Integer;
  1174. lName : string;
  1175. begin
  1176. if Pos(ComponentsRef,aRef)=1 then
  1177. begin
  1178. lLen:=Length(ComponentsRef);
  1179. lName:=Copy(aRef,lLen+1,Length(aRef)-lLen);
  1180. if aNameType=ntSchema then
  1181. Result:=lName
  1182. else
  1183. Result:=RawToNameType(lName,aNameType);
  1184. end
  1185. else
  1186. Result:='';
  1187. end;
  1188. function TAPIData.CheckOperationsInput(aPath : TPathItem; aData: TAPITypeData): Boolean;
  1189. function CheckOperation(aOperation : TAPIOperation) : Boolean;
  1190. var
  1191. lRef,lName : String;
  1192. lMediaType : TMediaType;
  1193. lInputType : TAPITypeData;
  1194. begin
  1195. Result:=False;
  1196. if aOperation=Nil then
  1197. exit;
  1198. if not aOperation.HasKeyWord(okRequestBody) then
  1199. exit;
  1200. if aOperation.RequestBody.HasReference then
  1201. lRef:=aOperation.RequestBody.Reference.ref
  1202. else
  1203. begin
  1204. if not aOperation.RequestBody.HasKeyWord(rbkContent) then
  1205. exit;
  1206. lMediaType:=aOperation.RequestBody.Content.MediaTypes['application/json'];
  1207. if assigned(lMediaType) and assigned(lMediaType.Schema) then
  1208. lRef:=lMediaType.Schema.Ref
  1209. end;
  1210. if lRef<>'' then
  1211. begin
  1212. lRef:=GetRefSchemaTypeName(lRef,ntSchema);
  1213. lName:=aData.SchemaName;
  1214. if lRef=lName then
  1215. Exit(True);
  1216. lInputType:=GetAPIType(lRef);
  1217. if Assigned(lInputType) and (lInputType.DependsOn(aData,True)<>dtNone) then
  1218. Exit(True);
  1219. end;
  1220. end;
  1221. var
  1222. aKeyword : TPathItemOperationKeyword;
  1223. begin
  1224. Result:=False;
  1225. For aKeyword in TPathItemOperationKeyword do
  1226. if CheckOperation(aPath.GetOperation(aKeyword)) then
  1227. Exit(True);
  1228. end;
  1229. function TAPIData.CheckOperationsOutput(aPath : TPathItem; aData: TAPITypeData): Boolean;
  1230. function CheckResponse(aResponse : TResponse) : Boolean;
  1231. var
  1232. lMediaType : TMediaType;
  1233. lName,lRef : String;
  1234. lInputType : TAPITypeData;
  1235. begin
  1236. Result:=False;
  1237. lMediaType:=aResponse.Content.MediaTypes['application/json'];
  1238. if Not (assigned(lMediaType) and assigned(lMediaType.Schema)) then
  1239. exit;
  1240. lRef:=lMediaType.Schema.Ref;
  1241. if lRef='' then
  1242. exit;
  1243. lRef:=GetRefSchemaTypeName(lRef,ntSchema);
  1244. lName:=aData.SchemaName;
  1245. if lRef=lName then
  1246. Exit(True);
  1247. lInputType:=GetAPIType(lRef);
  1248. if Assigned(lInputType) and (lInputType.DependsOn(aData,True)<>dtNone) then
  1249. Exit(True);
  1250. end;
  1251. function CheckOperation(aOperation : TAPIOperation) : Boolean;
  1252. var
  1253. I : Integer;
  1254. begin
  1255. Result:=False;
  1256. if not Assigned(aOperation) then
  1257. exit;
  1258. if not aOperation.HasKeyWord(okResponses) then
  1259. exit;
  1260. For I:=0 to aOperation.Responses.Count-1 do
  1261. If CheckResponse(aOperation.Responses.ResponseByIndex[I]) then
  1262. Exit(True);
  1263. end;
  1264. var
  1265. aKeyword : TPathItemOperationKeyword;
  1266. begin
  1267. Result:=False;
  1268. For aKeyword in TPathItemOperationKeyword do
  1269. if CheckOperation(aPath.GetOperation(aKeyword)) then
  1270. Exit(True);
  1271. end;
  1272. function TAPIData.NeedsSerialize(aData: TAPITypeData): Boolean;
  1273. var
  1274. lRef,lName : String;
  1275. Itm : TPathItem;
  1276. lParam: TParameterOrReference;
  1277. lParamType : TAPITypeData;
  1278. I,J : Integer;
  1279. begin
  1280. Result:=False;
  1281. lName:=aData.SchemaName;
  1282. For I:=0 to FAPI.Paths.Count-1 do
  1283. begin
  1284. Itm:=FAPI.Paths.PathByIndex[I];
  1285. for J:=0 to Itm.Parameters.Count-1 do
  1286. begin
  1287. lParam:=itm.Parameters[j];
  1288. if (lParam.HasReference) then
  1289. lRef:=lParam.Reference.ref
  1290. else if Assigned(lParam.Schema) then
  1291. lRef:=lParam.Schema.Ref;
  1292. if lRef<>'' then
  1293. begin
  1294. lRef:=GetRefSchemaTypeName(lRef,ntSchema);
  1295. if lRef=lName then
  1296. Exit(True);
  1297. lParamType:=GetAPIType(lRef);
  1298. if Assigned(lParamType) and (lParamType.DependsOn(aData,True)<>dtNone) then
  1299. Exit(True);
  1300. end;
  1301. end;
  1302. If CheckOperationsInput(Itm,aData) then
  1303. exit(True);
  1304. end;
  1305. end;
  1306. function TAPIData.NeedsDeSerialize(aData: TAPITypeData): Boolean;
  1307. var
  1308. Itm : TPathItem;
  1309. I : Integer;
  1310. begin
  1311. Result:=False;
  1312. For I:=0 to FAPI.Paths.Count-1 do
  1313. begin
  1314. Itm:=FAPI.Paths.PathByIndex[I];
  1315. if CheckOperationsOutput(Itm,aData) then
  1316. Exit(True);
  1317. end;
  1318. end;
  1319. function TAPIData.RawToNameType(const aName : string; aNameType: TNameType) : string;
  1320. var
  1321. lType : TAPITypeData;
  1322. begin
  1323. lType:=FindApiType(aName);
  1324. if Assigned(lType) then
  1325. Result:=lType.GetTypeName(aNameType)
  1326. else
  1327. Result:=aName;
  1328. end;
  1329. procedure TAPIData.FinishAutoCreatedType(aName: string; aType: TPascalTypeData; lElementTypeData: TPascalTypeData);
  1330. begin
  1331. if aType.Pascaltype=ptArray then
  1332. begin
  1333. if InterfaceArrayType<>'' then
  1334. aType.InterfaceName:=Format('%s<%s>',[InterfaceArrayType,lElementTypeData.InterfaceName])
  1335. else
  1336. aType.InterfaceName:=lElementTypeData.InterfaceName+ArrayTypeSuffix;
  1337. aType.ImplementationName:=aType.PascalName;
  1338. end;
  1339. Inherited;
  1340. end;
  1341. function TAPIData.GetSchemaTypeName(aSchema: TJSONSchema; aNameType: TNameType): String;
  1342. var
  1343. {
  1344. lTmp,elType : String;
  1345. lType : TPascalType;
  1346. }
  1347. lData : TPascalTypeData;
  1348. begin
  1349. lData:=GetSchemaTypeData(Nil,aSchema,False);
  1350. if assigned(lData) then
  1351. Result:=lData.GetTypeName(aNameType)
  1352. else
  1353. Raise Exception.CreateFmt('No name for schema %s',[aSchema.Name]);
  1354. (*
  1355. lType:=SchemaTypeToPascalType(aSchema,Result);
  1356. if lType in [ptInteger,ptInt64,ptBoolean,ptFloat32,ptFloat64,ptString] then
  1357. begin
  1358. lTmp:=Self.TypeMap[Result];
  1359. if lTmp<>'' then
  1360. Result:=lTmp;
  1361. end
  1362. else if lType=ptArray then
  1363. begin
  1364. if aNameType=ntInterface then
  1365. begin
  1366. ElType:=GetSchemaTypeName(aSchema.Items[0],aNametype);
  1367. Result:=Format(InterfaceArrayType,[elType])
  1368. end
  1369. else
  1370. if DelphiTypes then
  1371. Result:='TArray<'+GetSchemaTypeName(aSchema.Items[0],aNametype)+'>'
  1372. else
  1373. Result:='Array of '+GetSchemaTypeName(aSchema.Items[0],aNametype);
  1374. end;
  1375. end;
  1376. *)
  1377. end;
  1378. function TAPITypeData.CreateProperty(const aAPIName, aPascalName: string): TPascalPropertyData;
  1379. begin
  1380. Result:=TAPIProperty.Create(aAPIName,aPascalName);
  1381. end;
  1382. constructor TAPITypeData.CreateBinaryData(const aContentType: string);
  1383. begin
  1384. Inherited Create(0,ptUnknown,'','TStream',Nil);
  1385. FContentType:=aContentType;
  1386. FBinaryData:=True;
  1387. end;
  1388. function TAPITypeData.AddProperty(const aApiName, aPascalName: String): TAPIProperty;
  1389. begin
  1390. Result:=(Inherited AddProperty(aApiName,aPascalName)) as TAPIProperty;
  1391. end;
  1392. end.