sqldbrestio.pp 37 KB

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