#101 mORMot 1 » Streaming not working when requested more than one part » 2017-12-15 20:36:13

EMartin
Replies: 0

Hi @ab,

I need to use streaming feature of the framework and I found problems when the file  fire more of one request with the header "Range". In this link https://drive.google.com/file/d/1kO3dh2 … sp=sharing it's a demo with a server and a web page client that reproduce the problem.
 
In the subfolder "video" are the test video files with these problems:

video1.mp4: is OK with Firefox but not works with Chrome with error 416-Range Not Satisfiable
video2.mp4: fail with both Firefox and Chrome with error 416-Range Not Satisfiable
video3.mp4: is OK with Firefox but not works with Chrome with error 416-Range Not Satisfiable
video4.mp4: is OK with Firefox but not works with Chrome with error 416-Range Not Satisfiable

I have noticed that when the file size require more parts both the method service and the interface service are invoked more of once.

I tried solve this but was impossible for me and I really need this feature working. Time ago this was working I could play a movie.

Can you help me ?

Thanks in advance.

PD: the commit is 4088

#102 Re: mORMot 1 » Problem with WinHTTP client - error 87 » 2017-12-09 15:18:11

Winhttp.dll have two constants definition in syncrtsock.pas: winhttpdll and winhttp_dll, I think that the problem Is there.

#103 Re: mORMot 1 » Problem with WinHTTP client - error 87 » 2017-12-08 22:44:16

I think that it's related with new win HTTP API units of websockets from @mpv

#106 mORMot 1 » Custom fields serialization extending TSQLRecordHistory » 2017-12-01 21:53:25

EMartin
Replies: 4

Hi @ab,

   I need more fields in inherited TSQLRecordHistory:

  TSQLARISHistory = class(TSQLRecordHistory)
  protected
    fLogonName: RawUTF8;
    fTableName: RawUTF8;
    fRecID: TID;
  published
    property LogonName: RawUTF8 index 50 read fLogonName write fLogonName;
    property RecID: TID read fRecID write fRecID;
    property TableName: RawUTF8 index 50 read fTableName write fTableName;
  end;

and I have modified mORMot.pas in this way:

interface
...

type
  ...
  TSQLRestServer = class(TSQLRest)
  protected
    ...
    function MainEngineUpdateFieldIncrement(TableModelIndex: integer; ID: TID;
      const FieldName: RawUTF8; Increment: Int64): boolean; virtual; abstract;
    /// Fields serialization for history tracking changes
    // - Fields: name value pairs for JSON serialization.
    // - JSON: JSON serialized fields
    procedure DoHistoryFieldsSerialization(const Fields: array of const; var JSON: RawUTF8); virtual;
    ...
 end;

implementation
...
procedure TSQLRestServer.DoHistoryFieldsSerialization(const Fields: array of const; var JSON: RawUTF8);
begin
  JSON := JSONEncode(Fields);
end;

function TSQLRestServer.InternalUpdateEvent(aEvent: TSQLEvent; aTableIndex: integer;
  aID: TID; const aSentData: RawUTF8; aIsBlobFields: PSQLFieldBits): boolean;
procedure DoTrackChanges;
var TableHistoryIndex: integer;
    JSON: RawUTF8;
    Event: TSQLHistoryEvent;
begin
  case aEvent of
  seAdd:    Event := heAdd;
  seUpdate: Event := heUpdate;
  seDelete: Event := heDelete;
  else exit;
  end;
  TableHistoryIndex := fTrackChangesHistoryTableIndex[aTableIndex];
  fAcquireExecution[execORMWrite].Safe.Lock; // avoid race condition
  try // low-level Add(TSQLRecordHistory) without cache
    DoHistoryFieldsSerialization(['ModifiedRecord',aTableIndex+aID shl 6,'Event',ord(Event),
                        'SentDataJSON',aSentData,'TimeStamp',ServerTimeStamp], JSON); // <<--- call new procedure
    fAcquireExecution[execORMWrite].fSafe.Lock;
    try // may be within a batch in another thread
      EngineAdd(TableHistoryIndex,JSON);
    finally
      fAcquireExecution[execORMWrite].fSafe.Unlock;
    end;
    { TODO: use a BATCH (in background thread) to speed up TSQLHistory storage }
    if fTrackChangesHistory[TableHistoryIndex].CurrentRow>
        fTrackChangesHistory[TableHistoryIndex].MaxSentDataJsonRow then begin
      // gather & compress TSQLRecordHistory.SentDataJson into History BLOB
      TrackChangesFlush(TSQLRecordHistoryClass(Model.Tables[TableHistoryIndex]));
      fTrackChangesHistory[TableHistoryIndex].CurrentRow := 0;
    end else
      // fast append as JSON until reached MaxSentDataJsonRow
      inc(fTrackChangesHistory[TableHistoryIndex].CurrentRow);
  finally
    fAcquireExecution[execORMWrite].Safe.UnLock;
  end;
end;
begin
  ...
end;

and I implemented DoHistorySerialization in my code:

  ...
  procedure TARISRestServer.DoHistoryFieldsSerialization(const Fields: array of const; var JSON: RawUTF8);
  var
    lFields: TDocVariantData;
  begin
    lFields.InitObject(Fields);
    lFields.AddValue('LogonName',ServiceContext.Request.SessionUserName);
    lFields.AddValue('RecID',lFields.Value['ModifiedRecord'] shr 6);
    lFields.AddValue('TableName',fModel.Tables[lFields.I['ModifiedRecord'] and 63].SQLTableName);
    JSON := lFields.ToJSON;
  end;
  ...

and all works OK !!!

Are you agree with this modification and can you apply them ?

Thanks in advance.

#107 Re: mORMot 1 » How to use THTTPAPIWebSocketServer with TSQLRestServer ? » 2017-11-21 15:59:01

I don't think to have the enough knowledge base to implement this new feature, I'll think about this.

Thanks.

#108 mORMot 1 » How to use THTTPAPIWebSocketServer with TSQLRestServer ? » 2017-11-21 15:11:05

EMartin
Replies: 5

Hi @mpv, it is posible to use THTTPAPIWebSocketServer with TSQLRestServer in the same way that TSQLHTTPServer ?

Thanks.

#109 Re: mORMot 1 » POST Record to REST, how do I get the ID? » 2017-11-17 16:13:14

In "Location: Title/23" it's the answer: "23" is the new ID in table "Title".

#110 Re: mORMot 1 » POST Record to REST, how do I get the ID? » 2017-11-17 13:56:01

Check the headers in the HTTP response, the new record ID is there.

#111 Re: mORMot 1 » WinHTTP, http.sys and WebSockets » 2017-11-13 18:34:19

Great work @mpv !!!

Just for curiosity, why not just to use a function for readonly access in https://synopse.info/fossil/info/1ff1e5b5c276dcb5 ?

#112 Re: mORMot 1 » Sqlite3 busy error when update main DB from OnUpdateEvent » 2017-11-08 16:20:02

Solved busy error but still I need the fields, maybe de JSONEncode, in DoTrackChanges local procedure, can be moved as virtual function. What think you about this ?

Thanks.

#113 Re: mORMot 1 » Sqlite3 busy error when update main DB from OnUpdateEvent » 2017-11-08 15:12:00

I know, but for third party external processes accessing with JSON REST this values are not works.

#114 Re: mORMot 1 » Sqlite3 busy error when update main DB from OnUpdateEvent » 2017-11-08 14:23:18

@ab with TSQLRecordHistory the error still persists, how I must to configure the TSQLRestServerDB for ensure a single process open the associated SQLite3 database ? I have to define all "AcquireExecutionMode" to same value ? What would be those value ?

Sorry for many questions but I am confusing.

Thanks.

#115 Re: mORMot 1 » Sqlite3 busy error when update main DB from OnUpdateEvent » 2017-11-08 13:25:49

Thanks @ab, I see that TSQLRecordHistory can compliance my requirements but I would need that the record ID and the table name fields exists explicitly in the table, this is for external services that can read and delete the records. Can you add this fields ? I really would appreciate this feature.

Thanks.

#116 mORMot 1 » Sqlite3 busy error when update main DB from OnUpdateEvent » 2017-11-07 18:31:03

EMartin
Replies: 6

Hi @ab,

I have the secBusy error (code = 5) when storing in a local SQLite3 table (no external mapping) the notified changes in OnUpdateEventLog and the system is under high load. The local table it is in the main REST server together to external tables. I need notify to an third party external process the changes on some tables. Is there another way ?

Any suggestion I will appreciate you.

TIA.

#117 Re: mORMot 1 » Lazarus can no longer create SQLite3 databases using TSQLRestServerDB » 2017-10-05 13:29:10

OpenUI5 is oriented to enterprise, the development is very active and the UI tends to UX, it have 600+ components, routing, internalization, etc, builtin the framework but is big and not very easy. I have an eye on Google Polymer https://www.polymer-project.org/ and other eye on Vaadin elements based on Polymer https://vaadin.com/elements.

Best  regards.

#118 Re: mORMot 1 » Exception and Fix » 2017-09-21 13:55:55

I recommend you create a ticket.

Best regards.

#119 Re: mORMot 1 » Virtual table is created in first call but not returning field ID » 2017-06-15 15:58:13

Hi @ab, this is the sequence call between first call and next:

procedure TSQLRestServerURIContext.ExecuteORMGet;
...
                // use the first static table (poorman's JOIN)
                Static := Server.InternalAdaptSQL(TableIndexes[0],SQL); //--> always executed
              end;
            end;
            if Static<>nil then  begin
              TableEngine := Static;
              Call.OutBody := TableEngine.EngineList(SQL); //--> always executed after first time
            end else
              Call.OutBody := Server.MainEngineList(SQL,false,nil); //--> executed ONLY the firs time
            // security note: only first statement is run by EngineList()
            if Call.OutBody<>'' then begin // got JSON list '[{...}]' ?
...
end;

procedure TSQLRestServerDB.PrepareStatement(Cached: boolean);
var wasPrepared: boolean;
    timer: PPPrecisionTimer;
begin
  fStaticStatementTimer.Start;
  if not Cached then begin
    fStaticStatement.Prepare(DB.DB,fStatementGenericSQL); //--> executed the first time (called from PrepareStatement->GetAndPrepareStatement->MainEngineList)
    fStatementGenericSQL := '';			          //    then this call to TSQLRequest.Prepare->mORMotSQLite3.vt_create->TSQLVirtualTable.Create
  ...
end;

The first time the error is like this (varying the table and the field names):


20170615 12425630  ( EXC       ESQLite3Exception {"ErrorCode":1,"SQLite3ErrorCode":"secERROR","Message":"Error SQLITE_ERROR (1) [SELECT ID,IDMDService,Code,Statement,CRC,LastAccess,MDServiceType,Description,BinData,Internal,Statement_U,Description_U FROM MDServices] using 3.19.2 - no such column: ID, extended_errcode=1"} at 006FED8A SynSQLite3.sqlite3_check (5319)  stack trace API

The virtual table registration this table is:

VirtualTableExternalMap(fModel, TSQLMDServices, fConnPropSQLAdmin, 'TV_MDSERVICES').
  MapFields(['ID','IDMDSERVICE']).
  SetOptions([rpmAutoMapKeywordFields, rpmNoCreateMissingTable, rpmNoCreateMissingField]);

With the next calls all works fine.

My workaround is dirty and bad performance:

  for I := Low(fModel.Tables) to High(fModel.Tables) do begin
    if (fModel.Props[fModel.Tables[I]].Kind in [rCustomForcedID, rCustomAutoID]) then begin // only external tables
      lMappingVersion := fModel.Props[fModel.Tables[I]].ExternalDB.MappingVersion;
      if lMappingVersion>1 then // only with mapped fields
        fServerRest.TableHasRows(fModel.Tables[I]);
    end;
  end;

For me this a bug but you have the last word. big_smile

Thanks.

#120 Re: mORMot 1 » Virtual table is created in first call but not returning field ID » 2017-06-15 10:48:35

Yes of course. I have registered virtual external tables using SetOptions with NotCreateMissingFields (a database script make this), I can see the virtual external tables in the SQLite3 db. When the application start, the first time, the GET collection data request, the column ID is not sent, the next time yes. With non external tables I haven't this problem.

Thanks.

#121 mORMot 1 » Virtual table is created in first call but not returning field ID » 2017-06-14 21:29:51

EMartin
Replies: 3

Hi @ab, I have a problem with ID field in an external virtual table in the first call, the GET request http://host:port/root/tablename?select=* the answer is with error "400 bad request", in the next call the response is ok, when debugging I detected that with the first call the mORMotSQLite3.vt_Create function is invoked and then mORMot.TSQLVirtualTable.Create and this register the table in aServer.fStaticVirtualTable, with this allow the correct response in next calls.

Thanks.

#122 Re: mORMot 1 » Contribution: TSynRestDataset » 2017-05-27 14:43:58

Yes, is an general error with mORMot and Delphi Dataset from a few weeks ago, is not only TSynRestDataset problem. In the next two week I will be able to see to solve it.

#124 Re: mORMot 1 » TWinHTTP support SSL client certificates ? » 2017-05-22 14:48:56

@ab,  don't you agree with this modification ? I consider that it's an enhacement.

Best regards.

#125 Re: mORMot 1 » Fix to timestamp bug regression » 2017-05-20 15:52:56

my solution solved my problem with new data but not with existing, I can confirm that the bug regression still exists.

#126 Re: mORMot 1 » TWinHTTP support SSL client certificates ? » 2017-05-19 21:06:05

Hi @ab, I have modified SynCrtSock.pas adding response management to error ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED (12044) when the server requests client authentication but not require it.

It's not a big modification:

const
  // from http://www.tek-tips.com/faqs.cfm?fid=7493
  WINHTTP_OPTION_SECURITY_FLAGS = 31;
  SECURITY_FLAG_IGNORE_UNKNOWN_CA = $00000100;
  SECURITY_FLAG_IGNORE_CERT_DATE_INVALID = $00002000; // expired X509 Cert.
  SECURITY_FLAG_IGNORE_CERT_CN_INVALID = $00001000; // bad common name in X509 Cert.
  SECURITY_FLAG_IGNORE_CERT_WRONG_USAGE = $00000200;
  SECURITY_FLAT_IGNORE_CERTIFICATES: DWORD =
    SECURITY_FLAG_IGNORE_UNKNOWN_CA or
    SECURITY_FLAG_IGNORE_CERT_DATE_INVALID or
    SECURITY_FLAG_IGNORE_CERT_CN_INVALID or
    SECURITY_FLAG_IGNORE_CERT_WRONG_USAGE;

  WINHTTP_AUTH_TARGET_SERVER = 0;
  WINHTTP_AUTH_TARGET_PROXY = 1;
  WINHTTP_AUTH_SCHEME_BASIC = $00000001;
  WINHTTP_AUTH_SCHEME_NTLM = $00000002;
  WINHTTP_AUTH_SCHEME_PASSPORT = $00000004;
  WINHTTP_AUTH_SCHEME_DIGEST = $00000008;
  WINHTTP_AUTH_SCHEME_NEGOTIATE = $00000010;

  // client authentication certificate errors
  ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED = $00002F0C; // 12044 code error returned by GetLastError()

  WINHTTP_OPTION_CLIENT_CERT_CONTEXT = $0000002F;
  WINHTTP_NO_CLIENT_CERT_CONTEXT = $00000000;

procedure TWinHTTP.InternalSendRequest(const aData: SockString);
var L: integer;
    winAuth: DWORD;
begin
  with fExtendedOptions do
  if AuthScheme<>wraNone then begin
    case AuthScheme of
    wraBasic: winAuth := WINHTTP_AUTH_SCHEME_BASIC;
    wraDigest: winAuth := WINHTTP_AUTH_SCHEME_DIGEST;
    wraNegotiate: winAuth := WINHTTP_AUTH_SCHEME_NEGOTIATE;
    else raise EWinHTTP.CreateFmt('Unsupported AuthScheme=%d',[ord(AuthScheme)]);
    end;
    if not WinHttpSetCredentials(fRequest,WINHTTP_AUTH_TARGET_SERVER,
       winAuth,pointer(AuthUserName),pointer(AuthPassword),nil) then
      RaiseLastModuleError(winhttpdll,EWinHTTP);
  end;
  if fHTTPS and IgnoreSSLCertificateErrors then begin
    if not WinHttpSetOption(fRequest, WINHTTP_OPTION_SECURITY_FLAGS,
       @SECURITY_FLAT_IGNORE_CERTIFICATES, SizeOf(SECURITY_FLAT_IGNORE_CERTIFICATES)) then
      RaiseLastModuleError(winhttpdll,EWinHTTP);
  end;
  L := length(aData);
  if not WinHttpSendRequest(fRequest, nil, 0, pointer(aData), L, L, 0) or
     not WinHttpReceiveResponse(fRequest,nil) then begin
    if not fHTTPS then
      RaiseLastModuleError(winhttpdll,EWinHTTP);
    if GetLastError=ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED then begin
      if not WinHttpSetOption(fRequest, WINHTTP_OPTION_SECURITY_FLAGS,
         @SECURITY_FLAT_IGNORE_CERTIFICATES, SizeOf(SECURITY_FLAT_IGNORE_CERTIFICATES)) then
        RaiseLastModuleError(winhttpdll,EWinHTTP);
      if not WinHttpSetOption(fRequest, WINHTTP_OPTION_CLIENT_CERT_CONTEXT,
         pointer(WINHTTP_NO_CLIENT_CERT_CONTEXT), 0) then
        RaiseLastModuleError(winhttpdll,EWinHTTP);
      if not WinHttpSendRequest(fRequest, nil, 0, pointer(aData), L, L, 0) or
         not WinHttpReceiveResponse(fRequest,nil) then
        RaiseLastModuleError(winhttpdll,EWinHTTP);
    end;
  end;
end;

Do you agree with add this feature ?

Thanks.

#127 mORMot 1 » TWinHTTP support SSL client certificates ? » 2017-05-17 21:38:33

EMartin
Replies: 4

Hi people, I need to use TWinHTTP with a https server (non mORMot) and I receive the error error 12044 a certificate is required, is TWinHTTP prepared to work with certificates ?

Thanks in advance.

#129 Re: mORMot 1 » Documentation on ServiceNotificationMethodViaMessages » 2017-04-27 15:36:54

Sorry for my bad expressions. I describe you the usage of this method with my code for showing the ambiguous description in the documentation (for me).

I have my class declaration:

...
TARISClient = class(TObject)
protected
  ...
  fWebSocketClient: TSQLHttpClientWebsockets; // for websocket callbacks
  fCallbackService: IARISCallbackService; // callback interface
  fClient: TSQLRestClientURI; // mORMot client
  fLastErrorMessage: RawUTF8;
  ...
public
  ...
  CallbackClient: IARISCallbackClient;

  constructor Create;
  ...
  function CallbackCreate(const aHost, aPort: string; out aCallback: IARISCallbackClient): Boolean;
  ...
  /// Cliente websocket
  property WSClient: TSQLHttpClientWebsockets read fWebSocketClient;
  ...
end;

class implementation:

constructor TARISClient.Create;
  ...
  fClient := TSQLHttpClient.Create(...);
  ...
end;
...
function TARISClient.CallbackCreate(const aHost, aPort: string; out aCallback: IARISCallbackClient): Boolean;
begin
  // se crea el websocket cliente
  fWebSocketClient := TSQLHttpClientWebsockets.Create(...));
  fWebSocketClient.Model.Owner := fWebSocketClient;
  // se usa el websocket con protocolo binario
  fWebSocketClient.WebSocketsUpgrade(ARIS_WS_TRANSMISSION_KEY);
  if not fWebSocketClient.ServerTimeStampSynchronize then
  begin
    fLastErrorMessage := FormatUTF8(SErrorConnectingToTheServer, [lHost, lPort])  + ' / ' + fWebSocketClient.LastErrorMessage;;
    Exit;
  end;
  // se cargan los servicios definidos para los websockets
  fWebSocketClient.ServiceDefine([IARISCallbackService], sicShared);
  if not fWebSocketClient.Services.Resolve(IARISCallbackService, fCallbackService) then
  begin
    fLastErrorMessage := SServiceIARISCallbackServiceUnava + ' / ' + fWebSocketClient.LastErrorMessage;;
    Exit;
  end;
  // se crea el callback cliente que será invocado cuando se reciba una notificación websocket desde el servidor
  aCallback := TARISCallbackClient.Create(fWebSocketClient, IARISCallbackClient);
  // si está asignado el evento para cuando se recibe un callback se lo asigna al callback cliente creado para que
  // luego propague la notificación
  if Assigned(fOnCallback) then
    TARISCallbackClient(ObjectFromInterface(aCallback)).OnCallback := fOnCallback; // allow it callback propagation when used in a TForm
  Result := True;
end;
...

then I use the client and the callbacks in visual application:

class declaration:

const
  WM_SERVICENOTIFICATION = WM_USER+123;
type
...
TfrmARISClient = class(TForm)
protected
  ...
  fARISClient: TARISClient;
  fCallback: IARISCallbackClient;
  ...
  procedure ServiceNotification(var Msg: TMessage); message WM_SERVICENOTIFICATION;
  procedure DoOnCallback(const aJSONCallback: RawUTF8);
  ...
public
  ...
end;
...

class implementation and usage:

...
procedure TfrmARISClient.FormCreate(Sender: TObject);
begin
  ...
  // se crea el cliente de ARIS
  fARISClient := TARISClient.Create(...);
  if not fARISClient.Client.ServerTimeStampSynchronize then
    raise Exception.Create('No se pudo conectar con ARIS');
  ...
  // se suscribe al callback para recibir notificaciones
  fARISClient.CallbackCreate(...,...,fCallback);
  // evento que procesa los callbacks
  TARISCallbackClient(ObjectFromInterface(fCallback)).OnCallback := DoOnCallback;
  // procesar callbacks en la hebra principal
  fARISClient.WSClient.ServiceNotificationMethodViaMessages(Handle,WM_SERVICENOTIFICATION); //--> *** LINE 1 ***
  ...  
end;
...
procedure TfrmARISClient.ServiceNotification(var Msg: TMessage);
begin
  TSQLRestClientURI.ServiceNotificationMethodExecute(Msg); //--> *** LINE 2 ***
end;
...
procedure TfrmARISClient.DoOnCallback(const aJSONCallback: RawUTF8);
begin
  // process callback in main thread (show progress bar, update labels, etc.)
end;

*** LINE 1 ***: use of the client websocket, not of the fARISClient (TSQLRestClientURI/TSQLHttpClient).
*** LINE 2 ***: use of the class procedure of the TSQLRestClientURI, I know I can use fARISClient instead.

in the documentation only show Client.ServiceNotificationMethodViaMessages(MainForm.Handle,WM_SERVICENOTIFICATION) and, for me, is not clear the client type, it may be only me who does not understand.

I hope you understand, anyway this works like a charm.

Regards.

#130 Re: mORMot 1 » Documentation on ServiceNotificationMethodViaMessages » 2017-04-27 11:01:16

Not exactly, I créate a client with TSQLRestClientURI for communications with the server, but for websocket communications I créate with TSQLHttpClientWebsockets the callbacks and with this instance I call this method, then with TSQLRestClientURI I call the another method (execute). I do not have the code at hand.

#131 mORMot 1 » Documentation on ServiceNotificationMethodViaMessages » 2017-04-26 19:48:36

EMartin
Replies: 6

@ab, can you clarify in the documentation the use of this method ?

In section 16.7.2.6. Interacting with UI/VCL says:

Client.ServiceNotificationMethodViaMessages(MainForm.Handle,WM_SERVICENOTIFICATION);

and Client is a TSQLHttpClientWebsockets and not TSQLRestClientURI (my silly confusion).

Thanks.

#133 mORMot 1 » Fix to timestamp bug regression » 2017-04-26 16:15:55

EMartin
Replies: 7

Hi @ab, this fix https://synopse.info/fossil/info/f69f5eb7d2ed6541 introduced a bug with error "Invalid point operation".

your fix:

...
  ftDate, ftTime, ftDateTime:
    if PDateTime(Data)^=0 then
      result := false else begin
      TS := DateTimeToTimeStamp(PDateTime(Data)^);
      if (TS.Time<0) or (TS.Date<=0) then
        result := false else // matches ValidateTimeStamp() expectations
        case Field.DataType of
        ftDate:     PDateTimeRec(Dest)^.Date := TS.Date;
        ftTime:     PDateTimeRec(Dest)^.Time := TS.Time;
        ftDateTime: PDateTimeRec(Dest)^.DateTime := TimeStampToMSecs(TS) //-->> YOUR FIX !!!
        end;
    end;
  ftString: begin
...

the solution:

...
  ftDate, ftTime, ftDateTime:
    if PDateTime(Data)^=0 then
      result := false else begin
      TS := DateTimeToTimeStamp(PDateTime(Data)^);
      if (TS.Time<0) or (TS.Date<=0) then
        result := false else // matches ValidateTimeStamp() expectations
        case Field.DataType of
        ftDate:     PDateTimeRec(Dest)^.Date := TS.Date;
        ftTime:     PDateTimeRec(Dest)^.Time := TS.Time;
        ftDateTime: PDateTimeRec(Dest)^.DateTime := PDateTime(Data)^; //-->> THE SOLUTION !!!
        end;
    end;
  ftString: begin
...

this correction working for me, but I can be wrong.

Best regards.

#135 Re: mORMot 1 » AsynchRedirect usage example » 2017-04-20 18:24:48

Thank you very much @ab, I could make it work !! but neither in this life nor any other I could understand the use of this function. Can you update the documentation ?

Anyway, this is an example for any other that need use this function (adapted from example 31):

interface declaration:

  ...
type
  ILongWorkCallback = interface(IInvokable)
    ['{425BF199-19C7-4B2B-B1A4-A5BE7A9A4748}']
    procedure WorkFinished(const workName: string; timeTaken: integer);
    procedure WorkFailed(const workName, error: string);
  end;

  ILongWorkService = interface(IInvokable)
    ['{09FDFCEF-86E5-4077-80D8-661801A9224A}']
    procedure StartWork(const workName: string; const onFinish: ILongWorkCallback);
    function TotalWorkCount: Integer;
  end;

  IAsynchLongWorkService = interface(IInvokable)
    ['{CFA559E5-B181-45BB-84AB-7279DDDA51A9}']
    procedure StartWork(const workName: string; const onFinish: Integer);
  end;
  ...
initialization
  TInterfaceFactory.RegisterInterfaces([
    TypeInfo(ILongWorkService),TypeInfo(ILongWorkCallback),TypeInfo(IAsynchLongWorkService)]);

class declaration:

  ...
type
  TLongWorkService = class(TInterfacedObject,ILongWorkService)
  protected
    fCallbackInterface: IAsynchLongWorkService;
    fDestinationInterface: IAsynchLongWorkService;
  public
    procedure StartWork(const workName: string; const onFinish: ILongWorkCallback);
    function TotalWorkCount: Integer;
  end;

  TAsynchLongWorkService = class(TInterfacedObject,IAsynchLongWorkService)
  protected
    fCallback: ILongWorkCallback;
    fWorkName: string;
    fTotalWorkCount: Integer;
  public
    procedure StartWork(const workName: string; const onFinish: Integer);
    function TotalWorkCount: Integer;
  end;
  ...

class implementation:

...
{ TLongWorkAsyncService }

procedure TLongWorkService.StartWork(const workName: string; const onFinish: ILongWorkCallback);
begin
  if fCallbackInterface=nil then begin
    fDestinationInterface := TAsynchLongWorkService.Create;
    ServiceContext.Request.Server.AsynchRedirect(IAsynchLongWorkService,fDestinationInterface,fCallbackInterface);
  end;
  onFinish._AddRef; // avoid GPF and the interface is available in asynch. routine
  fCallbackInterface.StartWork(workName,Integer(onFinish));
end;

function TLongWorkService.TotalWorkCount: Integer;
begin
  result := MaxInt; // this shows the asynchronous functioning
end;
...
{ TAsynchLongWorkService }

procedure TAsynchLongWorkService.StartWork(const workName: string; const onFinish: Integer);
var tix: Int64;
begin
  InterlockedIncrement(fTotalWorkCount);
  fCallback := ILongWorkCallback(onFinish);
  fWorkName := workName;
  TSQLLog.Add.Log(sllInfo,'%.Execute(%) started',[self,fWorkName]);
  tix := GetTickCount64;
  Sleep(5000+Random(1000)); // some hard work
  if Random(100)>20 then
    fCallback.WorkFinished(fWorkName,GetTickCount64-tix) else
    fCallback.WorkFailed(fWorkName,'expected random failure');
  TSQLLog.Add.Log(sllInfo,'%.Execute(%) notified',[self,fWorkName]);
  fCallback._Release; // release the interface out this function
end;

function TAsynchLongWorkService.TotalWorkCount: Integer;
begin
  result := fTotalWorkCount;
end;

Best regards.

#136 Re: mORMot 1 » AsynchRedirect usage example » 2017-04-20 15:01:43

Good, both things are very useful for me, can you give me an example of how call AsynchRedirect, mainly I can't understood the parameters.

procedure AsynchRedirect(const aGUID: TGUID; const aDestinationInterface: IInvokable; out aCallbackInterface); overload;

- aGUID: is the source service that I want redirect ? What about the code in the method, must be empty ?
- aDestinationInterface: is any interface where the real process is executed ?
- aCallbackInterface: the aDestinationInterface must have it a callback interface ?

Thanks in advance.

#137 Re: mORMot 1 » AsynchRedirect usage example » 2017-04-20 14:44:57

@ab, Is AsynchRedirect working ?

Thanks.

#138 Re: mORMot 1 » AsynchRedirect usage example » 2017-04-19 20:22:24

My idea is to implement an asynchronous long work service with the possibility to the cancel it. What would be the best way do it ?

Thanks in advance.

#139 mORMot 1 » AsynchRedirect usage example » 2017-04-19 18:13:59

EMartin
Replies: 9

Hi @ab, can you post a simple use of TSQLRest.AsynchRedirect ? I tried but without luck.

Thanks in advance.

#140 Re: mORMot 1 » Contribution: TSynRestDataset » 2017-04-18 17:19:02

I developed TSynRestdataset with Delphi 7 under Windows, for support the any other platform, I do not have the knowledge.

#141 Re: mORMot 1 » Contribution: TSynRestDataset » 2017-04-17 21:00:03

@leus,  I'll try that, now I have not time.

Thanks.

#142 Re: mORMot 1 » Contribution: TSynRestDataset » 2017-04-14 20:53:05

@Stemon63, no it is. I documented that I don't know Linux.

@leus, because I tried.

#143 Re: mORMot 1 » Contribution: TSynRestDataset » 2017-04-07 18:12:01

Yes, but I cannot rename/drop folders inside the my folder, I don't know other constraints.

#144 Re: mORMot 1 » Contribution: TSynRestDataset » 2017-04-07 16:51:24

I prefer that when downloading mORMot all things together but as I cannot update the "ThirdParty\EMartin" folder maybe I should use github. I'll put in this thread when I have created the distribution in github.

Best regards.

#145 Re: mORMot 1 » Contribution: TSynRestDataset » 2017-04-06 18:11:49

In this thread, the #60, but the TSynRestdataset is not the problem, maybe you not defined the ORM table or not call CreateMissingTables in the REST server.

Best regards.

Esteban

#146 Re: mORMot 1 » Contribution: TSynRestDataset » 2017-04-06 17:10:45

Hi Stemon63, SynRestDataset use REST/JSON for ORM in a mORMot way, it should work with any mORMot backend through ORM tables.

Best regards.

Esteban.

#147 Re: mORMot 1 » Migrating big client-server (2 tier) application to mORMot » 2017-04-04 18:19:29

TSynRestDataset is not experimental, we will release it in production in the next weeks. The TSynRestDataset born into the need the migration three tier legacy software (RemObjects) to more stable application server, I did rewrite all legacy code for total use of mORMot benefits, and in the middle parallel running partial up to total migration.

Best regards.

#148 Re: mORMot 1 » Contribution: TSynRestDataset » 2017-03-30 13:23:49

No, but you can make it visual.

Best regards.

#149 Re: mORMot 1 » Use JSONToObject for tree structure » 2017-03-22 19:51:02

You can try my SynJSONTreeView https://drive.google.com/open?id=0Bx7LP … 0p2eWxNUUE, but not implemented/tested for FPC.

Best regards.

#150 Re: mORMot 1 » Bugs with TSQLRestClientURI.OnAuthentificationFailed » 2017-03-10 11:01:06

@jbroussia, create a ticket with the problem and the solution, @ab never answered in this thread.

Board footer

Powered by FluxBB