sqldbrestbridge.pp 84 KB

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