You are not logged in.
Arnold
First of all I want to congratulate you on the mormot. this is a great framework. Despite the vast documentation, many things are evident in it for those who are starting to use the framework.
I would like to understand the reason for this limitation, I am not talking about complex queries here, just talking about the fields to be returned by the ORM query.
This is not a complex query, I simply do the join to filter the records in the where clause.
From what I understand when using VirtualTableExternalRegisterAll with SQLITE_MEMORY_DATABASE_NAME my ID fields have to be treated as RowId when mentioning them in my source code. mormot will automatically convert RowID to ID to run against the database. Am I right?
What I don't understand is why something as basic as returning fields cannot be done in a simple way and consistent with what programmers are used to doing. Again this is not a criticism but an attempt to improve mormot so that we can make mormot closer to what programmers are used to.
So here samples that shouls be works IMHO:
vAmostras := FRestOrm.Orm.RetrieveDocVariantArray(TAmostra, '', 'left outer join Solicitacao s on (s.RowId = Amostra.solicitacao)', [], 'RowID');
Should be bring all Amostra records, just field ID, but mormot raise error "no such column: RowId" and not is because join
vAmostras := FRestOrm.Orm.RetrieveDocVariantArray(TAmostra, '', 'left outer join Solicitacao s on (s.RowId = Amostra.solicitacao)', [], '');
vAmostras := FRestOrm.Orm.RetrieveDocVariantArray(TAmostra, '', 'left outer join Solicitacao s on (s.RowId = Amostra.solicitacao)', [], '*');
vAmostras := FRestOrm.Orm.RetrieveDocVariantArray(TAmostra, '', 'left outer join Solicitacao s on (s.RowId = Amostra.solicitacao)', [], 'Amostra.*');
All this should be bring the samething, all amostra records.
by your documentation, the last parameter as '' should be bring all fields except RowId/Id and blob fields
vAmostras := FRestOrm.Orm.RetrieveDocVariantArray(TAmostra, '', 'left outer join Solicitacao s on (s.RowId = Amostra.solicitacao)', [], 'Amostra.*, s.*');
Should be bring all Amostra records and all fields, and all fields of solicitacao (as extra properties on variant).
What I mean is that it doesn't matter that the joins I put on where, should return only TORM´s fields passed on first parameter.
Unless I put join fields in the fields, but in this case I will be bringing the information in json without any problems
I have this instruction:
var
vAmostras: Variant;
begin
vAmostras := FRestOrm.Orm.RetrieveDocVariantArray(TAmostra, '',
'left outer join Solicitacao s on (s.RowId = Amostra.solicitacao)',
[], <MYFIELDS>);
...
end;
I make many tests replacing <MYFIELDS>
<MYFIELDS> = 'RowId'
Log: EXC ESqlite3Exception {Message:"Error SQLITE_ERROR (1) [SELECT RowId FROM Amostra left outer join Solicitacao s on (s.RowId = Amostra.solicitacao)] using 3.44.2 - no such column: RowId",ErrorCode:1,SQLite3ErrorCode:"secERROR"} [Main] at 8f207c
<MYFIELDS> = 'Id'
Log: EXC ESqlite3Exception {Message:"Error SQLITE_ERROR (1) [SELECT Id FROM Amostra left outer join Solicitacao s on (s.RowId = Amostra.solicitacao)] using 3.44.2 - no such column: Id",ErrorCode:1,SQLite3ErrorCode:"secERROR"} [Main] at 8f207c
<MYFIELDS> = 'amostra.RowId, s.numero, amostra.numero'
Log: works and bring Amostra ID rightly
<MYFIELDS> = 'amostra.Rowid, amostra.*'
Log: works and return amostra.Id, others fields...
<MYFIELDS> = 'amostra.*, s.numero, amostra.numero'
Log: works but dont return amostra.Id neither amostra.RowId
<MYFIELDS> = 'amostra.RowId'
Log: don't execute sql nothing at logger
<MYFIELDS> = 'amostra.Id'
Log: don't execute sql nothing at logger
<MYFIELDS> = 'amostra.*'
Log: don't execute sql nothing at logger
<MYFIELDS> = 'amostra.Id, s.numero, amostra.numero'
Log: EXC ESqlite3Exception {Message:"Error SQLITE_ERROR (1) [SELECT amostra.Id, s.numero, amostra.numero FROM Amostra left outer join Solicitacao s on (s.RowId = Amostra.solicitacao)] using 3.44.2 - no such column: amostra.Id",ErrorCode:1,SQLite3ErrorCode:"secERROR"} [Main] at 8f207c
<MYFIELDS> = '*'
Log: EXC ESqlite3Exception {Message:"Error SQLITE_ERROR (1) [SELECT RowID,DataCriacao,UltimaAlteracao,Numero,Solicitacao,Exames,DataEtiqueta,Rack,Linha,Coluna FROM Amostra left outer join Solicitacao s on (s.RowId = Amostra.solicitacao)] using 3.44.2 - no such column: RowID",ErrorCode:1,SQLite3ErrorCode:"secERROR"} [Main] at 8f207c
<MYFIELDS> = ''
Log: EXC ESqlite3Exception {Message:"Error SQLITE_ERROR (1) [SELECT RowID,DataCriacao,UltimaAlteracao,Numero,Solicitacao,Exames,DataEtiqueta,Rack,Linha,Coluna FROM Amostra left outer join Solicitacao s on (s.RowId = Amostra.solicitacao)] using 3.44.2 - no such column: RowID",ErrorCode:1,SQLite3ErrorCode:"secERROR"} [Main] at 8f207c
I forgot my configuration:
FConfigDB := TSqlDBPostgresConnectionProperties.Create(
'191.101.78.31:5001',
'DB_SOROTRACK',
'microprocess',
'mpPost19dokcer!');
FOrmModel := CreateModel;
VirtualTableExternalRegisterAll(FOrmModel, FConfigDB);
FOrmModel.Props[TAmostra].ExternalDB.MapField('Solicitacao', 'IdSolicitacao');
FRestOrm := TRestClientDB.Create(FOrmModel, nil, SQLITE_MEMORY_DATABASE_NAME, TRestServerDB, false, '');
FRestOrm.Server.Server.CreateMissingTables;
I want use ORM but can put my owner sqls too.
I read and reread documentation and dont understand when use RowID or ID (my field) too.
Other thing I'm doing a POST passing filter on body
{{URL}}/amostra/listar
.Post('/amostra/listar', 'amostraService/listaramostras')
TFiltro = packed record
Amostra: RawUTF8;
EventoTipo: Integer;
Solicitacao: RawUTF8;
Paciente: RawUTF8;
Posto: RawUTF8;
PeriodoTipo: Integer;
PeriodoInicial: TDateTime;
PeriodoFinal: TDateTime;
end;
function TAmostraService.ListarAmostras(const pFiltro: TFiltro): TServiceCustomAnswer;
Why I have to pass:
[{ "Amostra": "1234567891"}]
instead of
{ "Amostra": "1234567891" }
???
Great, I'm testing.
Arnold I think I found a possible bug:
FRestOrm.Orm.RetrieveDocVariantArray(TAmostra, '', FWhere, TDocVariantData(FWhereParamValues).ToArrayOfConst,
'Amostra.*') <<<<< this doesn't work. I lost a time debugging
FRestOrm.Orm.RetrieveDocVariantArray(TAmostra, '', FWhere, TDocVariantData(FWhereParamValues).ToArrayOfConst,
'*') <<<<< this work
Question 1
var
vAmostras: Variant;
...
vAmostras := FRestOrm.Orm.RetrieveDocVariantArray(TAmostra, '',
'left outer join Solicitacao s on (s.RowId = Amostra.solicitacao)', [], 's.numero, s.paciente, Amostra.*');
>>> SELECT s.numero, s.paciente, amostra.* FROM Amostra left outer join solicitacao s on (s.Id = Amostra.solicitacao)
I whould like define a alias to orm amostra ===> a
vAmostras := FRestOrm.Orm.RetrieveDocVariantArray(TAmostra, '',
'left outer join Solicitacao s on (s.RowId = a.solicitacao)', [], 's.numero, s.paciente, a.*');
>>> mormot cannot execute this select because it don't know about alias A.
i try too:
vAmostras := FRestOrm.Orm.RetrieveDocVariantArray(TAmostra, '',
'a left outer join Solicitacao s on (s.RowId = a.solicitacao)', [], 's.numero, s.paciente, a.*');
but mormot generate this select
>>> SELECT s.numero, s.paciente, a.* FROM Amostra WHERE a left outer join solicitacao s on (s.Id = Amostra.solicitacao)
Question 2
There is some way to pass the raw sql to orm? something like below. Be by ORM or other method do RestORM or other way?
vAmostras := FRestOrm.Orm.RetrieveDocVariantArray(TAmostra,
'SELECT s.numero, s.paciente, a.* FROM Amostra a '+
'left outer join solicitacao s on (s.Id = Amostra.solicitacao) '+
'WHERE s.numero = ?', [12345]);
I have this class.
TContatos = class(TOrmMany)
private
FSource: TPessoa;
FDest: TContato;
published
property Source: TPessoa read FSource;
property Dest: TContato read FDest;
end;
mormot create table with: id, source, dest
How to do mormot create the table with:
id, pessoa, contato?
or
id , pessoaid, contatoid?
Sry brind this questions but I need help.
this is my model:
TContatos = class(TOrmMany)
private
FSource: TPessoa;
FDest: TContato;
published
property Source: TPessoa read FSource;
property Dest: TContato read FDest;
end;
TContato = class(TOrm)
private
FTelefone: RawUTF8;
FEmail: RawUTF8;
published
property Telefone: RawUTF8 read FTelefone write FTelefone;
property Email: RawUTF8 read FEmail write FEmail;
end;
TRacas = (rPoodle, rViraLata, rPitbull, rShawShaw);
TPet = class(TOrm)
private
FRaca: TRacas;
FNome: RawUTF8;
FDono: TPessoa;
published
property Raca: TRacas read FRaca write FRaca;
property Nome: RawUTF8 read FNome write FNome;
property Dono: TPessoa read FDono write FDono;
end;
TCidade = class(TOrm)
private
FNome: RawUTF8;
published
property Nome: RawUTF8 read FNome write FNome;
end;
TPessoa = class(TOrm)
private
FNome: RawUTF8;
FIdade: Integer;
FCidade: TCidade;
FContatos: TContatos;
FPets: TObjectList;
public
function NewContato(const pEmail, pFone: RawUtf8): TContato;
function NewPet(const pRaca: TRacas; const pNome: RawUTF8): TPet;
property Pets: TObjectList read FPets write FPets;
destructor Destroy; override;
published
property Nome: RawUTF8 read FNome write FNome;
property Idade: Integer read FIdade write FIdade;
property Cidade: TCidade read FCidade write FCidade;
property Contatos: TContatos read FContatos write FContatos;
end;
this is my data on database
Pessoa
id,"nome","idade","cidade"
1,"Pedro","25","1"
2,"Maria","39","2"
pet
id,"raca","nome","dono"
1,0,"miau","1"
2,1,"auau","1"
3,3,"goal","1"
4,1,"sansao","2"
cidade
id,"nome"
1,"Conquista"
2,"Jequié"
contato
id,"telefone","email"
1,"77 988526666","pedro@gmail.com"
2,"77 988526666","joao@gmail.com"
3,"73 999566489","jose@gmail.com"
contatos
id,"source","dest"
1,"1","1"
2,"1","2"
3,"2","3"
4,"2","1"
There is some way to retrieve all Pessoa with all properties? Cidade, Contatos, Pets, How can I do this?
my tries:
var
vPessoas: TPessoa;
vCidade: TCidade;
vContato: TContato;
vOrm: IRestOrm;
vIdPessoa: TID;
vI: Integer;
begin
vPessoas := TPessoa.CreateAndFillPrepareJoined(FRestOrm.Orm, '', [], []);
try
Memo2.Lines.Add(Format('Quantidade de pessoas: %d', [vPessoas.FillTable.RowCount])); // ok return 2 pessoa
while vPessoas.FillOne do
begin
Memo2.Lines.Add(Format('Id: %d', [vPessoas.ID]));
Memo2.Lines.Add(Format('Nome: %s', [vPessoas.Nome]));
Memo2.Lines.Add(Format('Idade: %d', [vPessoas.Idade]));
Memo2.Lines.Add(Format('Cidade: %s', [vPessoas.Cidade.Nome]));
vPessoas.Contatos.FillPrepareMany(FRestOrm.Orm, 'RowId>=?', [], [vPessoas.ID]);
Memo2.Lines.Add(Format('Quantidade de Contatos: %d', [vPessoas.Contatos.FillTable.RowCount])); // ERROR HERE. return 0 should be 2 (pedro and joao) <<<<<<
while vPessoas.Contatos.Dest.FillOne do // fillone return false
begin
Memo2.Lines.Add(Format(' Contato: %s - %s', [vI, vPessoas.Contatos.Dest.Email, vPessoas.Contatos.Dest.Telefone])); //is not retrieving email and telefone value
end;
vPessoas.FillClose;
// pets is nil on this moment then i do this, but i don´t know if is the better aproach. <<<<<<
vPessoas.Pets := FRestOrm.RetrieveList(TPet, 'dono=?', [vPessoas.Id]);
Memo2.Lines.Add(Format('Quantidade de Pets: %d', [vPessoas.Pets.Count])); ok 3 from first pessoa and 1 to second
for vI := 0 to vPessoas.Pets.Count-1 do
Memo2.Lines.Add(Format(' Pets %d: %s', [vI, TPet(vPessoas.Pets[vI]).Nome]));
Memo2.Lines.Add('-----------------------');
vPessoas.Pets.Clear;
vPessoas.Pets.Free;
vPessoas.Pets := nil;
end;
finally
vPessoas.Free;
end;
I try too
vPessoas := TPessoa.CreateAndFillPrepareMany(FRestOrm.Orm, '', [], []);
but this way it retrieve 4 pessoa on while
I try too
vPessoas.Contatos.FillPrepareMany(FRestOrm.Orm, 'RowId>=?', [], [vPessoas.ID]);
but vPessoas.Contatos.FillTable.RowCount return 0 and vPessoas.Contatos.FillOne return false
I try too
vPessoas.Contatos.FillMany(FRestOrm.Orm, vPessoas.ID);
vPessoas.Contatos.FillTable.RowCount return 2 ok but vPessoas.Contatos.FillOne return false
Oh my god!!!! I read documentation already and cannot make this work.
Hello Community,
I'm facing a challenge with my API and would appreciate your help.
1 - In my project, I have the "TAmostra" table, where one of the columns is "Dados", of type "TDados", containing various fields.
TDados = packed record
NumeroSolicitacao: RawUTF8;
DataSolcitacao: TDateTime;
Exames: RawUTF8;
Paciente: RawUTF8;
Posto: RawUTF8;
Atendente: RawUTF8;
DataEtiqueta: TDateTime;
Temperatura: Double;
Motivo: RawUTF8;
Rack: RawUTF8;
Linha: RawUTF8;
Coluna: Int64;
end;
TAmostra = class(TOrm)
private
FNumero: RawUTF8;
FDados: TDados;
published
property Numero: RawUTF8 index 40 read FNumero write FNumero;
property Dados: TDados read FDados write FDados;
end;
TEvento = class(TOrm)
private
FTipo: TTipoEvento;
FAmostra: TAmostra;
published
property Id:
property Tipo: TTipoEvento read FTipo write FTipo;
property Amostra: TAmostra read FAmostra write FAmostra;
end;
How can I create a SELECT query to Evento join with Amostra by Dados.DataSolcitacao? SomethingLike:
SELECT A.*
FROM EVENTO A
LEFT OUTER JOIN AMOSTRA B ON (B.ID = A.ID)
WHERE B.AMOSTRA.DADOS.DATASOLICITACAO BETWEEN '01.12.2023' AND '01.01.2024'
if this not is possible, i can transform TDados in TOrm too, add TDados into TAmostra. then how make this retrieve on ORM?
SELECT A.*
FROM EVENTO A
INNER JOIN AMOSTRA B ON (B.ID = A.ID)
INNER JOIN DADOS C ON (C.ID = B.IDSOLCITACAO)
WHERE C.DATASOLICITACAO BETWEEN '01.12.2023' AND '01.01.2024'
I appreciate any guidance or suggestions you can offer.
Best regards,
Sry guys, my fault!
This is really very confusing!!!! RowID e ID
Hi A. Bouchez
A friend mine was comparing some REST frameworks delphi:
horse (use indy or nethttp) https://github.com/HashLoad/horse
restdataware (use indy or nethttp)
brooks framework (luse ibsagui Cross-platform library) https://github.com/risoflora/brookframework
We was impressive with benchmarks results with this comparassion.
this lib https://risoflora.github.io/libsagui/ is very very very faster and secure. more fast than HTTP.SYS and it is cross-plataform yet.
I know that u like adjust your framework to get more and more peformance.
My intention is this too.
U make a benchmark tool a sometime ago about this, where we can found this tests? can u include this lib on this benchmarks?
Note that the MVC requests are expected to be regular HTTP/Web request from HTML, so are usually GET and sometimes POST (from a form).
....
Please try https://github.com/synopse/mORMot2/commit/82ecb05f
Arnold I see your commit and I think that u put erro on your code no?
fAllowedMethods by default is [mGET, mPOST] but in some parts of code was [mGET, mHEAD]
sample
if publishMvcInfo in fPublishOptions then
fRestServer.ServiceMethodRegister(
before:
MVCINFO_URI, RunOnRestServerRoot, bypass, [mGET, mHEAD]);
now:
MVCINFO_URI, RunOnRestServerRoot, bypass, fAllowedMethods);
I understand u AB!
But we cannot force old companies chage your server to REST. Many companies using SOAP/XML.
I just would like some http socket client that could send a xml soap to this kind of server.
There some way? What base class could i use?
What class I can use to call a third server that use SOAP?
I don´t would like use delphi THttpRIO!
I think that I found a problem guys.
var
vParams1, vParams2: TDocVariantData;
vJson: String;
begin
vParams1.InitJson('{}');
vQueryTmp := NewQuery(' SELECT * FROM EST_ENTRADA_ITEM WHERE CD_ENTRADA_ITEM = 8', []);
vParams.AddOrUpdateValue('RetVariant1', vQueryTmp.FieldByName('CD_ENTRADA_ITEM').Value); // integer field
vParams.AddOrUpdateValue('RetVariant2', vQueryTmp.FieldByName('VL_TOTAL').Value); // float field VarTypeAsText(VarType(pValue)) => double
vParams.AddOrUpdateValue('RetVariant3', vQueryTmp.FieldByName('ST_NOTA_ITEM').Value); // smallint field
vParams.AddOrUpdateValue('RetVariant4', vQueryTmp.FieldByName('DT_CONFIRMACAO').Value); // datetime field
vParams.AddOrUpdateValue('RetVariant5', vQueryTmp.FieldByName('VL_RECEBIDO').Value); // numeric(15,2) field (currency)
vJson := vParams1.ToJson;
// vJson = '{"RetVariant1":8,"RetVariant2":39.9000015258789,"RetVariant3":1,"RetVariant4":"2019-03-15T16:12:21","RetVariant5":29.24}'
vParams2.InitJson(vJson);
vJson := vParams1.ToJson;
// '{"RetVariant1":8,"RetVariant2":"39.9000015258789","RetVariant3":1,"RetVariant4":"2019-03-15T16:12:21","RetVariant5":29.24}'
Why RetVariant2 change to a string? Why not hold 39.9000015258789?
I see that if I does
vParams2.InitJson(vJson, [dvoAllowDoubleValue]);
the value is converted correctly.
by default the options is []?
I solve this way. I hope that do not apear others type of variant.
function TCallServerParams.SetParam(const pName: String; pValue: Variant): ICallServerParams;
var
vSqlTimeStamp: TSQLTimeStamp;
vDateTime: TDateTime;
begin
if VarIsSQLTimeStamp(pValue) then
begin
vSqlTimeStamp := VarToSQLTimeStamp(pValue);
vDateTime := SQLTimeStampToDateTime(vSqlTimeStamp);
FParams.AddOrUpdateValue(pName, vDateTime)
end else
FParams.AddOrUpdateValue(pName, pValue);
Result := Self;
end;
You can play a bit with SQLTimeStampVariantType. Delphi just needs to know what you want:
var value: Variant := VarSQLTimeStampCreate(Now); // <- Variant of type SQLTimeStampVariantType
Or have a look in Unit Data.SqlTimSt at class TSQLTimeStampData and TSQLTimeStampVariantType.
With best regards
Thomas
I know that if i convert the variant all works TBO. But I need avoid that my programmers fails during use of this. then I was thinking generate a exception on cases were the mormot cannot identify the vartype! Do you understand?
I don't think such a method exists.
But its possible make it?
Where I can find on source code mormot.variants what vartypes it can treat?
Thanks guys.
A last question. There are something function to validate a variant type to know when mormot does not know what to do with the value of this variant?
I know that using. FieldByName('DT_NASC').AsDateTime or VarToDateTime(...) works
but some other programmer can do not use this way and cause erros on runtime!
There are something that can be done on mormot internal to dont need make this conversion by hand????
I have edited my post.
ShowMessage(VarTypeAsText(VarType(vValue))) => SQLTimeStampVariantType
looking for this type i found in SqlTimst.pas
TSQLTimeStampVariantType = class(TPublishableVariantType)
SQLTimeStampVariantType: TSQLTimeStampVariantType = nil;
I think that this type of variant not is treated by TDocVariantData
How we can resolve this?
var
vValue: Variant;
vQueryTmp: TSQLQuery;
vParams: TDocVariantData;
const
cSQL = ' SELECT DT_NASC FROM PACIENTE WHERE CD_PACIENTE = 1';
begin
vQueryTmp := NewQuery(cSQL, []);
try
vQueryTmp.First;
vValue := vQueryTmp.FieldByName('DT_NASC').Value; // vValue here has '01/07/2020'
finally
FreeQuery(vQueryTmp);
end;
vParams.InitJson('{}');
vParams.AddOrUpdateValue('MyInt', 10);
vParams.AddOrUpdateValue('MyBoolean', True);
vParams.AddOrUpdateValue('MyVariantDate', vValue);
ShowMessage(vParams.ToJson) => {"Myint":10,"MyBoolean":true,"MyVariantDate":null}' ?????
I was expecting {"Myint":10,"MyBoolean":true,"MyVariantDate": '2020-07-01T00:00:00'}'
What I should?
Tnks. I will study Better the auth mechanism I want jwt with authuser and authsession control.
When i ahould be use this in rest options?
i have created this record
TUsuarioRecuperaSenha = packed record
UserPass: RawByteString;
end;
...
TUsuarioService = class(TServiceBase, IUsuarioService)
function TrocaSenha(const pUsuario: TUsuarioTrocaSenha) : TServiceCustomAnswer;
...
GetApp.RegisterService(TUsuarioService, [TypeInfo(IUsuarioService)]);
GetApp.Put('/usuariotrocasenha', 'usuarioService/trocasenha');
I got erro on this part of mormot
constructor TInterfaceFactory.Create(aInterface: PRttiInfo);
...
imvRecord:
if ArgRtti.Size <= POINTERBYTES then <<<<<<<<<<<<<<<<<<< shouldnt be "if ArgRtti.Size < POINTERBYTES then" ??????????
raise EInterfaceFactory.CreateUtf8(
'%.Create: % record too small in %.% method % parameter: it ' +
'should be at least % bytes (i.e. a pointer) to be on stack',
[self, ArgTypeName^, fInterfaceName, URI, ParamName^, POINTERBYTES]);
If i put more fields on record it works
I see some posts on group about jwt. for sample:
1. https://synopse.info/forum/viewtopic.php?id=4840
but i have some problems with retrieve session, release session, etc..?
the implementation create 1 session to each request, when should be by user (TAuthSession.AuthUser).
I need refreshtoken too.
Anyone has a more complete implementation?
Anyone can help us?
To be honest, I don't expect "with ... do begin ... end" to work with late binding.
It is very confusing for the compiler and human reader.
Whatever solution you would find (like a pointer) just adds confusion.I won't use late binding for such code, but rather TDocVariantData.Update([]) by name/value pairs.
Perhaps with _Safe().
You sugestion is something like this?
TMyClasse = class
private
FParams: TDocVariantData;
public
procedure Params(const pName: String; pValue: Variant);
...
constructor TMyClasse.Create;
begin
inherited;
FParams.Init; <<<<<<< _Safe(Need Variant) and return PDocVariant, then I have used .Init. Some problem with this?
end;
procedure TMyClasse.Params(const pName: String; pValue: Variant);
begin
FParams.AddOrUpdateValue(pName, pValue); <<<<<<<<
end;
There are some way to use a property published to use latebiding? If yes, what type should be this property? else thanks AB! I will following this way.
I need make a class that expose a published property called Params.
I would like that this property as Variant with latebinding.
How I can do this?
v := TMyClasse.Create;
with Variant(v.Params^) do // this works, but i dislike have to use pointer
begin
Codigo := 10;
Nome := 'Teste de acéntúàção';
Data := StrToDate('18/09/1976');
Valor := 1254.96;
end;
// v.Params = {"Codigo":10,"Nome":"Teste de acéntúàção ","Data":"1976-09-18","Valor":1254.96}
with v.Params do // <<<< how make this way (using Params: Variant)???????????
begin
Codigo := 10;.Codigo := 10;
Nome := 'Teste de acéntúàção';
Data := StrToDate('18/09/1976');
Valor := 1254.96;
end;
// v.Params = {}
how should be my class?????
In this moment it use pointer. how return variant latebinding? or other thing to can use latebinding????
TMyClasse = class
private
FParams: Variant;
function GetParams: Pointer;
public
constructor Create;
property Params: Pointer read GetParams;
end;
...
constructor TMyClasse.Create;
begin
inherited;
TDocVariant.New(FParams, [dvoValueCopiedByReference]);
end;
function TMyClasse.GetParams: Pointer;
begin
Result := @FParams;
end;
Hi guys
What you think about define ByPassAuthentication during router.get/post/....?
This way I would not have to make 2 services just to separate routes that need or nor of authentication.
Or there are other way?
Also, in TRestStorageExternal methods like EngineList or EngineRetrieve and so on, exceptions are supressed in try..except block, without being stored anywhere.
do not store in StatementLastException ????????
This is a problem no?
Where i found help about The vantages to use sqlite ou external DB. Im using postgree database.
I see that all code framework, if the statement is wrong the exception do not explode to user.
In the framework I see this piece of code in several parts:
try
...
GetAndPrepareStatementRelease(...);
...
except
on E: Exception do
GetAndPrepareStatementRelease(E);
end;
So if I assemble an invalid sql statement by mistake when doing the orm.RetrieveDocVariant does not generate an exception bringing up the null record. example:
function TProjetoService.Busca(pId: Int64): TServiceCustomAnswer;
var
vProjeto, vPaineis: Variant;
begin
TDocVariant.New(vProjeto);
vProjeto := GlobalRepository.Orm.RetrieveDocVariant(TProjeto, '(ID=?)', [pId], '');
if VarIsEmptyOrNull(vProjeto) then
Result := ReturnRecordNotFound
else
begin
vPaineis := GlobalRepository.Orm.RetrieveDocVariantArray(TPainel, '',
'(IdProjeto=?)',
[pId],
'ID , Nome2'); <<<<<<<< Nome2 not exists, just Nome field (in runtime this do not generate exception)
vProjeto.Paineis := vPaineis; <<<<< this part run normally bring um Json "Paineis" : null
Result := ReturnOk(vProjeto);
end;
end;
Ab, in the code below I am using the RetrieveDocVariantArray function to perform a database search to bring tables's partial fields.
I need too bring a custom virtual string field with contatenated content. See the following situations:
// property ID, nome without virtual field concatenation, executes normally.
vPaineis := GlobalRepository.Orm.RetrieveDocVariantArray(TPainel, '',
'(IdProjeto=?)',
[pId],
'ID ,Nome');
// property RowID concatenated with a string, executes normally.
vPaineis := GlobalRepository.Orm.RetrieveDocVariantArray(TPainel, '',
'(IdProjeto=?)',
[pId],
'RowID ,Nome, "/Projeto/" || RowId AS LINK');
// property ID concatenated with a string, an error occurs. i would like use this way
vPaineis := GlobalRepository.Orm.RetrieveDocVariantArray(TPainel, '',
'(IdProjeto=?)',
[pId],
'ID ,Nome, "/Projeto/" || ID AS LINK');
I need this field to make some type of HATEOAS https://en.wikipedia.org/wiki/HATEOAS
Arnold, I would like to thank you for your comments here and apologize for the lack of knowledge in the framework, I'm still new to mormot, but I'll get there. Digging around the framework I discovered the properties:
TServiceFactoryServer's SetWholeOption([???])
optResultAsJsonObjectWithoutResult or optResultAsJsonObject
I just put the optResultAsJsonObjectWithoutResult option and the json I wanted was already returned the way I needed it.
function Busca(pId: Int64; var abc: TDTOProjeto; out pProjeto: TDTOProjeto; out pPaineis: TDTOPaineis): TServiceCustomStatus;
{
"abc": {
"RowID": 1,
"Nome": "Projeto c",
"DataCriacao": "2023-03-18T12:34:09",
"UltimaAlteracao": "2023-03-18T12:34:09",
"IdCriador": 5,
"Paineis": []
},
"pProjeto": {
"RowID": 1,
"Nome": "Projeto c",
"DataCriacao": "2023-03-18T12:34:09",
"UltimaAlteracao": "2023-03-18T12:34:09",
"IdCriador": 5,
"Paineis": [
{
"RowID": 1,
"Nome": "painel a",
"DataCriacao": "2023-03-20T10:08:48",
"UltimaAlteracao": "2023-03-20T10:08:48",
"IdCriador": 5,
"IdProjeto": 1
}
]
},
"pPaineis": [
{
"RowID": 1,
"Nome": "painel a",
"DataCriacao": "2023-03-20T10:08:48",
"UltimaAlteracao": "2023-03-20T10:08:48",
"IdCriador": 5,
"IdProjeto": 1
}
],
"Result": 0
}
long live the mormot!
I world like a copy too please. Cannyou put on github?
I can do this into component, or i will need call manually into application when using the component?
i have commented the picture (GIF) that was registred. the error happen yet
I removed TGifImage of matrixes too
const
PicturesExt: array[0..4] of TFileName =
('jpg','jpeg','png','tif','tiff');
PictureClasses: array[0..4] of TGraphicClass =
(TJpegImage,TJpegImage,TPngImage,TTiffImage,TTiffImage);
....
procedure TGDIPlus.RegisterPictures;
var
i: integer;
begin
SendDebug('register!');
// launch Gdip.RegisterPictures to initialize the GDI+ library if necessary
if (Self=nil) and (Gdip=nil) then
begin
SendDebug('gdipo criando!');
Gdip := TGDIPlus.Create('gdiplus.dll');
end;
// register JPG and PNG pictures as TGraphic
if GetClass('TTiffImage')=nil then
begin
RegisterClass(TJpegImage);
RegisterClass(TPngImage);
//RegisterClass(TGifImage); <<<<<<<<<<<<
RegisterClass(TTiffImage);
for i := 0 to high(PicturesExt) do
TPicture.RegisterFileFormat(PicturesExt[i],
PictureName(PictureClasses[i]),PictureClasses[i]);
end;
end;
AB,
Do you still provide any support for SynGDI Plus?
I have created a ThumbView component that uses the GR32 lib. In this component I put:
uses
Windows, Messages, Math, SysUtils, Classes, Controls, Types,
StdCtrls, Dialogs, Contnrs, CommCtrl, ActiveX, Graphics, Forms,
GR32_Image, GR32, GR32_Transforms, GR32_RangeBars, GR32_Blend,
SynGdiPlus,
BackgroundWorker;
....
//initialization
// Gdip.RegisterPictures;
end.
When I install the component without this initialization I can close delphi without any problems.
but if i uncomment that initialization Gdip.RegisterPictures, when I close delphi I get the following message
I have The same problem. I remove line that registerclass tgif because my delphi são that is duplicated register class.
Then now when I closet The delphi I get a Access violation in rtl70.bpl
What u think Arnold?
What i can see about the code, all workflow abou soa via interfaces is internal. Then i think this idea is viable.
Good Morning Arnold,
I thought TServiceCustomStatus was only for non mormot clients.
Atualmente o mormot traz no result uma matriz com valores diversos. E não é possível ler isso de clientes não mormot. Veja isso.
TPaineis = array or TPainel
function BuscaPaineis(idProject: integer; out paineis: TPaineis): boolean;
// if I put an idProject that doesn't exist, for example, a Json arrives in this format:
{
"result" = [ [], false ]
}
// if I put an idProject that exists for example a Json arrives in this format:
{
"result" = [ [{"painel": 'painel1'}, {"painel": 'painel1'}], true ]
}
A non-mormot client is expecting just an array of TPainel. Which is not the case with the true and false into array.
An example response that mormot could return and handle without impacting applications that use interface-based SOA could look like this:
function BuscaPaineis(idProject: integer; out paineis: TPaineis): boolean;
{
"result" = [{"painel": 'painel1'}, {"painel": 'painel1'}],
"return" = true
}
I will further detail the problems I am trying to solve as I want to work with interface based SOA. But I need the mormot server to return the JSON in a way that can be read by any 3rd party application.
1) Return the custom HTTP code
2) The returned JSON should follow a pattern for when the result is 200..399.
3) When the return is 400..599 I need the returned JSON to follow an exception pattern with the proper error message. This type really should not be an exception, even though they are expected errors. They should not be logged or handled by the framework.
When I use TServiceCustomStatus I can return the statuscode that I want, great!
But when I return a code between 400...599 I have to return a Json with the reason for this error.
So I thought of TServiceCustomStatus as a record. And I adjusted the code so that anyone who wants to use Mormot and is not using a Mormot client.
Maybe a way to standardize this would be that every return from mormot would return
{
"<result> or <classname>": <Jsonobject> or <JsonArray>
"errormessage": "<empty> or <custom error message>" <<< not out parameter but a TServiceCustomStatus.ErroMessage field
"return" : <null is procedure> or <value of function result> <<< if function is TServiceCustomStatus then TServiceCustomStatus.Status
}
Without losing the cardinal TServiceCustomStatus obviously. HTTPStatus code would return TServiceCustomStatus.Status too.
I'm asking this because my frontend tool (as well as many others) already evaluates the http statuscode to be able to direct the workflow to a success or error routine.
We don't always have control over the frontend that will be using our API, and some of these clients don't have the facility to change their programming.
I know someone here may say that it is the clients that should implement according to the API specification, but I have clients that use no-code tools that just receive the json and treat this json according to the returned http statuscode.
In summary. Your mormot clients would not have to change their programming at all, because if I have the following methods in the interface they would return the following json:
function TABC.FindOrder(Id: Integer; out Order: TOrder; out Items: TItems): TServiceCustomStatus;
{
"output1" or "order": { "id": 1, ... }
"output2" or "items": [{ "id": 10, ... }, { "id": 11, ... }, { "id": 12, ... }]
"errormessage": ""
"return" : 200 <<<< the same of HTTPStatus code returned
}
function TABC.FindOrder(Id: Integer; out Order: TOrder; out Items: TItems): boolean;
{
"output1" or "order": { "id": 1, ... }
"output2" or "items": [{ "id": 10, ... }, { "id": 11, ... }, { "id": 12, ... }]
"errormessage": ""
"return" : true <<<< HTTPStatus code always 200 if result <> TServiceCustomStatus
}
To internaol SOA process, "errormessage" field would be ignored unless the programmer put an out errormessage in the parameters.
Hi, I make some changes in TServiceCustomStatus
and put a PR #160
What did u think?
Are u see some problem in my changes?
Hi guys I try. look this sample:
TPainel = class;
TPaineis = array of TPainel;
TProjeto = class(TOrm)
private
FPaineis: TPaineis;
published
property Paineis: TPaineis read FPaineis;
end;
TPainel = class(TOrm)
private
FIdProjeto: TProjeto;
published
property IdProjeto: TProjeto read FIdProjeto write FIdProjeto;
end;
when I use this code
function TProjetoService.Busca(pId: Int64): TServiceCustomAnswer;
var
vProjeto: TProjeto;
vAuto: IAutoFree;
begin
vAuto := TOrm.AutoFree([@vProjeto, TProjeto]);
if not GlobalRepository.Orm.Retrieve(pId, vProjeto) then
Result := ReturnRecordNotFound
else
Result.Content := ObjectToJson(vProjeto);
end;
I receive this:
{
"RowID": 1,
"Nome": "Projeto a",
"DataCriacao": 135770589581,
"UltimaAlteracao": 135770590233,
"IdCriador": 5,
"Paineis": "[{\"RowID\":1,\"Nome\":\"painel a\",\"DataCriacao\":0,\"UltimaAlteracao\":0,\"IdCriador\":5,\"IdProjeto\":1},{\"RowID\":2,\"Nome\":\"painel b\",\"DataCriacao\":0,\"UltimaAlteracao\":0,\"IdCriador\":5,\"IdProjeto\":1},{\"RowID\":3,\"Nome\":\"painel c\",\"DataCriacao\":0,\"UltimaAlteracao\":0,\"IdCriador\":5,\"IdProjeto\":1}]"
}
I whould like this:
{
"Id": 1,
"Nome": "Projeto a",
"IdCriador": 5
"Paineis": [
{
"Id": 1,
"Nome": "painel a",
"IdCriador": 5,
"IdProjeto":1
},
{
"Id": 2,
"Nome": "painel b",
"IdCriador": 5,
"IdProjeto":1
},
{
"Id": 3,
"Nome": "painel c",
"IdCriador": 5,
"IdProjeto":1
}
]
}
1) How orm method i should be use to RETRIEVE the object TProjeto with the array Paineis filled? There are some?
2) This model create 2 tables Painel and Projeto but fill just Projeto and create a string field into Projeto table to store all TPanel as json. How I can store projeto and painel in diferent tables, but yet so make the same RETRIEVE above?
Very fast, Very thanks.
In this sample TBO. What type of TOrmPhoneObjArray? and How I can get a person id=? and the orm bring the person with all phones?
Thanks. Phone is just a sample.
I will use this strategy for more complex classes. Order and OrderItem for sample.
I dont think that OrderItem shoud be saved into a blob into order table.
Reasons:
1) Will be dificult mount reports. because by default FastReport and others reports generators work with Datasets, then we need get the records master detail in tables.
2) A bussiness inteligence tool that work with database need get OrderItems to make the graphics. Imagine this stored in blobs.
3) How to find something into objects that are store in this field blob?
Sample1: I want bring all orders that have product "xyz" (where product is into a blob field)
Sample2: I want sum(orderitem.total) where product "xyz" in a period (where product is into a blob field)
4) I think that exist other reason that do not appear in my mind at the moment.
How u treat this? Store all objects as documents (nosql style)?
I see all documentation. But TOrmMany is to pivottable, and i dont have source and dest in this case.
I what just a property returning list of objects TPhone into person!
Or I dont understand very well how to use!
Remerber that Tperson 1 - (0..n) Tphone (on database are different tables)
Problem with this is that we have to treat all input/output parameters of service method, getting of Ctx. very hard work!
There are some other way?
I Have 2 tables in my database
Person (id, name)
Phone (id, idPerson, number)
1) How can i map this in mormot?
TPerson = class;
TPhone = class(TOrm);
private
FIdPerson: TPerson;
FNumber: RawUTF8;
published
property Number: RawUTF8 index 25 read FNumber write FNumber;
property IdPerson: TPerson read FIdPersonwrite FIdPerson;
TPerson = class(TOrm)
private
FNome: RawUTF8;
published
property Nome: RawUTF8 index 100 read FNome write FNome;
property Phones: ???????? read FPhones <<<< that property type can i use here? (need persist in 2 separated tables)
end;
2) How I can call retrieve on orm and bring some person (id=10 for sample) and all your Phones?
Hi All,
I see that when implementing services interface based the framework always return 200.
But we whould like return others httpstatus.
then we discover TServiceCustomAnswer. this resolve the httpstatus problem, but this return do not accept parameters out neither var
sample. What i would like do:
function TProjetoService.GetProjetoPaineis(out pProjeto: TOrmProjeto): TServiceCustomAnswer;
begin
if not FRestOrm.Retrieve(pProjeto.Id, pProjeto) then
Result := ReturnError(Format('(%s) '+cErrorRecordNotFound, [GetClassName]), HTTP_NOTFOUND)
// else following the course, will return 200 ok!!!!
end;
function TServiceBase.ReturnError(const pErrorMessage: RawUTF8; pStatus: cardinal): TServiceCustomAnswer;
var
vError: Variant;
begin
TDocVariant.New(vError);
vError.ErrorMessage := pErrorMessage;
vError.StatusCode := pStatus;
Result.Content := VariantSaveJson(vError);
Result.Status := pStatus;
end;
How we can do this? What the recomendation?