sqldbrestio.pp 42 KB

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