sqldbrestbridge.pp 72 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 2019 by the Free Pascal development team
  4. SQLDB REST dispatcher component.
  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 sqldbrestbridge;
  12. {$mode objfpc}{$H+}
  13. interface
  14. uses
  15. Classes, SysUtils, DB, SQLDB, httpdefs, httproute, fpjson, sqldbrestschema, sqldbrestio, sqldbrestdata, sqldbrestauth;
  16. Type
  17. TRestDispatcherOption = (rdoConnectionInURL, // Route includes connection :Connection/:Resource[/:ID]
  18. rdoExposeMetadata, // expose metadata resource /metadata[/:Resource]
  19. rdoCustomView, // Expose custom view /customview
  20. rdoHandleCORS, // Handle CORS requests
  21. rdoAccessCheckNeedsDB, // Authenticate after connection to database was made.
  22. rdoConnectionResource // Enable connection managament through /_connection[/:Conn] resource
  23. // rdoServerInfo // Enable querying server info through /_serverinfo resource
  24. );
  25. TRestDispatcherOptions = set of TRestDispatcherOption;
  26. TRestDispatcherLogOption = (rloUser, // Include username in log messages, when available
  27. rtloHTTP, // Log HTTP request (remote, URL)
  28. rloResource, // Log resource requests (operation, resource)
  29. rloConnection, // Log database connections (connect to database)
  30. rloAuthentication, // Log authentication attempt
  31. rloSQL, // Log SQL statements. (not on user-supplied connection)
  32. rloResultStatus // Log result status.
  33. );
  34. TRestDispatcherLogOptions = Set of TRestDispatcherLogOption;
  35. Const
  36. DefaultDispatcherOptions = [rdoExposeMetadata];
  37. AllDispatcherLogOptions = [Low(TRestDispatcherLogOption)..High(TRestDispatcherLogOption)];
  38. DefaultDispatcherLogOptions = AllDispatcherLogOptions-[rloSQL];
  39. DefaultLogSQLOptions = LogAllEvents;
  40. Type
  41. { TSQLDBRestConnection }
  42. TSQLDBRestConnection = Class(TCollectionItem)
  43. private
  44. FCharSet: UTF8String;
  45. FConnection: TSQLConnection;
  46. FConnectionType: String;
  47. FDatabaseName: UTF8String;
  48. FEnabled: Boolean;
  49. FHostName: UTF8String;
  50. FName: UTF8String;
  51. FParams: TStrings;
  52. FPassword: UTF8String;
  53. FPort: Word;
  54. FRole: UTF8String;
  55. FSchemaName: UTF8String;
  56. FUserName: UTF8String;
  57. FNotifier : TComponent;
  58. function GetName: UTF8String;
  59. procedure SetConnection(AValue: TSQLConnection);
  60. procedure SetParams(AValue: TStrings);
  61. Protected
  62. Function GetDisplayName: string; override;
  63. // For use in the REST Connection resource
  64. Property SchemaName : UTF8String Read FSchemaName Write FSchemaName;
  65. Public
  66. constructor Create(ACollection: TCollection); override;
  67. Destructor Destroy; override;
  68. Procedure Assign(Source: TPersistent); override;
  69. Procedure ConfigConnection(aConn : TSQLConnection); virtual;
  70. Published
  71. // Always use this connection instance
  72. Property SingleConnection : TSQLConnection Read FConnection Write SetConnection;
  73. // Allow this connection to be used.
  74. Property Enabled : Boolean Read FEnabled Write FEnabled default true;
  75. // TSQLConnector type
  76. property ConnectionType : String Read FConnectionType Write FConnectionType;
  77. // Name for this connection
  78. Property Name : UTF8String Read GetName Write FName;
  79. // Database user password
  80. property Password : UTF8String read FPassword write FPassword;
  81. // Database username
  82. property UserName : UTF8String read FUserName write FUserName;
  83. // Database character set
  84. property CharSet : UTF8String read FCharSet write FCharSet;
  85. // Database hostname
  86. property HostName : UTF8String Read FHostName Write FHostName;
  87. // Database role
  88. Property Role : UTF8String read FRole write FRole;
  89. // Database database name
  90. property DatabaseName : UTF8String Read FDatabaseName Write FDatabaseName;
  91. // Other parameters
  92. Property Params : TStrings Read FParams Write SetParams;
  93. // Port DB is listening on
  94. Property Port : Word Read FPort Write FPort;
  95. end;
  96. { TSQLDBRestConnectionList }
  97. TSQLDBRestConnectionList = Class(TCollection)
  98. private
  99. function GetConn(aIndex : integer): TSQLDBRestConnection;
  100. procedure SetConn(aIndex : integer; AValue: TSQLDBRestConnection);
  101. Public
  102. // Index of connection by name (case insensitive)
  103. Function IndexOfConnection(const aName : UTF8string) : Integer;
  104. // Find connection by name (case insensitive), nil if none found
  105. Function FindConnection(const aName : UTF8string) : TSQLDBRestConnection;
  106. // Add new instance, setting basic properties. Return new instance
  107. Function AddConnection(Const AType,aHostName,aDatabaseName,aUserName,aPassword : UTF8String) : TSQLDBRestConnection;
  108. // Save connection definitions to JSON file.
  109. Procedure SaveToFile(Const aFileName : UTF8String);
  110. // Save connection definitions to JSON stream
  111. Procedure SaveToStream(Const aStream : TStream);
  112. // Return connection definitions as JSON object.
  113. function AsJSON(const aPropName: UTF8String=''): TJSONData; virtual;
  114. // Load connection definitions from JSON file.
  115. Procedure LoadFromFile(Const aFileName : UTF8String);
  116. // Load connection definitions from JSON stream.
  117. Procedure LoadFromStream(Const aStream : TStream);
  118. // Load connection definitions from JSON Object.
  119. Procedure FromJSON(aData: TJSONData;const aPropName: UTF8String=''); virtual;
  120. // Indexed access to connection definitions
  121. Property Connections [aIndex : integer] : TSQLDBRestConnection Read GetConn Write SetConn; default;
  122. end;
  123. { TSQLDBRestSchemaRef }
  124. TSQLDBRestSchemaRef = Class(TCollectionItem)
  125. Private
  126. FEnabled: Boolean;
  127. Fschema: TSQLDBRestSchema;
  128. FNotifier : TComponent;
  129. procedure SetSchema(AValue: TSQLDBRestSchema);
  130. Protected
  131. Function GetDisplayName: String; override;
  132. Public
  133. Constructor Create(ACollection: TCollection); override;
  134. Destructor Destroy; override;
  135. Procedure Assign(Source: TPersistent); override;
  136. Published
  137. // Schema reference
  138. Property Schema : TSQLDBRestSchema Read FSchema Write SetSchema;
  139. // Allow this schema to be used ?
  140. Property Enabled: Boolean Read FEnabled Write FEnabled default true;
  141. end;
  142. { TSQLDBRestSchemaList }
  143. TSQLDBRestSchemaList = Class(TCollection)
  144. private
  145. function GetSchema(aIndex : Integer): TSQLDBRestSchemaRef;
  146. procedure SetSchema(aIndex : Integer; AValue: TSQLDBRestSchemaRef);
  147. Public
  148. Function AddSchema (aSchema : TSQLDBRestSchema) : TSQLDBRestSchemaRef;
  149. Function IndexOfSchema(aSchemaName : String) : Integer;
  150. Property Schemas[aIndex :Integer] : TSQLDBRestSchemaRef Read GetSchema Write SetSchema;default;
  151. end;
  152. { TSQLDBRestDispatcher }
  153. TResourceAuthorizedEvent = Procedure (Sender : TObject; aRequest : TRequest; Const aResource : UTF8String; var AllowResource : Boolean) of object;
  154. TGetConnectionNameEvent = Procedure(Sender : TObject; aRequest : TRequest; Const AResource : String; var AConnectionName : UTF8String) of object;
  155. TGetConnectionEvent = Procedure(Sender : TObject; aDef : TSQLDBRestConnection; var aConnection : TSQLConnection) of object;
  156. TRestExceptionEvent = Procedure(Sender : TObject; aRequest : TRequest; Const AResource : string; E : Exception) of object;
  157. TRestOperationEvent = Procedure(Sender : TObject; aConn: TSQLConnection; aResource : TSQLDBRestResource) of object;
  158. TRestGetFormatEvent = Procedure(Sender : TObject; aRest : TRequest; var aFormat : String) of object;
  159. TRestLogEvent = Procedure(Sender : TObject; aType : TRestDispatcherLogOption; Const aMessage : UTF8String) of object;
  160. TSQLDBRestDispatcher = Class(TComponent)
  161. Private
  162. Class Var FIOClass : TRestIOClass;
  163. Class Var FDBHandlerClass : TSQLDBRestDBHandlerClass;
  164. private
  165. FAdminUserIDs: TStrings;
  166. FCORSAllowCredentials: Boolean;
  167. FCORSAllowedOrigins: String;
  168. FCORSMaxAge: Integer;
  169. FDBLogOptions: TDBEventTypes;
  170. FDispatchOptions: TRestDispatcherOptions;
  171. FInputFormat: String;
  172. FCustomViewResource : TSQLDBRestResource;
  173. FLogOptions: TRestDispatcherLogOptions;
  174. FMetadataResource : TSQLDBRestResource;
  175. FMetadataDetailResource : TSQLDBRestResource;
  176. FConnectionResource : TSQLDBRestResource;
  177. FActive: Boolean;
  178. FAfterDelete: TRestOperationEvent;
  179. FAfterGet: TRestOperationEvent;
  180. FAfterPost: TRestOperationEvent;
  181. FAfterPut: TRestOperationEvent;
  182. FAuthenticator: TRestAuthenticator;
  183. FBaseURL: UTF8String;
  184. FBeforeDelete: TRestOperationEvent;
  185. FBeforeGet: TRestOperationEvent;
  186. FBeforePost: TRestOperationEvent;
  187. FBeforePut: TRestOperationEvent;
  188. FConnections: TSQLDBRestConnectionList;
  189. FDefaultConnection: UTF8String;
  190. FEnforceLimit: Integer;
  191. FOnAllowResource: TResourceAuthorizedEvent;
  192. FOnBasicAuthentication: TBasicAuthenticationEvent;
  193. FOnException: TRestExceptionEvent;
  194. FOnGetConnection: TGetConnectionEvent;
  195. FOnGetConnectionName: TGetConnectionNameEvent;
  196. FOnGetInputFormat: TRestGetFormatEvent;
  197. FOnGetOutputFormat: TRestGetFormatEvent;
  198. FOnLog: TRestLogEvent;
  199. FOutputFormat: String;
  200. FOutputOptions: TRestOutputoptions;
  201. FSchemas: TSQLDBRestSchemaList;
  202. FListRoute: THTTPRoute;
  203. FItemRoute: THTTPRoute;
  204. FConnectionsRoute: THTTPRoute;
  205. FConnectionItemRoute: THTTPRoute;
  206. FMetadataRoute: THTTPRoute;
  207. FMetadataItemRoute: THTTPRoute;
  208. FStatus: TRestStatusConfig;
  209. FStrings: TRestStringsConfig;
  210. procedure SetActive(AValue: Boolean);
  211. procedure SetAdminUserIDS(AValue: TStrings);
  212. procedure SetAuthenticator(AValue: TRestAuthenticator);
  213. procedure SetConnections(AValue: TSQLDBRestConnectionList);
  214. procedure SetDispatchOptions(AValue: TRestDispatcherOptions);
  215. procedure SetSchemas(AValue: TSQLDBRestSchemaList);
  216. procedure SetStatus(AValue: TRestStatusConfig);
  217. procedure SetStrings(AValue: TRestStringsConfig);
  218. Protected
  219. // Logging
  220. Function MustLog(aLog : TRestDispatcherLogOption) : Boolean; inline;
  221. procedure DoSQLLog(Sender: TObject; EventType: TDBEventType; const Msg: String); virtual;
  222. procedure DoLog(aLog: TRestDispatcherLogOption; IO : TRestIO; const aMessage: UTF8String); virtual;
  223. procedure DoLog(aLog: TRestDispatcherLogOption; IO : TRestIO; const Fmt: UTF8String;
  224. Args: array of const);
  225. // Auxiliary methods.
  226. Procedure Loaded; override;
  227. Procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  228. function FindConnection(IO: TRestIO): TSQLDBRestConnection;
  229. // Factory methods. Override these to customize various helper classes.
  230. function CreateConnection: TSQLConnection; virtual;
  231. Function CreateConnectionList : TSQLDBRestConnectionList; virtual;
  232. Function CreateSchemaList : TSQLDBRestSchemaList; virtual;
  233. function CreateRestStrings: TRestStringsConfig; virtual;
  234. function CreateRestStatusConfig: TRestStatusConfig; virtual;
  235. function CreateDBHandler(IO: TRestIO): TSQLDBRestDBHandler; virtual;
  236. function CreateInputStreamer(IO: TRestIO): TRestInputStreamer; virtual;
  237. function CreateOutputStreamer(IO: TRestIO): TRestOutputStreamer; virtual;
  238. function CreateIO(aRequest: TRequest; aResponse: TResponse): TRestIO; virtual;
  239. function GetInputFormat(IO: TRestIO): String; virtual;
  240. function GetOutputFormat(IO: TRestIO): String; virtual;
  241. function GetConnectionName(IO: TRestIO): UTF8String;
  242. function GetSQLConnection(aConnection: TSQLDBRestConnection; Out aTransaction : TSQLTransaction): TSQLConnection; virtual;
  243. procedure DoneSQLConnection(aConnection: TSQLDBRestConnection; AConn: TSQLConnection; aTransaction : TSQLTransaction); virtual;
  244. // Connections dataset API
  245. procedure ConnectionsToDataset(D: TDataset); virtual;
  246. procedure DoConnectionDelete(DataSet: TDataSet); virtual;
  247. procedure DoConnectionPost(DataSet: TDataSet);virtual;
  248. procedure DatasetToConnection(D: TDataset; C: TSQLDBRestConnection); virtual;
  249. procedure ConnectionToDataset(C: TSQLDBRestConnection; D: TDataset); virtual;
  250. procedure DoConnectionResourceAllowed(aSender: TObject; aContext: TBaseRestContext; var allowResource: Boolean);
  251. // Error handling
  252. procedure CreateErrorContent(IO: TRestIO; aCode: Integer; AExtraMessage: UTF8String); virtual;
  253. procedure HandleException(E: Exception; IO: TRestIO); virtual;
  254. // REST request processing
  255. // Extract REST operation type from request
  256. procedure SetDefaultResponsecode(IO: TRestIO); virtual;
  257. // Must set result code and WWW-Authenticate header when applicable
  258. Function AuthenticateRequest(IO : TRestIO; Delayed : Boolean) : Boolean; virtual;
  259. function ExtractRestOperation(aRequest: TRequest;AccessControl : Boolean = false): TRestoperation; virtual;
  260. function FindRestResource(aResource: UTF8String): TSQLDBRestResource; virtual;
  261. function AllowRestResource(aIO : TRestIO): Boolean; virtual;
  262. function AllowRestOperation(aIO: TRestIO): Boolean; virtual;
  263. // Called twice: once before connection is established, once after.
  264. // checks rdoAccessCheckNeedsDB and availability of connection
  265. function CheckResourceAccess(IO: TRestIO): Boolean;
  266. function ExtractRestResourceName(IO: TRestIO): UTF8String; virtual;
  267. // Override if you want to create non-sqldb based resources
  268. function CreateSpecialResourceDataset(IO: TRestIO; AOwner: TComponent): TDataset; virtual;
  269. function IsSpecialResource(aResource: TSQLDBRestResource): Boolean; virtual;
  270. function FindSpecialResource(IO: TRestIO; aResource: UTF8String): TSQLDBRestResource; virtual;
  271. // Special resources for Metadata handling
  272. function CreateMetadataDataset(IO: TRestIO; AOwner: TComponent): TDataset; virtual;
  273. function CreateMetadataDetailDataset(IO: TRestIO; Const aResourceName : String; AOwner: TComponent): TDataset; virtual;
  274. function CreateConnectionDataset(IO: TRestIO; AOwner: TComponent): TDataset; virtual;
  275. function CreateMetadataDetailResource: TSQLDBRestResource; virtual;
  276. function CreateMetadataResource: TSQLDBRestResource; virtual;
  277. Function CreateConnectionResource : TSQLDBRestResource; virtual;
  278. // Custom view handling
  279. function CreateCustomViewResource: TSQLDBRestResource; virtual;
  280. function CreateCustomViewDataset(IO: TRestIO; const aSQL: String; AOwner: TComponent): TDataset;
  281. procedure ResourceToDataset(R: TSQLDBRestResource; D: TDataset); virtual;
  282. procedure SchemasToDataset(D: TDataset);virtual;
  283. // General HTTP handling
  284. procedure DoRegisterRoutes; virtual;
  285. procedure DoHandleEvent(IsBefore : Boolean;IO: TRestIO); virtual;
  286. function ResolvedCORSAllowedOrigins: String; virtual;
  287. procedure HandleCORSRequest(aConnection: TSQLDBRestConnection; IO: TRestIO); virtual;
  288. procedure HandleResourceRequest(aConnection : TSQLDBRestConnection; IO: TRestIO); virtual;
  289. procedure DoHandleRequest(IO: TRestIO); virtual;
  290. Public
  291. Class Procedure SetIOClass (aClass: TRestIOClass);
  292. Class Procedure SetDBHandlerClass (aClass: TSQLDBRestDBHandlerClass);
  293. Constructor Create(AOWner : TComponent); override;
  294. Destructor Destroy; override;
  295. procedure RegisterRoutes;
  296. procedure UnRegisterRoutes;
  297. procedure HandleMetadataRequest(aRequest : TRequest; aResponse : TResponse);
  298. procedure HandleConnRequest(aRequest : TRequest; aResponse : TResponse);
  299. procedure HandleRequest(aRequest : TRequest; aResponse : TResponse);
  300. Function ExposeDatabase(Const aType,aHostName,aDatabaseName,aUserName,aPassword : String; aTables : Array of String; aMinFieldOpts : TRestFieldOptions = []) : TSQLDBRestConnection;
  301. Function ExposeDatabase(Const aType,aHostName,aDatabaseName,aUserName,aPassword : String; aTables : TStrings = nil; aMinFieldOpts : TRestFieldOptions = []) : TSQLDBRestConnection;
  302. Function ExposeConnection(aOwner : TComponent; Const aConnection : TSQLDBRestConnection; aTables : TStrings = nil; aMinFieldOpts : TRestFieldOptions = []) : TSQLDBRestSchema;
  303. Function ExposeConnection(Const aConnection : TSQLDBRestConnection; aTables : TStrings = nil; aMinFieldOpts : TRestFieldOptions = []) : TSQLDBRestSchema;
  304. Published
  305. // Register or unregister HTTP routes
  306. Property Active : Boolean Read FActive Write SetActive;
  307. // List of database connections to connect to
  308. Property Connections : TSQLDBRestConnectionList Read FConnections Write SetConnections;
  309. // List of REST schemas to serve
  310. Property Schemas : TSQLDBRestSchemaList Read FSchemas Write SetSchemas;
  311. // Base URL
  312. property BasePath : UTF8String Read FBaseURL Write FBaseURL;
  313. // Default connection to use if none is detected from request/schema
  314. // This connection will also be used to authenticate the user for connection API,
  315. // so it must be set if you use SQL to authenticate the user.
  316. Property DefaultConnection : UTF8String Read FDefaultConnection Write FDefaultConnection;
  317. // Input/Output strings configuration
  318. Property Strings : TRestStringsConfig Read FStrings Write SetStrings;
  319. // HTTP Status codes configuration
  320. Property Statuses : TRestStatusConfig Read FStatus Write SetStatus;
  321. // default Output options, modifiable by query.
  322. Property OutputOptions : TRestOutputOptions Read FOutputOptions Write FOutputOptions Default allOutputOptions;
  323. // Set this to allow only this input format.
  324. Property InputFormat : String Read FInputFormat Write FInputFormat;
  325. // Set this to allow only this output format.
  326. Property OutputFormat : String Read FOutputFormat Write FOutputFormat;
  327. // Dispatcher options
  328. Property DispatchOptions : TRestDispatcherOptions Read FDispatchOptions Write SetDispatchOptions default DefaultDispatcherOptions;
  329. // Authenticator for requests
  330. Property Authenticator : TRestAuthenticator Read FAuthenticator Write SetAuthenticator;
  331. // If >0, Enforce a limit on output results.
  332. Property EnforceLimit : Integer Read FEnforceLimit Write FEnforceLimit;
  333. // Domains that are allowed to use this REST service
  334. Property CORSAllowedOrigins: String Read FCORSAllowedOrigins Write FCORSAllowedOrigins;
  335. // Access-Control-Max-Age header value. Set to zero not to send the header
  336. Property CORSMaxAge : Integer Read FCORSMaxAge Write FCORSMaxAge;
  337. // Access-Control-Allow-Credentials header value. Set to false not to send the header
  338. Property CORSAllowCredentials : Boolean Read FCORSAllowCredentials Write FCORSAllowCredentials;
  339. // UserIDs of the user(s) that are allowed to see and modify the connection resource.
  340. Property AdminUserIDs : TStrings Read FAdminUserIDs Write SetAdminUserIDS;
  341. // Logging options
  342. Property LogOptions : TRestDispatcherLogOptions Read FLogOptions write FLogOptions default DefaultDispatcherLogOptions;
  343. // SQL Log options. Only for connections managed by RestDispatcher
  344. Property LogSQLOptions : TDBEventTypes Read FDBLogOptions write FDBLogOptions default DefaultLogSQLOptions;
  345. // Called when Basic authentication is sufficient.
  346. Property OnBasicAuthentication : TBasicAuthenticationEvent Read FOnBasicAuthentication Write FOnBasicAuthentication;
  347. // Allow a particular resource or not.
  348. Property OnAllowResource : TResourceAuthorizedEvent Read FOnAllowResource Write FonAllowResource;
  349. // Called when determining the connection name for a request.
  350. Property OnGetConnectionName : TGetConnectionNameEvent Read FOnGetConnectionName Write FOnGetConnectionName;
  351. // Called when an exception happened during treatment of request.
  352. Property OnException : TRestExceptionEvent Read FOnException Write FOnException;
  353. // Called to get an actual connection.
  354. Property OnGetConnection : TGetConnectionEvent Read FOnGetConnection Write FOnGetConnection;
  355. // Called to determine input format based on request.
  356. Property OnGetInputFormat : TRestGetFormatEvent Read FOnGetInputFormat Write FOnGetInputFormat;
  357. // Called to determine output format based on request.
  358. Property OnGetOutputFormat : TRestGetFormatEvent Read FOnGetInputFormat Write FOnGetOutputFormat;
  359. // Called before a GET request.
  360. Property BeforeGet : TRestOperationEvent Read FBeforeGet Write FBeforeGet;
  361. // Called After a GET request.
  362. Property AfterGet : TRestOperationEvent Read FAfterGet Write FAfterGet;
  363. // Called before a PUT request.
  364. Property BeforePut : TRestOperationEvent Read FBeforePut Write FBeforePut;
  365. // Called After a PUT request.
  366. Property AfterPut : TRestOperationEvent Read FAfterPut Write FAfterPut;
  367. // Called before a POST request.
  368. Property BeforePost : TRestOperationEvent Read FBeforePost Write FBeforePost;
  369. // Called After a POST request.
  370. Property AfterPost : TRestOperationEvent Read FAfterPost Write FAfterPost;
  371. // Called before a DELETE request.
  372. Property BeforeDelete : TRestOperationEvent Read FBeforeDelete Write FBeforeDelete;
  373. // Called After a DELETE request.
  374. Property AfterDelete : TRestOperationEvent Read FAfterDelete Write FAfterDelete;
  375. // Called when logging
  376. Property OnLog : TRestLogEvent Read FOnLog Write FOnLog;
  377. end;
  378. Const
  379. LogNames : Array[TRestDispatcherLogOption] of string = (
  380. 'User','HTTP','Resource','Connection','Authentication','SQL','Result'
  381. );
  382. implementation
  383. uses fpjsonrtti, DateUtils, bufdataset, sqldbrestjson, sqldbrestconst;
  384. Type
  385. { TRestBufDataset }
  386. TRestBufDataset = class (TBufDataset)
  387. protected
  388. procedure LoadBlobIntoBuffer(FieldDef: TFieldDef; ABlobBuf: PBufBlobField); override;
  389. end;
  390. { TSchemaFreeNotifier }
  391. TSchemaFreeNotifier = Class(TComponent)
  392. FRef : TSQLDBRestSchemaRef;
  393. Procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  394. end;
  395. { TConnectionFreeNotifier }
  396. TConnectionFreeNotifier = Class(TComponent)
  397. FRef : TSQLDBRestConnection;
  398. Procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  399. end;
  400. { TRestBufDataset }
  401. procedure TRestBufDataset.LoadBlobIntoBuffer(FieldDef: TFieldDef; ABlobBuf: PBufBlobField);
  402. begin
  403. If (FieldDef=Nil) or (aBlobBuf=Nil) then
  404. exit;
  405. end;
  406. { TConnectionFreeNotifier }
  407. procedure TConnectionFreeNotifier.Notification(AComponent: TComponent; Operation: TOperation);
  408. begin
  409. inherited Notification(AComponent, Operation);
  410. if (Operation=opRemove) and Assigned(FRef) and (Fref.SingleConnection=aComponent) then
  411. Fref.SingleConnection:=Nil;
  412. end;
  413. { TSQLDBRestSchemaList }
  414. function TSQLDBRestSchemaList.GetSchema(aIndex : Integer): TSQLDBRestSchemaRef;
  415. begin
  416. Result:=TSQLDBRestSchemaRef(Items[aIndex]);
  417. end;
  418. procedure TSQLDBRestSchemaList.SetSchema(aIndex : Integer; AValue: TSQLDBRestSchemaRef);
  419. begin
  420. Items[aIndex]:=aValue;
  421. end;
  422. function TSQLDBRestSchemaList.AddSchema(aSchema: TSQLDBRestSchema): TSQLDBRestSchemaRef;
  423. begin
  424. Result:=(Add as TSQLDBRestSchemaRef);
  425. Result.Schema:=aSchema;
  426. Result.Enabled:=True;
  427. end;
  428. function TSQLDBRestSchemaList.IndexOfSchema(aSchemaName: String): Integer;
  429. begin
  430. Result:=Count-1;
  431. While (Result>=0) and Not (Assigned(GetSchema(Result).Schema) and SameText(GetSchema(Result).Schema.Name,aSchemaName)) do
  432. Dec(Result);
  433. end;
  434. { TSQLDBRestDispatcher }
  435. procedure TSQLDBRestDispatcher.SetConnections(AValue: TSQLDBRestConnectionList);
  436. begin
  437. if FConnections=AValue then Exit;
  438. FConnections.Assign(AValue);
  439. end;
  440. procedure TSQLDBRestDispatcher.SetDispatchOptions(AValue: TRestDispatcherOptions);
  441. begin
  442. if (rdoConnectionResource in aValue) then
  443. Include(aValue,rdoConnectionInURL);
  444. if FDispatchOptions=AValue then Exit;
  445. FDispatchOptions:=AValue;
  446. end;
  447. procedure TSQLDBRestDispatcher.DoConnectionResourceAllowed(aSender: TObject;
  448. aContext: TBaseRestContext; var allowResource: Boolean);
  449. begin
  450. AllowResource:=(AdminUserIDs.Count=0) or (AdminUserIDs.IndexOf(aContext.UserID)<>-1);
  451. end;
  452. procedure TSQLDBRestDispatcher.SetActive(AValue: Boolean);
  453. begin
  454. if FActive=AValue then
  455. Exit;
  456. if Not (csLoading in ComponentState) then
  457. begin
  458. if AValue then
  459. DoRegisterRoutes
  460. else
  461. UnRegisterRoutes;
  462. end;
  463. FActive:=AValue;
  464. end;
  465. procedure TSQLDBRestDispatcher.SetAdminUserIDS(AValue: TStrings);
  466. begin
  467. if FAdminUserIDs=AValue then Exit;
  468. FAdminUserIDs.Assign(AValue);
  469. end;
  470. procedure TSQLDBRestDispatcher.SetAuthenticator(AValue: TRestAuthenticator);
  471. begin
  472. if FAuthenticator=AValue then Exit;
  473. if Assigned(FAuthenticator) then
  474. FAuthenticator.RemoveFreeNotification(Self);
  475. FAuthenticator:=AValue;
  476. if Assigned(FAuthenticator) then
  477. FAuthenticator.FreeNotification(Self);
  478. end;
  479. procedure TSQLDBRestDispatcher.SetSchemas(AValue: TSQLDBRestSchemaList);
  480. begin
  481. if FSchemas=AValue then Exit;
  482. FSchemas.Assign(AValue);
  483. end;
  484. procedure TSQLDBRestDispatcher.SetStatus(AValue: TRestStatusConfig);
  485. begin
  486. if FStatus=AValue then Exit;
  487. FStatus.Assign(AValue);
  488. end;
  489. procedure TSQLDBRestDispatcher.SetStrings(AValue: TRestStringsConfig);
  490. begin
  491. if FStrings=AValue then Exit;
  492. FStrings.Assign(AValue);
  493. end;
  494. function TSQLDBRestDispatcher.MustLog(aLog: TRestDispatcherLogOption): Boolean;
  495. begin
  496. Result:=aLog in FLogOptions;
  497. end;
  498. procedure TSQLDBRestDispatcher.DoSQLLog(Sender: TObject; EventType: TDBEventType; const Msg: String);
  499. Const
  500. EventNames : Array [TDBEventType] of string =
  501. ('Custom','Prepare', 'Execute', 'Fetch', 'Commit', 'RollBack', 'ParamValue', 'ActualSQL');
  502. Var
  503. aMsg : UTF8String;
  504. begin
  505. if not MustLog(rloSQl) then // avoid string ops
  506. exit;
  507. aMsg:=EventNames[EventType]+': '+Msg;
  508. if Sender is TRestIO then
  509. DoLog(rloSQL,TRestIO(Sender),aMsg)
  510. else
  511. DoLog(rloSQL,Nil,aMsg)
  512. end;
  513. procedure TSQLDBRestDispatcher.DoLog(aLog: TRestDispatcherLogOption; IO: TRestIO; const aMessage: UTF8String);
  514. Var
  515. aMsg : UTF8String;
  516. begin
  517. aMsg:='';
  518. if MustLog(aLog) and Assigned(FOnLog) then
  519. begin
  520. if MustLog(rloUser) and Assigned(IO) then
  521. begin
  522. if IO.UserID='' then
  523. aMsg:='(User: ?) '
  524. else
  525. aMsg:=Format('(User: %s) ',[IO.UserID]);
  526. end;
  527. aMsg:=aMsg+aMessage;
  528. FOnLog(Self,aLog,aMsg);
  529. end;
  530. end;
  531. procedure TSQLDBRestDispatcher.DoLog(aLog: TRestDispatcherLogOption;IO: TRestIO;
  532. const Fmt: UTF8String; Args: array of const);
  533. Var
  534. S : UTF8string;
  535. begin
  536. if not MustLog(aLog) then exit; // avoid expensive format
  537. try
  538. S:=Format(fmt,Args); // Encode ?
  539. except
  540. on E : exception do
  541. S:=Format('Error "%s" formatting "%s" with %d arguments: %s',[E.ClassName,Fmt,Length(Args),E.Message])
  542. end;
  543. DoLog(aLog,IO,S);
  544. end;
  545. procedure TSQLDBRestDispatcher.Loaded;
  546. begin
  547. inherited Loaded;
  548. if FActive then
  549. RegisterRoutes;
  550. end;
  551. procedure TSQLDBRestDispatcher.HandleConnRequest(aRequest : TRequest; aResponse : TResponse);
  552. begin
  553. aRequest.RouteParams['resource']:=Strings.ConnectionResourceName;
  554. HandleRequest(aRequest,aResponse);
  555. end;
  556. procedure TSQLDBRestDispatcher.HandleMetadataRequest(aRequest: TRequest;aResponse: TResponse);
  557. Var
  558. LogMsg,UN : UTF8String;
  559. begin
  560. if MustLog(rtloHTTP) then
  561. begin
  562. LogMsg:='';
  563. With aRequest do
  564. begin
  565. UN:=RemoteHost;
  566. if (UN='') then
  567. UN:=RemoteAddr;
  568. if (UN<>'') then
  569. LogMsg:='From: '+UN+'; ';
  570. LogMsg:=LogMsg+'URL: '+URL;
  571. end;
  572. UN:=TRestBasicAuthenticator.ExtractUserName(aRequest);
  573. if (UN<>'?') then
  574. LogMsg:='User: '+UN+LogMsg;
  575. DoLog(rtloHTTP,Nil,LogMsg);
  576. end;
  577. aRequest.RouteParams['resource']:=Strings.MetadataResourceName;
  578. HandleRequest(aRequest,aResponse);
  579. end;
  580. procedure TSQLDBRestDispatcher.DoRegisterRoutes;
  581. Var
  582. Res,C : UTF8String;
  583. begin
  584. Res:=IncludeHTTPPathDelimiter(BasePath);
  585. if (rdoConnectionResource in DispatchOptions) then
  586. begin
  587. C:=Strings.GetRestString(rpConnectionResourceName);
  588. FConnectionsRoute:=HTTPRouter.RegisterRoute(res+C,@HandleConnRequest);
  589. FConnectionItemRoute:=HTTPRouter.RegisterRoute(res+C+'/:id',@HandleConnRequest);
  590. end;
  591. if (rdoConnectionInURL in DispatchOptions) then
  592. begin
  593. C:=Strings.GetRestString(rpMetadataResourceName);
  594. FMetadataRoute:=HTTPRouter.RegisterRoute(res+C,@HandleMetaDataRequest);
  595. FMetadataItemRoute:=HTTPRouter.RegisterRoute(res+C+'/:id',@HandleMetaDataRequest);
  596. Res:=Res+':connection/';
  597. end;
  598. Res:=Res+':resource';
  599. FListRoute:=HTTPRouter.RegisterRoute(res,@HandleRequest);
  600. FItemRoute:=HTTPRouter.RegisterRoute(Res+'/:id',@HandleRequest);
  601. end;
  602. function TSQLDBRestDispatcher.GetInputFormat(IO : TRestIO) : String;
  603. // Order is: InputFormat setting, Content-type, input format, output format if it exists as input
  604. Var
  605. U : UTF8String;
  606. D : TStreamerDef;
  607. begin
  608. Result:=InputFormat;
  609. if (Result='') then
  610. begin
  611. if Result='' then
  612. if IO.GetVariable(Fstrings.GetRestString(rpInputFormat),U,[vsQuery])<>vsNone then
  613. Result:=U;
  614. if (Result='') and (IO.Request.ContentType<>'') then
  615. begin
  616. D:=TStreamerFactory.Instance.FindStreamerByContentType(rstInput,IO.Request.ContentType);
  617. if D<>Nil then
  618. Result:=D.MyName;
  619. end;
  620. if (Result='') then
  621. if IO.GetVariable(Fstrings.GetRestString(rpOutputFormat),U,[vsQuery])<>vsNone then
  622. begin
  623. if TStreamerFactory.Instance.FindStreamerByName(rstInput,U)<>Nil then
  624. Result:=U;
  625. end;
  626. end;
  627. If Assigned(FOnGetInputFormat) then
  628. FOnGetInputFormat(Self,IO.Request,Result)
  629. end;
  630. function TSQLDBRestDispatcher.GetOutputFormat(IO : TRestIO) : String;
  631. // Order is: OutputFormat setting, output format, input Content-type, input format if it exists as output
  632. Var
  633. U : UTF8String;
  634. D : TStreamerDef;
  635. begin
  636. Result:=OutputFormat;
  637. if (Result='') then
  638. begin
  639. if IO.GetVariable(Fstrings.GetRestString(rpOutputFormat),U,[vsQuery])<>vsNone then
  640. Result:=U;
  641. if (Result='') and (IO.Request.ContentType<>'') then
  642. begin
  643. D:=TStreamerFactory.Instance.FindStreamerByContentType(rstOutput,IO.Request.ContentType);
  644. if D<>Nil then
  645. Result:=D.MyName;
  646. end;
  647. if Result='' then
  648. if IO.GetVariable(Fstrings.GetRestString(rpInputFormat),U,[vsQuery])<>vsNone then
  649. begin
  650. if TStreamerFactory.Instance.FindStreamerByName(rstOutput,U)<>Nil then
  651. Result:=U;
  652. end;
  653. end;
  654. If Assigned(FOnGetOutputFormat) then
  655. FOnGetOutputFormat(Self,IO.Request,Result)
  656. end;
  657. function TSQLDBRestDispatcher.CreateInputStreamer(IO : TRestIO): TRestInputStreamer;
  658. Var
  659. D : TStreamerDef;
  660. aName : String;
  661. begin
  662. aName:=GetInputFormat(IO);
  663. if aName='' then
  664. aName:='json';
  665. D:=TStreamerFactory.Instance.FindStreamerByName(rstInput,aName);
  666. if (D=Nil) then
  667. Raise ESQLDBRest.CreateFmt(FStatus.GetStatusCode(rsInvalidParam),SErrUnknownOrUnSupportedFormat,[aName]);
  668. Result:=TRestInputStreamer(D.MyClass.Create(IO.RequestContentStream,Fstrings,FStatus,@IO.DoGetVariable));
  669. end;
  670. function TSQLDBRestDispatcher.CreateOutputStreamer(IO : TRestIO): TRestOutputStreamer;
  671. Var
  672. D : TStreamerDef;
  673. aName : String;
  674. begin
  675. aName:=GetOutputFormat(IO);
  676. if aName='' then
  677. aName:='json';
  678. D:=TStreamerFactory.Instance.FindStreamerByName(rstOutput,aName);
  679. if (D=Nil) then
  680. Raise ESQLDBRest.CreateFmt(FStatus.GetStatusCode(rsInvalidParam),SErrUnknownOrUnSupportedFormat,[aName]);
  681. Result:=TRestOutputStreamer(D.MyClass.Create(IO.Response.ContentStream,Fstrings,FStatus,@IO.DoGetVariable));
  682. end;
  683. function TSQLDBRestDispatcher.CreateIO(aRequest: TRequest; aResponse: TResponse): TRestIO;
  684. Var
  685. aInput : TRestInputStreamer;
  686. aOutput : TRestOutputStreamer;
  687. begin
  688. aInput:=Nil;
  689. aOutput:=Nil;
  690. Result:=FIOClass.Create(aRequest,aResponse);
  691. try
  692. // Set up output
  693. Result.Response.ContentStream:=TMemoryStream.Create;
  694. Result.Response.FreeContentStream:=True;
  695. Result.SetRestStatuses(FStatus);
  696. Result.SetRestStrings(FStrings);
  697. aInput:=CreateInputStreamer(Result);
  698. aoutPut:=CreateOutPutStreamer(Result);
  699. Result.SetIO(aInput,aOutput);
  700. aInput:=Nil;
  701. aOutput:=Nil;
  702. aResponse.ContentType:=Result.RestOutput.GetContentType;
  703. Result.RestOutput.OutputOptions:=Result.GetRequestOutputOptions(OutputOptions);
  704. except
  705. On E : Exception do
  706. begin
  707. FreeAndNil(aInput);
  708. FreeAndNil(aOutput);
  709. FreeAndNil(Result);
  710. Raise;
  711. end;
  712. end;
  713. end;
  714. procedure TSQLDBRestDispatcher.CreateErrorContent(IO : TRestIO; aCode : Integer; AExtraMessage: UTF8String);
  715. begin
  716. IO.Response.Code:=aCode;
  717. IO.Response.CodeText:=aExtraMessage;
  718. IO.RestOutput.CreateErrorContent(aCode,aExtraMessage);
  719. IO.Response.SendResponse;
  720. end;
  721. class procedure TSQLDBRestDispatcher.SetIOClass(aClass: TRestIOClass);
  722. begin
  723. FIOClass:=aClass;
  724. if FIOClass=Nil then
  725. FIOClass:=TRestIO;
  726. end;
  727. class procedure TSQLDBRestDispatcher.SetDBHandlerClass(aClass: TSQLDBRestDBHandlerClass);
  728. begin
  729. FDBHandlerClass:=aClass;
  730. if FDBHandlerClass=Nil then
  731. FDBHandlerClass:=TSQLDBRestDBHandler;
  732. end;
  733. constructor TSQLDBRestDispatcher.Create(AOWner: TComponent);
  734. begin
  735. inherited Create(AOWner);
  736. FStrings:=CreateRestStrings;
  737. FConnections:=CreateConnectionList;
  738. FSchemas:=CreateSchemaList;
  739. FOutputOptions:=allOutputOptions;
  740. FDispatchOptions:=DefaultDispatcherOptions;
  741. FLogOptions:=DefaultDispatcherLogOptions;
  742. FDBLogOptions:=DefaultLogSQLOptions;
  743. FStatus:=CreateRestStatusConfig;
  744. FCORSMaxAge:=SecsPerDay;
  745. FCORSAllowCredentials:=True;
  746. FAdminUserIDs:=TStringList.Create;
  747. end;
  748. destructor TSQLDBRestDispatcher.Destroy;
  749. begin
  750. Authenticator:=Nil;
  751. FreeAndNil(FAdminUserIDs);
  752. FreeAndNil(FCustomViewResource);
  753. FreeAndNil(FMetadataResource);
  754. FreeAndNil(FMetadataDetailResource);
  755. FreeAndNil(FConnectionResource);
  756. FreeAndNil(FSchemas);
  757. FreeAndNil(FConnections);
  758. FreeAndNil(FStrings);
  759. FreeAndNil(FStatus);
  760. inherited Destroy;
  761. end;
  762. function TSQLDBRestDispatcher.CreateRestStrings : TRestStringsConfig;
  763. begin
  764. Result:=TRestStringsConfig.Create
  765. end;
  766. function TSQLDBRestDispatcher.CreateRestStatusConfig: TRestStatusConfig;
  767. begin
  768. Result:=TRestStatusConfig.Create;
  769. end;
  770. function TSQLDBRestDispatcher.ExtractRestResourceName(IO: TRestIO): UTF8String;
  771. begin
  772. Result:=IO.Request.RouteParams['resource'];
  773. if (Result='') then
  774. Result:=IO.Request.QueryFields.Values[Strings.ResourceParam];
  775. end;
  776. function TSQLDBRestDispatcher.AllowRestResource(aIO: TRestIO): Boolean;
  777. begin
  778. Result:=aIO.Resource.AllowResource(aIO.RestContext);
  779. if Assigned(FOnAllowResource) then
  780. FOnAllowResource(Self,aIO.Request,aIO.ResourceName,Result);
  781. end;
  782. function TSQLDBRestDispatcher.CreateCustomViewResource: TSQLDBRestResource;
  783. begin
  784. Result:=TCustomViewResource.Create(Nil);
  785. Result.ResourceName:=FStrings.GetRestString(rpCustomViewResourceName);
  786. if rdoHandleCORS in DispatchOptions then
  787. Result.AllowedOperations:=[roGet,roOptions,roHead]
  788. else
  789. Result.AllowedOperations:=[roGet,roHead];
  790. end;
  791. function TSQLDBRestDispatcher.CreateMetadataResource: TSQLDBRestResource;
  792. Var
  793. O : TRestOperation;
  794. S : String;
  795. begin
  796. Result:=TSQLDBRestResource.Create(Nil);
  797. Result.ResourceName:=Strings.GetRestString(rpMetadataResourceName);
  798. if rdoHandleCORS in DispatchOptions then
  799. Result.AllowedOperations:=[roGet,roOptions,roHead]
  800. else
  801. Result.AllowedOperations:=[roGet,roHead];
  802. Result.Fields.AddField('name',rftString,[foRequired]).MaxLen:=255;
  803. Result.Fields.AddField('schemaName',rftString,[foRequired]).MaxLen:=255;
  804. for O in TRestOperation do
  805. if O<>roUnknown then
  806. begin
  807. Str(O,S);
  808. delete(S,1,2);
  809. Result.Fields.AddField(S,rftBoolean,[foRequired]);
  810. end;
  811. end;
  812. function TSQLDBRestDispatcher.CreateConnectionResource: TSQLDBRestResource;
  813. Var
  814. Def : TRestFieldOptions;
  815. begin
  816. Def:=[foInInsert,foInUpdate,foFilter];
  817. Result:=TSQLDBRestResource.Create(Nil);
  818. Result.ResourceName:=Strings.GetRestString(rpConnectionResourceName);
  819. Result.AllowedOperations:=[roGet,roPut,roPost,roDelete];
  820. if rdoHandleCORS in DispatchOptions then
  821. Result.AllowedOperations:=Result.AllowedOperations+[roOptions,roHead];
  822. Result.Fields.AddField('name',rftString,Def+[foInKey,foRequired]);
  823. Result.Fields.AddField('dbType',rftString,Def+[foRequired]);
  824. Result.Fields.AddField('dbName',rftString,Def+[foRequired]);
  825. Result.Fields.AddField('dbHostName',rftString,Def);
  826. Result.Fields.AddField('dbUserName',rftString,Def);
  827. Result.Fields.AddField('dbPassword',rftString,Def);
  828. Result.Fields.AddField('dbCharSet',rftString,Def);
  829. Result.Fields.AddField('dbRole',rftString,Def);
  830. Result.Fields.AddField('dbPort',rftInteger,Def);
  831. Result.Fields.AddField('enabled',rftBoolean,Def);
  832. Result.Fields.AddField('expose',rftBoolean,Def);
  833. Result.Fields.AddField('exposeSchemaName',rftString,Def);
  834. Result.OnResourceAllowed:=@DoConnectionResourceAllowed;
  835. end;
  836. function TSQLDBRestDispatcher.CreateMetadataDetailResource: TSQLDBRestResource;
  837. Var
  838. O : TRestFieldOption;
  839. S : String;
  840. begin
  841. Result:=TSQLDBRestResource.Create(Nil);
  842. Result.ResourceName:='metaDataField';
  843. if rdoHandleCORS in DispatchOptions then
  844. Result.AllowedOperations:=[roGet,roOptions,roHead]
  845. else
  846. Result.AllowedOperations:=[roGet,roHead];
  847. Result.Fields.AddField('name',rftString,[]).MaxLen:=255;
  848. Result.Fields.AddField('type',rftString,[]).MaxLen:=20;
  849. Result.Fields.AddField('maxlen',rftInteger,[]);
  850. Result.Fields.AddField('format',rftString,[]).MaxLen:=50;
  851. for O in TRestFieldOption do
  852. begin
  853. Str(O,S);
  854. delete(S,1,2);
  855. Result.Fields.AddField(S,rftBoolean,[]);
  856. end;
  857. end;
  858. function TSQLDBRestDispatcher.FindSpecialResource(IO : TRestIO; aResource: UTF8String): TSQLDBRestResource;
  859. Function IsCustomView : Boolean;inline;
  860. begin
  861. Result:=(rdoCustomView in DispatchOptions)
  862. and SameText(aResource,Strings.GetRestString(rpCustomViewResourceName));
  863. end;
  864. Function IsMetadata : Boolean;inline;
  865. begin
  866. Result:=(rdoExposeMetadata in DispatchOptions)
  867. and SameText(aResource,Strings.GetRestString(rpMetaDataResourceName));
  868. end;
  869. Function IsConnection : Boolean;inline;
  870. begin
  871. Result:=(rdoConnectionResource in DispatchOptions)
  872. and SameText(aResource,Strings.GetRestString(rpConnectionResourceName));
  873. end;
  874. Var
  875. N : UTF8String;
  876. begin
  877. Result:=Nil;
  878. If isCustomView then
  879. begin
  880. if FCustomViewResource=Nil then
  881. FCustomViewResource:=CreateCustomViewResource;
  882. Result:=FCustomViewResource;
  883. end
  884. else if IsConnection then
  885. begin
  886. if FConnectionResource=Nil then
  887. FConnectionResource:=CreateConnectionResource;
  888. Result:=FConnectionResource;
  889. end
  890. else If isMetadata then
  891. if (IO.GetVariable('ID',N,[vsRoute,vsQuery])=vsNone) then
  892. begin
  893. if FMetadataResource=Nil then
  894. FMetadataResource:=CreateMetadataResource;
  895. Result:=FMetadataResource;
  896. end
  897. else
  898. begin
  899. if FindRestResource(N)<>Nil then
  900. begin
  901. if FMetadataDetailResource=Nil then
  902. FMetadataDetailResource:=CreateMetadataDetailResource;
  903. Result:=FMetadataDetailResource;
  904. end;
  905. end
  906. end;
  907. function TSQLDBRestDispatcher.FindRestResource(aResource: UTF8String): TSQLDBRestResource;
  908. Var
  909. I : integer;
  910. begin
  911. Result:=Nil;
  912. I:=0;
  913. While (Result=Nil) and (I<Schemas.Count) do
  914. begin
  915. if Schemas[i].Enabled then
  916. Result:=Schemas[i].Schema.Resources.FindResourceByName(aResource);
  917. Inc(I);
  918. end;
  919. end;
  920. function TSQLDBRestDispatcher.ExtractRestOperation(aRequest: TRequest;AccessControl : Boolean = false): TRestoperation;
  921. Var
  922. M : String;
  923. begin
  924. Result:=roUnknown;
  925. if not AccessControl then
  926. M:=aRequest.Method
  927. else
  928. M:=aRequest.CustomHeaders.Values['Access-Control-Request-Method'];
  929. Case lowercase(M) of
  930. 'get' : Result:=roGet;
  931. 'put' : Result:=roPut;
  932. 'post' : Result:=roPost;
  933. 'delete' : Result:=roDelete;
  934. 'options' : Result:=roOptions;
  935. 'head' : Result:=roHead;
  936. end;
  937. end;
  938. Type
  939. { TRestSQLConnector }
  940. { THackSQLConnector }
  941. THackSQLConnector = Class(TSQLConnection)
  942. Public
  943. function DoGetNextValueSQL(const SequenceName: string; IncrementBy: Integer): string;
  944. end;
  945. TRestSQLConnector = Class(TSQLConnector)
  946. Private
  947. FUse : Integer;
  948. FRequestCount : INteger;
  949. Protected
  950. function GetNextValueSQL(const SequenceName: string; IncrementBy: Integer): string; override;
  951. Procedure StartUsing;
  952. Function DoneUsing : Boolean;
  953. end;
  954. { THackSQLConnector }
  955. function THackSQLConnector.DoGetNextValueSQL(const SequenceName: string; IncrementBy: Integer): string;
  956. begin
  957. Result:=GetNextValueSQL(SequenceName,IncrementBy);
  958. end;
  959. { TRestSQLConnector }
  960. function TRestSQLConnector.GetNextValueSQL(const SequenceName: string; IncrementBy: Integer): string;
  961. begin
  962. Result:=THackSQLConnector(Proxy).DoGetNextValueSQL(SequenceName, IncrementBy);
  963. end;
  964. procedure TRestSQLConnector.StartUsing;
  965. begin
  966. InterLockedIncrement(FUse);
  967. Inc(FRequestCount);
  968. end;
  969. function TRestSQLConnector.DoneUsing: Boolean;
  970. begin
  971. InterLockedDecrement(Fuse);
  972. Result:=(FRequestCount>100) and (FUse=0);
  973. end;
  974. function TSQLDBRestDispatcher.CreateConnection : TSQLConnection;
  975. begin
  976. Result:=TRestSQLConnector.Create(Self);
  977. end;
  978. function TSQLDBRestDispatcher.GetSQLConnection(
  979. aConnection: TSQLDBRestConnection; out aTransaction: TSQLTransaction
  980. ): TSQLConnection;
  981. begin
  982. Result:=Nil;
  983. aTransaction:=Nil;
  984. if aConnection=Nil then
  985. exit;
  986. Result:=aConnection.SingleConnection;
  987. if (Result=Nil) then
  988. begin
  989. if Assigned(OnGetConnection) then
  990. OnGetConnection(Self,aConnection,Result);
  991. if (Result=Nil) then
  992. begin
  993. Result:=CreateConnection;
  994. aConnection.ConfigConnection(Result);
  995. aConnection.SingleConnection:=Result;
  996. end;
  997. end;
  998. If (Result is TRestSQLConnector) then
  999. TRestSQLConnector(Result).StartUsing;
  1000. aTransaction:=TSQLTransaction.Create(Self);
  1001. aTransaction.Database:=Result;
  1002. end;
  1003. procedure TSQLDBRestDispatcher.DoHandleEvent(IsBefore: Boolean; IO: TRestIO);
  1004. Var
  1005. R : TRestOperationEvent;
  1006. begin
  1007. R:=Nil;
  1008. if isBefore then
  1009. Case IO.Operation of
  1010. roGet : R:=FBeforeGet;
  1011. roPut : R:=FBeforePut;
  1012. roPost : R:=FBeforePost;
  1013. roDelete : R:=FBeforeDelete;
  1014. end
  1015. else
  1016. Case IO.Operation of
  1017. roGet : R:=FAfterGet;
  1018. roPut : R:=FAfterPut;
  1019. roPost : R:=FAfterPost;
  1020. roDelete : R:=FAfterDelete;
  1021. end;
  1022. If Assigned(R) then
  1023. R(Self,IO.Connection,IO.Resource)
  1024. end;
  1025. procedure TSQLDBRestDispatcher.DoneSQLConnection(
  1026. aConnection: TSQLDBRestConnection; AConn: TSQLConnection;
  1027. aTransaction: TSQLTransaction);
  1028. Var
  1029. NeedNil : Boolean;
  1030. begin
  1031. FreeAndNil(aTransaction);
  1032. if (aConn is TRestSQLConnector) then
  1033. begin
  1034. NeedNil:= (aConnection.SingleConnection=aConn) ;
  1035. if TRestSQLConnector(aConn).DoneUsing then
  1036. FreeAndNil(aConn);
  1037. If NeedNil then
  1038. aConnection.SingleConnection:=Nil;
  1039. end;
  1040. end;
  1041. function TSQLDBRestDispatcher.CreateDBHandler(IO: TRestIO): TSQLDBRestDBHandler;
  1042. begin
  1043. Result:=FDBHandlerClass.Create(Self) ;
  1044. Result.Init(IO,FStrings,TSQLQuery);
  1045. Result.EnforceLimit:=Self.EnforceLimit;
  1046. end;
  1047. procedure TSQLDBRestDispatcher.SetDefaultResponsecode(IO : TRestIO);
  1048. Const
  1049. DefaultCodes : Array[TRestOperation] of TRestStatus = (rsError,rsGetOK,rsPOSTOK,rsPUTOK,rsDeleteOK,rsCORSOK,rsGetOK);
  1050. DefaultTexts : Array[TRestOperation] of string = ('Internal Error','OK','Created','OK','No content','OK','OK');
  1051. Var
  1052. aCode : TRestStatus;
  1053. aText : String;
  1054. begin
  1055. aCode:=DefaultCodes[IO.Operation];
  1056. aText:=DefaultTexts[IO.Operation];
  1057. if IO.Response.Code=0 then
  1058. IO.Response.Code:=FStatus.GetStatusCode(aCode);
  1059. if (IO.Response.CodeText='') then
  1060. IO.Response.CodeText:=aText;
  1061. end;
  1062. function TSQLDBRestDispatcher.IsSpecialResource(aResource: TSQLDBRestResource
  1063. ): Boolean;
  1064. begin
  1065. Result:=(aResource<>Nil);
  1066. if not Result then exit;
  1067. Result:=(aResource=FMetadataResource) or
  1068. (aResource=FMetadataDetailResource) or
  1069. (aResource=FConnectionResource) or
  1070. (aResource=FCustomViewResource);
  1071. end;
  1072. procedure TSQLDBRestDispatcher.SchemasToDataset(D: TDataset);
  1073. Var
  1074. S : TSQLDBRestSchema;
  1075. R : TSQLDBRestResource;
  1076. O : TRestOperation;
  1077. I,J : Integer;
  1078. SO : String;
  1079. FName,FSchema : TField;
  1080. FOperations : Array[TRestOperation] of TField;
  1081. begin
  1082. FName:=D.FieldByName('name');
  1083. FSchema:=D.FieldByName('schemaName');
  1084. for O in TRestOperation do
  1085. if O<>roUnknown then
  1086. begin
  1087. Str(O,SO);
  1088. delete(SO,1,2);
  1089. FOperations[O]:=D.FieldByName(SO);
  1090. end;
  1091. For I:=0 to Schemas.Count-1 do
  1092. if Schemas[I].Enabled then
  1093. begin
  1094. S:=Schemas[I].Schema;
  1095. For J:=0 to S.Resources.Count-1 do
  1096. begin
  1097. R:=S.Resources[J];
  1098. if R.Enabled and R.InMetadata then
  1099. begin
  1100. D.Append;
  1101. FName.AsString:=R.ResourceName;
  1102. FSchema.AsString:=S.Name;
  1103. for O in TRestOperation do
  1104. if O<>roUnknown then
  1105. FOperations[O].AsBoolean:=O in R.AllowedOperations;
  1106. end;
  1107. D.Post;
  1108. end;
  1109. end;
  1110. end;
  1111. function TSQLDBRestDispatcher.CreateMetadataDataset(IO: TRestIO;
  1112. AOwner: TComponent): TDataset;
  1113. Var
  1114. BD : TRestBufDataset;
  1115. O : TRestOperation;
  1116. SO : String;
  1117. begin
  1118. if IO=Nil then exit;
  1119. BD:=TRestBufDataset.Create(aOwner);
  1120. try
  1121. Result:=BD;
  1122. Result.FieldDefs.Add('name',ftString,255,False);
  1123. Result.FieldDefs.Add('schemaName',ftString,255,False);
  1124. for O in TRestOperation do
  1125. if O<>roUnknown then
  1126. begin
  1127. Str(O,SO);
  1128. delete(SO,1,2);
  1129. Result.FieldDefs.Add(SO,ftBoolean,0,False);
  1130. end;
  1131. BD.CreateDataset;
  1132. SchemasToDataset(BD);
  1133. BD.First;
  1134. except
  1135. BD.Free;
  1136. Raise;
  1137. end;
  1138. end;
  1139. procedure TSQLDBRestDispatcher.ResourceToDataset(R: TSQLDBRestResource;
  1140. D: TDataset);
  1141. Var
  1142. F : TSQLDBRestField;
  1143. O : TRestFieldOption;
  1144. I : Integer;
  1145. SO : String;
  1146. FName,FType,fMaxLen,fFormat : TField;
  1147. FOptions : Array[TRestFieldOption] of TField;
  1148. begin
  1149. FName:=D.FieldByName('name');
  1150. FType:=D.FieldByName('type');
  1151. FMaxLen:=D.FieldByName('maxlen');
  1152. FFormat:=D.FieldByName('format');
  1153. for O in TRestFieldOption do
  1154. begin
  1155. Str(O,SO);
  1156. delete(SO,1,2);
  1157. FOptions[O]:=D.FieldByName(SO);
  1158. end;
  1159. For I:=0 to R.Fields.Count-1 do
  1160. begin
  1161. F:=R.Fields[i];
  1162. D.Append;
  1163. FName.AsString:=F.PublicName;
  1164. Ftype.AsString:=TypeNames[F.FieldType];
  1165. FMaxLen.AsInteger:=F.MaxLen;
  1166. Case F.FieldType of
  1167. rftDate : FFormat.AsString:=FStrings.GetRestString(rpDateFormat);
  1168. rftDateTime : FFormat.AsString:=FStrings.GetRestString(rpDatetimeFormat);
  1169. rftTime : FFormat.AsString:=FStrings.GetRestString(rpTimeFormat);
  1170. end;
  1171. for O in TRestFieldOption do
  1172. FOptions[O].AsBoolean:=O in F.Options;
  1173. D.Post;
  1174. end;
  1175. end;
  1176. function TSQLDBRestDispatcher.CreateMetadataDetailDataset(IO: TRestIO;
  1177. const aResourceName: String; AOwner: TComponent): TDataset;
  1178. Var
  1179. BD : TRestBufDataset;
  1180. O : TRestFieldOption;
  1181. SO : String;
  1182. R : TSQLDBRestResource;
  1183. begin
  1184. if IO=Nil then exit;
  1185. BD:=TRestBufDataset.Create(aOwner);
  1186. try
  1187. Result:=BD;
  1188. Result.FieldDefs.Add('name',ftString,255,False);
  1189. Result.FieldDefs.Add('type',ftString,255,False);
  1190. Result.FieldDefs.Add('maxlen',ftInteger,0,false);
  1191. Result.FieldDefs.Add('format',ftString,50,false);
  1192. for O in TRestFieldOption do
  1193. begin
  1194. Str(O,SO);
  1195. delete(SO,1,2);
  1196. Result.FieldDefs.Add(SO,ftBoolean,0,False);
  1197. end;
  1198. BD.CreateDataset;
  1199. R:=FindRestResource(aResourceName);
  1200. ResourceToDataset(R,BD);
  1201. BD.First;
  1202. except
  1203. BD.Free;
  1204. Raise;
  1205. end;
  1206. end;
  1207. procedure TSQLDBRestDispatcher.DatasetToConnection(D: TDataset; C : TSQLDBRestConnection);
  1208. begin
  1209. C.Name:=UTF8Encode(D.FieldByName('name').AsWideString);
  1210. C.ConnectionType:=D.FieldByName('dbType').AsString;
  1211. C.DatabaseName:=UTF8Encode(D.FieldByName('dbName').AsWideString);
  1212. C.HostName:=D.FieldByName('dbHostName').AsString;
  1213. C.UserName:=UTF8Encode(D.FieldByName('dbUserName').AsWideString);
  1214. C.Password:=UTF8Encode(D.FieldByName('dbPassword').AsWideString);
  1215. C.CharSet:=D.FieldByName('dbCharSet').AsString;
  1216. C.Role:=D.FieldByName('dbRole').AsString;
  1217. C.Port:=D.FieldByName('dbPort').AsInteger;
  1218. C.Enabled:=D.FieldByName('enabled').AsBoolean;
  1219. if D.FieldByName('expose').AsBoolean then
  1220. C.SchemaName:=D.FieldByName('exposeSchemaName').AsString;
  1221. end;
  1222. procedure TSQLDBRestDispatcher.ConnectionToDataset(C : TSQLDBRestConnection;D: TDataset);
  1223. begin
  1224. D.FieldByName('key').AsWideString:=UTF8Decode(C.Name);
  1225. D.FieldByName('name').AsWideString:=UTF8Decode(C.Name);
  1226. D.FieldByName('dbType').AsString:=C.ConnectionType;
  1227. D.FieldByName('dbName').AsWideString:=UTF8Decode(C.DatabaseName);
  1228. D.FieldByName('dbHostName').AsString:=C.HostName;
  1229. D.FieldByName('dbUserName').AsWideString:=UTF8Decode(C.UserName);
  1230. D.FieldByName('dbPassword').AsWideString:=UTF8Decode(C.Password);
  1231. D.FieldByName('dbCharSet').AsString:=C.CharSet;
  1232. D.FieldByName('dbRole').AsString:=C.Role;
  1233. D.FieldByName('dbPort').AsInteger:=C.Port;
  1234. D.FieldByName('enabled').AsBoolean:=C.Enabled;
  1235. D.FieldByName('expose').AsBoolean:=(C.SchemaName<>'');
  1236. D.FieldByName('exposeSchemaName').AsString:=C.SchemaName;
  1237. end;
  1238. procedure TSQLDBRestDispatcher.ConnectionsToDataset(D: TDataset);
  1239. Var
  1240. C : TSQLDBRestConnection;
  1241. I : Integer;
  1242. begin
  1243. For I:=0 to Connections.Count-1 do
  1244. begin
  1245. C:=Connections[i];
  1246. D.Append;
  1247. ConnectionToDataset(C,D);
  1248. D.Post;
  1249. end;
  1250. end;
  1251. procedure TSQLDBRestDispatcher.DoConnectionDelete(DataSet: TDataSet);
  1252. Var
  1253. I,J : Integer;
  1254. C : TSQLDBRestConnection;
  1255. begin
  1256. I:=Connections.IndexOfConnection(UTF8Encode(Dataset.FieldByName('name').AsWideString));
  1257. if I<>-1 then
  1258. begin
  1259. C:=Connections[i];
  1260. if C.SingleConnection<>Nil then
  1261. DoneSQLConnection(C,C.SingleConnection,Nil);
  1262. if C.SchemaName<>'' then
  1263. begin
  1264. J:=Schemas.IndexOfSchema(C.SchemaName);
  1265. if J<>-1 then
  1266. begin
  1267. Schemas[J].Schema.Free;
  1268. Schemas[J].Schema:=Nil;
  1269. end;
  1270. Schemas.Delete(J);
  1271. end;
  1272. Connections.Delete(I);
  1273. end
  1274. else
  1275. Raise ESQLDBRest.Create(404,'NOT FOUND');
  1276. end;
  1277. procedure TSQLDBRestDispatcher.DoConnectionPost(DataSet: TDataSet);
  1278. Var
  1279. isNew : Boolean;
  1280. C : TSQLDBRestConnection;
  1281. N : UTF8String;
  1282. UN : UnicodeString;
  1283. S : TSQLDBRestSchema;
  1284. begin
  1285. IsNew:=Dataset.State=dsInsert;
  1286. if IsNew then
  1287. C:=Connections.Add as TSQLDBRestConnection
  1288. else
  1289. begin
  1290. UN:=UTF8Decode(Dataset.FieldByName('key').AsString);
  1291. // C:=Connections[Dataset.RecNo-1];
  1292. C:=Connections.FindConnection(Utf8Encode(UN));
  1293. if (C=Nil) then
  1294. Raise ESQLDBRest.Create(404,'NOT FOUND');
  1295. end;
  1296. if Assigned(C.SingleConnection) then
  1297. DoneSQLConnection(C,C.SingleConnection,Nil);
  1298. DatasetToConnection(Dataset,C);
  1299. if (Dataset.FieldByName('expose').AsBoolean) and isNew then
  1300. begin
  1301. N:=C.SchemaName;
  1302. if N='' then
  1303. N:=C.Name+'schema';
  1304. if (Schemas.IndexOfSchema(N)<>-1) then
  1305. Raise ESQLDBRest.Create(400,'DUPLICATE SCHEMA');
  1306. try
  1307. S:=ExposeConnection(C,Nil);
  1308. except
  1309. if IsNew then
  1310. C.Free;
  1311. Raise;
  1312. end;
  1313. S.Name:=N;
  1314. end;
  1315. end;
  1316. function TSQLDBRestDispatcher.CreateConnectionDataset(IO: TRestIO; AOwner: TComponent): TDataset;
  1317. Var
  1318. BD : TRestBufDataset;
  1319. begin
  1320. if IO=Nil then exit;
  1321. BD:=TRestBufDataset.Create(aOwner);
  1322. try
  1323. Result:=BD;
  1324. // Key field is not exposed
  1325. Result.FieldDefs.add('key',ftWidestring,255);
  1326. Result.FieldDefs.add('name',ftWidestring,255);
  1327. Result.FieldDefs.add('dbType',ftString,20);
  1328. Result.FieldDefs.add('dbName',ftWideString,255);
  1329. Result.FieldDefs.add('dbHostName',ftString,255);
  1330. Result.FieldDefs.add('dbUserName',ftWideString,255);
  1331. Result.FieldDefs.add('dbPassword',ftWideString,255);
  1332. Result.FieldDefs.add('dbCharSet',ftString,50);
  1333. Result.FieldDefs.add('dbRole',ftString,255);
  1334. Result.FieldDefs.add('dbPort',ftInteger,0);
  1335. Result.FieldDefs.add('enabled',ftBoolean,0);
  1336. Result.FieldDefs.add('expose',ftBoolean,0);
  1337. Result.FieldDefs.add('exposeSchemaName',ftWideString,255);
  1338. BD.CreateDataset;
  1339. ConnectionsToDataset(BD);
  1340. BD.IndexDefs.Add('uName','name',[ixUnique]);
  1341. BD.IndexName:='uName';
  1342. BD.First;
  1343. BD.BeforePost:=@DoConnectionPost;
  1344. BD.BeforeDelete:=@DoConnectionDelete;
  1345. except
  1346. BD.Free;
  1347. Raise;
  1348. end;
  1349. end;
  1350. function TSQLDBRestDispatcher.CreateCustomViewDataset(IO: TRestIO;
  1351. const aSQL: String; AOwner: TComponent): TDataset;
  1352. Var
  1353. Q : TRestSQLQuery;
  1354. ST : TStatementType;
  1355. begin
  1356. ST:=IO.Connection.GetStatementInfo(aSQL).StatementType;
  1357. if (st<>stSelect) then
  1358. raise ESQLDBRest.Create(FStatus.GetStatusCode(rsInvalidParam), SErrOnlySELECTSQLAllowedInCustomView); // Should never happen.
  1359. Q:=TRestSQLQuery.Create(aOwner);
  1360. try
  1361. Q.DataBase:=IO.Connection;
  1362. Q.Transaction:=IO.Transaction;
  1363. Q.ParseSQL:=True;
  1364. Q.SQL.Text:=aSQL;
  1365. Result:=Q;
  1366. except
  1367. Q.Free;
  1368. Raise;
  1369. end;
  1370. end;
  1371. function TSQLDBRestDispatcher.CreateSpecialResourceDataset(IO: TRestIO;
  1372. AOwner: TComponent): TDataset;
  1373. Var
  1374. RN : UTF8String;
  1375. begin
  1376. Result:=Nil;
  1377. if (IO.Resource=FMetadataResource) then
  1378. Result:=CreateMetadataDataset(IO,AOwner)
  1379. else if (IO.Resource=FConnectionResource) then
  1380. Result:=CreateConnectionDataset(IO,AOwner)
  1381. else if (IO.Resource=FMetadataDetailResource) then
  1382. begin
  1383. if IO.GetVariable('ID',RN,[vsRoute,vsQuery])=vsNone then
  1384. raise ESQLDBRest.Create(FStatus.GetStatusCode(rsError), SErrCouldNotFindResourceName); // Should never happen.
  1385. Result:=CreateMetadataDetailDataset(IO,RN,AOwner)
  1386. end
  1387. else if (IO.Resource=FCustomViewResource) then
  1388. begin
  1389. if IO.GetVariable(FStrings.GetRestString(rpCustomViewSQLParam),RN,[vsRoute,vsQuery])=vsNone then
  1390. raise ESQLDBRest.Create(FStatus.GetStatusCode(rsInvalidParam), SErrNoSQLStatement); // Should never happen.
  1391. Result:=CreateCustomViewDataset(IO,RN,aOwner);
  1392. end
  1393. end;
  1394. function TSQLDBRestDispatcher.ResolvedCORSAllowedOrigins: String;
  1395. begin
  1396. Result:=FCORSAllowedOrigins;
  1397. if Result='' then
  1398. Result:='*';
  1399. end;
  1400. procedure TSQLDBRestDispatcher.HandleCORSRequest(aConnection : TSQLDBRestConnection; IO : TRestIO);
  1401. Var
  1402. S : String;
  1403. Allowed : Boolean;
  1404. begin
  1405. Allowed:=(rdoHandleCORS in DispatchOptions) and (roOptions in IO.Resource.AllowedOperations);
  1406. if Allowed then
  1407. Allowed:=(ExtractRestOperation(IO.Request,True) in ([roUnknown]+IO.Resource.AllowedOperations));
  1408. if not Allowed then
  1409. begin
  1410. IO.Response.Code:=FStatus.GetStatusCode(rsCORSNotAllowed);
  1411. IO.Response.CodeText:='FORBIDDEN';
  1412. IO.CreateErrorResponse;
  1413. end
  1414. else
  1415. begin
  1416. IO.Response.SetCustomHeader('Access-Control-Allow-Origin',ResolvedCORSAllowedOrigins);
  1417. S:=IO.Resource.GetHTTPAllow;
  1418. IO.Response.SetCustomHeader('Access-Control-Allow-Methods',S);
  1419. IO.Response.SetCustomHeader('Access-Control-Allow-Headers','x-requested-with, content-type, authorization');
  1420. if CorsMaxAge>0 then
  1421. IO.Response.SetCustomHeader('Access-Control-Max-Age',IntToStr(CorsMaxAge));
  1422. IO.Response.SetCustomHeader('Access-Control-Allow-Credentials',BoolToStr(CORSAllowCredentials,'true','false'));
  1423. IO.Response.Code:=FStatus.GetStatusCode(rsCORSOK);
  1424. IO.Response.CodeText:='OK';
  1425. end;
  1426. end;
  1427. procedure TSQLDBRestDispatcher.HandleResourceRequest(aConnection : TSQLDBRestConnection; IO : TRestIO);
  1428. Var
  1429. Conn : TSQLConnection;
  1430. TR : TSQLTransaction;
  1431. H : TSQLDBRestDBHandler;
  1432. l,o : Int64;
  1433. begin
  1434. if MustLog(rloResource) then
  1435. DoLog(rloResource,IO,'Resource: %s; Operation: %s',[IO.ResourceName,RestMethods[IO.Operation]]);
  1436. H:=Nil;
  1437. Conn:=GetSQLConnection(aConnection,Tr);
  1438. try
  1439. IO.SetConn(Conn,TR);
  1440. Try
  1441. if MustLog(rloConnection) then
  1442. if Assigned(Conn) then
  1443. DoLog(rloConnection,IO,'Using connection to Host: %s; Database: %s',[Conn.HostName,Conn.DatabaseName])
  1444. else
  1445. DoLog(rloConnection,IO,'Resource %s does not require connection',[IO.ResourceName]);
  1446. if assigned(Conn) and MustLog(rloSQL) then
  1447. begin
  1448. Conn.LogEvents:=LogSQLOptions;
  1449. Conn.OnLog:[email protected];
  1450. end;
  1451. if (rdoHandleCORS in DispatchOptions) then
  1452. IO.Response.SetCustomHeader('Access-Control-Allow-Origin',ResolvedCORSAllowedOrigins);
  1453. if not AuthenticateRequest(IO,True) then
  1454. exit;
  1455. if Not CheckResourceAccess(IO) then
  1456. exit;
  1457. DoHandleEvent(True,IO);
  1458. H:=CreateDBHandler(IO);
  1459. if IsSpecialResource(IO.Resource) then
  1460. begin
  1461. H.ExternalDataset:=CreateSpecialResourceDataset(IO,H);
  1462. if (IO.Resource=FCustomViewResource) then
  1463. H.DeriveResourceFromDataset:=True;
  1464. H.EmulateOffsetLimit:=IO.GetLimitOffset(EnforceLimit,l,o);
  1465. end;
  1466. H.ExecuteOperation;
  1467. DoHandleEvent(False,IO);
  1468. if Assigned(TR) then
  1469. TR.Commit;
  1470. SetDefaultResponseCode(IO);
  1471. except
  1472. TR.RollBack;
  1473. Raise;
  1474. end;
  1475. finally
  1476. IO.SetConn(Nil,Nil);
  1477. DoneSQLConnection(aConnection,Conn,Tr);
  1478. end;
  1479. end;
  1480. function TSQLDBRestDispatcher.GetConnectionName(IO: TRestIO): UTF8String;
  1481. Var
  1482. N : UTF8String;
  1483. R : TSQLDBRestResource;
  1484. begin
  1485. R:=IO.Resource;
  1486. N:='';
  1487. if (N='') then
  1488. N:=R.ConnectionName;
  1489. if (N='') and assigned(R.GetSchema) then
  1490. N:=R.GetSchema.ConnectionName;
  1491. if (N='') then
  1492. IO.GetVariable(Strings.ConnectionParam,N,[vsQuery]);
  1493. if (N='') and (rdoConnectionInURL in DispatchOptions) then
  1494. IO.GetVariable(Strings.ConnectionParam,N,[vsQuery]);
  1495. If Assigned(FOnGetConnectionName) then
  1496. FOnGetConnectionName(Self,IO.Request,R.ResourceName,N);
  1497. if (N='') then
  1498. N:=DefaultConnection;
  1499. Result:=N;
  1500. end;
  1501. function TSQLDBRestDispatcher.FindConnection(IO: TRestIO): TSQLDBRestConnection;
  1502. Var
  1503. N : UTF8String;
  1504. begin
  1505. N:=GetConnectionName(IO);
  1506. // If we have a name, look for it
  1507. if (N<>'') then
  1508. begin
  1509. Result:=Connections.FindConnection(N);
  1510. if Assigned(Result) and not (Result.Enabled) then
  1511. Result:=Nil;
  1512. end
  1513. else if Connections.Count=1 then
  1514. Result:=Connections[0]
  1515. else
  1516. Result:=Nil;
  1517. end;
  1518. function TSQLDBRestDispatcher.CreateConnectionList: TSQLDBRestConnectionList;
  1519. begin
  1520. Result:=TSQLDBRestConnectionList.Create(TSQLDBRestConnection);
  1521. end;
  1522. function TSQLDBRestDispatcher.CreateSchemaList: TSQLDBRestSchemaList;
  1523. begin
  1524. Result:=TSQLDBRestSchemaList.Create(TSQLDBRestSchemaRef);
  1525. end;
  1526. function TSQLDBRestDispatcher.AllowRestOperation(aIO: TRestIO): Boolean;
  1527. begin
  1528. Result:=(aIO.Operation in aIO.Resource.GetAllowedOperations(aIO.RestContext));
  1529. end;
  1530. function TSQLDBRestDispatcher.CheckResourceAccess(IO: TRestIO): Boolean;
  1531. Var
  1532. NeedDB : Boolean;
  1533. begin
  1534. NeedDB:=(rdoAccessCheckNeedsDB in DispatchOptions);
  1535. Result:=NeedDB<>Assigned(IO.Connection);
  1536. if Result then
  1537. exit;
  1538. Result:=AllowRestResource(IO);
  1539. if not Result then
  1540. CreateErrorContent(IO,FStatus.GetStatusCode(rsResourceNotAllowed),'FORBIDDEN')
  1541. else
  1542. begin
  1543. Result:=AllowRestOperation(IO);
  1544. if not Result then
  1545. CreateErrorContent(IO,FStatus.GetStatusCode(rsRestOperationNotAllowed),'METHOD NOT ALLOWED')
  1546. end;
  1547. end;
  1548. procedure TSQLDBRestDispatcher.DoHandleRequest(IO : TRestIO);
  1549. var
  1550. ResourceName : UTF8String;
  1551. Operation : TRestOperation;
  1552. Resource : TSQLDBRestResource;
  1553. Connection : TSQLDBRestConnection;
  1554. begin
  1555. Operation:=ExtractRestOperation(IO.Request);
  1556. if (Operation=roUnknown) then
  1557. CreateErrorContent(IO,FStatus.GetStatusCode(rsInvalidMethod),'INVALID METHOD')
  1558. else
  1559. begin
  1560. IO.SetOperation(Operation);
  1561. ResourceName:=ExtractRestResourceName(IO);
  1562. if (ResourceName='') then
  1563. CreateErrorContent(IO,FStatus.GetStatusCode(rsNoResourceSpecified),'INVALID RESOURCE')
  1564. else
  1565. begin
  1566. Resource:=FindSpecialResource(IO,ResourceName);
  1567. If Resource=Nil then
  1568. Resource:=FindRestResource(ResourceName);
  1569. if Resource=Nil then
  1570. CreateErrorContent(IO,FStatus.GetStatusCode(rsUnknownResource),'NOT FOUND')
  1571. else
  1572. begin
  1573. IO.SetResource(Resource);
  1574. Connection:=FindConnection(IO);
  1575. if (Connection=Nil) and not IsSpecialResource(Resource) then
  1576. begin
  1577. if (rdoConnectionInURL in DispatchOptions) then
  1578. CreateErrorContent(IO,FStatus.GetStatusCode(rsNoConnectionSpecified),Format(SErrNoconnection,[GetConnectionName(IO)]))
  1579. else
  1580. CreateErrorContent(IO,FStatus.GetStatusCode(rsError), Format(SErrNoconnection,[GetConnectionName(IO)]));
  1581. end
  1582. else if CheckResourceAccess(IO) then
  1583. if Operation=roOptions then
  1584. HandleCORSRequest(Connection,IO)
  1585. else
  1586. HandleResourceRequest(Connection,IO);
  1587. end;
  1588. end;
  1589. end;
  1590. end;
  1591. procedure TSQLDBRestDispatcher.UnRegisterRoutes;
  1592. Procedure Un(Var a : THTTPRoute);
  1593. begin
  1594. if A=Nil then
  1595. exit;
  1596. HTTPRouter.DeleteRoute(A);
  1597. A:=Nil;
  1598. end;
  1599. begin
  1600. Un(FListRoute);
  1601. Un(FItemRoute);
  1602. Un(FConnectionItemRoute);
  1603. Un(FConnectionsRoute);
  1604. Un(FMetadataItemRoute);
  1605. Un(FMetadataRoute);
  1606. end;
  1607. procedure TSQLDBRestDispatcher.RegisterRoutes;
  1608. begin
  1609. if (FListRoute<>Nil) then
  1610. UnRegisterRoutes;
  1611. DoRegisterRoutes;
  1612. end;
  1613. procedure TSQLDBRestDispatcher.HandleException(E : Exception; IO : TRestIO);
  1614. Function StripCR(S : String) : String;
  1615. begin
  1616. Result:=StringReplace(S,#13#10,' ',[rfReplaceAll]);
  1617. Result:=StringReplace(Result,#13,' ',[rfReplaceAll]);
  1618. Result:=StringReplace(Result,#10,' ',[rfReplaceAll]);
  1619. end;
  1620. Var
  1621. Code : Integer;
  1622. Msg : String;
  1623. begin
  1624. try
  1625. if Assigned(FOnException) then
  1626. FOnException(Self,IO.Request,IO.ResourceName,E);
  1627. if not IO.Response.ContentSent then
  1628. begin
  1629. Code:=0;
  1630. If E is ESQLDBRest then
  1631. begin
  1632. Code:=ESQLDBRest(E).ResponseCode;
  1633. Msg:=E.Message;
  1634. end;
  1635. if (Code=0) then
  1636. begin
  1637. Code:=FStatus.GetStatusCode(rsError);
  1638. Msg:=Format(SErrUnexpectedException,[E.ClassName,E.Message]);
  1639. end;
  1640. IO.Response.Code:=Code;
  1641. IO.Response.CodeText:=StripCR(Msg);
  1642. if (IO.Response.Code=405) and Assigned(IO.Resource) then
  1643. IO.Response.Allow:=IO.Resource.GetHTTPAllow; // ([rmHead,rmOptions]) ?
  1644. IO.RESTOutput.CreateErrorContent(Code,Msg);
  1645. end;
  1646. except
  1647. on Ex : exception do
  1648. begin
  1649. IO.Response.Code:=FStatus.GetStatusCode(rsError);
  1650. IO.Response.CodeText:=Format('Unexpected exception %s while handling original exception %s : "%s" (Original: "%s")',[Ex.ClassName,E.ClassName,Ex.Message,E.Message]);
  1651. end;
  1652. end;
  1653. end;
  1654. function TSQLDBRestDispatcher.AuthenticateRequest(IO: TRestIO; Delayed : Boolean): Boolean;
  1655. Var
  1656. B : TRestBasicAuthenticator;
  1657. A : TRestAuthenticator;
  1658. begin
  1659. A:=Nil;
  1660. B:=Nil;
  1661. If Assigned(FAuthenticator) then
  1662. A:=FAuthenticator
  1663. else If Assigned(FOnBAsicAuthentication) then
  1664. begin
  1665. B:=TRestBasicAuthenticator.Create(Self);
  1666. A:=B;
  1667. B.OnBasicAuthentication:=Self.OnBasicAuthentication;
  1668. end;
  1669. try
  1670. Result:=A=Nil;
  1671. if Not Result Then
  1672. begin
  1673. Result:=(A.NeedConnection<>Delayed);
  1674. If Not Result then
  1675. begin
  1676. Result:=A.AuthenticateRequest(IO);
  1677. if MustLog(rloAuthentication) then
  1678. if Result then
  1679. DoLog(rloAuthentication,IO,'Authenticated user: %s',[IO.UserID])
  1680. else
  1681. DoLog(rloAuthentication,IO,'Authentication failed for user: %s',[TRestBasicAuthenticator.ExtractUserName(IO.Request)]);
  1682. end;
  1683. end;
  1684. finally
  1685. if Assigned(B) then
  1686. B.Free;
  1687. end;
  1688. end;
  1689. procedure TSQLDBRestDispatcher.Notification(AComponent: TComponent;
  1690. Operation: TOperation);
  1691. begin
  1692. inherited Notification(AComponent, Operation);
  1693. if Operation=opRemove then
  1694. begin
  1695. if AComponent=FAuthenticator then
  1696. FAuthenticator:=Nil
  1697. end;
  1698. end;
  1699. procedure TSQLDBRestDispatcher.HandleRequest(aRequest: TRequest; aResponse: TResponse);
  1700. Var IO : TRestIO;
  1701. begin
  1702. aResponse.Code:=0; // Sentinel
  1703. IO:=CreateIO(aRequest,aResponse);
  1704. try
  1705. try
  1706. // Call initstreaming only here, so IO has set var callback.
  1707. // First output, then input
  1708. IO.RestOutput.InitStreaming;
  1709. IO.RestInput.InitStreaming;
  1710. IO.OnSQLLog:[email protected];
  1711. if AuthenticateRequest(IO,False) then
  1712. DoHandleRequest(IO)
  1713. except
  1714. On E : Exception do
  1715. HandleException(E,IO);
  1716. end;
  1717. Finally
  1718. // Make sure there is a document in case of error
  1719. if (aResponse.ContentStream.Size=0) and Not ((aResponse.Code div 100)=2) then
  1720. IO.RESTOutput.CreateErrorContent(aResponse.Code,aResponse.CodeText);
  1721. if Not (IO.Operation in [roOptions,roHEAD]) then
  1722. IO.RestOutput.FinalizeOutput;
  1723. aResponse.ContentStream.Position:=0;
  1724. aResponse.ContentLength:=aResponse.ContentStream.Size;
  1725. if not aResponse.ContentSent then
  1726. aResponse.SendContent;
  1727. if MustLog(rloResultStatus) then
  1728. DoLog(rloResultStatus,IO,'Resource: %s; Operation: %s; Status: %d; Text: %s',[IO.ResourceName,RestMethods[IO.Operation],aResponse.Code,aResponse.CodeText]);
  1729. IO.Free;
  1730. end;
  1731. end;
  1732. function TSQLDBRestDispatcher.ExposeDatabase(const aType, aHostName, aDatabaseName, aUserName, aPassword: String;
  1733. aTables: array of String; aMinFieldOpts: TRestFieldOptions): TSQLDBRestConnection;
  1734. Var
  1735. L : TStringList;
  1736. S : String;
  1737. begin
  1738. L:=TStringList.Create;
  1739. try
  1740. L.Capacity:=Length(aTables);
  1741. For S in aTables do
  1742. L.Add(S);
  1743. L.Sorted:=True;
  1744. Result:=ExposeDatabase(aType, aHostName, aDatabaseName, aUserName, aPassword,L, aMinFieldOpts);
  1745. finally
  1746. l.Free;
  1747. end;
  1748. end;
  1749. function TSQLDBRestDispatcher.ExposeDatabase(const aType, aHostName, aDatabaseName, aUserName, aPassword: String; aTables : TStrings = nil; aMinFieldOpts : TRestFieldOptions = []): TSQLDBRestConnection;
  1750. begin
  1751. Result:=Connections.AddConnection(aType,aHostName,aDatabaseName,aUserName,aPassword);
  1752. ExposeConnection(Result,aTables,aMinFieldOpts);
  1753. end;
  1754. function TSQLDBRestDispatcher.ExposeConnection(aOwner: TComponent;
  1755. const aConnection: TSQLDBRestConnection; aTables: TStrings;
  1756. aMinFieldOpts: TRestFieldOptions): TSQLDBRestSchema;
  1757. Var
  1758. Conn : TSQLConnection;
  1759. TR : TSQLTransaction;
  1760. S : TSQLDBRestSchema;
  1761. begin
  1762. Conn:=GetSQLConnection(aConnection,TR);
  1763. S:=TSQLDBRestSchema.Create(aOwner);
  1764. S.Name:='Schema'+aConnection.Name;
  1765. S.PopulateResources(Conn,aTables,aMinFieldOpts);
  1766. if not (rdoConnectionInURL in DispatchOptions) then
  1767. S.ConnectionName:=aConnection.Name;
  1768. Schemas.AddSchema(S).Enabled:=true;
  1769. Result:=S;
  1770. end;
  1771. function TSQLDBRestDispatcher.ExposeConnection(
  1772. const aConnection: TSQLDBRestConnection; aTables: TStrings;
  1773. aMinFieldOpts: TRestFieldOptions): TSQLDBRestSchema;
  1774. begin
  1775. Result:=ExposeConnection(Self,aConnection,aTables,aMinFieldOpts);
  1776. end;
  1777. { TSchemaFreeNotifier }
  1778. procedure TSchemaFreeNotifier.Notification(AComponent: TComponent; Operation: TOperation);
  1779. begin
  1780. inherited Notification(AComponent, Operation);
  1781. if (Operation=opRemove) and Assigned(FRef) and (Fref.Schema=aComponent) then
  1782. Fref.Schema:=nil;
  1783. end;
  1784. { TSQLDBRestSchemaRef }
  1785. procedure TSQLDBRestSchemaRef.SetSchema(AValue: TSQLDBRestSchema);
  1786. begin
  1787. if (FSchema=AValue) then Exit;
  1788. if Assigned(FSchema) then
  1789. FSchema.RemoveFreeNotification(FNotifier);
  1790. FSchema:=AValue;
  1791. if Assigned(FSchema) then
  1792. FSchema.FreeNotification(FNotifier);
  1793. end;
  1794. function TSQLDBRestSchemaRef.GetDisplayName: String;
  1795. begin
  1796. if Assigned(FSchema) then
  1797. Result:=FSchema.Name
  1798. else
  1799. Result:=inherited GetDisplayName;
  1800. end;
  1801. constructor TSQLDBRestSchemaRef.Create(ACollection: TCollection);
  1802. begin
  1803. inherited Create(ACollection);
  1804. FNotifier:=TSchemaFreeNotifier.Create(Nil);
  1805. TSchemaFreeNotifier(FNotifier).FRef:=Self;
  1806. FEnabled:=True;
  1807. end;
  1808. destructor TSQLDBRestSchemaRef.Destroy;
  1809. begin
  1810. FreeAndNil(FNotifier);
  1811. inherited Destroy;
  1812. end;
  1813. procedure TSQLDBRestSchemaRef.Assign(Source: TPersistent);
  1814. Var
  1815. R : TSQLDBRestSchemaRef;
  1816. begin
  1817. if (Source is TSQLDBRestSchemaRef) then
  1818. begin
  1819. R:=Source as TSQLDBRestSchemaRef;
  1820. Schema:=R.Schema;
  1821. Enabled:=R.Enabled;
  1822. end
  1823. else
  1824. inherited Assign(Source);
  1825. end;
  1826. { TSQLDBRestConnectionList }
  1827. function TSQLDBRestConnectionList.GetConn(aIndex : integer): TSQLDBRestConnection;
  1828. begin
  1829. Result:=TSQLDBRestConnection(Items[aIndex]);
  1830. end;
  1831. procedure TSQLDBRestConnectionList.SetConn(aIndex : integer; AValue: TSQLDBRestConnection);
  1832. begin
  1833. Items[aIndex]:=aValue;
  1834. end;
  1835. function TSQLDBRestConnectionList.IndexOfConnection(const aName: UTF8string
  1836. ): Integer;
  1837. begin
  1838. Result:=Count-1;
  1839. While (Result>=0) and not SameText(GetConn(Result).Name,aName) do
  1840. Dec(Result);
  1841. end;
  1842. function TSQLDBRestConnectionList.FindConnection(const aName: UTF8string): TSQLDBRestConnection;
  1843. Var
  1844. Idx : Integer;
  1845. begin
  1846. Idx:=IndexOfConnection(aName);
  1847. if Idx=-1 then
  1848. Result:=Nil
  1849. else
  1850. Result:=GetConn(Idx);
  1851. end;
  1852. function TSQLDBRestConnectionList.AddConnection(const AType, aHostName, aDatabaseName, aUserName, aPassword: UTF8String): TSQLDBRestConnection;
  1853. Var
  1854. Idx : Integer;
  1855. N : String;
  1856. begin
  1857. Result:=Add as TSQLDBRestConnection;
  1858. IDX:=Result.ID;
  1859. Repeat
  1860. N:='Connection'+IntToStr(IDX);
  1861. Inc(Idx);
  1862. Until IndexOfConnection(N)=-1;
  1863. Result.Name:=N;
  1864. Result.ConnectionType:=aType;
  1865. Result.HostName:=aHostName;
  1866. Result.DatabaseName:=aDatabaseName;
  1867. Result.UserName:=aUserName;
  1868. Result.Password:=aPassword;
  1869. end;
  1870. procedure TSQLDBRestConnectionList.SaveToFile(const aFileName: UTF8String);
  1871. Var
  1872. F : TFileStream;
  1873. begin
  1874. F:=TFileStream.Create(aFileName,fmCreate);
  1875. try
  1876. SaveToStream(F);
  1877. finally
  1878. F.Free;
  1879. end;
  1880. end;
  1881. procedure TSQLDBRestConnectionList.SaveToStream(const aStream: TStream);
  1882. Var
  1883. D : TJSONData;
  1884. S : TJSONStringType;
  1885. begin
  1886. D:=asJSON(JSONConnectionsRoot);
  1887. try
  1888. S:=D.FormatJSON();
  1889. finally
  1890. D.Free;
  1891. end;
  1892. aStream.WriteBuffer(S[1],Length(S)*SizeOf(TJSONCharType));
  1893. end;
  1894. function TSQLDBRestConnectionList.AsJSON(const aPropName: UTF8String): TJSONData;
  1895. Var
  1896. S : TJSONStreamer;
  1897. A : TJSONArray;
  1898. begin
  1899. S:=TJSONStreamer.Create(Nil);
  1900. try
  1901. A:=S.StreamCollection(Self);
  1902. finally
  1903. S.Free;
  1904. end;
  1905. if aPropName='' then
  1906. Result:=A
  1907. else
  1908. Result:=TJSONObject.Create([aPropName,A]);
  1909. end;
  1910. procedure TSQLDBRestConnectionList.LoadFromFile(const aFileName: UTF8String);
  1911. Var
  1912. F : TFileStream;
  1913. begin
  1914. F:=TFileStream.Create(aFileName,fmOpenRead or fmShareDenyWrite);
  1915. try
  1916. LoadFromStream(F);
  1917. finally
  1918. F.Free;
  1919. end;
  1920. end;
  1921. procedure TSQLDBRestConnectionList.LoadFromStream(const aStream: TStream);
  1922. Var
  1923. D : TJSONData;
  1924. begin
  1925. D:=GetJSON(aStream);
  1926. try
  1927. FromJSON(D,JSONConnectionsRoot);
  1928. finally
  1929. D.Free;
  1930. end;
  1931. end;
  1932. procedure TSQLDBRestConnectionList.FromJSON(aData: TJSONData; const aPropName: UTF8String);
  1933. Var
  1934. A : TJSONArray;
  1935. D : TJSONDestreamer;
  1936. begin
  1937. if (aPropName<>'') then
  1938. A:=(aData as TJSONObject).Arrays[aPropName]
  1939. else
  1940. A:=aData as TJSONArray;
  1941. D:=TJSONDestreamer.Create(Nil);
  1942. try
  1943. Clear;
  1944. D.JSONToCollection(A,Self);
  1945. finally
  1946. D.Free;
  1947. end;
  1948. end;
  1949. { TSQLDBRestConnection }
  1950. procedure TSQLDBRestConnection.SetParams(AValue: TStrings);
  1951. begin
  1952. if FParams=AValue then Exit;
  1953. FParams.Assign(AValue);
  1954. end;
  1955. function TSQLDBRestConnection.GetDisplayName: string;
  1956. begin
  1957. Result:=Name;
  1958. end;
  1959. procedure TSQLDBRestConnection.SetConnection(AValue: TSQLConnection);
  1960. begin
  1961. if FConnection=AValue then Exit;
  1962. if Assigned(FConnection) then
  1963. FConnection.RemoveFreeNotification(FNotifier);
  1964. FConnection:=AValue;
  1965. if Assigned(FConnection) then
  1966. FConnection.FreeNotification(FNotifier);
  1967. end;
  1968. function TSQLDBRestConnection.GetName: UTF8String;
  1969. begin
  1970. Result:=FName;
  1971. if (Result='') and Assigned(SingleConnection) then
  1972. Result:=SingleConnection.Name;
  1973. if (Result='') then
  1974. Result:='Connection'+IntToStr(ID);
  1975. end;
  1976. constructor TSQLDBRestConnection.Create(ACollection: TCollection);
  1977. begin
  1978. inherited Create(ACollection);
  1979. FParams:=TStringList.Create;
  1980. FNotifier:=TConnectionFreeNotifier.Create(Nil);
  1981. TConnectionFreeNotifier(FNotifier).FRef:=Self;
  1982. FEnabled:=True;
  1983. end;
  1984. destructor TSQLDBRestConnection.Destroy;
  1985. begin
  1986. TConnectionFreeNotifier(FNotifier).FRef:=Nil;
  1987. FreeAndNil(FNotifier);
  1988. FreeAndNil(FParams);
  1989. inherited Destroy;
  1990. end;
  1991. procedure TSQLDBRestConnection.Assign(Source: TPersistent);
  1992. Var
  1993. C : TSQLDBRestConnection;
  1994. begin
  1995. if (Source is TSQLDBRestConnection) then
  1996. begin
  1997. C:=Source as TSQLDBRestConnection;
  1998. Password:=C.Password;
  1999. UserName:=C.UserName;
  2000. CharSet :=C.CharSet;
  2001. HostName:=C.HostName;
  2002. Role:=C.Role;
  2003. DatabaseName:=C.DatabaseName;
  2004. ConnectionType:=C.ConnectionType;
  2005. Port:=C.Port;
  2006. Name:=C.Name;
  2007. SchemaName:=C.SchemaName;
  2008. Params.Assign(C.Params);
  2009. end
  2010. else
  2011. inherited Assign(Source);
  2012. end;
  2013. procedure TSQLDBRestConnection.ConfigConnection(aConn: TSQLConnection);
  2014. begin
  2015. aConn.CharSet:=Self.CharSet;
  2016. aConn.HostName:=Self.HostName;
  2017. aConn.DatabaseName:=Self.DatabaseName;
  2018. aConn.UserName:=Self.UserName;
  2019. aConn.Password:=Self.Password;
  2020. aConn.Params:=Self.Params;
  2021. if aConn is TSQLConnector then
  2022. TSQLConnector(aConn).ConnectorType:=Self.ConnectionType;
  2023. end;
  2024. Procedure InitSQLDBRest;
  2025. begin
  2026. TSQLDBRestDispatcher.SetIOClass(TRestIO);
  2027. TSQLDBRestDispatcher.SetDBHandlerClass(TSQLDBRestDBHandler);
  2028. TSQLDBRestResource.DefaultFieldListClass:=TSQLDBRestFieldList;
  2029. TSQLDBRestResource.DefaultFieldClass:=TSQLDBRestField;
  2030. end;
  2031. Initialization
  2032. InitSQLDBRest;
  2033. end.