-
-
Notifications
You must be signed in to change notification settings - Fork 137
/
Copy pathmormot.rest.core.pas
4759 lines (4357 loc) · 182 KB
/
mormot.rest.core.pas
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/// REpresentation State Tranfer (REST) Core Types and Classes
// - this unit is a part of the Open Source Synopse mORMot framework 2,
// licensed under a MPL/GPL/LGPL three license - see LICENSE.md
unit mormot.rest.core;
{
*****************************************************************************
Shared Types and Definitions for Abstract REST Process
- Customize REST Execution
- TRestBackgroundTimer for Multi-Thread Process
- TRestRunThreads Multi-Threading Process of a REST instance
- TRest Abstract Parent Class
- RESTful Authentication Support
- TRestUriParams REST URI Definitions
- TRestUriContext REST Parent Process on Server Side
- TRestThread Background Process of a REST instance
- TOrmHistory/TOrmTableDeleted Modifications Tracked Persistence
*****************************************************************************
}
interface
{$I ..\mormot.defines.inc}
uses
sysutils,
classes,
variants,
contnrs,
mormot.core.base,
mormot.core.os,
mormot.core.buffers,
mormot.core.unicode,
mormot.core.text,
mormot.core.datetime,
mormot.core.variants,
mormot.core.data,
mormot.core.rtti,
mormot.core.json,
mormot.core.threads,
mormot.core.perf,
mormot.crypt.core,
mormot.crypt.secure,
mormot.crypt.jwt,
mormot.core.log,
mormot.core.interfaces,
mormot.orm.base,
mormot.orm.core, // for TOrm and IRestOrm
mormot.soa.core,
mormot.db.core;
{ ************ Customize REST Execution }
type
/// all commands which may be executed by TRestServer.Uri() method
// - execSoaByMethod for method-based services
// - execSoaByInterface for interface-based services
// - execOrmGet for ORM reads i.e. Retrieve*() methods
// - execOrmWrite for ORM writes i.e. Add Update Delete TransactionBegin
// Commit Rollback methods
TRestServerUriContextCommand = (
execNone,
execSoaByMethod,
execSoaByInterface,
execOrmGet,
execOrmWrite);
/// how a TRest class may execute read or write operations
// - used e.g. for TRestServer.AcquireWriteMode or
// TRestServer.AcquireExecutionMode/AcquireExecutionLockedTimeOut
TRestServerAcquireMode = (
amUnlocked,
amLocked,
amBackgroundThread,
amBackgroundOrmSharedThread,
amMainThread);
/// used to store the execution parameters for a TRest instance
TRestAcquireExecution = class(TSynLocked)
public
/// how read or write operations will be executed
Mode: TRestServerAcquireMode;
/// ms delay before failing to acquire the lock
LockedTimeOut: cardinal;
/// background thread instance (if any)
Thread: TSynBackgroundThreadMethod;
/// finalize the memory structure, and the associated background thread
destructor Destroy; override;
end;
PRestAcquireExecution = ^TRestAcquireExecution;
/// define how a TRest class may execute its ORM and SOA operations
TRestAcquireExecutions =
array[TRestServerUriContextCommand] of TRestAcquireExecution;
/// a genuine identifier for a given client connection on server side
// - see also THttpServerConnectionID as defined in mormot.net.http: may map
// the http.sys ID, or a genuine 31-bit value from increasing sequence
TRestConnectionID = Int64;
const
/// size in bytes, to log up to 2 KB of JSON response, to save space
MAX_SIZE_RESPONSE_LOG = 2 shl 10;
CONTENT_TYPE_WEBFORM: PAnsiChar = 'APPLICATION/X-WWW-FORM-URLENCODED';
CONTENT_TYPE_MULTIPARTFORM: PAnsiChar = 'MULTIPART/FORM-DATA';
{ ************ TRestBackgroundTimer for Multi-Thread Process }
type
TRest = class;
/// optionally called after TRest.AsyncRedirect background execution
// - to retrieve any output result value, as JSON-encoded content
// - as used in TRestBackgroundTimer.AsyncBackgroundExecute protected method
TOnAsyncRedirectResult = procedure(const aMethod: TInterfaceMethod;
const aInstance: IInvokable; const aParams, aResult: RawUtf8) of object;
/// TThread able to run one or several tasks at a periodic pace, or do
// asynchronous interface or batch execution, with proper TRest integration
// - used e.g. by TRest.TimerEnable/AsyncRedirect/AsyncBatchStart methods
// - TRest.BackgroundTimer will define one instance, but you may create
// other dedicated instances to instantiate separated threads
TRestBackgroundTimer = class(TSynBackgroundTimer)
protected
fRest: TRest;
fBackgroundBatch: TRestBatchLockedDynArray;
fBackgroundInterning: array of TRawUtf8Interning;
fBackgroundInterningMaxRefCount: integer;
fBackgroundInterningSafe: TLightLock; // paranoid lock
procedure SystemUseBackgroundExecute(Sender: TSynBackgroundTimer;
const Msg: RawUtf8);
// used by AsyncRedirect/AsyncBatch/AsyncInterning
function AsyncBatchIndex(aTable: TOrmClass): PtrInt;
function AsyncBatchLocked(aTable: TOrmClass;
out aBatch: TRestBatchLocked): boolean;
procedure AsyncBatchUnLock(aBatch: TRestBatchLocked);
procedure AsyncBatchExecute(Sender: TSynBackgroundTimer;
const Msg: RawUtf8);
procedure AsyncBackgroundExecute(Sender: TSynBackgroundTimer;
const Msg: RawUtf8);
procedure AsyncBackgroundInterning(Sender: TSynBackgroundTimer;
const Msg: RawUtf8);
public
/// initialize the thread for a periodic task processing
constructor Create(aRest: TRest; const aThreadName: RawUtf8 = '';
aStats: TSynMonitorClass = nil); reintroduce; virtual;
/// finalize the thread
destructor Destroy; override;
/// define asynchronous execution of interface methods in a background thread
// - this method implements any interface via a fake class, which will
// redirect all methods calls into calls of another interface, but as a FIFO
// in a background thread, shared with TimerEnable/TimerDisable process
// - parameters will be serialized and stored as JSON in the queue
// - by design, only procedure methods without any output parameters are
// allowed, since their execution will take place asynchronously
// - of course, a slight delay is introduced in aDestinationInterface
// methods execution, but the main process thread is not delayed any more,
// and is free from potential race conditions
// - the returned fake aCallbackInterface should be freed before TRest
// is destroyed, to release the redirection resources
// - it is an elegant resolution to the most difficult implementation
// problem of SOA callbacks, which is to avoid race condition on reentrance,
// e.g. if a callback is run from a thread, and then the callback code try
// to execute something in the context of the initial thread, protected
// by a critical section (mutex)
procedure AsyncRedirect(const aGuid: TGuid;
const aDestinationInterface: IInvokable; out aCallbackInterface;
const aOnResult: TOnAsyncRedirectResult = nil); overload;
/// define asynchronous execution of interface methods in a background thread
// - this method implements any interface via a fake class, which will
// redirect all methods calls into calls of another interface, but as a FIFO
// in a background thread, shared with TimerEnable/TimerDisable process
// - parameters will be serialized and stored as JSON in the queue
// - by design, only procedure methods without any output parameters are
// allowed, since their execution will take place asynchronously
// - of course, a slight delay is introduced in aDestinationInterface
// methods execution, but the main process thread is not delayed any more,
// and is free from potential race conditions
// - the returned fake aCallbackInterface should be freed before TRest
// is destroyed, to release the redirection resources
// - it is an elegant resolution to the most difficult implementation
// problem of SOA callbacks, which is to avoid race condition on reentrance,
// e.g. if a callback is run from a thread, and then the callback code try
// to execute something in the context of the initial thread, protected
// by a critical section (mutex)
procedure AsyncRedirect(const aGuid: TGuid;
const aDestinationInstance: TInterfacedObject; out aCallbackInterface;
const aOnResult: TOnAsyncRedirectResult = nil); overload;
/// prepare an asynchronous ORM BATCH process, executed in a background thread
// - will initialize a TRestBatch and call TimerEnable to initialize the
// background thread, following the given processing period (in seconds),
// or the TRestBatch.Count threshold to call BatchSend
// - actual REST/CRUD commands will take place via AsyncBatchAdd,
// AsyncBatchUpdate and AsyncBatchDelete methods
// - only a single AsyncBatch() call per Table is allowed at a time, unless
// AsyncBatchStop method is used to flush the current asynchronous BATCH
// - using a BATCH in a dedicated thread will allow very fast background
// asynchronous process of ORM methods, sufficient for most use cases
function AsyncBatchStart(Table: TOrmClass;
SendSeconds: integer; PendingRowThreshold: integer = 500;
AutomaticTransactionPerRow: integer = 1000;
Options: TRestBatchOptions = [boExtendedJson]): boolean;
/// finalize asynchronous ORM BATCH process, executed in a background thread
// - should have been preceded by a call to AsyncBatch(), or returns false
// - Table=nil will release all existing batch instances
function AsyncBatchStop(Table: TOrmClass): boolean;
/// create a new ORM member in a BATCH to be written in a background thread
// - should have been preceded by a call to AsyncBatchStart(), or returns -1
// - is a wrapper around TRestBatch.Add() sent in the Timer thread,
// so will return the index in the BATCH rows, not the created TID
// - this method is thread-safe
function AsyncBatchAdd(Value: TOrm; SendData: boolean;
ForceID: boolean = false; const CustomFields: TFieldBits = [];
DoNotAutoComputeFields: boolean = false): integer;
/// append some JSON content in a BATCH to be writen in a background thread
// - could be used to emulate AsyncBatchAdd() with an already pre-computed
// JSON object
// - is a wrapper around TRestBatch.RawAdd() sent in the Timer thread,
// so will return the index in the BATCH rows, not the created TID
// - this method is thread-safe
function AsyncBatchRawAdd(Table: TOrmClass; const SentData: RawUtf8): integer;
/// append some JSON content in a BATCH to be writen in a background thread
// - could be used to emulate AsyncBatchAdd() with an already pre-computed
// JSON object, as stored in a TJsonWriter instance
// - is a wrapper around TRestBatch.RawAppend.AddNoJsonEscape(SentData)
// in the Timer thread
// - this method is thread-safe
procedure AsyncBatchRawAppend(Table: TOrmClass; SentData: TJsonWriter);
/// update an ORM member in a BATCH to be written in a background thread
// - should have been preceded by a call to AsyncBatchStart(), or returns -1
// - is a wrapper around the TRestBatch.Update() sent in the Timer thread
// - this method is thread-safe
function AsyncBatchUpdate(Value: TOrm;
const CustomFields: TFieldBits = [];
DoNotAutoComputeFields: boolean = false): integer;
/// delete an ORM member in a BATCH to be written in a background thread
// - should have been preceded by a call to AsyncBatchStart(), or returns -1
// - is a wrapper around the TRestBatch.Delete() sent in the Timer thread
// - this method is thread-safe
function AsyncBatchDelete(Table: TOrmClass; ID: TID): integer;
/// allows background garbage collection of specified RawUtf8 interning
// - will run Interning.Clean(2) every 5 minutes by default
// - set InterningMaxRefCount=0 to disable process of the Interning instance
procedure AsyncInterning(Interning: TRawUtf8Interning;
InterningMaxRefCount: integer = 2; PeriodMinutes: integer = 5);
/// direct access to the TRest instance owner
property Rest: TRest
read fRest;
/// direct access to the background thread TRestBatch instances
property BackgroundBatch: TRestBatchLockedDynArray
read fBackgroundBatch;
published
/// the identifier of the thread, as logged
property Name: RawUtf8
read fThreadName;
end;
// backward compatibility types redirections
{$ifndef PUREMORMOT2}
TSqlRestServerUriContextCommand = TRestServerUriContextCommand;
TSqlRestServerAcquireMode = TRestServerAcquireMode;
TSqlRestAcquireExecution = TRestAcquireExecution;
TSqlRestBackgroundTimer = TRestBackgroundTimer;
{$endif PUREMORMOT2}
{ ************ TRestRunThreads Multi-Threading Process of a REST instance }
/// access to the Multi-Threading process of a TRest instance
TRestRunThreads = class(TSynLocked)
protected
fOwner: TRest;
fBackgroundTimer: TRestBackgroundTimer;
fShutdown: boolean;
public
/// initialize the threading process
constructor Create(aOwner: TRest); reintroduce;
/// notify that no new registration is allowed
procedure Shutdown;
/// finalize the threading process
destructor Destroy; override;
/// allows to safely execute a processing method in a background thread
// - returns a TSynBackgroundThreadMethod instance, ready to execute any
// background task via its RunAndWait() method
// - will properly call BeginCurrentThread/EndCurrentThread methods
// - you should supply some runtime information to name the thread, for
// proper debugging
function NewBackgroundThreadMethod(const Format: RawUtf8;
const Args: array of const): TSynBackgroundThreadMethod;
/// allows to safely execute a process at a given pace
// - returns a TSynBackgroundThreadProcess instance, ready to execute the
// supplied aOnProcess event in a loop, as aOnProcessMS periodic task
// - will properly call BeginCurrentThread/EndCurrentThread methods
// - you should supply some runtime information to name the thread, for
// proper debugging
function NewBackgroundThreadProcess(
const aOnProcess: TOnSynBackgroundThreadProcess; aOnProcessMS: cardinal;
const Format: RawUtf8; const Args: array of const;
aStats: TSynMonitorClass=nil): TSynBackgroundThreadProcess;
/// allows to safely execute a process in parallel
// - returns a TSynParallelProcess instance, ready to execute any task
// in parrallel in a thread-pool given by ThreadCount
// - will properly call BeginCurrentThread/EndCurrentThread methods
// - you should supply some runtime information to name the thread, for
// proper debugging
function NewParallelProcess(ThreadCount: integer; const Format: RawUtf8;
const Args: array of const): TSynParallelProcess;
/// define a task running on a periodic number of seconds in a background thread
// - could be used to run background maintenance or monitoring tasks on
// this TRest instance, at a low pace (typically every few minutes)
// - will instantiate and run a shared TSynBackgroundTimer instance for this
// TRest, so all tasks will share the very same thread
// - you can run BackgroundTimer.EnQueue or ExecuteNow methods to implement
// a FIFO queue, or force immediate execution of the process
// - will call BeginCurrentThread/EndCurrentThread as expected e.g. by logs
function TimerEnable(const aOnProcess: TOnSynBackgroundTimerProcess;
aOnProcessSecs: cardinal): TRestBackgroundTimer;
/// undefine a task running on a periodic number of seconds
// - should have been registered by a previous call to TimerEnable() method
// - returns true on success, false if the supplied task was not registered
function TimerDisable(const aOnProcess: TOnSynBackgroundTimerProcess): boolean;
/// execute once a task in the background, without waiting for it
function Once(const aOnProcess: TOnSynBackgroundTimerProcess): boolean;
/// will gather CPU and RAM information in a background thread
// - you can specify the update frequency, in seconds
// - access to the information via the returned instance, which maps
// the TSystemUse.Current class function
// - do nothing if global TSystemUse.Current was already assigned
function SystemUseTrack(periodSec: integer = 10): TSystemUse;
/// low-level access with optional initialization of the associated timer
// - this function is thread-safe
function EnsureBackgroundTimerExists: TRestBackgroundTimer;
/// you can call this method in TThread.Execute to ensure that
// the thread will be taken into account during process
// - this method will redirect TRestServer.OnBeginCurrentThread
procedure BeginCurrentThread(Sender: TThread);
/// you can call this method just before a thread is finished to ensure
// e.g. that the associated external DB connection will be released
// - this method will redirect TRestServer.OnEndCurrentThread
procedure EndCurrentThread(Sender: TThread);
/// define asynchronous execution of interface methods in a background thread
// - this class allows to implements any interface via a fake class, which will
// redirect all methods calls into calls of another interface, but as a FIFO
// in a background thread, shared with TimerEnable/TimerDisable process
// - it is an elegant resolution to the most difficult implementation
// problem of SOA callbacks, which is to avoid race condition on reentrance,
// e.g. if a callback is run from a thread, and then the callback code try
// to execute something in the context of the initial thread, protected
// by a critical section (mutex)
// - is a wrapper around BackgroundTimer.AsyncRedirect()
procedure AsyncRedirect(const aGuid: TGuid;
const aDestinationInterface: IInvokable; out aCallbackInterface;
const aOnResult: TOnAsyncRedirectResult = nil); overload;
/// define asynchronous execution of interface methods in a background thread
// - this class allows to implements any interface via a fake class, which will
// redirect all methods calls into calls of another interface, but as a FIFO
// in a background thread, shared with TimerEnable/TimerDisable process
// - it is an elegant resolution to the most difficult implementation
// problem of SOA callbacks, which is to avoid race condition on reentrance,
// e.g. if a callback is run from a thread, and then the callback code try
// to execute something in the context of the initial thread, protected
// by a critical section (mutex)
// - is a wrapper around BackgroundTimer.AsyncRedirect()
procedure AsyncRedirect(const aGuid: TGuid;
const aDestinationInstance: TInterfacedObject; out aCallbackInterface;
const aOnResult: TOnAsyncRedirectResult = nil); overload;
/// allows background garbage collection of specified RawUtf8 interning
// - will run Interning.Clean(2) every 5 minutes by default
// - set InterningMaxRefCount=0 to disable process of the Interning instance
// - note that InterningMaxRefCount and PeriodMinutes parameters (if not 0),
// are common for all TRawUtf8Interning instances (the latest value wins)
// - you may e.g. run the following to clean up TDocVariant interned RawUtf8:
// ! aRest.Run.AsyncInterning(DocVariantType.InternNames);
// ! aRest.Run.AsyncInterning(DocVariantType.InternValues);
procedure AsyncInterning(Interning: TRawUtf8Interning;
InterningMaxRefCount: integer = 2; PeriodMinutes: integer = 5);
/// define redirection of interface methods calls in one or several instances
// - this class allows to implements any interface via a fake class, which
// will redirect all methods calls to one or several other interfaces
// - returned aCallbackInterface will redirect all its methods (identified
// by aGuid) into an internal list handled by IMultiCallbackRedirect.Redirect
// - typical use is thefore:
// ! fSharedCallback: IMyService;
// ! fSharedCallbacks: IMultiCallbackRedirect;
// ! ...
// ! if fSharedCallbacks = nil then
// ! begin
// ! fSharedCallbacks := aRest.Run.MultiRedirect(IMyService, fSharedCallback);
// ! aServices.SubscribeForEvents(fSharedCallback);
// ! end;
// ! fSharedCallbacks.Redirect(TMyCallback.Create,[]);
// ! // now each time fSharedCallback receive one event, all callbacks
// ! // previously registered via Redirect() will receive it
// ! ...
// ! fSharedCallbacks := nil; // will stop redirection
// ! // and unregister callbacks, if needed
function MultiRedirect(const aGuid: TGuid; out aCallbackInterface;
aCallBackUnRegisterNeeded: boolean = true): IMultiCallbackRedirect; overload;
/// low-level access to the associated timer
// - may contain nil if EnsureBackgroundTimerExists has not yet been called
property BackgroundTimer: TRestBackgroundTimer
read fBackgroundTimer;
end;
{ ************ TRest Abstract Parent Class }
/// Exception class raised on TRest issues
ERestException = class(ESynException);
/// class-reference type (metaclass) of a TRest kind
TRestClass = class of TRest;
/// a dynamic array of TRest instances
TRestDynArray = array of TRest;
/// a dynamic array of TRest instances, owning the instances
TRestObjArray = array of TRest;
/// abstract REpresentational State Transfer (REST) client/server class
// - see Orm: IRestOrm, Services: TServiceContainer and Run: TRestRunThreads
// main properties for its actual REST-oriented process
// - in PUREMORMOT2 mode, all direct ORM or threading methods are hidden
// - is a TInterfaceResolver so is able to resolve IRestOrm
// - do NOT use this abstract class, but one of its fully implemented children
TRest = class(TInterfaceResolver)
protected
fOrm: IRestOrm;
fOrmInstance: TRestOrmParent; // is a TRestOrm from mormot.orm.rest.pas
fModel: TOrmModel;
fServices: TServiceContainer;
fRun: TRestRunThreads;
fLogClass: TSynLogClass;
fLogFamily: TSynLogFamily;
fLogLevel: TSynLogLevels;
fServerTimestampCacheTix: cardinal;
fAcquireExecution: TRestAcquireExecutions;
fPrivateGarbageCollector: TSynObjectList;
fServerTimestampOffset: TDateTime;
fServerTimestampCacheValue: TTimeLogBits;
function TryResolve(aInterface: PRttiInfo; out Obj): boolean; override;
procedure SetLogClass(aClass: TSynLogClass); virtual;
/// wrapper methods to access fAcquireExecution[]
function GetAcquireExecutionMode(
Cmd: TRestServerUriContextCommand): TRestServerAcquireMode;
procedure SetAcquireExecutionMode(
Cmd: TRestServerUriContextCommand; Value: TRestServerAcquireMode);
function GetAcquireExecutionLockedTimeOut(
Cmd: TRestServerUriContextCommand): cardinal;
procedure SetAcquireExecutionLockedTimeOut(
Cmd: TRestServerUriContextCommand; Value: cardinal);
/// any overriden TRest class should call it in the initialization section
class procedure RegisterClassNameForDefinition;
/// ensure the thread will be taken into account during process
// - will redirect to fOrmInstance: TRestOrmParent corresponding methods
procedure OnBeginCurrentThread(Sender: TThread); virtual;
procedure OnEndCurrentThread(Sender: TThread); virtual;
procedure OnRestBackgroundTimerCreate; virtual;
public
/// initialize the class, and associate it to a specified database Model
constructor Create(aModel: TOrmModel); virtual;
// inherited classes should unserialize the other aDefinition properties by
// overriding this method, in a reverse logic to overriden DefinitionTo()
constructor RegisteredClassCreateFrom(aModel: TOrmModel;
aDefinition: TSynConnectionDefinition;
aServerHandleAuthentication: boolean); virtual;
/// release internal used instances
// - e.g. release associated TOrmModel and TServiceContainer
destructor Destroy; override;
/// called by TRestOrm.Create overriden constructor to set fOrm from IRestOrm
procedure SetOrmInstance(aORM: TRestOrmParent); virtual;
/// save the TRest properties into a persistent storage object
// - you can then use TRest.CreateFrom() to re-instantiate it
// - current Definition.Key value will be used for the password encryption
// - this default implementation will set the class name in Definition.Kind:
// inherited classes should override this method and serialize other
// properties, then override RegisteredClassCreateFrom() protected method
// to initiate the very same instance
procedure DefinitionTo(Definition: TSynConnectionDefinition); virtual;
/// save the properties into a JSON file
// - you can then use TRest.CreateFromJson() to re-instantiate it
// - you can specify a custom Key, if the default is not enough for you
function DefinitionToJson(Key: cardinal = 0): RawUtf8;
/// save the properties into a JSON file
// - you can then use TRest.CreateFromFile() to re-instantiate it
// - you can specify a custom Key, if the default is not enough for you
procedure DefinitionToFile(const aJsonFile: TFileName; aKey: cardinal = 0);
/// create a new TRest instance from its Model and stored values
// - aDefinition.Kind will define the actual class which will be
// instantiated: currently TRestServerFullMemory, TRestServerDB,
// TRestClientUriNamedPipe, TRestClientUriMessage,
// TRestHttpClientSocket, TRestHttpClientWinINet, TRestHttpClientWinHttp,
// and TRestHttpClientCurl classes are recognized by this method
// - then other aDefinition fields will be used to refine the instance:
// please refer to each overriden DefinitionTo() method documentation
// - use TRestMongoDBCreate() and/or TRestExternalDBCreate() instead
// to create a TRest instance will all tables defined as external when
// aDefinition.Kind is 'MongoDB' or a TSqlDBConnectionProperties class
// - will raise an exception if the supplied definition are not valid
class function CreateFrom(aModel: TOrmModel;
aDefinition: TSynConnectionDefinition; aServerHandleAuthentication: boolean): TRest;
/// try to create a new TRest instance from its Model and stored values
// - will return nil if the supplied definition are not valid
// - if the newly created instance is a TRestServer, will force the
// supplied aServerHandleAuthentication parameter to enable authentication
class function CreateTryFrom(aModel: TOrmModel;
aDefinition: TSynConnectionDefinition; aServerHandleAuthentication: boolean): TRest;
/// create a new TRest instance from its Model and JSON stored values
// - aDefinition.Kind will define the actual class which will be instantiated
// - you can specify a custom Key, if the default is not safe enough for you
class function CreateFromJson(aModel: TOrmModel;
const aJsonDefinition: RawUtf8; aServerHandleAuthentication: boolean;
aKey: cardinal = 0): TRest;
/// create a new TRest instance from its Model and a JSON file
// - aDefinition.Kind will define the actual class which will be instantiated
// - you can specify a custom Key, if the default is not safe enough for you
class function CreateFromFile(aModel: TOrmModel;
const aJsonFile: TFileName; aServerHandleAuthentication: boolean;
aKey: cardinal = 0): TRest;
/// retrieve the registered class from the aDefinition.Kind string
class function ClassFrom(aDefinition: TSynConnectionDefinition): TRestClass;
/// ease logging of some text in the context of the current TRest
procedure InternalLog(const Text: RawUtf8; Level: TSynLogLevel); overload;
{$ifdef HASINLINE} inline; {$endif}
/// ease logging of some text in the context of the current TRest
procedure InternalLog(const Format: RawUtf8; const Args: array of const;
Level: TSynLogLevel = sllTrace); overload;
/// ease logging of method enter/leave in the context of the current TRest
function Enter(const TextFmt: RawUtf8; const TextArgs: array of const;
aInstance: TObject = nil): ISynLog;
/// internal method to retrieve the current Session TAuthUser.ID
function GetCurrentSessionUserID: TID; virtual; abstract;
/// retrieve the server time stamp
// - default implementation will use an internal Offset to compute
// the value from PC time (i.e. NowUtc+Offset as TTimeLog)
// - inherited classes may override this method, or set the appropriate
// value in Offset field
function GetServerTimestamp(tix64: Int64): TTimeLog; virtual;
/// set the server time stamp offset from the given date/time
// - if Value is 0, the current local UTC time will be used
procedure SetServerTimestamp(const Value: TTimeLog);
/// main access to the IRestOrm methods of this instance
property Orm: IRestOrm
read fOrm;
/// low-level access to the associated Data Model
property Model: TOrmModel
read fModel;
/// access to the interface-based services list
// - may be nil if no service interface has been registered yet: so be
// aware that the following line may trigger an access violation if
// no ICalculator is defined on server side:
// ! if fServer.Services['Calculator'].Get(Calc)) then
// ! ...
// - safer typical use, following the DI/IoC pattern, and which will not
// trigger any access violation if Services=nil, could be:
// ! if fServer.Services.Resolve(ICalculator, Calc) then
// ! ...
property Services: TServiceContainer
read fServices;
/// access or initialize the internal IoC resolver, used for interface-based
// remote services, and more generaly any Services.Resolve() call
// - create and initialize the internal TServiceContainer if no service
// interface has been registered yet
// - may be used to inject some dependencies, which are not interface-based
// remote services, but internal IoC, without the ServiceRegister()
// or ServiceDefine() methods - e.g.
// ! aRest.ServiceContainer.InjectResolver([TInfraRepoUserFactory.Create(aRest)], true);
// - overriden methods will return TServiceContainerClient or
// TServiceContainerServer instances, on TRestClient or TRestServer
function ServiceContainer: TServiceContainer; virtual; abstract;
/// internal procedure called to implement TServiceContainer.Release
procedure ServicesRelease(Caller: TServiceContainer);
/// access to the Multi-Threading process of this instance
property Run: TRestRunThreads
read fRun;
/// how this class execute its internal commands
// - by default, TRestServer.Uri() will lock for Write ORM according to
// AcquireWriteMode (i.e. AcquireExecutionMode[execOrmWrite]=amLocked) and
// other operations won't be protected (for better scaling)
// - you can tune this behavior by setting this property to the expected
// execution mode, e.g. execute all method-based services in a dedicated
// thread via
// ! aServer.AcquireExecutionMode[execSoaByMethod] := amBackgroundThread;
// - if you use external DB and a custom ConnectionTimeOutMinutes value,
// both read and write access should be locked, so you should set:
// ! aServer.AcquireExecutionMode[execOrmGet] := am***;
// ! aServer.AcquireExecutionMode[execOrmWrite] := am***;
// here, safe blocking am*** modes are any mode but amUnlocked, i.e. either
// amLocked, amBackgroundThread, amBackgroundOrmSharedThread or amMainThread
property AcquireExecutionMode[Cmd: TRestServerUriContextCommand]: TRestServerAcquireMode
read GetAcquireExecutionMode write SetAcquireExecutionMode;
/// the time (in milli seconds) to try locking internal commands of this class
// - this value is used only for AcquireExecutionMode[*]=amLocked
// - by default, TRestServer.Uri() will lock for Write ORM according to
// AcquireWriteTimeOut (i.e. AcquireExecutionLockedTimeOut[execOrmWrite])
// and other operations won't be locked nor have any time out set
property AcquireExecutionLockedTimeOut[Cmd: TRestServerUriContextCommand]: cardinal
read GetAcquireExecutionLockedTimeOut write SetAcquireExecutionLockedTimeOut;
/// how this class will handle write access to the database
// - is a common wrapper to AcquireExecutionMode[execOrmWrite] property
// - default amLocked mode will wait up to AcquireWriteTimeOut milli seconds
// to have a single access to the server write ORM methods
// - amBackgroundThread will execute the write methods in a queue, in a
// dedicated unique thread (which can be convenient, especially for
// external database transaction process)
// - amBackgroundOrmSharedThread will execute all ORM methods in a queue, in
// a dedicated unique thread, shared for both execOrmWrite and execOrmGet,
// but still dedicated for execSoaByMethod and execSoaByInterface
// - a slower alternative to amBackgroundThread may be amMainThread
// - you can set amUnlocked for a concurrent write access, but be aware
// that it may lead into multi-thread race condition issues, depending on
// the database engine used
property AcquireWriteMode: TRestServerAcquireMode index execOrmWrite
read GetAcquireExecutionMode write SetAcquireExecutionMode;
/// the time (in milli seconds) which the class will wait for acquiring a
// write acccess to the database, when AcquireWriteMode is amLocked
// - is a common wrapper to AcquireExecutionLockedTimeOut[execOrmWrite]
// - in order to handle safe transactions and multi-thread safe writing, the
// server will identify transactions using the client Session ID: this
// property will set the time out wait period
// - default value is 5000, i.e. TRestServer.Uri will wait up to 5 seconds
// in order to acquire the right to write on the database before returning
// a "408 Request Time-out" status error
property AcquireWriteTimeOut: cardinal index execOrmWrite
read GetAcquireExecutionLockedTimeOut write SetAcquireExecutionLockedTimeOut;
/// low-level access to the execution mode of the ORM and SOA process
property AcquireExecution: TRestAcquireExecutions
read fAcquireExecution;
/// a local "Garbage collector" list, for some classes instances which must
// live during the whole TRestServer process
// - is used internally by the class, but can be used for business code
property PrivateGarbageCollector: TSynObjectList
read fPrivateGarbageCollector;
/// access to the TSynLog class used for logging
// - equals TSynLog by default - but you could change it to a custom class
property LogClass: TSynLogClass
read fLogClass write SetLogClass;
/// access to the associate TSynLog class familly
property LogFamily: TSynLogFamily
read fLogFamily;
/// access to the associate TSynLog class events
property LogLevel: TSynLogLevels
read fLogLevel;
{$ifndef PUREMORMOT2}
// backward compatibility redirections to the homonymous IRestOrm methods
// see IRestOrm documentation for the proper use information
public
function TableRowCount(Table: TOrmClass): Int64;
function TableHasRows(Table: TOrmClass): boolean;
function TableMaxID(Table: TOrmClass): TID;
function MemberExists(Table: TOrmClass; ID: TID): boolean;
function OneFieldValue(Table: TOrmClass;
const FieldName, WhereClause: RawUtf8): RawUtf8; overload;
function OneFieldValueInt64(Table: TOrmClass;
const FieldName, WhereClause: RawUtf8; Default: Int64 = 0): Int64;
function OneFieldValue(Table: TOrmClass; const FieldName: RawUtf8;
const FormatSqlWhere: RawUtf8; const BoundsSqlWhere: array of const): RawUtf8; overload;
function OneFieldValue(Table: TOrmClass; const FieldName: RawUtf8;
const WhereClauseFmt: RawUtf8; const Args, Bounds: array of const): RawUtf8; overload;
function OneFieldValue(Table: TOrmClass; const FieldName: RawUtf8;
const WhereClauseFmt: RawUtf8; const Args, Bounds: array of const;
out Data: Int64): boolean; overload;
function OneFieldValue(Table: TOrmClass; const FieldName: RawUtf8;
WhereID: TID): RawUtf8; overload;
function MultiFieldValue(Table: TOrmClass;
const FieldName: array of RawUtf8; var FieldValue: array of RawUtf8;
const WhereClause: RawUtf8): boolean; overload;
function MultiFieldValue(Table: TOrmClass;
const FieldName: array of RawUtf8; var FieldValue: array of RawUtf8;
WhereID: TID): boolean; overload;
function OneFieldValues(Table: TOrmClass; const FieldName: RawUtf8;
const WhereClause: RawUtf8; out Data: TRawUtf8DynArray): boolean; overload;
function OneFieldValues(Table: TOrmClass; const FieldName: RawUtf8;
const WhereClause: RawUtf8; var Data: TInt64DynArray;
SQL: PRawUtf8 = nil): boolean; overload;
function OneFieldValues(Table: TOrmClass; const FieldName: RawUtf8;
const WhereClause: RawUtf8 = ''; const Separator: RawUtf8 = ','): RawUtf8; overload;
function OneFieldValues(Table: TOrmClass; const FieldName, WhereClause:
RawUtf8; Strings: TStrings; IDToIndex: PID = nil): boolean; overload;
function MultiFieldValues(Table: TOrmClass; const FieldNames: RawUtf8;
const WhereClause: RawUtf8 = ''): TOrmTable; overload;
function MultiFieldValues(Table: TOrmClass; const FieldNames: RawUtf8;
const WhereClauseFormat: RawUtf8; const BoundsSqlWhere: array of const): TOrmTable; overload;
function MultiFieldValues(Table: TOrmClass; const FieldNames: RawUtf8;
const WhereClauseFormat: RawUtf8; const Args, Bounds: array of const): TOrmTable; overload;
function FtsMatch(Table: TOrmFts3Class; const WhereClause: RawUtf8;
var DocID: TIDDynArray): boolean; overload;
function FtsMatch(Table: TOrmFts3Class; const MatchClause: RawUtf8;
var DocID: TIDDynArray; const PerFieldWeight: array of double;
limit: integer = 0; offset: integer = 0): boolean; overload;
function MainFieldValue(Table: TOrmClass; ID: TID;
ReturnFirstIfNoUnique: boolean = false): RawUtf8;
function MainFieldID(Table: TOrmClass; const Value: RawUtf8): TID;
function MainFieldIDs(Table: TOrmClass; const Values: array of RawUtf8;
out IDs: TIDDynArray): boolean;
function Retrieve(const SqlWhere: RawUtf8; Value: TOrm;
const FieldsCsv: RawUtf8 = ''): boolean; overload;
function Retrieve(const WhereClauseFmt: RawUtf8;
const Args, Bounds: array of const; Value: TOrm;
const FieldsCsv: RawUtf8 = ''): boolean; overload;
function Retrieve(aID: TID; Value: TOrm;
ForUpdate: boolean = false): boolean; overload;
function Retrieve(Reference: TRecordReference;
ForUpdate: boolean = false): TOrm; overload;
function Retrieve(aPublishedRecord, aValue: TOrm): boolean; overload;
function RetrieveList(Table: TOrmClass;
const FormatSqlWhere: RawUtf8; const BoundsSqlWhere: array of const;
const FieldsCsv: RawUtf8 = ''): TObjectList; overload;
function RetrieveListJson(Table: TOrmClass;
const FormatSqlWhere: RawUtf8; const BoundsSqlWhere: array of const;
const FieldsCsv: RawUtf8 = ''; aForceAjax: boolean = false): RawJson; overload;
function RetrieveListJson(Table: TOrmClass;
const SqlWhere: RawUtf8; const FieldsCsv: RawUtf8 = '';
aForceAjax: boolean = false): RawJson; overload;
function RetrieveDocVariantArray(Table: TOrmClass;
const ObjectName, FieldsCsv: RawUtf8;
FirstRecordID: PID = nil; LastRecordID: PID = nil): variant; overload;
function RetrieveDocVariantArray(Table: TOrmClass;
const ObjectName: RawUtf8; const FormatSqlWhere: RawUtf8;
const BoundsSqlWhere: array of const; const FieldsCsv: RawUtf8;
FirstRecordID: PID = nil; LastRecordID: PID = nil): variant; overload;
function RetrieveOneFieldDocVariantArray(Table: TOrmClass;
const FieldName, FormatSqlWhere: RawUtf8;
const BoundsSqlWhere: array of const): variant;
function RetrieveDocVariant(Table: TOrmClass;
const FormatSqlWhere: RawUtf8; const BoundsSqlWhere: array of const;
const FieldsCsv: RawUtf8): variant;
function RetrieveListObjArray(var ObjArray; Table: TOrmClass;
const FormatSqlWhere: RawUtf8; const BoundsSqlWhere: array of const;
const FieldsCsv: RawUtf8 = ''): boolean;
procedure AppendListAsJsonArray(Table: TOrmClass;
const FormatSqlWhere: RawUtf8; const BoundsSqlWhere: array of const;
const OutputFieldName: RawUtf8; W: TOrmWriter;
const FieldsCsv: RawUtf8 = '');
function RTreeMatch(DataTable: TOrmClass;
const DataTableBlobFieldName: RawUtf8; RTreeTable: TOrmRTreeClass;
const DataTableBlobField: RawByteString; var DataID: TIDDynArray): boolean;
function ExecuteList(const Tables: array of TOrmClass;
const SQL: RawUtf8): TOrmTable;
function ExecuteJson(const Tables: array of TOrmClass;
const SQL: RawUtf8; ForceAjax: boolean = false;
ReturnedRowCount: PPtrInt = nil): RawJson;
function Execute(const aSql: RawUtf8): boolean;
function ExecuteFmt(const SqlFormat: RawUtf8;
const Args: array of const): boolean; overload;
function ExecuteFmt(const SqlFormat: RawUtf8;
const Args, Bounds: array of const): boolean; overload;
function UnLock(Table: TOrmClass; aID: TID): boolean; overload;
function UnLock(Rec: TOrm): boolean; overload;
function Add(Value: TOrm; SendData: boolean;
ForceID: boolean = false; DoNotAutoComputeFields: boolean = false): TID; overload;
function Add(Value: TOrm; const CustomCsvFields: RawUtf8;
ForceID: boolean = false; DoNotAutoComputeFields: boolean = false): TID; overload;
function Add(Value: TOrm; const CustomFields: TFieldBits;
ForceID: boolean = false; DoNotAutoComputeFields: boolean = false): TID; overload;
function AddWithBlobs(Value: TOrm;
ForceID: boolean = false; DoNotAutoComputeFields: boolean = false): TID;
function AddSimple(aTable: TOrmClass;
const aSimpleFields: array of const; ForcedID: TID = 0): TID;
function Update(Value: TOrm; const CustomFields: TFieldBits = [];
DoNotAutoComputeFields: boolean = false): boolean; overload;
function Update(Value: TOrm; const CustomCsvFields: RawUtf8;
DoNotAutoComputeFields: boolean = false): boolean; overload;
function Update(aTable: TOrmClass; aID: TID;
const aSimpleFields: array of const): boolean; overload;
function AddOrUpdate(Value: TOrm; ForceID: boolean = false): TID;
function UpdateField(Table: TOrmClass; ID: TID;
const FieldName: RawUtf8; const FieldValue: array of const): boolean; overload;
function UpdateField(Table: TOrmClass; const WhereFieldName: RawUtf8;
const WhereFieldValue: array of const; const FieldName: RawUtf8;
const FieldValue: array of const): boolean; overload;
function UpdateField(Table: TOrmClass; ID: TID;
const FieldName: RawUtf8; const FieldValue: variant): boolean; overload;
function UpdateField(Table: TOrmClass;
const WhereFieldName: RawUtf8; const WhereFieldValue: variant;
const FieldName: RawUtf8; const FieldValue: variant): boolean; overload;
function UpdateField(Table: TOrmClass; const IDs: array of TID;
const FieldName: RawUtf8; const FieldValue: variant): boolean; overload;
function UpdateFieldIncrement(Table: TOrmClass; ID: TID;
const FieldName: RawUtf8; Increment: Int64 = 1): boolean;
function RecordCanBeUpdated(Table: TOrmClass; ID: TID;
Action: TOrmEvent; ErrorMsg: PRawUtf8 = nil): boolean;
function Delete(Table: TOrmClass; ID: TID): boolean; overload;
function Delete(Table: TOrmClass; const SqlWhere: RawUtf8): boolean; overload;
function Delete(Table: TOrmClass; const FormatSqlWhere: RawUtf8;
const BoundsSqlWhere: array of const): boolean; overload;
function RetrieveBlob(Table: TOrmClass; aID: TID; const BlobFieldName: RawUtf8;
out BlobData: RawBlob): boolean; overload;
function RetrieveBlob(Table: TOrmClass; aID: TID; const BlobFieldName: RawUtf8;
out BlobStream: TCustomMemoryStream): boolean; overload;
function UpdateBlob(Table: TOrmClass; aID: TID;
const BlobFieldName: RawUtf8; const BlobData: RawBlob): boolean; overload;
function UpdateBlob(Table: TOrmClass; aID: TID;
const BlobFieldName: RawUtf8; BlobData: TStream): boolean; overload;
function UpdateBlob(Table: TOrmClass; aID: TID;
const BlobFieldName: RawUtf8; BlobData: pointer; BlobSize: integer): boolean; overload;
function UpdateBlobFields(Value: TOrm): boolean;
function RetrieveBlobFields(Value: TOrm): boolean;
function TransactionBegin(aTable: TOrmClass; SessionID: cardinal): boolean;
function TransactionActiveSession: cardinal;
procedure Commit(SessionID: cardinal; RaiseException: boolean = false);
procedure RollBack(SessionID: cardinal);
procedure WriteLock;
procedure WriteUnLock;
function BatchSend(Batch: TRestBatch; var Results: TIDDynArray): integer; overload;
function BatchSend(Batch: TRestBatch): integer; overload;
function AsyncBatchStart(Table: TOrmClass; SendSeconds: integer;
PendingRowThreshold: integer = 500; AutomaticTransactionPerRow: integer = 1000;
Options: TRestBatchOptions = [boExtendedJson]): boolean;
function AsyncBatchStop(Table: TOrmClass): boolean;
function AsyncBatchAdd(Value: TOrm; SendData: boolean;
ForceID: boolean = false; const CustomFields: TFieldBits = [];
DoNotAutoComputeFields: boolean = false): integer;
function AsyncBatchRawAdd(Table: TOrmClass; const SentData: RawUtf8): integer;
procedure AsyncBatchRawAppend(Table: TOrmClass; SentData: TJsonWriter);
function AsyncBatchUpdate(Value: TOrm; const CustomFields: TFieldBits = [];
DoNotAutoComputeFields: boolean = false): integer;
function AsyncBatchDelete(Table: TOrmClass; ID: TID): integer;
function Cache: TOrmCache;
function CacheOrNil: TOrmCache;
function CacheWorthItForTable(aTableIndex: cardinal): boolean;
public
// TRestRunThreads compatibility methods
function NewBackgroundThreadMethod(const Format: RawUtf8;
const Args: array of const): TSynBackgroundThreadMethod;
function NewBackgroundThreadProcess(const aOnProcess: TOnSynBackgroundThreadProcess;
aOnProcessMS: cardinal; const Format: RawUtf8; const Args: array of const;
aStats: TSynMonitorClass=nil): TSynBackgroundThreadProcess;
function NewParallelProcess(ThreadCount: integer; const Format: RawUtf8;
const Args: array of const): TSynParallelProcess;
function TimerEnable(const aOnProcess: TOnSynBackgroundTimerProcess;
aOnProcessSecs: cardinal): TRestBackgroundTimer;
function TimerDisable(const aOnProcess: TOnSynBackgroundTimerProcess): boolean;
function SystemUseTrack(periodSec: integer = 10): TSystemUse;
function EnsureBackgroundTimerExists: TRestBackgroundTimer;
procedure BeginCurrentThread(Sender: TThread); virtual;
procedure EndCurrentThread(Sender: TThread); virtual;
procedure AsyncRedirect(const aGuid: TGuid;
const aDestinationInterface: IInvokable; out aCallbackInterface;
const aOnResult: TOnAsyncRedirectResult = nil); overload;
procedure AsyncRedirect(const aGuid: TGuid;
const aDestinationInstance: TInterfacedObject; out aCallbackInterface;
const aOnResult: TOnAsyncRedirectResult = nil); overload;
procedure AsyncInterning(Interning: TRawUtf8Interning;
InterningMaxRefCount: integer = 2; PeriodMinutes: integer = 5);
function MultiRedirect(const aGuid: TGuid; out aCallbackInterface;
aCallBackUnRegisterNeeded: boolean = true): IMultiCallbackRedirect; overload;
function BackgroundTimer: TRestBackgroundTimer;
{$ifdef HASINLINE}inline;{$endif}
{$endif PUREMORMOT2}
end;
// backward compatibility types redirections
{$ifndef PUREMORMOT2}
type
TSqlRest = TRest;
TSqlRestClass = TRestClass;
TSqlRestDynArray = TRestDynArray;
{$endif PUREMORMOT2}
function ToText(cmd: TRestServerUriContextCommand): PShortString; overload;
const
/// custom contract value to ignore contract validation from client side
// - you could set the aContractExpected parameter to this value for
// TRestClientUri.ServiceDefine or TRestClientUri.ServiceRegister
// so that the contract won't be checked with the server
// - it will be used e.g. if the remote server is not a mORMot server,
// but a plain REST/HTTP server - e.g. for public API notifications
SERVICE_CONTRACT_NONE_EXPECTED = '*';
{ ************ RESTful Authentication Support }
const
/// default hashed password set by TAuthGroup.InitializeTable for all users
// - contains TAuthUser.ComputeHashedPassword('synopse')
// - override AuthAdminDefaultPassword, AuthSupervisorDefaultPassword and
// AuthUserDefaultPassword values to follow your own application expectations
DEFAULT_HASH_SYNOPSE =
'67aeea294e1cb515236fd7829c55ec820ef888e8e221814d24d83b3dc4d825dd';
var
/// default timeout period set by TAuthGroup.InitializeTable for 'Admin' group
// - you can override this value to follow your own application expectations
AuthAdminGroupDefaultTimeout: integer = 10;
/// default timeout period set by TAuthGroup.InitializeTable for 'Supervisor' group
// - you can override this value to follow your own application expectations
// - note that clients will maintain the session alive using CacheFlush/_ping_
AuthSupervisorGroupDefaultTimeout: integer = 60;
/// default timeout period set by TAuthGroup.InitializeTable for 'User' group
// - you can override this value to follow your own application expectations
// - note that clients will maintain the session alive using CacheFlush/_ping_
AuthUserGroupDefaultTimeout: integer = 60;
/// default timeout period set by TAuthGroup.InitializeTable for 'Guest' group
// - you can override this value to follow your own application expectations
// - note that clients will maintain the session alive using CacheFlush/_ping_
AuthGuestGroupDefaultTimeout: integer = 60;
/// default hashed password set by TAuthGroup.InitializeTable for 'Admin' user
// - you can override this value to follow your own application expectations
AuthAdminDefaultPassword: RawUtf8 = DEFAULT_HASH_SYNOPSE;
/// default hashed password set by TAuthGroup.InitializeTable for 'Supervisor' user
// - you can override this value to follow your own application expectations
AuthSupervisorDefaultPassword: RawUtf8 = DEFAULT_HASH_SYNOPSE;
/// default hashed password set by TAuthGroup.InitializeTable for 'User' user
// - you can override this value to follow your own application expectations
AuthUserDefaultPassword: RawUtf8 = DEFAULT_HASH_SYNOPSE;
type
/// table containing the available user access rights for authentication
// - this class should be added to the TOrmModel, together with TAuthUser,
// to allow authentication support
// - you can inherit from it to add your custom properties to each user info:
// TOrmModel will search for any class inheriting from TAuthGroup to
// manage per-group authorization data
// - by default, it won't be accessible remotely by anyone
TAuthGroup = class(TOrm)
private
fIdent: RawUtf8;
fSessionTimeOut: integer;
fAccessRights: RawUtf8;
function GetOrmAccessRights: TOrmAccessRights;
procedure SetOrmAccessRights(const Value: TOrmAccessRights);
public
/// called when the associated table is created in the database
// - on a new database, if TAuthUser and TAuthGroup tables are defined
// in the associated TOrmModel, it this will add 'Admin', 'Supervisor',
// and 'User' rows in the AuthUser table (with 'synopse' as default password),
// and associated 'Admin', 'Supervisor', 'User' and 'Guest' groups, with the
// following access rights to the AuthGroup table:
// $ POSTSQL SELECTSQL Service AuthR AuthW TablesR TablesW
// $ Admin Yes Yes Yes Yes Yes Yes Yes
// $ Supervisor No Yes Yes Yes No Yes Yes
// $ User No No Yes No No Yes Yes
// $ Guest No No No No No Yes No
// - 'Admin' will be the only able to execute remote not SELECT SQL statements
// for POST commands (reSQL flag in TOrmAccessRights.AllowRemoteExecute) and
// modify the Auth tables (i.e. AuthUser and AuthGroup)
// - 'Admin' and 'Supervisor' will allow any SELECT SQL statements to be
// executed, even if the table can't be retrieved and checked (corresponding
// to the reSqlSelectWithoutTable flag)
// - 'User' won't have the reSqlSelectWithoutTable flag, nor the right
// to retrieve the Auth tables data for other users
// - 'Guest' won't have access to the interface-based remote JSON-RPC service
// (no reService flag), nor perform any modification to a table: in short,
// this is an ORM read-only limited user
// - you MUST override the default 'synopse' password to a custom value,
// or at least customize the global AuthAdminDefaultPassword,
// AuthSupervisorDefaultPassword, AuthUserDefaultPassword variables
// - of course, you can change and tune the settings of the AuthGroup and
// AuthUser tables, but only 'Admin' group users will be able to remotely
// modify the content of those two tables
class procedure InitializeTable(const Server: IRestOrmServer;
const FieldName: RawUtf8; Options: TOrmInitializeTableOptions); override;
/// corresponding TOrmAccessRights for this authentication group
// - content is converted into/from text format via AccessRight DB property
// (so it will be not fixed e.g. by the binary TOrmTableBits layout, i.e.
// the MAX_TABLES constant value)
// - if you want to call Edit on the returned value, use a temp variable:
// !var
// ! oar: TOrmAccessRights;
// !begin
// ! oar := Group.OrmAccessRights;
// ! oar.Edit(....);
// ! oar.Edit(....);
// ! Group.OrmAccessRights := oar;
// ! rest.Update(Group);
property OrmAccessRights: TOrmAccessRights
read GetOrmAccessRights write SetOrmAccessRights;
{$ifndef PUREMORMOT2}
property SqlAccessRights: TOrmAccessRights
read GetOrmAccessRights write SetOrmAccessRights;
{$endif PUREMORMOT2}
published
/// the access right identifier, ready to be displayed
// - the same identifier can be used only once (this column is marked as
// unique via a "stored AS_UNIQUE" (i.e. "stored false") attribute)
// - so you can retrieve a TAuthGroup ID from its identifier, as such:
// ! UserGroupID := fClient.MainFieldID(TAuthGroup,'User');
property Ident: RawUtf8 index 50
read fIdent write fIdent stored AS_UNIQUE;
/// the number of minutes a session is kept alive
property SessionTimeout: integer