restconnection.pas 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. unit RestConnection;
  2. {$mode objfpc}
  3. interface
  4. uses
  5. Classes, SysUtils, Web, DB;
  6. Type
  7. { TRESTConnection }
  8. TRestGetURLEvent = Procedure (Sender : TComponent; aRequest : TDataRequest; Var aURL : String) of Object;
  9. TRestUpdateURLEvent = Procedure (Sender : TComponent; aRequest : TRecordUpdateDescriptor; Var aURL : String) of Object;
  10. TRESTConnection = Class(TComponent)
  11. private
  12. FBaseURL: String;
  13. FDataProxy : TDataProxy;
  14. FOnGetURL: TRestGetURLEvent;
  15. FOnUpdateURL: TRestUpdateURLEvent;
  16. FPageParam: String;
  17. function GetDataProxy: TDataProxy;
  18. Protected
  19. Function GetUpdateBaseURL : String; virtual;
  20. Function GetReadBaseURL : String; virtual;
  21. Function GetPageURL(aRequest : TDataRequest) : String;
  22. Function GetRecordUpdateURL(aRequest : TRecordUpdateDescriptor) : String;
  23. Public
  24. Function DoGetDataProxy : TDataProxy; virtual;
  25. Public
  26. Property DataProxy : TDataProxy Read GetDataProxy;
  27. Property BaseURL : String Read FBaseURL Write FBaseURL;
  28. Property PageParam : String Read FPageParam Write FPageParam;
  29. Property OnGetURL : TRestGetURLEvent Read FOnGetURL Write FOnGetURL;
  30. Property OnUpdateURL : TRestUpdateURLEvent Read FOnUpdateURL Write FOnUpdateURL;
  31. end;
  32. { TRESTDataProxy }
  33. TRESTDataProxy = class(TDataProxy)
  34. private
  35. FConnection: TRESTConnection;
  36. protected
  37. Procedure CheckBatchComplete(aBatch : TRecordUpdateBatch); virtual;
  38. Public
  39. Function GetUpdateDescriptorClass : TRecordUpdateDescriptorClass; override;
  40. Function ProcessUpdateBatch(aBatch : TRecordUpdateBatch): Boolean; override;
  41. Function DoGetData(aRequest: TDataRequest): Boolean; override;
  42. Function GetDataRequest(aOptions: TLoadOptions; aAfterRequest: TDataRequestEvent; aAfterLoad: TDatasetLoadEvent) : TDataRequest; override;
  43. Constructor Create(AOwner: TComponent); override;
  44. Property Connection : TRESTConnection Read FConnection;
  45. end;
  46. { TRESTDataRequest }
  47. TRESTDataRequest = Class(TDataRequest)
  48. Private
  49. FXHR : TJSXMLHttpRequest;
  50. protected
  51. function onLoad(Event{%H-}: TEventListenerEvent): boolean; virtual;
  52. function TransformResult: JSValue; virtual;
  53. end;
  54. { TRESTUpdateRequest }
  55. TRESTUpdateRequest = Class(TRecordUpdateDescriptor)
  56. Private
  57. FXHR : TJSXMLHttpRequest;
  58. FBatch : TRecordUpdateBatch;
  59. protected
  60. function onLoad(Event{%H-}: TEventListenerEvent): boolean; virtual;
  61. end;
  62. implementation
  63. uses js;
  64. { TRESTUpdateRequest }
  65. function TRESTUpdateRequest.onLoad(Event: TEventListenerEvent): boolean;
  66. begin
  67. if (FXHR.Status div 100)=2 then
  68. begin
  69. Resolve(FXHR.response);
  70. Result:=True;
  71. end
  72. else
  73. ResolveFailed(FXHR.StatusText);
  74. (Proxy as TRestDataProxy).CheckBatchComplete(FBatch);
  75. end;
  76. { TRESTDataRequest }
  77. function TRESTDataRequest.TransformResult : JSValue;
  78. begin
  79. Result:=FXHR.responseText;
  80. end;
  81. function TRESTDataRequest.onLoad(Event: TEventListenerEvent): boolean;
  82. begin
  83. if (FXHR.Status=200) then
  84. begin
  85. Data:=TransformResult;
  86. Success:=rrOK;
  87. end
  88. else
  89. begin
  90. Data:=Nil;
  91. if (loAtEOF in LoadOptions) and (FXHR.Status=404) then
  92. Success:=rrEOF
  93. else
  94. begin
  95. Success:=rrFail;
  96. ErrorMsg:=FXHR.StatusText;
  97. end;
  98. end;
  99. DoAfterRequest;
  100. Result:=True;
  101. end;
  102. { TRESTConnection }
  103. function TRESTConnection.GetDataProxy: TDataProxy;
  104. begin
  105. if (FDataProxy=Nil) then
  106. FDataProxy:=DoGetDataProxy;
  107. Result:=FDataProxy;
  108. end;
  109. function TRESTConnection.GetUpdateBaseURL: String;
  110. begin
  111. Result:=BaseURL;
  112. end;
  113. function TRESTConnection.GetReadBaseURL: String;
  114. begin
  115. Result:=BaseURL;
  116. end;
  117. function TRESTConnection.GetPageURL(aRequest: TDataRequest): String;
  118. Var
  119. URL : String;
  120. begin
  121. URL:=GetReadBaseURL;
  122. if (PageParam<>'') then
  123. begin
  124. if Pos('?',URL)<>0 then
  125. URL:=URL+'&'
  126. else
  127. URL:=URL+'?';
  128. URL:=URL+PageParam+'='+IntToStr(ARequest.RequestID-1);
  129. end;
  130. if Assigned(FOnGetURL) then
  131. FOnGetURL(Self,aRequest,URL);
  132. Result:=URL;
  133. end;
  134. function TRESTConnection.GetRecordUpdateURL(aRequest: TRecordUpdateDescriptor): String;
  135. Var
  136. I : integer;
  137. Base,KeyField : String;
  138. begin
  139. KeyField:='';
  140. Result:='';
  141. Base:=GetUpdateBaseURL;
  142. if aRequest.Status in [usModified,usDeleted] then
  143. begin
  144. I:=aRequest.Dataset.Fields.Count-1;
  145. While (KeyField='') and (I>=0) do
  146. begin
  147. if pfInKey in aRequest.Dataset.Fields[i].ProviderFlags then
  148. KeyField:=aRequest.Dataset.Fields[i].FieldName;
  149. Dec(I);
  150. end;
  151. if (KeyField='') then
  152. DatabaseError('No key field',aRequest.Dataset);
  153. end;
  154. if (KeyField<>'') and (Base<>'') and (Base[Length(Base)]<>'/') then
  155. Base:=Base+'/';
  156. Case aRequest.Status of
  157. usModified,
  158. usDeleted: Result:=Base+TJSJSON.stringify(TJSObject(aRequest.Data)[KeyField]);
  159. usInserted : Result:=Base;
  160. end;
  161. If Assigned(FOnUpdateURL) then
  162. FOnUpdateURL(Self,aRequest,Result);
  163. end;
  164. function TRESTConnection.DoGetDataProxy: TDataProxy;
  165. begin
  166. Result:=TRESTDataProxy.Create(Self);
  167. end;
  168. { TRESTDataProxy }
  169. procedure TRESTDataProxy.CheckBatchComplete(aBatch: TRecordUpdateBatch);
  170. Var
  171. BatchOK : Boolean;
  172. I : Integer;
  173. begin
  174. BatchOK:=True;
  175. I:=aBatch.List.Count-1;
  176. While BatchOK and (I>=0) do
  177. begin
  178. BatchOK:=aBatch.List[I].Status in [usResolved,usResolveFailed];
  179. Dec(I);
  180. end;
  181. If BatchOK and Assigned(aBatch.OnResolve) then
  182. aBatch.OnResolve(Self,aBatch);
  183. end;
  184. function TRESTDataProxy.GetUpdateDescriptorClass: TRecordUpdateDescriptorClass;
  185. begin
  186. Result:=TRESTUpdateRequest;
  187. end;
  188. function TRESTDataProxy.ProcessUpdateBatch(aBatch: TRecordUpdateBatch): Boolean;
  189. Var
  190. R : TRESTUpdateRequest;
  191. i : Integer;
  192. Method,URl : String;
  193. begin
  194. Result:=False;
  195. For I:=0 to aBatch.List.Count-1 do
  196. begin
  197. R:=aBatch.List[i] as TRESTUpdateRequest;
  198. R.FBatch:=aBatch;
  199. R.FXHR:=TJSXMLHttpRequest.New;
  200. R.FXHR.AddEventListener('load',@R.onLoad);
  201. URL:=FConnection.GetRecordUpdateURL(R);
  202. Case R.Status of
  203. usInserted :
  204. Method:='POST';
  205. usModified:
  206. Method:='PUT';
  207. usDeleted:
  208. Method:='DELETE';
  209. end;
  210. R.FXHR.open(Method,URL);
  211. R.FXHR.setRequestHeader('content-type','application/json');
  212. if R.Status in [usInserted,usModified] then
  213. R.FXHR.Send(TJSJSON.Stringify(R.Data))
  214. else
  215. R.FXHR.Send;
  216. end;
  217. Result:=True;
  218. end;
  219. function TRESTDataProxy.DoGetData(aRequest: TDataRequest): Boolean;
  220. Var
  221. R : TRestDataRequest;
  222. URL : String;
  223. begin
  224. Result:=False;
  225. R:=aRequest as TRestDataRequest;
  226. R.FXHR:=TJSXMLHttpRequest.New;
  227. R.FXHR.AddEventListener('load',@R.onLoad);
  228. URL:=Connection.GetPageURL(aRequest);
  229. if (URL='') then
  230. begin
  231. if loAtEOF in R.LoadOptions then
  232. R.Success:=rrEOF
  233. else
  234. begin
  235. R.Success:=rrFail;
  236. R.ErrorMsg:='No URL to get data';
  237. R.DoAfterRequest; // This will free request !
  238. end;
  239. end
  240. else
  241. begin
  242. if (loAtEOF in R.LoadOptions) and (Connection.PageParam='') then
  243. R.Success:=rrEOF
  244. else
  245. begin
  246. R.FXHR.open('GET',URL,true);
  247. R.FXHR.send;
  248. Result:=True;
  249. end;
  250. end;
  251. end;
  252. function TRESTDataProxy.GetDataRequest(aOptions: TLoadOptions; aAfterRequest: TDataRequestEvent; aAfterLoad: TDatasetLoadEvent): TDataRequest;
  253. begin
  254. Result:=TRestDataRequest.Create(Self,aOptions, aAfterRequest,aAfterLoad);
  255. end;
  256. constructor TRESTDataProxy.Create(AOwner: TComponent);
  257. begin
  258. Inherited;
  259. If AOwner is TRestConnection then
  260. FConnection:=TRestConnection(aOwner);
  261. end;
  262. end.