All conflicts have been fixed
This commit is contained in:
Daniele Teti 2019-05-19 14:35:34 +02:00
commit efd170d9ba
8 changed files with 186 additions and 15 deletions

View File

@ -130,16 +130,34 @@ Render(lPerson, False,
- Added! New overloads for all the Log\* calls. Now it is possibile to call `LogD(lMyObject)` to get logged `lMyObject` as JSON (custom type serializers not supported in log). - Added! New overloads for all the Log\* calls. Now it is possibile to call `LogD(lMyObject)` to get logged `lMyObject` as JSON (custom type serializers not supported in log).
- Fixed! [issue164](https://github.com/danieleteti/delphimvcframework/issues/164) - Fixed! [issue164](https://github.com/danieleteti/delphimvcframework/issues/164)
- Fixed! [issue182](https://github.com/danieleteti/delphimvcframework/issues/182) - Fixed! [issue182](https://github.com/danieleteti/delphimvcframework/issues/182)
- New! `Dict(array of string, array of string)` function allows to render a dictionary of strings in a really simple way. - New! `StrDict(array of string, array of string)` function allows to render a dictionary of strings in a really simple way. See the following action sample.
```delphi
procedure TMy.GetPeople(const Value: Integer);
begin
if Value mod 2 <> 0 then
begin
raise EMVCException.Create(HTTP_STATUS.NotAcceptable, 'We don''t like odd numbers');
end;
Render(
StrDict(
['id', 'message'],
['123', 'We like even numbers, thank you for your ' + Value.ToString]
));
end;
```
- New! Custom Exception Handling (Based on work of [David Moorhouse](https://github.com/fastbike)). Sample "custom_exception_handling" show how to use it. - New! Custom Exception Handling (Based on work of [David Moorhouse](https://github.com/fastbike)). Sample "custom_exception_handling" show how to use it.
- Improved! Exceptions rendering while using MIME types different to `application/json`. - Improved! Exceptions rendering while using MIME types different to `application/json`.
- Improved! JSONRPC Automatic Object Publishing can not invoke inherited methods if not explicitely defined with `MVCInheritable` attribute. - Improved! JSONRPC Automatic Object Publishing can not invoke inherited methods if not explicitely defined with `MVCInheritable` attribute.
- Improved! Datasets serialization speed improvement. In some case the performace [improves of 2 order of magnitude](https://github.com/danieleteti/delphimvcframework/issues/205#issuecomment-479513158). (Thanks to https://github.com/pedrooliveira01) - Improved! Datasets serialization speed improvement. In some case the performace [improves of 2 order of magnitude](https://github.com/danieleteti/delphimvcframework/issues/205#issuecomment-479513158). (Thanks to https://github.com/pedrooliveira01)
- New! Added `in` operator in RQL parser (Thank you [João Antônio Duarte](https://github.com/joaoduarte19)) - New! Added `in` operator in RQL parser (Thank you to [João Antônio Duarte](https://github.com/joaoduarte19) for his initial work on this)
- New! Added `TMVCActiveRecord.Count<T>(RQL)` to count record based on RQL criteria - New! Added `TMVCActiveRecord.Count<T>(RQL)` to count record based on RQL criteria
- New! Calling `<jsonrpcendpoint>/describe` returns the methods list available for that endpoint. - New! Calling `<jsonrpcendpoint>/describe` returns the methods list available for that endpoint.
- New! Experimental (alpha stage) support for Android servers! - New! Experimental (alpha stage) support for Android servers!
- New! Added support for `X-HTTP-Method-Override` to work behind corporate firewalls. - New! Added support for `X-HTTP-Method-Override` to work behind corporate firewalls.
- New Sample! Server in DLL
- Improved! Now Firebird RQL' SQLGenerator can include primary key in "createinsert" if not autogenerated.
- New! Added support for `TArray<String>` and `TArray<Integer>` in default json serializer (Thank you [Pedro Oliveira](https://github.com/pedrooliveira01))
- Improved! DMVCFramework now has 130+ unit tests that checks its funtionalities at every build!
- New Installation procedure! Just open the project group, build all and install the design-time package (which is `dmvcframeworkDT`) - New Installation procedure! Just open the project group, build all and install the design-time package (which is `dmvcframeworkDT`)

View File

@ -10,8 +10,8 @@ uses
MVCFramework.Logger, MVCFramework.Logger,
Winapi.Windows, Winapi.Windows,
System.Classes, System.Classes,
MainWebModuleUnit in '..\winecellarserver\MainWebModuleUnit.pas' {wm: TWebModule}, MainWebModuleUnit in '..\winecellarserver\MainWebModuleUnit.pas' {wm: TWebModule} ,
MainDataModuleUnit in '..\winecellarserver\MainDataModuleUnit.pas' {WineCellarDataModule: TDataModule}, MainDataModuleUnit in '..\winecellarserver\MainDataModuleUnit.pas' {WineCellarDataModule: TDataModule} ,
WineCellarAppControllerU in '..\winecellarserver\WineCellarAppControllerU.pas', WineCellarAppControllerU in '..\winecellarserver\WineCellarAppControllerU.pas',
WinesBO in '..\winecellarserver\WinesBO.pas'; WinesBO in '..\winecellarserver\WinesBO.pas';
@ -41,6 +41,10 @@ var
exports exports
GModuleData name 'dmvc_module'; GModuleData name 'dmvc_module';
{
Navigate to http://localhost/winecellar/
}
begin begin
CoInitFlags := COINIT_MULTITHREADED; CoInitFlags := COINIT_MULTITHREADED;
Web.ApacheApp.InitApplication(@GModuleData); Web.ApacheApp.InitApplication(@GModuleData);
@ -48,5 +52,4 @@ begin
Application.WebModuleClass := WebModuleClass; Application.WebModuleClass := WebModuleClass;
Application.Run; Application.Run;
end. end.

View File

@ -1,7 +1,7 @@
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup> <PropertyGroup>
<ProjectGuid>{61ADE231-72F2-4E11-8EDD-62C5AFEF0463}</ProjectGuid> <ProjectGuid>{61ADE231-72F2-4E11-8EDD-62C5AFEF0463}</ProjectGuid>
<ProjectVersion>18.4</ProjectVersion> <ProjectVersion>18.6</ProjectVersion>
<FrameworkType>VCL</FrameworkType> <FrameworkType>VCL</FrameworkType>
<MainSource>mod_dmvc.dpr</MainSource> <MainSource>mod_dmvc.dpr</MainSource>
<Base>True</Base> <Base>True</Base>
@ -141,7 +141,6 @@
<Operation>1</Operation> <Operation>1</Operation>
</Platform> </Platform>
<Platform Name="Win32"> <Platform Name="Win32">
<RemoteDir>Contents\MacOS</RemoteDir>
<Operation>0</Operation> <Operation>0</Operation>
</Platform> </Platform>
</DeployClass> </DeployClass>
@ -151,6 +150,12 @@
<Operation>1</Operation> <Operation>1</Operation>
</Platform> </Platform>
</DeployClass> </DeployClass>
<DeployClass Name="AndroidFileProvider">
<Platform Name="Android">
<RemoteDir>res\xml</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="AndroidGDBServer"> <DeployClass Name="AndroidGDBServer">
<Platform Name="Android"> <Platform Name="Android">
<RemoteDir>library\lib\armeabi-v7a</RemoteDir> <RemoteDir>library\lib\armeabi-v7a</RemoteDir>
@ -188,6 +193,12 @@
<Operation>1</Operation> <Operation>1</Operation>
</Platform> </Platform>
</DeployClass> </DeployClass>
<DeployClass Name="AndroidSplashStylesV21">
<Platform Name="Android">
<RemoteDir>res\values-v21</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_DefaultAppIcon"> <DeployClass Name="Android_DefaultAppIcon">
<Platform Name="Android"> <Platform Name="Android">
<RemoteDir>res\drawable</RemoteDir> <RemoteDir>res\drawable</RemoteDir>
@ -264,6 +275,10 @@
<Operation>1</Operation> <Operation>1</Operation>
<Extensions>.framework</Extensions> <Extensions>.framework</Extensions>
</Platform> </Platform>
<Platform Name="OSX64">
<Operation>1</Operation>
<Extensions>.framework</Extensions>
</Platform>
<Platform Name="Win32"> <Platform Name="Win32">
<Operation>0</Operation> <Operation>0</Operation>
</Platform> </Platform>
@ -273,6 +288,10 @@
<Operation>1</Operation> <Operation>1</Operation>
<Extensions>.dylib</Extensions> <Extensions>.dylib</Extensions>
</Platform> </Platform>
<Platform Name="OSX64">
<Operation>1</Operation>
<Extensions>.dylib</Extensions>
</Platform>
<Platform Name="Win32"> <Platform Name="Win32">
<Operation>0</Operation> <Operation>0</Operation>
<Extensions>.dll;.bpl</Extensions> <Extensions>.dll;.bpl</Extensions>
@ -295,6 +314,10 @@
<Operation>1</Operation> <Operation>1</Operation>
<Extensions>.dylib</Extensions> <Extensions>.dylib</Extensions>
</Platform> </Platform>
<Platform Name="OSX64">
<Operation>1</Operation>
<Extensions>.dylib</Extensions>
</Platform>
<Platform Name="Win32"> <Platform Name="Win32">
<Operation>0</Operation> <Operation>0</Operation>
<Extensions>.bpl</Extensions> <Extensions>.bpl</Extensions>
@ -316,6 +339,9 @@
<Platform Name="OSX32"> <Platform Name="OSX32">
<Operation>0</Operation> <Operation>0</Operation>
</Platform> </Platform>
<Platform Name="OSX64">
<Operation>0</Operation>
</Platform>
<Platform Name="Win32"> <Platform Name="Win32">
<Operation>0</Operation> <Operation>0</Operation>
</Platform> </Platform>
@ -426,6 +452,7 @@
<Operation>1</Operation> <Operation>1</Operation>
</Platform> </Platform>
</DeployClass> </DeployClass>
<DeployClass Name="ProjectOSXDebug"/>
<DeployClass Name="ProjectOSXEntitlements"/> <DeployClass Name="ProjectOSXEntitlements"/>
<DeployClass Name="ProjectOSXInfoPList"/> <DeployClass Name="ProjectOSXInfoPList"/>
<DeployClass Name="ProjectOSXResource"> <DeployClass Name="ProjectOSXResource">
@ -433,6 +460,10 @@
<RemoteDir>Contents\Resources</RemoteDir> <RemoteDir>Contents\Resources</RemoteDir>
<Operation>1</Operation> <Operation>1</Operation>
</Platform> </Platform>
<Platform Name="OSX64">
<RemoteDir>Contents\Resources</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass> </DeployClass>
<DeployClass Required="true" Name="ProjectOutput"> <DeployClass Required="true" Name="ProjectOutput">
<Platform Name="Android"> <Platform Name="Android">
@ -454,6 +485,9 @@
<Platform Name="OSX32"> <Platform Name="OSX32">
<Operation>1</Operation> <Operation>1</Operation>
</Platform> </Platform>
<Platform Name="OSX64">
<Operation>1</Operation>
</Platform>
<Platform Name="Win32"> <Platform Name="Win32">
<Operation>0</Operation> <Operation>0</Operation>
</Platform> </Platform>
@ -493,6 +527,7 @@
<ProjectRoot Platform="Linux64" Name="$(PROJECTNAME)"/> <ProjectRoot Platform="Linux64" Name="$(PROJECTNAME)"/>
<ProjectRoot Platform="OSX32" Name="$(PROJECTNAME)"/> <ProjectRoot Platform="OSX32" Name="$(PROJECTNAME)"/>
<ProjectRoot Platform="Android" Name="$(PROJECTNAME)"/> <ProjectRoot Platform="Android" Name="$(PROJECTNAME)"/>
<ProjectRoot Platform="OSX64" Name="$(PROJECTNAME)"/>
<ProjectRoot Platform="iOSSimulator" Name="$(PROJECTNAME).app"/> <ProjectRoot Platform="iOSSimulator" Name="$(PROJECTNAME).app"/>
</Deployment> </Deployment>
<Platforms> <Platforms>

View File

@ -747,6 +747,7 @@ begin
FHTTP.HandleRedirects := False; // DT 2016/09/16 FHTTP.HandleRedirects := False; // DT 2016/09/16
FHTTP.OnRedirect := OnHTTPRedirect; // DT 2016/09/16 FHTTP.OnRedirect := OnHTTPRedirect; // DT 2016/09/16
FHTTP.ReadTimeOut := 20000; FHTTP.ReadTimeOut := 20000;
FHTTP.Request.UserAgent := 'Mozilla/3.0 (compatible; IndyLibrary)'; // Resolve 403 Forbidden error in REST API SSL
if (AIOHandler <> nil) then if (AIOHandler <> nil) then
FHTTP.IOHandler := AIOHandler FHTTP.IOHandler := AIOHandler

View File

@ -94,12 +94,18 @@ begin
lSB.Append('INSERT INTO ' + TableName + '('); lSB.Append('INSERT INTO ' + TableName + '(');
for lKeyValue in Map do for lKeyValue in Map do
lSB.Append(lKeyValue.value + ','); lSB.Append(lKeyValue.value + ',');
if (not (TMVCActiveRecordFieldOption.foAutoGenerated in PKOptions))and(PKFieldName<>'') then
lSB.Append(PKFieldName + ',');
lSB.Remove(lSB.Length - 1, 1); lSB.Remove(lSB.Length - 1, 1);
lSB.Append(') values ('); lSB.Append(') values (');
for lKeyValue in Map do for lKeyValue in Map do
begin
lSB.Append(':' + lKeyValue.value + ','); lSB.Append(':' + lKeyValue.value + ',');
end;
if (not (TMVCActiveRecordFieldOption.foAutoGenerated in PKOptions))and(PKFieldName<>'') then
lSB.Append(':' +PKFieldName + ',');
lSB.Remove(lSB.Length - 1, 1); lSB.Remove(lSB.Length - 1, 1);
lSB.Append(')'); lSB.Append(')');

View File

@ -96,6 +96,7 @@ type
const AIgnoredFields: TMVCIgnoredList; const ANameCase: TMVCNameCase); const AIgnoredFields: TMVCIgnoredList; const ANameCase: TMVCNameCase);
procedure JsonArrayToDataSet(const AJsonArray: TJDOJsonArray; const ADataSet: TDataSet; procedure JsonArrayToDataSet(const AJsonArray: TJDOJsonArray; const ADataSet: TDataSet;
const AIgnoredFields: TMVCIgnoredList; const ANameCase: TMVCNameCase); const AIgnoredFields: TMVCIgnoredList; const ANameCase: TMVCNameCase);
function JsonArrayToArray(const AJsonArray: TJDOJsonArray):TValue;
{ IMVCSerializer } { IMVCSerializer }
function SerializeObject(const AObject: TObject; const AType: TMVCSerializationType = stDefault; function SerializeObject(const AObject: TObject; const AType: TMVCSerializationType = stDefault;
const AIgnoredAttributes: TMVCIgnoredList = []; const AIgnoredAttributes: TMVCIgnoredList = [];
@ -189,6 +190,7 @@ var
ChildList: IMVCList; ChildList: IMVCList;
ValueTypeAtt: MVCValueAsTypeAttribute; ValueTypeAtt: MVCValueAsTypeAttribute;
CastValue, CastedValue: TValue; CastValue, CastedValue: TValue;
i:integer;
begin begin
if AValue.IsEmpty then if AValue.IsEmpty then
begin begin
@ -340,8 +342,23 @@ begin
tkArray, tkDynArray: tkArray, tkDynArray:
begin begin
raise EMVCSerializationException.CreateFmt if aValue.getarraylength>0 then
('Cannot serialize %s of TypeKind tkArray or tkDynArray.', [AName]); Begin
for i := 0 to aValue.getarraylength-1 do
Begin
case aValue.GetArrayElement(i).Kind of
tkChar, tkString, tkWChar, tkLString, tkWString, tkUString:
AJsonObject.A[AName].Add(aValue.GetArrayElement(i).AsString);
tkInteger:
AJsonObject.A[AName].Add(aValue.GetArrayElement(i).AsInteger);
tkInt64:
AJsonObject.A[AName].Add(aValue.GetArrayElement(i).AsInt64);
else
raise EMVCSerializationException.CreateFmt
('Cannot serialize %s of TypeKind tkArray or tkDynArray.', [AName]);
end;
End;
End;
end; end;
tkUnknown: tkUnknown:
@ -571,6 +588,24 @@ begin
end; end;
end; end;
function TMVCJsonDataObjectsSerializer.JsonArrayToArray(
const AJsonArray: TJDOJsonArray):TValue;
var i:integer;
lStrArr:TArray<String>;
lIntArr:TArray<Integer>;
begin
for I := 0 to Pred(AJsonArray.Count) do
case AJsonArray.types[0] of
jdtString : lStrArr := lStrArr + [AJsonArray.Items[i].Value];
jdtInt : lIntArr := lIntArr + [AJsonArray.Items[i].Value.ToInteger];
end;
if Length(lStrArr)>0 then
result := TValue.From<TArray<String>>(lStrArr)
else
result := TValue.From<TArray<Integer>>(lIntArr);
end;
procedure TMVCJsonDataObjectsSerializer.JsonArrayToDataSet(const AJsonArray: TJDOJsonArray; procedure TMVCJsonDataObjectsSerializer.JsonArrayToDataSet(const AJsonArray: TJDOJsonArray;
const ADataSet: TDataSet; const ADataSet: TDataSet;
const AIgnoredFields: TMVCIgnoredList; const ANameCase: TMVCNameCase); const AIgnoredFields: TMVCIgnoredList; const ANameCase: TMVCNameCase);
@ -716,7 +751,11 @@ begin
raise EMVCDeserializationException.CreateFmt raise EMVCDeserializationException.CreateFmt
('You can not deserialize a list %s without the MVCListOf attribute.', [AName]); ('You can not deserialize a list %s without the MVCListOf attribute.', [AName]);
end; end;
end; end
else if AValue.isArray then
Begin
AValue := JsonArrayToArray(AJsonObject.A[AName]);
End;
end; end;
end; end;
end; end;

View File

@ -273,6 +273,17 @@ type
property NotesAsString: TStringStream read FNotesAsString write FNotesAsString; property NotesAsString: TStringStream read FNotesAsString write FNotesAsString;
end; end;
TEntityWithArray = class
private
FId: Int64;
FNames: TArray<String>;
FValues: TArray<Integer>;
public
property Id: Int64 read FId write FId;
property Names: TArray<String> read FNames write FNames;
property Values: TArray<Integer> read FValues write FValues;
end;
implementation implementation

View File

@ -64,6 +64,8 @@ type
[Test] [Test]
procedure TestSerializeEntityUpperCaseNames; procedure TestSerializeEntityUpperCaseNames;
[Test] [Test]
procedure TestSerializeEntityWithArray;
[Test]
procedure TestSerializeEntityLowerCaseNames; procedure TestSerializeEntityLowerCaseNames;
[Test] [Test]
procedure TestSerializeEntityNameAs; procedure TestSerializeEntityNameAs;
@ -77,8 +79,6 @@ type
procedure TestSerializeCollection; procedure TestSerializeCollection;
[Test] [Test]
procedure TestSerializeDataSet; procedure TestSerializeDataSet;
[Test]
procedure TestSerializeEmptyDataSet;
{ deserialize declarations } { deserialize declarations }
[Test] [Test]
procedure TestDeserializeEntity; procedure TestDeserializeEntity;
@ -94,6 +94,10 @@ type
procedure TestDeserializeCollection; procedure TestDeserializeCollection;
[Test] [Test]
procedure TestDeserializeDataSet; procedure TestDeserializeDataSet;
[Test]
procedure TestSerializeEmptyDataSet;
[Test]
procedure TestDeserializeEntityWithArray;
{ full cycle } { full cycle }
[Test] [Test]
procedure TestSerializeDeSerializeEntityWithEnums; procedure TestSerializeDeSerializeEntityWithEnums;
@ -620,6 +624,35 @@ begin
end; end;
end; end;
procedure TMVCTestSerializerJsonDataObjects.TestDeserializeEntityWithArray;
procedure CheckObject(const AEntity: TEntityWithArray);
begin
Assert.isTrue(AEntity.Id = 1);
Assert.isTrue(AEntity.Names[0] = 'Pedro');
Assert.isTrue(AEntity.Names[1] = 'Oliveira');
Assert.isTrue(AEntity.Values[0] = 1);
Assert.isTrue(AEntity.Values[1] = 2);
end;
const
JSON_WITH_ARRAY =
'{' +
'"Id":1,' +
'"Names":["Pedro","Oliveira"],' +
'"Values":[1,2]' +
'}';
var
O: TEntityWithArray;
begin
O := TEntityWithArray.Create;
try
FSerializer.DeserializeObject(JSON_WITH_ARRAY, O);
CheckObject(O);
finally
O.Free;
end;
end;
procedure TMVCTestSerializerJsonDataObjects.TestSerializeCollection; procedure TMVCTestSerializerJsonDataObjects.TestSerializeCollection;
const const
JSON = JSON =
@ -863,7 +896,7 @@ begin
try try
Dm.Entity.EmptyDataSet; Dm.Entity.EmptyDataSet;
S := FSerializer.SerializeDataSet(Dm.Entity); S := FSerializer.SerializeDataSet(Dm.Entity);
Assert.AreEqual('[]', S); Assert.areEqual('[]', S);
finally finally
Dm.Free; Dm.Free;
end; end;
@ -1215,6 +1248,31 @@ begin
end; end;
end; end;
procedure TMVCTestSerializerJsonDataObjects.TestSerializeEntityWithArray;
const
JSON_WITH_ARRAY =
'{' +
'"Id":1,' +
'"Names":["Pedro","Oliveira"],' +
'"Values":[1,2]' +
'}';
var
O: TEntityWithArray;
S: string;
begin
O := TEntityWithArray.Create;
try
O.Id := 1;
O.Names := ['Pedro', 'Oliveira'];
O.Values := [1, 2];
S := FSerializer.SerializeObject(O);
Assert.areEqual(JSON_WITH_ARRAY, S);
finally
O.Free;
end;
end;
procedure TMVCTestSerializerJsonDataObjects.TestSerializeNil; procedure TMVCTestSerializerJsonDataObjects.TestSerializeNil;
begin begin
Assert.areEqual('null', FSerializer.SerializeObject(nil)); Assert.areEqual('null', FSerializer.SerializeObject(nil));