sqldbrestio.pp 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167
  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 basic I/O environment.
  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 sqldbrestio;
  12. {$mode objfpc}{$H+}
  13. interface
  14. uses
  15. Classes, SysUtils, fpjson, bufdataset, sqldb, db, httpdefs, sqldbrestschema;
  16. Type
  17. TRestOutputOption = (ooMetadata,ooSparse,ooHumanReadable);
  18. TRestOutputOptions = Set of TRestOutputOption;
  19. TNullBoolean = (nbNone,nbFalse,nbTrue);
  20. TNullBooleans = set of TNullBoolean;
  21. Const
  22. AllVariableSources = [Low(TVariableSource)..High(TVariableSource)];
  23. allOutputOptions = [Low(TRestOutputOption)..High(TRestOutputOption)];
  24. Type
  25. TRestIO = Class;
  26. TRestStringProperty = (rpDateFormat,
  27. rpDateTimeFormat,
  28. rpTimeFormat,
  29. rpDataRoot,
  30. rpMetaDataRoot,
  31. rpErrorRoot,
  32. rpFieldNameProp,
  33. rpFieldTypeProp,
  34. rpFieldDateFormatProp,
  35. rpFieldMaxLenProp,
  36. rpHumanReadable,
  37. rpFieldList,
  38. rpExcludeFieldList,
  39. rpConnection,
  40. rpResource,
  41. rpIncludeMetadata,
  42. rpSparse,
  43. rpRowName,
  44. rpMetaDataFields,
  45. rpMetaDataField,
  46. rpErrorCode,
  47. rpErrorMessage,
  48. rpFilterEqual,
  49. rpFilterLessThan,
  50. rpFilterGreaterThan,
  51. rpFilterLessThanEqual,
  52. rpFilterGreaterThanEqual,
  53. rpFilterIsNull,
  54. rpLimit,
  55. rpOffset,
  56. rpOrderBy,
  57. rpMetadataResourceName,
  58. rpInputFormat,
  59. rpOutputFormat,
  60. rpCustomViewResourceName,
  61. rpCustomViewSQLParam,
  62. rpXMLDocumentRoot,
  63. rpConnectionResourceName,
  64. rpParametersResourceName,
  65. rpParametersRoutePart
  66. );
  67. TRestStringProperties = Set of TRestStringProperty;
  68. TRestGetVariableEvent = Procedure (Sender : TObject; Const aName : UTF8String; Out aVal : UTF8String) of object;
  69. { TRestStringsConfig }
  70. TRestStringsConfig = Class(TPersistent)
  71. private
  72. FValues : Array[TRestStringProperty] of UTF8String;
  73. function GetRestPropName(AIndex: Integer): UTF8String;
  74. function IsRestStringStored(AIndex: Integer): Boolean;
  75. procedure SetRestPropName(AIndex: Integer; AValue: UTF8String);
  76. Public
  77. Class Function GetDefaultString(aString : TRestStringProperty) :UTF8String;
  78. Function GetRestString(aString : TRestStringProperty) :UTF8String;
  79. Procedure SetRestString(aString : TRestStringProperty; AValue :UTF8String);
  80. Procedure Assign(aSource : TPersistent); override;
  81. Published
  82. // Indexes here MUST match TRestProperty
  83. Property RESTDateFormat : UTF8String Index ord(rpDateFormat) Read GetRestPropName Write SetRestPropName Stored IsRestStringStored;
  84. Property RESTDateTimeFormat : UTF8String Index ord(rpDateTimeFormat) Read GetRestPropName Write SetRestPropName Stored IsRestStringStored;
  85. Property RESTTimeFormat : UTF8String Index ord(rpTimeFormat) Read GetRestPropName Write SetRestPropName Stored IsRestStringStored;
  86. Property DataProperty : UTF8String Index ord(rpDataRoot) Read GetRestPropName Write SetRestPropName Stored IsRestStringStored;
  87. Property MetaDataRoot : UTF8String Index ord(rpMetaDataRoot) Read GetRestPropName Write SetRestPropName Stored IsRestStringStored;
  88. Property ErrorProperty : UTF8String Index ord(rpErrorRoot) Read GetRestPropName Write SetRestPropName Stored IsRestStringStored;
  89. Property FieldNameProperty : UTF8String Index ord(rpFieldNameProp) Read GetRestPropName Write SetRestPropName Stored IsRestStringStored;
  90. Property FieldTypeProperty : UTF8String Index ord(rpFieldTypeProp) Read GetRestPropName Write SetRestPropName Stored IsRestStringStored;
  91. Property DateFormatProperty : UTF8String Index ord(rpFieldDateFormatProp) Read GetRestPropName Write SetRestPropName Stored IsRestStringStored;
  92. Property MaxLenProperty : UTF8String Index ord(rpFieldMaxLenProp) Read GetRestPropName Write SetRestPropName Stored IsRestStringStored;
  93. Property HumanReadableParam : UTF8String Index ord(rpHumanReadable) Read GetRestPropName Write SetRestPropName Stored IsRestStringStored;
  94. Property FieldListParam : UTF8String Index ord(rpFieldList) Read GetRestPropName Write SetRestPropName Stored IsRestStringStored;
  95. Property ExcludeFieldListParam : UTF8String Index ord(rpExcludeFieldList) Read GetRestPropName Write SetRestPropName Stored IsRestStringStored;
  96. Property ConnectionParam : UTF8String Index Ord(rpConnection) Read GetRestPropName Write SetRestPropName Stored IsRestStringStored;
  97. Property ResourceParam : UTF8String Index ord(rpResource) Read GetRestPropName Write SetRestPropName Stored IsRestStringStored;
  98. Property IncludeMetadataParam : UTF8String Index ord(rpIncludeMetadata) Read GetRestPropName Write SetRestPropName Stored IsRestStringStored;
  99. Property SparseParam : UTF8String Index Ord(rpSparse) Read GetRestPropName Write SetRestPropName Stored IsRestStringStored;
  100. Property RowName : UTF8String Index Ord(rpRowName) Read GetRestPropName Write SetRestPropName Stored IsRestStringStored;
  101. Property MetadataFields : UTF8String Index Ord(rpMetadataFields) Read GetRestPropName Write SetRestPropName Stored IsRestStringStored;
  102. Property MetadataField : UTF8String Index Ord(rpMetadataField) Read GetRestPropName Write SetRestPropName Stored IsRestStringStored;
  103. Property ErrorCode : UTF8String Index ord(rpErrorCode) Read GetRestPropName Write SetRestPropName Stored IsRestStringStored;
  104. Property ErrorMessage : UTF8String Index ord(rpErrorMessage) Read GetRestPropName Write SetRestPropName Stored IsRestStringStored;
  105. Property FilterParamEqual : UTF8String Index ord(rpFilterEqual) Read GetRestPropName Write SetRestPropName Stored IsRestStringStored;
  106. Property FilterParamLessThan : UTF8String Index ord(rpFilterLessThan) Read GetRestPropName Write SetRestPropName Stored IsRestStringStored;
  107. Property FilterParamGreaterThan : UTF8String Index ord(rpFilterGreaterThan) Read GetRestPropName Write SetRestPropName Stored IsRestStringStored;
  108. Property FilterParamLessThanEqual : UTF8String Index ord(rpFilterLessThanEqual) Read GetRestPropName Write SetRestPropName Stored IsRestStringStored;
  109. Property FilterParamGreaterThanEqual : UTF8String Index ord(rpFilterGreaterThanEqual) Read GetRestPropName Write SetRestPropName Stored IsRestStringStored;
  110. Property FilterParamIsNull : UTF8String Index ord(rpFilterIsNull) Read GetRestPropName Write SetRestPropName Stored IsRestStringStored;
  111. Property LimitParam : UTF8string Index ord(rpLimit) Read GetRestPropName Write SetRestPropName Stored IsRestStringStored;
  112. Property OffsetParam : UTF8string Index ord(rpOffset) Read GetRestPropName Write SetRestPropName Stored IsRestStringStored;
  113. Property SortParam : UTF8string Index ord(rpOrderBy) Read GetRestPropName Write SetRestPropName Stored IsRestStringStored;
  114. Property MetadataResourceName : UTF8string Index ord(rpMetadataResourceName) Read GetRestPropName Write SetRestPropName Stored IsRestStringStored;
  115. Property MetadataParametersName : UTF8string Index ord(rpParametersResourceName) Read GetRestPropName Write SetRestPropName Stored IsRestStringStored;
  116. Property MetadataParametersRoutePart : UTF8string Index ord(rpParametersRoutePart) Read GetRestPropName Write SetRestPropName Stored IsRestStringStored;
  117. Property InputFormatParam : UTF8string Index ord(rpInputFormat) Read GetRestPropName Write SetRestPropName Stored IsRestStringStored;
  118. Property OutputFormatParam : UTF8string Index ord(rpOutputFormat) Read GetRestPropName Write SetRestPropName Stored IsRestStringStored;
  119. Property CustomViewResourceName : UTF8string Index ord(rpCustomViewResourceName) Read GetRestPropName Write SetRestPropName Stored IsRestStringStored;
  120. Property CustomViewSQLParam : UTF8string Index ord(rpCustomViewSQLParam) Read GetRestPropName Write SetRestPropName Stored IsRestStringStored;
  121. Property XMLDocumentRoot : UTF8string Index ord(rpXMLDocumentRoot) Read GetRestPropName Write SetRestPropName Stored IsRestStringStored;
  122. Property ConnectionResourceName : UTF8string Index ord(rpConnectionResourceName) Read GetRestPropName Write SetRestPropName Stored IsRestStringStored;
  123. end;
  124. TRestStatus = (rsError, // Internal logic/unexpected error (500)
  125. rsGetOK, // GET command completed OK (200)
  126. rsPostOK, // POST command completed OK (204)
  127. rsPutOK, // PUT command completed OK (200)
  128. rsDeleteOK, // DELETE command completed OK (204)
  129. rsInvalidParam, // Something wrong/missing in Query parameters (400)
  130. rsCORSOK, // CORS request completed OK (200)
  131. rsCORSNotAllowed, // CORS request not allowed (403)
  132. rsUnauthorized, // Authentication failed (401)
  133. rsResourceNotAllowed, // Resource request not allowed (403)
  134. rsRestOperationNotAllowed, // Resource operation (method) not allowed (405)
  135. rsInvalidMethod, // Invalid HTTP method (400)
  136. rsUnknownResource, // Unknown resource (404)
  137. rsNoResourceSpecified, // Unable to determine resource (404)
  138. rsNoConnectionSpecified, // Unable to determine connection for (400)
  139. rsRecordNotFound, // Query did not return record for single resource (404)
  140. rsInvalidContent, // Invalid content for POST/PUT operation (400)
  141. rsPatchOK // PATCH command completed OK (200)
  142. );
  143. TRestStatuses = set of TRestStatus;
  144. { TRestStatusConfig }
  145. TRestStatusConfig = Class(TPersistent)
  146. private
  147. FStatus : Array[TRestStatus] of Word;
  148. function GetStatus(AIndex: Integer): Word;
  149. function IsStatusStored(AIndex: Integer): Boolean;
  150. procedure SetStatus(AIndex: Integer; AValue: Word);
  151. Public
  152. Procedure Assign(aSource : TPersistent); override;
  153. function GetStatusCode(aStatus : TRestStatus): Word;
  154. Published
  155. // Internal logic/unexpected error (500)
  156. Property Error : Word Index Ord(rsError) Read GetStatus Write SetStatus Stored IsStatusStored;
  157. // GET command completed OK (200)
  158. Property GetOK : Word Index Ord(rsGetOK) Read GetStatus Write SetStatus Stored IsStatusStored;
  159. // POST command completed OK (204)
  160. Property PostOK : Word Index Ord(rsPostOK) Read GetStatus Write SetStatus Stored IsStatusStored;
  161. // PUT command completed OK (200)
  162. Property PutOK : Word Index Ord(rsPutOK) Read GetStatus Write SetStatus Stored IsStatusStored;
  163. // DELETE command completed OK (204)
  164. Property DeleteOK : Word Index Ord(rsDeleteOK) Read GetStatus Write SetStatus Stored IsStatusStored;
  165. // Something wrong/missing in Query parameters (400)
  166. Property InvalidParam : Word Index Ord(rsInvalidParam) Read GetStatus Write SetStatus Stored IsStatusStored;
  167. // CORS request completed OK (200)
  168. Property CORSOK : Word Index Ord(rsCORSOK) Read GetStatus Write SetStatus Stored IsStatusStored;
  169. // CORS request not allowed (403)
  170. Property CORSNotAllowed : Word Index Ord(rsCORSNotAllowed) Read GetStatus Write SetStatus Stored IsStatusStored;
  171. // Authentication failed (401)
  172. Property Unauthorized : Word Index Ord(rsUnauthorized) Read GetStatus Write SetStatus Stored IsStatusStored;
  173. // Resource request not allowed (403)
  174. Property ResourceNotAllowed : Word Index Ord(rsResourceNotAllowed) Read GetStatus Write SetStatus Stored IsStatusStored;
  175. // Resource operation (method) not allowed (405)
  176. Property RestOperationNotAllowed : Word Index Ord(rsRestOperationNotAllowed) Read GetStatus Write SetStatus Stored IsStatusStored;
  177. // Invalid HTTP method (400)
  178. Property InvalidMethod : Word Index Ord(rsInvalidMethod) Read GetStatus Write SetStatus Stored IsStatusStored;
  179. // Unknown resource (404)
  180. Property UnknownResource : Word Index Ord(rsUnknownResource) Read GetStatus Write SetStatus Stored IsStatusStored;
  181. // Unable to determine resource (404)
  182. Property NoResourceSpecified : Word Index Ord(rsNoResourceSpecified) Read GetStatus Write SetStatus Stored IsStatusStored;
  183. // Unable to determine connection for (400)
  184. Property NoConnectionSpecified : Word Index Ord(rsNoConnectionSpecified) Read GetStatus Write SetStatus Stored IsStatusStored;
  185. // Query did not return record for single resource (404)
  186. Property RecordNotFound : Word Index Ord(rsRecordNotFound) Read GetStatus Write SetStatus Stored IsStatusStored;
  187. // Invalid content for POST/PUT operation (400)
  188. Property InvalidContent : Word Index Ord(rsInvalidContent) Read GetStatus Write SetStatus Stored IsStatusStored;
  189. end;
  190. { TRestStreamer }
  191. TRestStreamer = Class(TObject)
  192. private
  193. FStream: TStream;
  194. FOnGetVar : TRestGetVariableEvent;
  195. FStrings: TRestStringsConfig;
  196. FStatuses : TRestStatusConfig;
  197. Public
  198. // Registry
  199. Class Function GetContentType : String; virtual;
  200. Constructor Create(aStream : TStream;aStrings : TRestStringsConfig;aStatus : TRestStatusConfig; aOnGetVar : TRestGetVariableEvent);
  201. Function GetString(aString : TRestStringProperty) : UTF8String;
  202. Property Strings : TRestStringsConfig Read FStrings;
  203. Property Statuses : TRestStatusConfig Read FStatuses;
  204. procedure InitStreaming; virtual; abstract;
  205. Function GetVariable(const aName : UTF8String) : UTF8String;
  206. Property Stream : TStream Read FStream;
  207. end;
  208. TRestStreamerClass = Class of TRestStreamer;
  209. { TRestInputStreamer }
  210. TRestInputStreamer = Class(TRestStreamer)
  211. Public
  212. // Select input object aIndex. Must return False if no such object in input
  213. // Currently aIndex=0, but for batch operations this may later become nonzero.
  214. Function SelectObject(aIndex : Integer) : Boolean; virtual; abstract;
  215. // Return Nil if none found. If result is non-nil, caller will free.
  216. Function GetContentField(aName : UTF8string) : TJSONData; virtual; abstract;
  217. Function HaveInputData(aName : UTF8string) : Boolean; virtual;
  218. Class Procedure RegisterStreamer(Const aName : String);
  219. Class Procedure UnRegisterStreamer(Const aName : String);
  220. end;
  221. TRestInputStreamerClass = Class of TRestInputStreamer;
  222. { TRestOutputStreamer }
  223. TRestOutputStreamer = Class(TRestStreamer)
  224. private
  225. FOutputOptions: TRestOutputOptions;
  226. Protected
  227. procedure SetOutputOptions(AValue: TRestOutputOptions); virtual;
  228. Public
  229. Class Procedure RegisterStreamer(Const aName : String);
  230. Class Procedure UnRegisterStreamer(Const aName : String);
  231. function RequireMetadata : Boolean; virtual;
  232. Function FieldToString(aFieldType : TRestFieldType; F : TField) : UTF8string; virtual;
  233. function FieldToBase64(F: TField): UTF8String; virtual;
  234. Function HasOption(aOption : TRestOutputOption) : Boolean;
  235. Procedure CreateErrorContent(aCode : Integer; Const aMessage: String); virtual; abstract;
  236. Procedure CreateErrorContent(aCode : Integer; Const Fmt: String; Const Args : Array of const);
  237. Procedure WriteMetadata(aFieldList : TRestFieldPairArray); virtual; abstract;
  238. Procedure StartData; virtual; abstract;
  239. Procedure StartRow; virtual; abstract;
  240. Procedure WriteField(aPair : TRestFieldPair); virtual; abstract;
  241. Procedure EndRow; virtual; abstract;
  242. Procedure EndData; virtual; abstract;
  243. Procedure FinalizeOutput; virtual; abstract;
  244. // Set before InitStreaming is called;
  245. Property OutputOptions : TRestOutputOptions Read FOutputOptions Write SetOutputOptions;
  246. end;
  247. TRestOutputStreamerClass = class of TRestOutputStreamer;
  248. { TRestContext }
  249. TRestContext = Class(TBaseRestContext)
  250. Private
  251. FIO : TRestIO;
  252. Protected
  253. function GetConnection: TSQLConnection; override;
  254. function GetTransaction: TSQLTransaction; override;
  255. Function DoGetInputData(const aName : UTF8string) : TJSONData; override;
  256. Function GetUpdateData : TDataset; override;
  257. property IO : TRestIO Read FIO;
  258. Public
  259. Function GetVariable(Const aName : UTF8String; aSources : TVariableSources; Out aValue : UTF8String) : Boolean; override;
  260. end;
  261. { TRestIO }
  262. TSQLLogNotifyEvent = Procedure (Sender : TObject; EventType : TDBEventType; Const Msg : String) of object;
  263. TRestIO = Class
  264. private
  265. FConn: TSQLConnection;
  266. FCOnnection: UTF8String;
  267. FInput: TRestInputStreamer;
  268. FOnSQLLog: TSQLLogNotifyEvent;
  269. FOperation: TRestOperation;
  270. FOutput: TRestOutputStreamer;
  271. FRequest: TRequest;
  272. FResource: TSQLDBRestResource;
  273. FResponse: TResponse;
  274. FRestContext: TRestContext;
  275. FRestStatuses: TRestStatusConfig;
  276. FRestStrings: TRestStringsConfig;
  277. FSchema: UTF8String;
  278. FTrans: TSQLTransaction;
  279. FContentStream : TStream;
  280. FUpdatedData: TBufDataset;
  281. function GetResourceName: UTF8String;
  282. function GetUserID: String;
  283. procedure SetUserID(const AValue: String);
  284. Protected
  285. Public
  286. Constructor Create(aRequest : TRequest; aResponse : TResponse); virtual;
  287. Destructor Destroy; override;
  288. // Log callback for SQL. Rerouted here, because we need IO
  289. procedure DoSQLLog(Sender: TSQLConnection; EventType: TDBEventType; const Msg: String);
  290. // Set things.
  291. Procedure SetIO(aInput : TRestInputStreamer;aOutput : TRestOutputStreamer);
  292. Procedure SetConn(aConn : TSQLConnection; ATrans : TSQLTransaction);
  293. Procedure SetResource(aResource : TSQLDBRestResource);
  294. procedure SetOperation(aOperation : TRestOperation);
  295. Procedure SetRestStrings(aValue : TRestStringsConfig);
  296. Procedure SetRestStatuses(aValue : TRestStatusConfig);
  297. // Get things
  298. class function StrToNullBoolean(const S: String; Strict: Boolean): TNullBoolean;
  299. Procedure DoGetVariable(Sender : TObject; Const aName : UTF8String; Out aVal : UTF8String);
  300. Function GetVariable (Const aName : UTF8String; Out aVal : UTF8String; AllowedSources : TVAriableSources = AllVariableSources) : TVariableSource; virtual;
  301. function GetFilterVariable(const aName: UTF8String; AFilter: TRestFieldFilter; out aValue: UTF8String): TVariableSource;
  302. Function GetBooleanVar(Const aName : UTF8String; aStrict : Boolean = False) : TNullBoolean;
  303. function GetRequestOutputOptions(aDefault: TRestOutputOptions): TRestOutputOptions;
  304. function GetLimitOffset(aEnforceLimit: Int64; out aLimit, aOffset: Int64): boolean;
  305. // Create error response in output
  306. function CreateRestContext: TRestContext; virtual;
  307. Procedure CreateErrorResponse;
  308. Property Operation : TRestOperation Read FOperation;
  309. // Not owned by TRestIO
  310. Property Request : TRequest read FRequest;
  311. Property Response : TResponse read FResponse;
  312. Property Connection : TSQLConnection Read FConn Write FConn;
  313. Property Transaction : TSQLTransaction Read FTrans Write FTrans;
  314. Property Resource : TSQLDBRestResource Read FResource;
  315. Property RestStrings : TRestStringsConfig Read FRestStrings;
  316. Property RestStatuses : TRestStatusConfig Read FRestStatuses;
  317. // owned by TRestIO
  318. Property UpdatedData : TBufDataset Read FUpdatedData;
  319. Property RESTInput : TRestInputStreamer read FInput;
  320. Property RESTOutput : TRestOutputStreamer read FOutput;
  321. Property RequestContentStream : TStream Read FContentStream;
  322. Property RestContext : TRestContext Read FRestContext;
  323. // For informative purposes
  324. Property ResourceName : UTF8String Read GetResourceName;
  325. Property Schema : UTF8String Read FSchema;
  326. Property ConnectionName : UTF8String Read FCOnnection;
  327. Property UserID : String Read GetUserID Write SetUserID;
  328. // For logging
  329. Property OnSQLLog :TSQLLogNotifyEvent Read FOnSQLLog Write FOnSQLLog;
  330. end;
  331. TRestIOClass = Class of TRestIO;
  332. { TStreamerDef }
  333. TStreamerDef = Class (TCollectionItem)
  334. private
  335. FClass: TRestStreamerClass;
  336. FName: String;
  337. Public
  338. Property MyClass : TRestStreamerClass Read FClass Write FClass;
  339. Property MyName : String Read FName Write Fname;
  340. end;
  341. { TStreamerDefList }
  342. TStreamerDefList = Class(TCollection)
  343. private
  344. function GetD(aIndex : integer): TStreamerDef;
  345. Public
  346. Function IndexOfStreamer(const aName : string) : Integer;
  347. Function IndexOfStreamerContentType(const aContentType : string) : Integer;
  348. Property Defs[aIndex : integer] : TStreamerDef Read GetD; default;
  349. end;
  350. { TStreamerFactory }
  351. TRestStreamerType = (rstInput,rstOutput);
  352. TStreamerFactory = Class (TObject)
  353. Private
  354. class var FGlobal : TStreamerFactory;
  355. Private
  356. FDefs : Array[TRestStreamerType] of TStreamerDefList;
  357. Protected
  358. Function FindDefByName(aType : TRestStreamerType; const aName : String) : TStreamerDef;
  359. Function FindDefByContentType(aType : TRestStreamerType; const aContentType : String) : TStreamerDef;
  360. Function IndexOfStreamer(aType : TRestStreamerType; const aName : string) : Integer;
  361. Function IndexOfStreamerContentType(aType : TRestStreamerType; const aContentType : string) : Integer;
  362. Procedure RegisterStreamer(aType : TRestStreamerType; Const aName : String; aClass : TRestStreamerClass);
  363. Procedure UnRegisterStreamer(aType : TRestStreamerType; Const aName : String);
  364. Public
  365. Constructor Create;
  366. Destructor Destroy; override;
  367. Class Function Instance : TStreamerFactory;
  368. Class Procedure GetStreamerList(aList : TStrings; atype : TRestStreamerType);
  369. Procedure GetStreamerDefNames(aList : TStrings; atype : TRestStreamerType);
  370. Function FindStreamerByName(aType : TRestStreamerType; const aName : string) : TStreamerDef;
  371. Function FindStreamerByContentType(aType : TRestStreamerType; const aContentType : string) : TStreamerDef;
  372. end;
  373. { TRestBufDataset }
  374. TRestBufDataset = class (TBufDataset)
  375. protected
  376. procedure LoadBlobIntoBuffer(FieldDef: TFieldDef; ABlobBuf: PBufBlobField); override;
  377. end;
  378. implementation
  379. uses base64, dateutils, sqldbrestconst;
  380. Const
  381. DefaultPropertyNames : Array[TRestStringProperty] of UTF8String = (
  382. ISODateFormat, { rpDateFormat }
  383. ISODateTimeFormat, { rpDateTimeFormat }
  384. ISOTimeFormat, { rpTimeFormat }
  385. 'data', { rpDataRoot}
  386. 'metaData', { rpMetaDataRoot }
  387. 'error', { rpErrorRoot }
  388. 'name', { rpFieldNameProp }
  389. 'type', { rpFieldTypeProp }
  390. 'format', { rpFieldDateFormatProp }
  391. 'maxLen', { rpFieldMaxLenProp }
  392. 'humanreadable', { rpHumanReadable }
  393. 'fl', { rpFieldList }
  394. 'xl', { rpExcludeFieldList }
  395. 'Connection', { rpConnection }
  396. 'Resource', { rpResource }
  397. 'metadata', { rpIncludeMetadata }
  398. 'sparse', { rpSparse }
  399. 'row', { rpRowName }
  400. 'fields', { rpMetaDataFields }
  401. 'field', { rpMetaDataField }
  402. 'code', { rpErrorCode }
  403. 'message', { rpErrorMessage }
  404. '', { rpFilterEqual }
  405. '_lt', { rpFilterLessThan }
  406. '_gt', { rpFilterGreaterThan }
  407. '_lte', { rpFilterLessThanEqual }
  408. '_gte', { rpFilterGreaterThanEqual }
  409. '_null', { rpFilterIsNull }
  410. 'limit', { rpLimit }
  411. 'offset', { rpOffset }
  412. 'sort', { rpOrderBy }
  413. 'metadata', { rpMetadataResourceName }
  414. 'fmtin', { rpInputFormat }
  415. 'fmt', { rpOutputFormat }
  416. 'customview', { rpCustomViewResourceName }
  417. 'sql', { rpCustomViewSQLParam }
  418. 'datapacket', { rpXMLDocumentRoot}
  419. '_connection', { rpConnectionResourceName }
  420. '_parameters', { rpParametersResourceName }
  421. 'parameters' { rpParametersRoutePart }
  422. );
  423. DefaultStatuses : Array[TRestStatus] of Word = (
  424. 500, { rsError }
  425. 200, { rsGetOK }
  426. 201, { rsPostOK }
  427. 200, { rsPutOK }
  428. 204, { rsDeleteOK }
  429. 400, { rsInvalidParam }
  430. 200, { rsCORSOK}
  431. 403, { rsCORSNotallowed}
  432. 401, { rsUnauthorized }
  433. 403, { rsResourceNotAllowed }
  434. 405, { rsRestOperationNotAllowed }
  435. 400, { rsInvalidMethod }
  436. 404, { rsUnknownResource }
  437. 404, { rsNoResourceSpecified }
  438. 400, { rsNoConnectionSpecified }
  439. 404, { rsRecordNotFound }
  440. 400, { rsInvalidContent }
  441. 200 { rsPatchOK }
  442. );
  443. { TRestBufDataset }
  444. procedure TRestBufDataset.LoadBlobIntoBuffer(FieldDef: TFieldDef; ABlobBuf: PBufBlobField);
  445. begin
  446. If (FieldDef=Nil) or (aBlobBuf=Nil) then
  447. exit;
  448. end;
  449. { TRestStatusConfig }
  450. function TRestStatusConfig.GetStatus(AIndex: Integer): Word;
  451. begin
  452. Result:=GetStatusCode(TRestStatus(aIndex));
  453. end;
  454. function TRestStatusConfig.IsStatusStored(AIndex: Integer): Boolean;
  455. Var
  456. W : Word;
  457. begin
  458. W:=FStatus[TRestStatus(aIndex)];
  459. Result:=(W<>0) and (W<>DefaultStatuses[TRestStatus(aIndex)]);
  460. end;
  461. procedure TRestStatusConfig.SetStatus(AIndex: Integer; AValue: Word);
  462. begin
  463. if (aValue<>DefaultStatuses[TRestStatus(aIndex)]) then
  464. aValue:=0;
  465. FStatus[TRestStatus(aIndex)]:=aValue;
  466. end;
  467. procedure TRestStatusConfig.Assign(aSource: TPersistent);
  468. Var
  469. C : TRestStatusConfig;
  470. S : TRestStatus;
  471. begin
  472. if aSource is TRestStatusConfig then
  473. begin
  474. C:=aSource as TRestStatusConfig;
  475. for S in TRestStatus do
  476. FStatus[S]:=C.FStatus[S];
  477. end
  478. else
  479. inherited Assign(aSource);
  480. end;
  481. function TRestStatusConfig.GetStatusCode(aStatus: TRestStatus): Word;
  482. begin
  483. Result:=FStatus[aStatus];
  484. if Result=0 then
  485. Result:=DefaultStatuses[aStatus];
  486. end;
  487. { TRestContext }
  488. function TRestContext.GetVariable(const aName: UTF8String; aSources : TVariableSources; out aValue: UTF8String): Boolean;
  489. Var
  490. D : TJSONData;
  491. begin
  492. Result:=FIO.GetVariable(aName,aValue,aSources)<>vsNone;
  493. if Not Result and (vsData in aSources) then
  494. begin
  495. // Will be freed.
  496. D:=GetInputData(aName);
  497. Result:=Assigned(D);
  498. if Result then
  499. if D.JSONType in StructuredJSONTypes then
  500. aValue:=D.AsJSON
  501. else
  502. aValue:=D.AsString;
  503. end;
  504. end;
  505. function TRestContext.GetConnection: TSQLConnection;
  506. begin
  507. Result:=IO.Connection;
  508. end;
  509. function TRestContext.GetTransaction: TSQLTransaction;
  510. begin
  511. Result:=IO.Transaction;
  512. end;
  513. function TRestContext.DoGetInputData(const aName: UTF8string): TJSONData;
  514. begin
  515. Result:=IO.RESTInput.GetContentField(aName);
  516. end;
  517. function TRestContext.GetUpdateData: TDataset;
  518. begin
  519. Result:=IO.UpdatedData;
  520. end;
  521. { TStreamerDefList }
  522. function TStreamerDefList.GetD(aIndex : integer): TStreamerDef;
  523. begin
  524. Result:=TStreamerDef(Items[aIndex])
  525. end;
  526. function TStreamerDefList.IndexOfStreamer(const aName: string): Integer;
  527. begin
  528. Result:=Count-1;
  529. While (Result>=0) and Not SameText(GetD(Result).MyName,aName) do
  530. Dec(Result);
  531. end;
  532. function TStreamerDefList.IndexOfStreamerContentType(const aContentType: string): Integer;
  533. begin
  534. Result:=Count-1;
  535. While (Result>=0) and Not SameText(GetD(Result).MyClass.GetContentType, aContentType) do
  536. Dec(Result);
  537. end;
  538. { TStreamerFactory }
  539. function TStreamerFactory.FindDefByName(aType : TRestStreamerType; const aName: String): TStreamerDef;
  540. Var
  541. Idx : integer;
  542. begin
  543. Idx:=FDefs[aType].IndexOfStreamer(aName);
  544. if Idx=-1 then
  545. Result:=Nil
  546. else
  547. Result:=FDefs[aType][Idx];
  548. end;
  549. function TStreamerFactory.FindDefByContentType(aType : TRestStreamerType; Const aContentType: String): TStreamerDef;
  550. Var
  551. Idx : integer;
  552. begin
  553. Idx:=FDefs[aType].IndexOfStreamerContentType(aContentType);
  554. if Idx=-1 then
  555. Result:=Nil
  556. else
  557. Result:=FDefs[aType][Idx];
  558. end;
  559. procedure TStreamerFactory.RegisterStreamer(aType : TRestStreamerType; const aName: String; aClass: TRestStreamerClass);
  560. Var
  561. D : TStreamerDef;
  562. begin
  563. D:=FindDefByName(atype,aName);
  564. if D=Nil then
  565. begin
  566. D:=FDefs[atype].Add as TStreamerDef;
  567. D.MyName:=aName;
  568. end;
  569. D.MyClass:=aClass;
  570. end;
  571. procedure TStreamerFactory.UnRegisterStreamer(aType : TRestStreamerType; const aName: String);
  572. begin
  573. FindDefByName(aType,aName).Free;
  574. end;
  575. constructor TStreamerFactory.Create;
  576. Var
  577. T : TRestStreamerType;
  578. begin
  579. for T in TRestStreamerType do
  580. FDefs[T]:=TStreamerDefList.Create(TStreamerDef);
  581. end;
  582. destructor TStreamerFactory.Destroy;
  583. Var
  584. T : TRestStreamerType;
  585. begin
  586. for T in TRestStreamerType do
  587. FreeAndNil(FDefs[T]);
  588. inherited Destroy;
  589. end;
  590. class function TStreamerFactory.Instance: TStreamerFactory;
  591. begin
  592. if FGlobal=Nil then
  593. FGlobal:=TStreamerFactory.Create;
  594. Result:=FGlobal;
  595. end;
  596. class procedure TStreamerFactory.GetStreamerList(aList: TStrings;
  597. atype: TRestStreamerType);
  598. begin
  599. TStreamerFactory.Instance.GetStreamerDefNames(aList,aType);
  600. end;
  601. procedure TStreamerFactory.GetStreamerDefNames(aList: TStrings; atype: TRestStreamerType);
  602. var
  603. I : Integer;
  604. begin
  605. aList.Clear;
  606. For I:=0 to FDefs[aType].Count-1 do
  607. aList.Add(FDefs[aType][I].MyName);
  608. end;
  609. function TStreamerFactory.IndexOfStreamer(aType : TRestStreamerType; const aName: string): Integer;
  610. begin
  611. Result:=FDefs[aType].IndexOfStreamer(aName);
  612. end;
  613. function TStreamerFactory.IndexOfStreamerContentType(aType : TRestStreamerType; const aContentType: string): Integer;
  614. begin
  615. Result:=FDefs[aType].IndexOfStreamerContentType(aContentType);
  616. end;
  617. function TStreamerFactory.FindStreamerByName(aType : TRestStreamerType; const aName: string): TStreamerDef;
  618. begin
  619. Result:=FindDefByName(aType,aName);
  620. end;
  621. function TStreamerFactory.FindStreamerByContentType(aType : TRestStreamerType; const aContentType: string): TStreamerDef;
  622. begin
  623. Result:=FindDefByContentType(aType,aContentType);
  624. end;
  625. { TRestStringsConfig }
  626. function TRestStringsConfig.GetRestPropName(AIndex: Integer): UTF8String;
  627. begin
  628. Result:=FValues[TRestStringProperty(AIndex)];
  629. if (Result='') then
  630. Result:=DefaultPropertyNames[TRestStringProperty(AIndex)]
  631. end;
  632. function TRestStringsConfig.IsRestStringStored(AIndex: Integer): Boolean;
  633. Var
  634. V : UTF8String;
  635. begin
  636. V:=FValues[TRestStringProperty(AIndex)];
  637. Result:=(V<>'') and (V<>DefaultPropertyNames[TRestStringProperty(AIndex)]);
  638. end;
  639. procedure TRestStringsConfig.SetRestPropName(AIndex: Integer; AValue: UTF8String);
  640. begin
  641. FValues[TRestStringProperty(AIndex)]:=aValue;
  642. end;
  643. class function TRestStringsConfig.GetDefaultString(aString: TRestStringProperty): UTF8String;
  644. begin
  645. Result:=DefaultPropertyNames[aString]
  646. end;
  647. function TRestStringsConfig.GetRestString(aString: TRestStringProperty): UTF8String;
  648. begin
  649. Result:=FValues[aString];
  650. if (Result='') then
  651. Result:=GetDefaultString(aString);
  652. end;
  653. procedure TRestStringsConfig.SetRestString(aString: TRestStringProperty; AValue: UTF8String);
  654. begin
  655. FValues[AString]:=aValue;
  656. end;
  657. procedure TRestStringsConfig.Assign(aSource: TPersistent);
  658. Var
  659. R : TRestStringsConfig;
  660. S : TRestStringProperty;
  661. begin
  662. if (aSource is TRestStringsConfig) then
  663. begin
  664. R:=aSource as TRestStringsConfig;
  665. For S in TRestStringProperty do
  666. FValues[S]:=R.FValues[S];
  667. end;
  668. inherited Assign(aSource);
  669. end;
  670. { TRestOutputStreamer }
  671. procedure TRestOutputStreamer.SetOutputOptions(AValue: TRestOutputOptions);
  672. begin
  673. if FOutputOptions=AValue then Exit;
  674. FOutputOptions:=AValue;
  675. if RequireMetadata then
  676. Include(FOutputOptions,ooMetadata);
  677. end;
  678. procedure TRestOutputStreamer.CreateErrorContent(aCode: Integer;
  679. const Fmt: String; const Args: array of const);
  680. Var
  681. S : String;
  682. begin
  683. Try
  684. S:=Format(Fmt,Args);
  685. except
  686. On E : Exception do
  687. begin
  688. S:=Format('Error formatting string "%s" with %d arguments. Original code: %d',[Fmt,Length(Args),aCode]);
  689. aCode:=Statuses.GetStatusCode(rsError);
  690. end;
  691. end;
  692. CreateErrorContent(aCode,S);
  693. end;
  694. function TRestOutputStreamer.HasOption(aOption: TRestOutputOption): Boolean;
  695. begin
  696. Result:=aOption in OutputOptions;
  697. end;
  698. Function TRestOutputStreamer.FieldToBase64(F : TField) : UTF8String;
  699. var
  700. BF : TBlobField absolute F;
  701. Src : TStream;
  702. Dest : TStringStream;
  703. E : TBase64EncodingStream;
  704. begin
  705. Src:=Nil;
  706. Dest:=nil;
  707. E:=Nil;
  708. Try
  709. if f is TBlobField then
  710. begin
  711. Src:=TMemoryStream.Create;
  712. Src.Size:=BF.DataSize;
  713. BF.SaveToStream(Src);
  714. end
  715. else
  716. Src:=TStringStream.Create(F.AsString);
  717. Src.Position:=0;
  718. Dest:=TStringStream.Create(''{,CP_UTF8});
  719. E:=TBase64EncodingStream.Create(Dest);
  720. E.CopyFrom(Src,0);
  721. FreeAndNil(E); // Will flush
  722. Result:=Dest.DataString;
  723. Finally
  724. Src.Free;
  725. Dest.Free;
  726. end;
  727. end;
  728. { TRestStreamer }
  729. constructor TRestStreamer.Create(aStream: TStream; aStrings: TRestStringsConfig; aStatus : TRestStatusConfig; aOnGetVar: TRestGetVariableEvent);
  730. begin
  731. FStream:=aStream;
  732. FOnGetVar:=aOnGetVar;
  733. FStrings:=aStrings;
  734. FStatuses:=aStatus;
  735. end;
  736. function TRestStreamer.GetString(aString: TRestStringProperty): UTF8String;
  737. begin
  738. If Assigned(FStrings) then
  739. Result:=FStrings.GetRestString(aString)
  740. else
  741. Result:=DefaultPropertyNames[aString];
  742. end;
  743. function TRestStreamer.GetVariable(const aName: UTF8String): UTF8String;
  744. begin
  745. Result:='';
  746. if Assigned(FOnGetVar) then
  747. FOnGetVar(Self,aName,Result);
  748. end;
  749. Class function TRestStreamer.GetContentType: String;
  750. begin
  751. Result:='text/html';
  752. end;
  753. function TRestInputStreamer.HaveInputData(aName: UTF8string): Boolean;
  754. Var
  755. D : TJSONData;
  756. begin
  757. D:=GetContentField(aName);
  758. Result:=D<>Nil;
  759. D.Free;
  760. end;
  761. class procedure TRestInputStreamer.RegisterStreamer(const aName: String);
  762. begin
  763. TStreamerFactory.Instance.RegisterStreamer(rstInput,aName,Self)
  764. end;
  765. class procedure TRestInputStreamer.UnRegisterStreamer(const aName: String);
  766. begin
  767. TStreamerFactory.Instance.UnRegisterStreamer(rstInput,aName);
  768. end;
  769. class procedure TRestOutputStreamer.RegisterStreamer(const aName: String);
  770. begin
  771. TStreamerFactory.Instance.RegisterStreamer(rstOutput,aName,Self)
  772. end;
  773. class procedure TRestOutPutStreamer.UnRegisterStreamer(const aName: String);
  774. begin
  775. TStreamerFactory.Instance.UnRegisterStreamer(rstOutput,aName)
  776. end;
  777. function TRestOutputStreamer.RequireMetadata: Boolean;
  778. begin
  779. Result:=False;
  780. end;
  781. function TRestOutputStreamer.FieldToString(aFieldType : TRestFieldType; F: TField): UTF8string;
  782. begin
  783. Case aFieldType of
  784. rftInteger : Result:=F.AsString;
  785. rftLargeInt : Result:=F.AsString;
  786. rftFloat : Result:=F.AsString;
  787. rftDate : Result:=FormatDateTime(GetString(rpDateFormat),DateOf(F.AsDateTime));
  788. rftTime : Result:=FormatDateTime(GetString(rpTimeFormat),TimeOf(F.AsDateTime));
  789. rftDateTime : Result:=FormatDateTime(GetString(rpDateTimeFormat),F.AsDateTime);
  790. rftString : Result:=F.AsString;
  791. rftBoolean : Result:=BoolToStr(F.AsBoolean,'true','false');
  792. rftBlob : Result:=FieldToBase64(F);
  793. else
  794. Result:='';
  795. end;
  796. end;
  797. { TRestIO }
  798. procedure TRestIO.SetIO(aInput: TRestInputStreamer; aOutput: TRestOutputStreamer);
  799. begin
  800. Finput:=aInput;
  801. Finput.FOnGetVar:=@DoGetVariable;
  802. Foutput:=aOutput;
  803. FOutput.FOnGetVar:=@DoGetVariable;
  804. end;
  805. procedure TRestIO.SetConn(aConn: TSQLConnection; ATrans: TSQLTransaction);
  806. begin
  807. FConn:=aConn;
  808. FTrans:=aTrans;
  809. end;
  810. procedure TRestIO.SetResource(aResource: TSQLDBRestResource);
  811. begin
  812. Fresource:=AResource;
  813. end;
  814. procedure TRestIO.SetOperation(aOperation: TRestOperation);
  815. begin
  816. FOperation:=aOperation;
  817. end;
  818. procedure TRestIO.SetRestStrings(aValue: TRestStringsConfig);
  819. begin
  820. FRestStrings:=aValue;
  821. end;
  822. procedure TRestIO.SetRestStatuses(aValue: TRestStatusConfig);
  823. begin
  824. FRestStatuses:=aValue;
  825. end;
  826. procedure TRestIO.DoGetVariable(Sender: TObject; const aName: UTF8String; out
  827. aVal: UTF8String);
  828. begin
  829. GetVariable(aName,aVal);
  830. end;
  831. procedure TRestIO.SetUserID(const AValue: String);
  832. begin
  833. if (UserID=AValue) then Exit;
  834. FRestContext.UserID:=AValue;
  835. end;
  836. function TRestIO.GetUserID: String;
  837. begin
  838. Result:=FRestContext.UserID;
  839. end;
  840. function TRestIO.GetResourceName: UTF8String;
  841. begin
  842. if Assigned(FResource) then
  843. Result:=FResource.ResourceName
  844. else
  845. Result:='?';
  846. end;
  847. constructor TRestIO.Create(aRequest: TRequest; aResponse: TResponse);
  848. begin
  849. FRequest:=aRequest;
  850. FResponse:=aResponse;
  851. FContentStream:=TStringStream.Create(aRequest.Content);
  852. FRestContext:=CreateRestContext;
  853. FRestContext.FIO:=Self;
  854. FUpdatedData:=TRestBufDataset.Create(Nil);
  855. end;
  856. destructor TRestIO.Destroy;
  857. begin
  858. FreeAndNil(FUpdatedData);
  859. FreeAndNil(FRestContext);
  860. if Assigned(FInput) then
  861. Finput.FOnGetVar:=Nil;
  862. if Assigned(Foutput) then
  863. FOutput.FOnGetVar:=Nil;
  864. FreeAndNil(FContentStream) ;
  865. FreeAndNil(Finput);
  866. FreeAndNil(Foutput);
  867. inherited Destroy;
  868. end;
  869. procedure TRestIO.DoSQLLog(Sender: TSQLConnection; EventType: TDBEventType; const Msg: String);
  870. begin
  871. If Assigned(OnSQLLog) then
  872. FOnSQLLog(Self,EventType,Msg);
  873. end;
  874. function TRestIO.CreateRestContext : TRestContext;
  875. begin
  876. Result:=TRestContext.Create;
  877. end;
  878. function TRestIO.GetVariable(const aName: UTF8String; out aVal: UTF8String;
  879. AllowedSources: TVAriableSources): TVariableSource;
  880. Function FindInList(aSource : TVariableSource;L : TStrings) : Boolean;
  881. Var
  882. I : Integer;
  883. N,V : String;
  884. begin
  885. Result:=(aSource in AllowedSources);
  886. if Result then
  887. begin
  888. I:=L.IndexOfName(aName);
  889. Result:=I<>-1;
  890. if Result then
  891. begin
  892. L.GetNameValue(I,N,V);
  893. aVal:=V;
  894. GetVariable:=aSource;
  895. end;
  896. end;
  897. end;
  898. begin
  899. Result:=vsNone;
  900. With Request do
  901. if not FIndInList(vsQuery,QueryFields) then
  902. if not FindInList(vsContent,ContentFields) then
  903. begin
  904. aVal:=RouteParams[aName];
  905. if (aVal<>'') then
  906. result:=vsRoute
  907. else
  908. FindInList(vsHeader,CustomHeaders);
  909. end;
  910. end;
  911. function TRestIO.GetFilterVariable(const aName: UTF8String; AFilter: TRestFieldFilter;out aValue: UTF8String) : TVariableSource;
  912. Const
  913. FilterStrings : Array[TRestFieldFilter] of TRestStringProperty =
  914. (rpFilterEqual,rpFilterLessThan,rpFilterGreaterThan,rpFilterLessThanEqual,rpFilterGreaterThanEqual,rpFilterIsNull);
  915. begin
  916. aValue:='';
  917. Result:=GetVariable(aName+FRestStrings.GetRestString(FilterStrings[aFilter]),aValue,[vsQuery]);
  918. end;
  919. class function TRestIO.StrToNullBoolean(const S: String; Strict: Boolean): TNullBoolean;
  920. var
  921. ls : string;
  922. begin
  923. result:=nbNone;
  924. ls:=lowercase(s);
  925. if (ls<>'') then
  926. if (ls='1') or (ls='t') or (ls='true') or (ls='y') then
  927. Result:=nbTrue
  928. else
  929. if (ls='0') or (ls='f') or (ls='false') or (ls='n') then
  930. Result:=nbFalse
  931. else if not Strict then
  932. Result:=nbNone
  933. else
  934. Raise EConvertError.CreateFmt('Not a correct boolean value: "%s"',[S])
  935. end;
  936. function TRestIO.GetBooleanVar(const aName: UTF8String; aStrict : Boolean = False): TNullBoolean;
  937. Var
  938. S : UTF8String;
  939. begin
  940. result:=nbNone;
  941. if GetVariable(aName,S)=vsNone then
  942. Result:=nbNone
  943. else
  944. Result:=StrToNullBoolean(S,aStrict);
  945. end;
  946. function TRestIO.GetRequestOutputOptions(aDefault: TRestOutputOptions
  947. ): TRestOutputOptions;
  948. Procedure CheckParam(aName : String; aOption: TRestOutputOption);
  949. begin
  950. Case GetBooleanVar(aName) of
  951. nbFalse : Exclude(Result,aOption);
  952. nbTrue : Include(Result,aOption);
  953. else
  954. // nbNull: keep default
  955. end
  956. end;
  957. begin
  958. Result:=aDefault;
  959. CheckParam(FRestStrings.GetRestString(rpHumanReadable),ooHumanReadable);
  960. CheckParam(FRestStrings.GetRestString(rpSparse),ooSparse);
  961. CheckParam(FRestStrings.GetRestString(rpIncludeMetadata),ooMetadata);
  962. end;
  963. function TRestIO.GetLimitOffset(aEnforceLimit : Int64; out aLimit, aOffset: Int64): boolean;
  964. Var
  965. P,S : UTF8String;
  966. begin
  967. aLimit:=0;
  968. aOffset:=0;
  969. P:=RestStrings.GetRestString(rpLimit);
  970. Result:=GetVariable(P,S,[vsQuery])<>vsNone;
  971. if Not Result then
  972. Exit;
  973. if (S<>'') and not TryStrToInt64(S,aLimit) then
  974. Raise ESQLDBRest.CreateFmt(RestStatuses.GetStatusCode(rsInvalidParam),SErrInvalidParam,[P]);
  975. P:=RestStrings.GetRestString(rpOffset);
  976. if GetVariable(P,S,[vsQuery])<>vsNone then
  977. if (S<>'') and not TryStrToInt64(S,aOffset) then
  978. Raise ESQLDBRest.CreateFmt(RestStatuses.GetStatusCode(rsInvalidParam),SErrInvalidParam,[P]);
  979. if (aEnforceLimit>0) and (aLimit>aEnforceLimit) then
  980. aLimit:=aEnforceLimit;
  981. end;
  982. procedure TRestIO.CreateErrorResponse;
  983. begin
  984. RestOutput.CreateErrorContent(Response.Code,Response.CodeText);
  985. end;
  986. finalization
  987. FreeAndNil(TStreamerFactory.Fglobal);
  988. end.