Skip to content

Commit

Permalink
First public version of TMVCAPIBinder
Browse files Browse the repository at this point in the history
  • Loading branch information
danieleteti committed Mar 20, 2020
1 parent 527e389 commit 6e19787
Show file tree
Hide file tree
Showing 8 changed files with 669 additions and 48 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -94,3 +94,5 @@ samples/winecellarclient_mobile/WinesClient/res/values/styles.xml
samples/winecellarclient_mobile/WinesClient/AndroidManifest.xml
unittests/serializer/jsondataobjects/Win32/CI/dunitx-results.xml
unittests/serializer/jsondataobjects/Win32/Debug/dunitx-results.xml
unittests/general/Several/Win32/CI/sqlitetest.db
unittests/general/Several/Win32/CONSOLE/sqlitetest.db
Binary file modified samples/data/activerecorddb.db
Binary file not shown.
441 changes: 439 additions & 2 deletions samples/serversideviews_mustache/ServerSideViews.dproj

Large diffs are not rendered by default.

206 changes: 189 additions & 17 deletions sources/MVCFramework.DataSet.Utils.pas
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ interface
System.Rtti,
JsonDataObjects,
MVCFramework.Commons,
MVCFramework.Serializer.Commons;
MVCFramework.Serializer.Commons,
MVCFramework.RESTClient;

type
TFieldNamePolicy = (fpLowerCase, fpUpperCase, fpAsIs);
Expand Down Expand Up @@ -71,6 +72,7 @@ TDataSetHelper = class helper for TDataSet
function AsObjectList<T: class, constructor>(CloseAfterScroll: boolean = false; OwnsObjects: boolean = true)
: TObjectList<T>;
function AsObject<T: class, constructor>(CloseAfterScroll: boolean = false): T;

end;

TDataSetUtils = class sealed
Expand All @@ -87,20 +89,52 @@ TDataSetUtils = class sealed
[MVCNameCase(ncLowerCase)]
TDataSetHolder = class
private
FDataSet: TDataSet;
FMetadata: TMVCStringDictionary;
FOwns: boolean;
FDataSetSerializationType: TMVCDatasetSerializationType;
fDataSet: TDataSet;
fMetadata: TMVCStringDictionary;
fOwns: boolean;
fDataSetSerializationType: TMVCDatasetSerializationType;
public
constructor Create(const ADataSet: TDataSet; const AOwns: boolean = false;
const ADataSetSerializationType: TMVCDatasetSerializationType = TMVCDatasetSerializationType.
dstAllRecords); virtual;
destructor Destroy; override;
function SerializationType: TMVCDatasetSerializationType;
[MVCNameAs('data')]
property Items: TDataSet read FDataSet;
property Items: TDataSet read fDataSet;
[MVCNameAs('meta')]
property Metadata: TMVCStringDictionary read FMetadata;
property Metadata: TMVCStringDictionary read fMetadata;
end;

TMVCAPIBinder = class
protected
type
TMVCAPIBinderItem = class
private
fRESTClient: TRESTClient;
fDataSet: TDataSet;
fURI: string;
fPrimaryKeyNAme: string;
fLoading: boolean;
procedure ShowError(const AResponse: IRESTResponse);
public
constructor Create(const aRESTClient: TRESTClient; const ADataSet: TDataSet;
const aURI, aPrimaryKeyName: string);
destructor Destroy; override;
procedure HookBeforePost(DataSet: TDataSet);
procedure HookBeforeDelete(DataSet: TDataSet);
procedure HookBeforeRefresh(DataSet: TDataSet);
procedure HookAfterOpen(DataSet: TDataSet);
// procedure HookBeforeRowRequest(DataSet: TFDDataSet);
end;
private
fRESTClient: TRESTClient;

protected
fItems: TObjectList<TMVCAPIBinderItem>;
public
constructor Create(const aRESTClient: TRESTClient);
destructor Destroy; override;
procedure BindDataSetToAPI(const ADataSet: TDataSet; const aURI: string; const aPrimaryKeyName: string);
end;

function NewDataSetHolder(const ADataSet: TDataSet; const AMetaFiller: TProc<TMVCStringDictionary> = nil;
Expand All @@ -120,7 +154,7 @@ function NewDataSetRecordHolder(const ADataSet: TDataSet; const AMetaFiller: TPr
Result := TDataSetHolder.Create(ADataSet, AOwns, dstSingleRecord);
if Assigned(AMetaFiller) then
begin
AMetaFiller(Result.FMetadata);
AMetaFiller(Result.fMetadata);
end;
end;

Expand All @@ -130,7 +164,7 @@ function NewDataSetHolder(const ADataSet: TDataSet; const AMetaFiller: TProc<TMV
Result := TDataSetHolder.Create(ADataSet, AOwns, dstAllRecords);
if Assigned(AMetaFiller) then
begin
AMetaFiller(Result.FMetadata);
AMetaFiller(Result.fMetadata);
end;
end;

Expand Down Expand Up @@ -493,25 +527,163 @@ constructor TDataSetHolder.Create(const ADataSet: TDataSet; const AOwns: boolean
const ADataSetSerializationType: TMVCDatasetSerializationType = TMVCDatasetSerializationType.dstAllRecords);
begin
inherited Create;
FDataSet := ADataSet;
FMetadata := TMVCStringDictionary.Create;
FOwns := AOwns;
FDataSetSerializationType := ADataSetSerializationType;
fDataSet := ADataSet;
fMetadata := TMVCStringDictionary.Create;
fOwns := AOwns;
fDataSetSerializationType := ADataSetSerializationType;
end;

destructor TDataSetHolder.Destroy;
begin
FMetadata.Free;
if FOwns then
fMetadata.Free;
if fOwns then
begin
FDataSet.Free;
fDataSet.Free;
end;
inherited;
end;

function TDataSetHolder.SerializationType: TMVCDatasetSerializationType;
begin
Result := FDataSetSerializationType;
Result := fDataSetSerializationType;
end;

{ TMVCAPIBinder }

procedure TMVCAPIBinder.BindDataSetToAPI(const ADataSet: TDataSet; const aURI,
aPrimaryKeyName: string);
begin
fItems.Add(TMVCAPIBinderItem.Create(fRESTClient, ADataSet, aURI, aPrimaryKeyName));
end;

constructor TMVCAPIBinder.Create(const aRESTClient: TRESTClient);
begin
inherited Create;
fItems := TObjectList<TMVCAPIBinderItem>.Create(true);
fRESTClient := aRESTClient;
end;

destructor TMVCAPIBinder.Destroy;
begin
fItems.Free;
inherited;
end;

{ TMVCAPIBinder.TMVCAPIBinderItem }

constructor TMVCAPIBinder.TMVCAPIBinderItem.Create(const aRESTClient: TRESTClient; const ADataSet: TDataSet;
const aURI, aPrimaryKeyName: string);
begin
inherited Create;
fRESTClient := aRESTClient;
fDataSet := ADataSet;
fURI := aURI;
fPrimaryKeyNAme := aPrimaryKeyName;

// procedure HookBeforePost(DataSet: TDataSet);
// procedure HookBeforeDelete(DataSet: TDataSet);
// procedure HookBeforeRefresh(DataSet: TDataSet);
// procedure HookAfterOpen(DataSet: TDataSet);

fDataSet.BeforePost := HookBeforePost;
fDataSet.BeforeDelete := HookBeforeDelete;
fDataSet.BeforeRefresh := HookBeforeRefresh;
fDataSet.AfterOpen := HookAfterOpen;
end;

destructor TMVCAPIBinder.TMVCAPIBinderItem.Destroy;
begin

inherited;
end;

procedure TMVCAPIBinder.TMVCAPIBinderItem.HookAfterOpen(DataSet: TDataSet);
var
Res: IRESTResponse;
lData: TJSONObject;
begin
// this a simple sychronous request...
Res := fRESTClient.doGET('/api/customers', []);
if Res.HasError then
begin
ShowError(Res);
end;

lData := StrToJSONObject(Res.BodyAsString);
try
DataSet.DisableControls;
try
fLoading := true;
DataSet.LoadFromJSONArray(lData.A['items']);
fLoading := false;
DataSet.First;
finally
DataSet.EnableControls;
end;
finally
lData.Free;
end;
end;

procedure TMVCAPIBinder.TMVCAPIBinderItem.HookBeforeDelete(DataSet: TDataSet);
var
Res: IRESTResponse;
begin
if DataSet.State = dsBrowse then
Res := fRESTClient.DataSetDelete(fURI, DataSet.FieldByName(fPrimaryKeyNAme).AsString);
if not(Res.ResponseCode in [200]) then
begin
ShowError(Res);
end;
end;

procedure TMVCAPIBinder.TMVCAPIBinderItem.HookBeforePost(DataSet: TDataSet);
var
lRes: IRESTResponse;
lLastID: Integer;
begin
if not fLoading then
begin
lLastID := -1;
if fDataSet.State = dsInsert then
begin
lRes := fRESTClient.DataSetInsert(fURI, DataSet)
end
else
begin
lLastID := fDataSet.FieldByName(fPrimaryKeyNAme).AsInteger;
lRes := fRESTClient.DataSetUpdate(fURI, DataSet, lLastID.ToString);
end;
if not(lRes.ResponseCode in [200, 201]) then
begin
ShowError(lRes);
end
else
begin
DataSet.Refresh;
if lLastID > -1 then
begin
DataSet.Locate('id', lLastID, []);
end;
end;
end;
end;

procedure TMVCAPIBinder.TMVCAPIBinderItem.HookBeforeRefresh(DataSet: TDataSet);
begin
DataSet.Close;
DataSet.Open;
end;

procedure TMVCAPIBinder.TMVCAPIBinderItem.ShowError(const AResponse: IRESTResponse);
begin
if AResponse.HasError then
raise EMVCException.Create(AResponse.Error.Status);
// else
// MessageDlg(
// AResponse.ResponseCode.ToString + ': ' + AResponse.ResponseText + sLineBreak +
// AResponse.BodyAsString,
// mtError, [mbOK], 0);
end;

end.
21 changes: 10 additions & 11 deletions sources/MVCFramework.RESTClient.pas
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ interface
IdURI,
MVCFramework.Commons,
MVCFramework.Serializer.Commons,
MVCFramework.DataSet.Utils,
IdMultipartFormData,
System.SysUtils,
Data.DB,
Expand Down Expand Up @@ -201,7 +200,8 @@ TRESTClient = class(TInterfacedObject)
function doGET(): IRESTResponse; overload;
function doGET(const AResource: string; const AParams: array of string; const aQueryStringParams: TStrings = nil)
: IRESTResponse; overload;
function doGET(const AResource: string; const AParams: array of string; const aQueryStringParamNames: array of string;
function doGET(const AResource: string; const AParams: array of string;
const aQueryStringParamNames: array of string;
const aQueryStringParamValues: array of string): IRESTResponse; overload;
function doPOST(const ABody: string): IRESTResponse; overload;
function doPOST<TBodyType: class>(ABody: TBodyType; const AOwnsBody: Boolean = True): IRESTResponse; overload;
Expand Down Expand Up @@ -267,9 +267,8 @@ TRESTClient = class(TInterfacedObject)
implementation

uses
MVCFramework.Serializer.Defaults

,
MVCFramework.Serializer.Defaults,
MVCFramework.DataSet.Utils,
System.ZLib

{$IFNDEF ANDROID OR IOS}
Expand Down Expand Up @@ -750,7 +749,7 @@ constructor TRESTClient.Create(const AHost: string; const APort: Word; AIOHandle
FHTTP.HandleRedirects := False; // DT 2016/09/16
FHTTP.OnRedirect := OnHTTPRedirect; // DT 2016/09/16
FHTTP.ReadTimeOut := 20000;
FHTTP.Request.UserAgent := 'Mozilla/3.0 (compatible; IndyLibrary)'; // Resolve 403 Forbidden error in REST API SSL
FHTTP.Request.UserAgent := 'Mozilla/3.0 (compatible; IndyLibrary)'; // Resolve 403 Forbidden error in REST API SSL

if (AIOHandler <> nil) then
FHTTP.IOHandler := AIOHandler
Expand All @@ -761,7 +760,7 @@ constructor TRESTClient.Create(const AHost: string; const APort: Word; AIOHandle

FHTTP.HandleRedirects := True;
FHTTP.Request.CustomHeaders.FoldLines := False;
FHTTP.Request.BasicAuthentication := False; //DT 2018/07/24
FHTTP.Request.BasicAuthentication := False; // DT 2018/07/24

FSerializer := GetDefaultSerializer;
end;
Expand Down Expand Up @@ -1306,7 +1305,7 @@ function TRESTClient.SendHTTPCommand(const ACommand: TMVCHTTPMethodType;
lDecomp: TZDecompressionStream;
lCompressionType: TMVCCompressionType;
begin
FHTTP.Request.BasicAuthentication := not Username.IsEmpty; //DT 2019/08/23
FHTTP.Request.BasicAuthentication := not Username.IsEmpty; // DT 2019/08/23

FContentEncoding := '';
Result := TRESTResponse.Create;
Expand Down Expand Up @@ -1396,11 +1395,11 @@ function TRESTClient.SendHTTPCommand(const ACommand: TMVCHTTPMethodType;
lTmp := TMemoryStream.Create;
try
Result.Body.Position := 0;
{$IF Defined(SeattleOrBetter)}
{$IF Defined(SeattleOrBetter)}
lDecomp := TZDecompressionStream.Create(Result.Body, MVC_COMPRESSION_ZLIB_WINDOW_BITS[lCompressionType], False);
{$ELSE}
{$ELSE}
lDecomp := TZDecompressionStream.Create(Result.Body, MVC_COMPRESSION_ZLIB_WINDOW_BITS[lCompressionType]);
{$ENDIF}
{$ENDIF}
try
lTmp.CopyFrom(lDecomp, 0);
Result.Body.Size := 0;
Expand Down
Loading

0 comments on commit 6e19787

Please sign in to comment.