├── .gitignore ├── Demo ├── Demo.dpr ├── Demo.dproj ├── Demo.res ├── MainU.dfm ├── MainU.pas ├── PersonU.pas ├── Pkg.Json.DTO.pas └── RootU.pas ├── LICENSE ├── ORM ├── DelphiRestOrm.ORM.IRequest.pas ├── DelphiRestOrm.ORM.Request.pas ├── DelphiRestOrm.ORM.Response.pas └── Helpers │ ├── DelphiRestOrm.ORM.Helper.Azure.pas │ ├── DelphiRestOrm.ORM.Helper.GenericListHelper.pas │ ├── DelphiRestOrm.ORM.Helper.ObjectContainer.pas │ └── DelphiRestOrm.ORM.Helper.ThreadingEx.pas ├── README.md └── RestServer ├── .vs └── RestServer │ ├── DesignTimeBuild │ └── .dtbcache.v2 │ ├── config │ └── applicationhost.config │ └── v16 │ └── .suo ├── Demo Data ├── Create Table.sql └── DemoData.json ├── RestServer.sln └── RestServer ├── Controllers └── ContactController.cs ├── Helpers ├── BasicAuthenticationHandler.cs └── ExtensionMethods.cs ├── Models ├── DemoDataContext.cs ├── Person.cs └── User.cs ├── Program.cs ├── Properties └── launchSettings.json ├── RestServer.csproj ├── RestServer.csproj.user ├── Services └── UserService.cs ├── Startup.cs ├── appsettings.Development.json └── appsettings.json /.gitignore: -------------------------------------------------------------------------------- 1 | # Uncomment these types if you want even more clean repository. But be careful. 2 | # It can make harm to an existing project source. Read explanations below. 3 | # 4 | # Resource files are binaries containing manifest, project icon and version info. 5 | # They can not be viewed as text or compared by diff-tools. Consider replacing them with .rc files. 6 | #*.res 7 | # 8 | # Type library file (binary). In old Delphi versions it should be stored. 9 | # Since Delphi 2009 it is produced from .ridl file and can safely be ignored. 10 | #*.tlb 11 | # 12 | # Diagram Portfolio file. Used by the diagram editor up to Delphi 7. 13 | # Uncomment this if you are not using diagrams or use newer Delphi version. 14 | #*.ddp 15 | # 16 | # Visual LiveBindings file. Added in Delphi XE2. 17 | # Uncomment this if you are not using LiveBindings Designer. 18 | #*.vlb 19 | # 20 | # Deployment Manager configuration file for your project. Added in Delphi XE2. 21 | # Uncomment this if it is not mobile development and you do not use remote debug feature. 22 | #*.deployproj 23 | # 24 | # C++ object files produced when C/C++ Output file generation is configured. 25 | # Uncomment this if you are not using external objects (zlib library for example). 26 | #*.obj 27 | # 28 | 29 | # Delphi compiler-generated binaries (safe to delete) 30 | *.exe 31 | *.dll 32 | *.bpl 33 | *.bpi 34 | *.dcp 35 | *.so 36 | *.apk 37 | *.drc 38 | *.map 39 | *.dres 40 | *.rsm 41 | *.tds 42 | *.dcu 43 | *.lib 44 | *.a 45 | *.o 46 | *.ocx 47 | 48 | # Delphi autogenerated files (duplicated info) 49 | *.cfg 50 | *.hpp 51 | *Resource.rc 52 | 53 | # Delphi local files (user-specific info) 54 | *.local 55 | *.identcache 56 | *.projdata 57 | *.tvsconfig 58 | *.dsk 59 | 60 | # Delphi history and backups 61 | __history/ 62 | __recovery/ 63 | *.~* 64 | 65 | # Castalia statistics file (since XE7 Castalia is distributed with Delphi) 66 | *.stat 67 | 68 | # Boss dependency manager vendor folder https://github.com/HashLoad/boss 69 | modules/ 70 | 71 | *.skincfg 72 | 73 | RestServer/RestServer/bin/ 74 | RestServer/RestServer/obj/ 75 | -------------------------------------------------------------------------------- /Demo/Demo.dpr: -------------------------------------------------------------------------------- 1 | program Demo; 2 | 3 | uses 4 | Vcl.Forms, 5 | MainU in 'MainU.pas' {Form59}, 6 | DelphiRestOrm.ORM.Request in '..\ORM\DelphiRestOrm.ORM.Request.pas', 7 | DelphiRestOrm.ORM.Helper.ObjectContainer in '..\ORM\Helpers\DelphiRestOrm.ORM.Helper.ObjectContainer.pas', 8 | DelphiRestOrm.ORM.IRequest in '..\ORM\DelphiRestOrm.ORM.IRequest.pas', 9 | PersonU in 'PersonU.pas', 10 | DelphiRestOrm.ORM.Response in '..\ORM\DelphiRestOrm.ORM.Response.pas', 11 | DelphiRestOrm.ORM.Helper.GenericListHelper in '..\ORM\Helpers\DelphiRestOrm.ORM.Helper.GenericListHelper.pas', 12 | DelphiRestOrm.ORM.Helper.Azure in '..\ORM\Helpers\DelphiRestOrm.ORM.Helper.Azure.pas', 13 | DelphiRestOrm.ORM.Helper.ThreadingEx in '..\ORM\Helpers\DelphiRestOrm.ORM.Helper.ThreadingEx.pas'; 14 | 15 | {$R *.res} 16 | 17 | begin 18 | ReportMemoryLeaksOnShutdown := True; 19 | Application.Initialize; 20 | Application.MainFormOnTaskbar := true; 21 | Application.CreateForm(TForm59, Form59); 22 | Application.Run; 23 | 24 | end. 25 | -------------------------------------------------------------------------------- /Demo/Demo.dproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | {7DD92F11-8410-406D-861F-FE71C43E6588} 4 | 19.0 5 | VCL 6 | True 7 | Debug 8 | Win32 9 | 1 10 | Application 11 | Demo.dpr 12 | 13 | 14 | true 15 | 16 | 17 | true 18 | Base 19 | true 20 | 21 | 22 | true 23 | Base 24 | true 25 | 26 | 27 | true 28 | Base 29 | true 30 | 31 | 32 | true 33 | Cfg_1 34 | true 35 | true 36 | 37 | 38 | true 39 | Base 40 | true 41 | 42 | 43 | true 44 | Cfg_2 45 | true 46 | true 47 | 48 | 49 | .\$(Platform)\$(Config) 50 | .\$(Platform)\$(Config) 51 | false 52 | false 53 | false 54 | false 55 | false 56 | System;Xml;Data;Datasnap;Web;Soap;Vcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples;Vcl.Shell;$(DCC_Namespace) 57 | $(BDS)\bin\delphi_PROJECTICON.ico 58 | $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png 59 | $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png 60 | Demo 61 | 62 | 63 | DBXSqliteDriver;DBXDb2Driver;vclactnband;vclFireDAC;tethering;FireDACADSDriver;cxLibraryRS27;dxRibbonCustomizationFormRS27;FireDACMSSQLDriver;vcltouch;vcldb;svn;dxPScxExtCommonRS27;cxTreeListRS27;dxPScxSchedulerLnkRS27;dxRichEditDocumentModelRS27;dxPsPrVwAdvRS27;FireDACDBXDriver;vclx;dxPSdxFCLnkRS27;dxSpellCheckerRS27;dxPDFViewerRS27;RESTBackendComponents;VCLRESTComponents;dxWizardControlRS27;cxSchedulerTreeBrowserRS27;vclie;bindengine;CloudService;dxBarExtDBItemsRS27;FireDACMySQLDriver;cxExportRS27;DataSnapClient;cxSchedulerWebServiceStorageRS27;bindcompdbx;IndyIPServer;DBXSybaseASEDriver;dxPScxPCProdRS27;IndySystem;dxHttpIndyRequestRS27;dxFlowChartRS27;dsnapcon;FireDACMSAccDriver;fmxFireDAC;FireDACInfxDriver;vclimg;dxmdsRS27;Jcl;dxPSPrVwRibbonRS27;dxCoreRS27;emshosting;DBXOdbcDriver;FireDACTDataDriver;FMXTee;cxSchedulerRibbonStyleEventEditorRS27;soaprtl;DbxCommonDriver;dxRichEditCoreRS27;dxCloudServiceLibraryRS27;xmlrtl;soapmidas;DataSnapNativeClient;fmxobj;rtl;emsserverresource;DbxClientDriver;dxdbtrRS27;DBXSybaseASADriver;dxdborRS27;dxPSDBTeeChartRS27;appanalytics;dxSpreadSheetCoreRS27;dxRichEditControlCoreRS27;dxGDIPlusRS27;IndyIPClient;dxFireDACServerModeRS27;dxFlowChartAdvancedCustomizeFormRS27;bindcompvcl;dxServerModeRS27;TeeUI;dxPSLnksRS27;dxorgcRS27;VclSmp;dxDBXServerModeRS27;FireDACODBCDriver;JclVcl;DataSnapIndy10ServerTransport;cxGridRS27;DataSnapProviderClient;FireDACMongoDBDriver;dxFireDACEMFRS27;dxtrmdRS27;dxSpreadSheetCoreConditionalFormattingDialogsRS27;RESTComponents;DataSnapServerMidas;DBXInterBaseDriver;dxSpreadSheetConditionalFormattingDialogsRS27;dxGaugeControlRS27;dxSpreadSheetReportDesignerRS27;dxSpreadSheetRS27;bindcompvclsmp;emsclientfiredac;DataSnapFireDAC;svnui;cxPivotGridOLAPRS27;dxPSRichEditControlLnkRS27;dxBarDBNavRS27;DBXMSSQLDriver;cxPivotGridRS27;dxADOServerModeRS27;DatasnapConnectorsFreePascal;bindcompfmx;DBXOracleDriver;inetdb;dxFlowChartDesignerRS27;FmxTeeUI;emsedge;fmx;FireDACIBDriver;fmxdae;vcledge;dbexpress;IndyCore;dxRibbonRS27;dxOrgChartAdvancedCustomizeFormRS27;dsnap;emsclient;DataSnapCommon;FireDACCommon;dxTileControlRS27;dxBarExtItemsRS27;DataSnapConnectors;cxSchedulerRS27;cxGridEMFRS27;soapserver;JclDeveloperTools;dxRichEditControlRS27;dxPScxTLLnkRS27;FireDACOracleDriver;DBXMySQLDriver;dxFlowChartLayoutsRS27;dxPSCoreRS27;dxADOEMFRS27;DBXFirebirdDriver;FireDACCommonODBC;FireDACCommonDriver;dxPScxPivotGridLnkRS27;inet;cxPivotGridChartRS27;IndyIPCommon;dxPSdxOCLnkRS27;vcl;FireDACDb2Driver;dxMapControlRS27;dxPSdxDBOCLnkRS27;dxComnRS27;dxPSdxGaugeControlLnkRS27;dxEMFRS27;TeeDB;FireDAC;cxTreeListdxBarPopupMenuRS27;dxPScxVGridLnkRS27;dxPSdxPDFViewerLnkRS27;dxPSdxSpreadSheetLnkRS27;FireDACSqliteDriver;FireDACPgDriver;FireDACASADriver;dxTabbedMDIRS27;Tee;DataSnapServer;vclwinx;FireDACDSDriver;dxPScxCommonRS27;CustomIPTransport;vcldsnap;dxPSTeeChartRS27;dxSkinsCoreRS27;dxPScxGridLnkRS27;bindcomp;DBXInformixDriver;cxVerticalGridRS27;cxSchedulerGridRS27;dxDockingRS27;dxNavBarRS27;dbxcds;adortl;dsnapxml;dbrtl;IndyProtocols;inetdbxpress;dxBarRS27;dxPSdxLCLnkRS27;dxPSdxMapControlLnkRS27;JclContainers;dxPSdxDBTVLnkRS27;fmxase;$(DCC_UsePackage) 64 | Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) 65 | Debug 66 | true 67 | CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= 68 | 1033 69 | $(BDS)\bin\default_app.manifest 70 | 71 | 72 | DBXSqliteDriver;DBXDb2Driver;vclactnband;vclFireDAC;tethering;FireDACADSDriver;cxLibraryRS27;dxRibbonCustomizationFormRS27;FireDACMSSQLDriver;vcltouch;vcldb;dxPScxExtCommonRS27;cxTreeListRS27;dxPScxSchedulerLnkRS27;dxRichEditDocumentModelRS27;dxPsPrVwAdvRS27;FireDACDBXDriver;vclx;dxPSdxFCLnkRS27;dxSpellCheckerRS27;dxPDFViewerRS27;RESTBackendComponents;VCLRESTComponents;dxWizardControlRS27;cxSchedulerTreeBrowserRS27;vclie;bindengine;CloudService;dxBarExtDBItemsRS27;FireDACMySQLDriver;cxExportRS27;DataSnapClient;cxSchedulerWebServiceStorageRS27;bindcompdbx;IndyIPServer;DBXSybaseASEDriver;dxPScxPCProdRS27;IndySystem;dxHttpIndyRequestRS27;dxFlowChartRS27;dsnapcon;FireDACMSAccDriver;fmxFireDAC;FireDACInfxDriver;vclimg;dxmdsRS27;dxPSPrVwRibbonRS27;dxCoreRS27;emshosting;DBXOdbcDriver;FireDACTDataDriver;FMXTee;cxSchedulerRibbonStyleEventEditorRS27;soaprtl;DbxCommonDriver;dxRichEditCoreRS27;dxCloudServiceLibraryRS27;xmlrtl;soapmidas;DataSnapNativeClient;fmxobj;rtl;emsserverresource;DbxClientDriver;dxdbtrRS27;DBXSybaseASADriver;dxdborRS27;dxPSDBTeeChartRS27;appanalytics;dxSpreadSheetCoreRS27;dxRichEditControlCoreRS27;dxGDIPlusRS27;IndyIPClient;dxFireDACServerModeRS27;dxFlowChartAdvancedCustomizeFormRS27;bindcompvcl;dxServerModeRS27;TeeUI;dxPSLnksRS27;dxorgcRS27;VclSmp;dxDBXServerModeRS27;FireDACODBCDriver;DataSnapIndy10ServerTransport;cxGridRS27;DataSnapProviderClient;FireDACMongoDBDriver;dxFireDACEMFRS27;dxtrmdRS27;dxSpreadSheetCoreConditionalFormattingDialogsRS27;RESTComponents;DataSnapServerMidas;DBXInterBaseDriver;dxSpreadSheetConditionalFormattingDialogsRS27;dxGaugeControlRS27;dxSpreadSheetReportDesignerRS27;dxSpreadSheetRS27;bindcompvclsmp;emsclientfiredac;DataSnapFireDAC;cxPivotGridOLAPRS27;dxPSRichEditControlLnkRS27;dxBarDBNavRS27;DBXMSSQLDriver;cxPivotGridRS27;dxADOServerModeRS27;DatasnapConnectorsFreePascal;bindcompfmx;DBXOracleDriver;inetdb;dxFlowChartDesignerRS27;FmxTeeUI;emsedge;fmx;FireDACIBDriver;fmxdae;vcledge;dbexpress;IndyCore;dxRibbonRS27;dxOrgChartAdvancedCustomizeFormRS27;dsnap;emsclient;DataSnapCommon;FireDACCommon;dxTileControlRS27;dxBarExtItemsRS27;DataSnapConnectors;cxSchedulerRS27;cxGridEMFRS27;soapserver;dxRichEditControlRS27;dxPScxTLLnkRS27;FireDACOracleDriver;DBXMySQLDriver;dxFlowChartLayoutsRS27;dxPSCoreRS27;dxADOEMFRS27;DBXFirebirdDriver;FireDACCommonODBC;FireDACCommonDriver;dxPScxPivotGridLnkRS27;inet;cxPivotGridChartRS27;IndyIPCommon;dxPSdxOCLnkRS27;vcl;FireDACDb2Driver;dxMapControlRS27;dxPSdxDBOCLnkRS27;dxComnRS27;dxPSdxGaugeControlLnkRS27;dxEMFRS27;TeeDB;FireDAC;cxTreeListdxBarPopupMenuRS27;dxPScxVGridLnkRS27;dxPSdxPDFViewerLnkRS27;dxPSdxSpreadSheetLnkRS27;FireDACSqliteDriver;FireDACPgDriver;FireDACASADriver;dxTabbedMDIRS27;Tee;DataSnapServer;vclwinx;FireDACDSDriver;dxPScxCommonRS27;CustomIPTransport;vcldsnap;dxPSTeeChartRS27;dxSkinsCoreRS27;dxPScxGridLnkRS27;bindcomp;DBXInformixDriver;cxVerticalGridRS27;cxSchedulerGridRS27;dxDockingRS27;dxNavBarRS27;dbxcds;adortl;dsnapxml;dbrtl;IndyProtocols;inetdbxpress;dxBarRS27;dxPSdxLCLnkRS27;dxPSdxMapControlLnkRS27;dxPSdxDBTVLnkRS27;fmxase;$(DCC_UsePackage) 73 | 74 | 75 | DEBUG;$(DCC_Define) 76 | true 77 | false 78 | true 79 | true 80 | true 81 | 82 | 83 | false 84 | true 85 | PerMonitorV2 86 | 87 | 88 | false 89 | RELEASE;$(DCC_Define) 90 | 0 91 | 0 92 | 93 | 94 | true 95 | PerMonitorV2 96 | 97 | 98 | 99 | MainSource 100 | 101 | 102 |
Form59
103 | dfm 104 |
105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | Cfg_2 115 | Base 116 | 117 | 118 | Base 119 | 120 | 121 | Cfg_1 122 | Base 123 | 124 |
125 | 126 | Delphi.Personality.12 127 | Application 128 | 129 | 130 | 131 | Demo.dpr 132 | 133 | 134 | 135 | True 136 | False 137 | 138 | 139 | 140 | 141 | Demo.exe 142 | true 143 | 144 | 145 | 146 | 147 | 1 148 | 149 | 150 | Contents\MacOS 151 | 1 152 | 153 | 154 | 0 155 | 156 | 157 | 158 | 159 | classes 160 | 1 161 | 162 | 163 | classes 164 | 1 165 | 166 | 167 | 168 | 169 | res\xml 170 | 1 171 | 172 | 173 | res\xml 174 | 1 175 | 176 | 177 | 178 | 179 | library\lib\armeabi-v7a 180 | 1 181 | 182 | 183 | 184 | 185 | library\lib\armeabi 186 | 1 187 | 188 | 189 | library\lib\armeabi 190 | 1 191 | 192 | 193 | 194 | 195 | library\lib\armeabi-v7a 196 | 1 197 | 198 | 199 | 200 | 201 | library\lib\mips 202 | 1 203 | 204 | 205 | library\lib\mips 206 | 1 207 | 208 | 209 | 210 | 211 | library\lib\armeabi-v7a 212 | 1 213 | 214 | 215 | library\lib\arm64-v8a 216 | 1 217 | 218 | 219 | 220 | 221 | library\lib\armeabi-v7a 222 | 1 223 | 224 | 225 | 226 | 227 | res\drawable 228 | 1 229 | 230 | 231 | res\drawable 232 | 1 233 | 234 | 235 | 236 | 237 | res\values 238 | 1 239 | 240 | 241 | res\values 242 | 1 243 | 244 | 245 | 246 | 247 | res\values-v21 248 | 1 249 | 250 | 251 | res\values-v21 252 | 1 253 | 254 | 255 | 256 | 257 | res\values 258 | 1 259 | 260 | 261 | res\values 262 | 1 263 | 264 | 265 | 266 | 267 | res\drawable 268 | 1 269 | 270 | 271 | res\drawable 272 | 1 273 | 274 | 275 | 276 | 277 | res\drawable-xxhdpi 278 | 1 279 | 280 | 281 | res\drawable-xxhdpi 282 | 1 283 | 284 | 285 | 286 | 287 | res\drawable-ldpi 288 | 1 289 | 290 | 291 | res\drawable-ldpi 292 | 1 293 | 294 | 295 | 296 | 297 | res\drawable-mdpi 298 | 1 299 | 300 | 301 | res\drawable-mdpi 302 | 1 303 | 304 | 305 | 306 | 307 | res\drawable-hdpi 308 | 1 309 | 310 | 311 | res\drawable-hdpi 312 | 1 313 | 314 | 315 | 316 | 317 | res\drawable-xhdpi 318 | 1 319 | 320 | 321 | res\drawable-xhdpi 322 | 1 323 | 324 | 325 | 326 | 327 | res\drawable-mdpi 328 | 1 329 | 330 | 331 | res\drawable-mdpi 332 | 1 333 | 334 | 335 | 336 | 337 | res\drawable-hdpi 338 | 1 339 | 340 | 341 | res\drawable-hdpi 342 | 1 343 | 344 | 345 | 346 | 347 | res\drawable-xhdpi 348 | 1 349 | 350 | 351 | res\drawable-xhdpi 352 | 1 353 | 354 | 355 | 356 | 357 | res\drawable-xxhdpi 358 | 1 359 | 360 | 361 | res\drawable-xxhdpi 362 | 1 363 | 364 | 365 | 366 | 367 | res\drawable-xxxhdpi 368 | 1 369 | 370 | 371 | res\drawable-xxxhdpi 372 | 1 373 | 374 | 375 | 376 | 377 | res\drawable-small 378 | 1 379 | 380 | 381 | res\drawable-small 382 | 1 383 | 384 | 385 | 386 | 387 | res\drawable-normal 388 | 1 389 | 390 | 391 | res\drawable-normal 392 | 1 393 | 394 | 395 | 396 | 397 | res\drawable-large 398 | 1 399 | 400 | 401 | res\drawable-large 402 | 1 403 | 404 | 405 | 406 | 407 | res\drawable-xlarge 408 | 1 409 | 410 | 411 | res\drawable-xlarge 412 | 1 413 | 414 | 415 | 416 | 417 | res\values 418 | 1 419 | 420 | 421 | res\values 422 | 1 423 | 424 | 425 | 426 | 427 | 1 428 | 429 | 430 | Contents\MacOS 431 | 1 432 | 433 | 434 | 0 435 | 436 | 437 | 438 | 439 | Contents\MacOS 440 | 1 441 | .framework 442 | 443 | 444 | Contents\MacOS 445 | 1 446 | .framework 447 | 448 | 449 | 0 450 | 451 | 452 | 453 | 454 | 1 455 | .dylib 456 | 457 | 458 | 1 459 | .dylib 460 | 461 | 462 | 1 463 | .dylib 464 | 465 | 466 | Contents\MacOS 467 | 1 468 | .dylib 469 | 470 | 471 | Contents\MacOS 472 | 1 473 | .dylib 474 | 475 | 476 | 0 477 | .dll;.bpl 478 | 479 | 480 | 481 | 482 | 1 483 | .dylib 484 | 485 | 486 | 1 487 | .dylib 488 | 489 | 490 | 1 491 | .dylib 492 | 493 | 494 | Contents\MacOS 495 | 1 496 | .dylib 497 | 498 | 499 | Contents\MacOS 500 | 1 501 | .dylib 502 | 503 | 504 | 0 505 | .bpl 506 | 507 | 508 | 509 | 510 | 0 511 | 512 | 513 | 0 514 | 515 | 516 | 0 517 | 518 | 519 | 0 520 | 521 | 522 | 0 523 | 524 | 525 | Contents\Resources\StartUp\ 526 | 0 527 | 528 | 529 | Contents\Resources\StartUp\ 530 | 0 531 | 532 | 533 | 0 534 | 535 | 536 | 537 | 538 | ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset 539 | 1 540 | 541 | 542 | ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset 543 | 1 544 | 545 | 546 | 547 | 548 | ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset 549 | 1 550 | 551 | 552 | ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset 553 | 1 554 | 555 | 556 | 557 | 558 | ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset 559 | 1 560 | 561 | 562 | ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset 563 | 1 564 | 565 | 566 | 567 | 568 | ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset 569 | 1 570 | 571 | 572 | ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset 573 | 1 574 | 575 | 576 | 577 | 578 | ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset 579 | 1 580 | 581 | 582 | ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset 583 | 1 584 | 585 | 586 | 587 | 588 | ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset 589 | 1 590 | 591 | 592 | ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset 593 | 1 594 | 595 | 596 | 597 | 598 | 1 599 | 600 | 601 | 1 602 | 603 | 604 | 605 | 606 | ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF 607 | 1 608 | 609 | 610 | ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF 611 | 1 612 | 613 | 614 | 615 | 616 | 1 617 | 618 | 619 | 1 620 | 621 | 622 | 623 | 624 | ..\ 625 | 1 626 | 627 | 628 | ..\ 629 | 1 630 | 631 | 632 | 633 | 634 | 1 635 | 636 | 637 | 1 638 | 639 | 640 | 1 641 | 642 | 643 | 644 | 645 | ..\$(PROJECTNAME).launchscreen 646 | 64 647 | 648 | 649 | ..\$(PROJECTNAME).launchscreen 650 | 64 651 | 652 | 653 | 654 | 655 | 1 656 | 657 | 658 | 1 659 | 660 | 661 | 1 662 | 663 | 664 | 665 | 666 | ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF 667 | 1 668 | 669 | 670 | 671 | 672 | ..\ 673 | 1 674 | 675 | 676 | ..\ 677 | 1 678 | 679 | 680 | 681 | 682 | Contents 683 | 1 684 | 685 | 686 | Contents 687 | 1 688 | 689 | 690 | 691 | 692 | Contents\Resources 693 | 1 694 | 695 | 696 | Contents\Resources 697 | 1 698 | 699 | 700 | 701 | 702 | library\lib\armeabi-v7a 703 | 1 704 | 705 | 706 | library\lib\arm64-v8a 707 | 1 708 | 709 | 710 | 1 711 | 712 | 713 | 1 714 | 715 | 716 | 1 717 | 718 | 719 | 1 720 | 721 | 722 | Contents\MacOS 723 | 1 724 | 725 | 726 | Contents\MacOS 727 | 1 728 | 729 | 730 | 0 731 | 732 | 733 | 734 | 735 | library\lib\armeabi-v7a 736 | 1 737 | 738 | 739 | 740 | 741 | 1 742 | 743 | 744 | 1 745 | 746 | 747 | 748 | 749 | Assets 750 | 1 751 | 752 | 753 | Assets 754 | 1 755 | 756 | 757 | 758 | 759 | Assets 760 | 1 761 | 762 | 763 | Assets 764 | 1 765 | 766 | 767 | 768 | 769 | 770 | 771 | 772 | 773 | 774 | 775 | 776 | 777 | 778 | 779 | 12 780 | 781 | 782 | 783 | 784 |
785 | -------------------------------------------------------------------------------- /Demo/Demo.res: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JensBorrisholt/Delphi-REST-Orm/01cbb0ead71d3a98125e7613058e226ce76f330f/Demo/Demo.res -------------------------------------------------------------------------------- /Demo/MainU.dfm: -------------------------------------------------------------------------------- 1 | object Form59: TForm59 2 | Left = 0 3 | Top = 0 4 | Caption = 'Form59' 5 | ClientHeight = 411 6 | ClientWidth = 852 7 | Color = clBtnFace 8 | Font.Charset = DEFAULT_CHARSET 9 | Font.Color = clWindowText 10 | Font.Height = -11 11 | Font.Name = 'Tahoma' 12 | Font.Style = [] 13 | OldCreateOrder = False 14 | OnCreate = FormCreate 15 | PixelsPerInch = 96 16 | TextHeight = 13 17 | object Memo1: TMemo 18 | Left = 0 19 | Top = 0 20 | Width = 852 21 | Height = 411 22 | Align = alClient 23 | Lines.Strings = ( 24 | 'Memo1') 25 | TabOrder = 0 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /Demo/MainU.pas: -------------------------------------------------------------------------------- 1 | unit MainU; 2 | 3 | interface 4 | 5 | uses 6 | Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, 7 | Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, 8 | 9 | DelphiRestOrm.ORM.Helper.ObjectContainer; 10 | 11 | type 12 | TForm59 = class(TForm) 13 | Memo1: TMemo; 14 | procedure FormCreate(Sender: TObject); 15 | private 16 | { Private declarations } 17 | 18 | public 19 | { Public declarations } 20 | end; 21 | 22 | var 23 | Form59: TForm59; 24 | 25 | implementation 26 | 27 | uses 28 | System.Generics.Collections, 29 | DelphiRestOrm.ORM.IRequest, DelphiRestOrm.ORM.Response, DelphiRestOrm.ORM.Helper.Azure, 30 | 31 | RootU, PersonU; 32 | {$R *.dfm} 33 | 34 | procedure TForm59.FormCreate(Sender: TObject); 35 | var 36 | Container: TObjectContainer; 37 | AzureAuthenticator: TAzureAuthenticator; 38 | Person: TPerson; 39 | Persons: TObjectList; 40 | s: string; 41 | Root: TPersons; 42 | begin 43 | Memo1.Clear; 44 | Memo1.Lines.Add('** Get all **'); 45 | Memo1.Lines.Add(''); 46 | 47 | s := NewRequest('https://localhost:44303/Contact').Credentials('test', 'test').GET.ToString; 48 | Memo1.Lines.Add(s.Substring(0, 1000) + ' ...'); 49 | 50 | Memo1.Lines.Add(''); 51 | Memo1.Lines.Add('** Get single **'); 52 | Memo1.Lines.Add(''); 53 | 54 | s := NewRequest('https://localhost:44303/Contact').GET(8).ToString; 55 | Memo1.Lines.Add(s); 56 | 57 | Memo1.Lines.Add(''); 58 | Memo1.Lines.Add('** Get All AsType **'); 59 | Memo1.Lines.Add(''); 60 | 61 | Persons := NewRequest('https://localhost:44303/Contact').ObjectContainer(Container).Credentials('test', 'test').GET.AsType>; 62 | Memo1.Lines.Add('(Random) Person.Address :' + Persons[Random(Persons.Count)].Address); 63 | 64 | Memo1.Lines.Add(''); 65 | Memo1.Lines.Add('** Get Single AsType **'); 66 | Memo1.Lines.Add(''); 67 | 68 | Person := NewRequest('https://localhost:44303/Contact').ObjectContainer(Container).Credentials('test', 'test').GET(5).AsType; 69 | Memo1.Lines.Add('Person.Address :' + Person.Address); 70 | 71 | 72 | Memo1.Lines.Add(''); 73 | Memo1.Lines.Add('** TJsonDTO test **'); 74 | Memo1.Lines.Add(''); 75 | 76 | Root := NewRequest('https://localhost:44303/Contact').ObjectContainer(Container).Credentials('test', 'test').GET.AsType; 77 | Memo1.Lines.Add('(Random) Person.Address :' + Persons.Items[Random(Root.Items.Count)].Address); 78 | (* 79 | Replace with your own Azure credentials 80 | AzureAuthenticator := TAzureAuthenticator.Create('aTenantID', 'aClientID', 'aClientSecret', 'aResourceId'); 81 | Memo1.Lines.Add(''); 82 | Memo1.Lines.Add('** Azure test **'); 83 | Memo1.Lines.Add(''); 84 | s := NewRequest('').Param(17794452).QueryParam('country', 'DK').AzureCredentials(AzureAuthenticator).GET.ToString; 85 | Memo1.Lines.Add(s); 86 | 87 | Memo1.Lines.Add(''); 88 | Memo1.Lines.Add('** Azure test async **'); 89 | Memo1.Lines.Add(''); 90 | 91 | NewRequestAsync('', 92 | procedure(const Response: TResponse) 93 | begin 94 | Memo1.Lines.Add(Response.ToString) 95 | end).Param(17794452).QueryParam('country', 'DK').AzureCredentials((aTenantID', 'aClientID', 'aClientSecret', 'aResourceId').GET; 96 | *) 97 | Container.Free; 98 | end; 99 | 100 | end. 101 | -------------------------------------------------------------------------------- /Demo/PersonU.pas: -------------------------------------------------------------------------------- 1 | unit PersonU; 2 | 3 | interface 4 | 5 | {$M+} 6 | 7 | type 8 | TPerson = class 9 | private 10 | FAbout: string; 11 | FAddress: string; 12 | FAge: Integer; 13 | FBalance: string; 14 | FCompany: string; 15 | FEmail: string; 16 | FEyeColor: string; 17 | FFavoriteFruit: string; 18 | FGender: string; 19 | FGreeting: string; 20 | FGuId: string; 21 | FId: string; 22 | FIndex: Integer; 23 | FIsActive: Boolean; 24 | FLatitude: Double; 25 | FLongitude: Double; 26 | FName: string; 27 | FPhone: string; 28 | FPicture: string; 29 | FRegistered: string; 30 | published 31 | property About: string read FAbout write FAbout; 32 | property Address: string read FAddress write FAddress; 33 | property Age: Integer read FAge write FAge; 34 | property Balance: string read FBalance write FBalance; 35 | property Company: string read FCompany write FCompany; 36 | property Email: string read FEmail write FEmail; 37 | property EyeColor: string read FEyeColor write FEyeColor; 38 | property FavoriteFruit: string read FFavoriteFruit write FFavoriteFruit; 39 | property Gender: string read FGender write FGender; 40 | property Greeting: string read FGreeting write FGreeting; 41 | property GuId: string read FGuId write FGuId; 42 | property Id: string read FId write FId; 43 | property Index: Integer read FIndex write FIndex; 44 | property IsActive: Boolean read FIsActive write FIsActive; 45 | property Latitude: Double read FLatitude write FLatitude; 46 | property Longitude: Double read FLongitude write FLongitude; 47 | property Name: string read FName write FName; 48 | property Phone: string read FPhone write FPhone; 49 | property Picture: string read FPicture write FPicture; 50 | property Registered: string read FRegistered write FRegistered; 51 | end; 52 | 53 | implementation 54 | 55 | end. 56 | -------------------------------------------------------------------------------- /Demo/Pkg.Json.DTO.pas: -------------------------------------------------------------------------------- 1 | unit Pkg.Json.DTO; 2 | 3 | 4 | 5 | interface 6 | 7 | uses 8 | System.Classes, System.Json, Rest.Json, System.Generics.Collections, Rest.JsonReflect; 9 | 10 | type 11 | TArrayMapper = class 12 | protected 13 | procedure RefreshArray(aSource: TList; var aDestination: TArray); 14 | function List(var aList: TList; aSource: TArray): TList; 15 | function ObjectList(var aList: TObjectList; aSource: TArray): TObjectList; 16 | public 17 | constructor Create; virtual; 18 | end; 19 | 20 | TJsonDTO = class(TArrayMapper) 21 | private 22 | FOptions: TJsonOptions; 23 | class procedure PrettyPrintPair(aJSONValue: TJSONPair; aOutputStrings: TStrings; Last: Boolean; Indent: Integer); 24 | class procedure PrettyPrintJSON(aJSONValue: TJsonValue; aOutputStrings: TStrings; Indent: Integer = 0); overload; 25 | class procedure PrettyPrintArray(aJSONValue: TJSONArray; aOutputStrings: TStrings; Last: Boolean; Indent: Integer); 26 | protected 27 | function GetAsJson: string; virtual; 28 | procedure SetAsJson(aValue: string); virtual; 29 | public 30 | constructor Create; override; 31 | class function PrettyPrintJSON(aJson: string): string; overload; 32 | function ToString : string; override; 33 | property AsJson: string read GetAsJson write SetAsJson; 34 | end; 35 | 36 | GenericListReflectAttribute = class(JsonReflectAttribute) 37 | public 38 | constructor Create; 39 | end; 40 | 41 | implementation 42 | 43 | uses 44 | System.Sysutils, System.JSONConsts, System.Rtti; 45 | 46 | { TJsonDTO } 47 | 48 | constructor TJsonDTO.Create; 49 | begin 50 | inherited; 51 | FOptions := [joDateIsUTC, joDateFormatISO8601]; 52 | end; 53 | 54 | function TJsonDTO.GetAsJson: string; 55 | begin 56 | Result := TJson.ObjectToJsonString(Self, FOptions); 57 | end; 58 | 59 | const 60 | INDENT_SIZE = 2; 61 | 62 | class procedure TJsonDTO.PrettyPrintJSON(aJSONValue: TJsonValue; aOutputStrings: TStrings; Indent: Integer); 63 | var 64 | i: Integer; 65 | Ident: Integer; 66 | begin 67 | Ident := Indent + INDENT_SIZE; 68 | i := 0; 69 | 70 | if aJSONValue is TJSONObject then 71 | begin 72 | aOutputStrings.Add(StringOfChar(' ', Ident) + '{'); 73 | for i := 0 to TJSONObject(aJSONValue).Count - 1 do 74 | PrettyPrintPair(TJSONObject(aJSONValue).Pairs[i], aOutputStrings, i = TJSONObject(aJSONValue).Count - 1, Ident); 75 | 76 | aOutputStrings.Add(StringOfChar(' ', Ident) + '}'); 77 | end 78 | else if aJSONValue is TJSONArray then 79 | PrettyPrintArray(TJSONArray(aJSONValue), aOutputStrings, i = TJSONObject(aJSONValue).Count - 1, Ident) 80 | else 81 | aOutputStrings.Add(StringOfChar(' ', Ident) + aJSONValue.ToString); 82 | end; 83 | 84 | class procedure TJsonDTO.PrettyPrintArray(aJSONValue: TJSONArray; aOutputStrings: TStrings; Last: Boolean; Indent: Integer); 85 | var 86 | i: Integer; 87 | begin 88 | aOutputStrings.Add(StringOfChar(' ', Indent + INDENT_SIZE) + '['); 89 | 90 | for i := 0 to aJSONValue.Count - 1 do 91 | begin 92 | PrettyPrintJSON(aJSONValue.Items[i], aOutputStrings, Indent); 93 | if i < aJSONValue.Count - 1 then 94 | aOutputStrings[aOutputStrings.Count - 1] := aOutputStrings[aOutputStrings.Count - 1] + ','; 95 | end; 96 | 97 | aOutputStrings.Add(StringOfChar(' ', Indent + INDENT_SIZE - 2) + ']'); 98 | end; 99 | 100 | class function TJsonDTO.PrettyPrintJSON(aJson: string): string; 101 | var 102 | StringList: TStringlist; 103 | JSONValue: TJsonValue; 104 | begin 105 | StringList := TStringlist.Create; 106 | try 107 | JSONValue := TJSONObject.ParseJSONValue(aJson); 108 | try 109 | if JSONValue <> nil then 110 | PrettyPrintJSON(JSONValue, StringList); 111 | finally 112 | JSONValue.Free; 113 | end; 114 | 115 | Result := StringList.Text; 116 | finally 117 | StringList.Free; 118 | end; 119 | end; 120 | 121 | class procedure TJsonDTO.PrettyPrintPair(aJSONValue: TJSONPair; aOutputStrings: TStrings; Last: Boolean; Indent: Integer); 122 | const 123 | TEMPLATE = '%s:%s'; 124 | var 125 | Line: string; 126 | NewList: TStringlist; 127 | begin 128 | NewList := TStringlist.Create; 129 | try 130 | PrettyPrintJSON(aJSONValue.JSONValue, NewList, Indent); 131 | Line := Format(TEMPLATE, [aJSONValue.JsonString.ToString, Trim(NewList.Text)]); 132 | finally 133 | NewList.Free; 134 | end; 135 | 136 | Line := StringOfChar(' ', Indent + INDENT_SIZE) + Line; 137 | if not Last then 138 | Line := Line + ','; 139 | aOutputStrings.Add(Line); 140 | end; 141 | 142 | procedure TJsonDTO.SetAsJson(aValue: string); 143 | var 144 | JSONValue: TJsonValue; 145 | JSONObject: TJSONObject; 146 | begin 147 | JSONValue := TJSONObject.ParseJSONValue(aValue); 148 | try 149 | if not Assigned(JSONValue) then 150 | Exit; 151 | 152 | if (JSONValue is TJSONArray) then 153 | begin 154 | with TJSONUnMarshal.Create do 155 | try 156 | SetFieldArray(Self, 'Items', (JSONValue as TJSONArray)); 157 | finally 158 | Free; 159 | end; 160 | 161 | Exit; 162 | end; 163 | 164 | if (JSONValue is TJSONObject) then 165 | JSONObject := JSONValue as TJSONObject 166 | else 167 | begin 168 | aValue := aValue.Trim; 169 | if (aValue = '') and not Assigned(JSONValue) or (aValue <> '') and Assigned(JSONValue) and JSONValue.Null then 170 | Exit 171 | else 172 | raise EConversionError.Create(SCannotCreateObject); 173 | end; 174 | 175 | TJson.JsonToObject(Self, JSONObject, FOptions); 176 | finally 177 | JSONValue.Free; 178 | end; 179 | end; 180 | 181 | function TJsonDTO.ToString: string; 182 | begin 183 | Result := AsJson; 184 | end; 185 | 186 | { TArrayMapper } 187 | 188 | constructor TArrayMapper.Create; 189 | begin 190 | inherited; 191 | end; 192 | 193 | function TArrayMapper.List(var aList: TList; aSource: TArray): TList; 194 | begin 195 | if aList = nil then 196 | begin 197 | aList := TList.Create; 198 | aList.AddRange(aSource); 199 | end; 200 | 201 | Exit(aList); 202 | end; 203 | 204 | function TArrayMapper.ObjectList(var aList: TObjectList; aSource: TArray): TObjectList; 205 | var 206 | Element: T; 207 | begin 208 | if aList = nil then 209 | begin 210 | aList := TObjectList.Create; 211 | for Element in aSource do 212 | aList.Add(Element); 213 | end; 214 | 215 | Exit(aList); 216 | end; 217 | 218 | procedure TArrayMapper.RefreshArray(aSource: TList; var aDestination: TArray); 219 | begin 220 | if aSource <> nil then 221 | aDestination := aSource.ToArray; 222 | end; 223 | 224 | type 225 | TGenericListFieldInterceptor = class(TJSONInterceptor) 226 | public 227 | function ObjectsConverter(Data: TObject; Field: string): TListOfObjects; override; 228 | end; 229 | 230 | { TListFieldInterceptor } 231 | 232 | function TGenericListFieldInterceptor.ObjectsConverter(Data: TObject; Field: string): TListOfObjects; 233 | var 234 | ctx: TRttiContext; 235 | List: TList; 236 | RttiProperty: TRttiProperty; 237 | begin 238 | RttiProperty := ctx.GetType(Data.ClassInfo).GetProperty(Copy(Field, 2, MAXINT)); 239 | List := TList(RttiProperty.GetValue(Data).AsObject); 240 | Result := TListOfObjects(List.List); 241 | SetLength(Result, List.Count); 242 | end; 243 | 244 | constructor GenericListReflectAttribute.Create; 245 | begin 246 | inherited Create(ctObjects, rtObjects, TGenericListFieldInterceptor, nil, false); 247 | end; 248 | 249 | end. 250 | 251 | 252 | -------------------------------------------------------------------------------- /Demo/RootU.pas: -------------------------------------------------------------------------------- 1 | unit RootU; 2 | 3 | interface 4 | 5 | uses 6 | Pkg.Json.DTO, System.Generics.Collections, REST.Json.Types; 7 | 8 | {$M+} 9 | 10 | type 11 | TPersonDTO = class 12 | private 13 | FAbout: string; 14 | FAddress: string; 15 | FAge: Integer; 16 | FBalance: string; 17 | FCompany: string; 18 | FEmail: string; 19 | FEyeColor: string; 20 | FFavoriteFruit: string; 21 | FGender: string; 22 | FGreeting: string; 23 | FGuId: string; 24 | FId: string; 25 | FIndex: Integer; 26 | FIsActive: Boolean; 27 | FLatitude: Double; 28 | FLongitude: Double; 29 | FName: string; 30 | FPhone: string; 31 | FPicture: string; 32 | FRegistered: string; 33 | published 34 | property About: string read FAbout write FAbout; 35 | property Address: string read FAddress write FAddress; 36 | property Age: Integer read FAge write FAge; 37 | property Balance: string read FBalance write FBalance; 38 | property Company: string read FCompany write FCompany; 39 | property Email: string read FEmail write FEmail; 40 | property EyeColor: string read FEyeColor write FEyeColor; 41 | property FavoriteFruit: string read FFavoriteFruit write FFavoriteFruit; 42 | property Gender: string read FGender write FGender; 43 | property Greeting: string read FGreeting write FGreeting; 44 | property GuId: string read FGuId write FGuId; 45 | property Id: string read FId write FId; 46 | property Index: Integer read FIndex write FIndex; 47 | property IsActive: Boolean read FIsActive write FIsActive; 48 | property Latitude: Double read FLatitude write FLatitude; 49 | property Longitude: Double read FLongitude write FLongitude; 50 | property Name: string read FName write FName; 51 | property Phone: string read FPhone write FPhone; 52 | property Picture: string read FPicture write FPicture; 53 | property Registered: string read FRegistered write FRegistered; 54 | end; 55 | 56 | TPersons = class(TJsonDTO) 57 | private 58 | [JSONName('Items'), JSONMarshalled(False)] 59 | FItemsArray: TArray; 60 | [GenericListReflect] 61 | FItems: TObjectList; 62 | function GeTPersonDTO: TObjectList; 63 | published 64 | property Items: TObjectList read GeTPersonDTO; 65 | public 66 | destructor Destroy; override; 67 | end; 68 | 69 | implementation 70 | 71 | { TRoot } 72 | 73 | destructor TPersons.Destroy; 74 | begin 75 | GeTPersonDTO.Free; 76 | inherited; 77 | end; 78 | 79 | function TPersons.GeTPersonDTO: TObjectList; 80 | begin 81 | Result := ObjectList(FItems, FItemsArray); 82 | end; 83 | 84 | end. 85 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /ORM/DelphiRestOrm.ORM.IRequest.pas: -------------------------------------------------------------------------------- 1 | unit DelphiRestOrm.ORM.IRequest; 2 | 3 | interface 4 | 5 | uses 6 | System.Classes, System.Rtti, Rest.Client, Rest.Types, 7 | 8 | DelphiRestOrm.ORM.Response, DelphiRestOrm.ORM.Helper.ObjectContainer, DelphiRestOrm.ORM.Helper.Azure; 9 | 10 | const 11 | ctDefault = ctAPPLICATION_JSON; 12 | 13 | type 14 | TOrmCompletionHandler = reference to procedure(const Response: TResponse); 15 | 16 | IRestRequest = interface 17 | ['{C117D3C1-B571-4CD9-A571-3C720DE689CE}'] 18 | 19 | /// 20 | /// Adds ABodyContent as Body parameter to the request. 21 | /// 22 | /// 23 | /// An already existing body parameter will NOT be replaced/deleted! 24 | /// 25 | function AddBody(const aBodyContent: string; aContentType: TRESTContentType = ctNone): IRestRequest; overload; 26 | 27 | /// 28 | /// Encodes the string representation of a JSON Object and adds it to the request body. 29 | /// 30 | /// The object to serialize, uning the objects ToString method 31 | /// Defines who will own the object. 32 | /// 33 | /// An already existing body parameter will be replaced/deleted! 34 | /// 35 | function AddBody(AObject: TObject; aContentType: TRESTContentType = ctDefault): IRestRequest; overload; 36 | 37 | /// 38 | /// Add an authentication parameter 39 | /// 40 | function AddAuthParameter(const AName, aValue: string; const AKind: TRESTRequestParameterKind = TRESTRequestParameterKind.pkGetOrPost; const AOptions: TRESTRequestParameterOptions = []) 41 | : IRestRequest; 42 | 43 | /// 44 | /// Adds ABodyContent as Body parameter to the request. 45 | /// 46 | /// 47 | /// An already existing body parameter will be replaced/deleted! 48 | /// 49 | function AddBody(aBodyContent: TStream; aContentType: TRESTContentType = ctNone; AOwnsStream: TRESTObjectOwnership = ooCopy): IRestRequest; overload; 50 | 51 | /// 52 | /// Adds ABodyContent as Body parameter to the request. 53 | /// 54 | /// 55 | /// An already existing body parameter will be replaced/deleted! 56 | /// 57 | function AddBody(AObject: TObject): IRestRequest; overload; 58 | 59 | /// 60 | /// Removes all body parameters (TRESTRequestParameterKind.pkREQUESTBODY) from the request. 61 | /// 62 | function ClearBody: IRestRequest; 63 | 64 | /// 65 | /// Adds file content to the request. If same AName parameter already exists, then the previous content 66 | /// will be replaced by new one. 67 | /// 68 | /// Name of the parameter 69 | /// Path to the file 70 | /// Content type of the file, for HTTP body parameter. 71 | /// If AContentType=ctNone program automatically define the Content type of the file 72 | function AddFile(const AName, AFileName: string; aContentType: TRESTContentType = TRESTContentType.ctNone): IRestRequest; overload; 73 | 74 | /// 75 | /// Adds file content to the request. Only one file can be added. 76 | /// If this function is executed second time, then previous content will be replaced by new one. 77 | /// 78 | /// Path to the file 79 | /// Content type of the file, for HTTP body parameter. 80 | /// If AContentType=ctNone program automatically define the Content type of the file 81 | function AddFile(const AFileName: string; aContentType: TRESTContentType = TRESTContentType.ctNone): IRestRequest; overload; 82 | 83 | /// 84 | /// Adds a parameter to the header. If a parameter of the same name already exists, then the previous parameter 85 | /// will be removed and freed.

There are five kinds of parameters: - GetOrPost: Either a QueryString 86 | /// value or encoded form value based on method - HttpHeader: Adds the name/value pair to the HTTP request's 87 | /// Headers collection - UrlSegment: Inserted into URL if there is a matching url token e.g. {AccountId} - 88 | /// Cookie: Adds the name/value pair to the HTTP request's Cookies collection - RequestBody: Used by AddBody() 89 | /// (not recommended to use directly) 90 | ///
91 | /// 92 | /// Name of the parameter 93 | /// 94 | /// 95 | /// Value of the parameter 96 | /// 97 | /// 98 | /// The kind of parameter to add 99 | /// 100 | /// 101 | /// Options for processing the parameter 102 | /// 103 | function AddHeader(const AName, aValue: string; const AOptions: TRESTRequestParameterOptions = [poDoNotEncode]): IRestRequest; overload; 104 | 105 | /// 106 | /// Sets all fields to their default value. No state information is kept. If Response 107 | /// is assigned, then Response.ResetToDefaults is called too. 108 | /// 109 | function ResetToDefaults: IRestRequest; 110 | 111 | /// 112 | /// Cancels REST request. This will not raise an exception. 113 | /// Check ARequest.IsCancelled that the request was cancelled. 114 | /// 115 | function Cancel: IRestRequest; 116 | /// 117 | /// Scpecifies when the request was cancalled using Cancel call. 118 | /// 119 | 120 | function Accept(aContentType: TRESTContentType = ctDefault; const AOptions: TRESTRequestParameterOptions = [poDoNotEncode]): IRestRequest; 121 | function ContentType(aContentType: TRESTContentType = ctDefault; const AOptions: TRESTRequestParameterOptions = [poDoNotEncode]): IRestRequest; 122 | function AzureCredentials(const aTennantID, aClientID, aClientSecret, aResourceId: string): IRestRequest; overload; 123 | function AzureCredentials(const aAzureAuthenticator: TAzureAuthenticator): IRestRequest; overload; 124 | function Credentials(const aUsername, aPassword: string): IRestRequest; 125 | function ConnectionTimeOut(const aValue: Integer = 30000): IRestRequest; 126 | function ObjectContainer(var aObjectContainer: TObjectContainer): IRestRequest; 127 | function OwnsObjects(const aValue: Boolean = True): IRestRequest; 128 | function QueryParam(const AName: string; aValue: TValue): IRestRequest; 129 | function Param(aValue: TValue): IRestRequest; 130 | 131 | function IsCancelled: Boolean; 132 | /// 133 | /// Sends a NEW object/entity to the server. 134 | /// 135 | function Post: TResponse; overload; 136 | 137 | /// 138 | /// Sends a NEW object/entity to the server. 139 | /// 140 | function Post(AObject: TObject): TResponse; overload; 141 | 142 | /// 143 | /// Sends a NEW object/entity to the server. 144 | /// 145 | function Post(aValue: TValue; AObject: TObject): TResponse; overload; 146 | 147 | /// 148 | /// Updates an already existing object/entity on the server. PUT may also 149 | /// allow for sending a new entity (depends on the actual server/API 150 | /// implementation). 151 | /// 152 | function Put: TResponse; overload; 153 | 154 | /// 155 | /// Updates an already existing object/entity on the server. PUT may also 156 | /// allow for sending a new entity (depends on the actual server/API implementation). 157 | /// 158 | function Put(AObject: TObject): TResponse; overload; 159 | 160 | /// 161 | /// Updates an already existing object/entity on the server. PUT may also 162 | /// allow for sending a new entity (depends on the actual server/API implementation). 163 | /// 164 | function Put(aValue: TValue; AObject: TObject): TResponse; overload; 165 | 166 | /// 167 | /// Retrieves an object/entity from the server. 168 | /// 169 | function GET: TResponse; overload; 170 | 171 | /// 172 | /// Retrieves an object/entity from the server. 173 | /// 174 | function GET(AObject: TObject): TResponse; overload; 175 | 176 | /// 177 | /// Retrieves an object/entity from the server. 178 | /// 179 | function GET(aValue: TValue): TResponse; overload; 180 | 181 | /// 182 | /// Deletes an object/entity from the server. 183 | /// 184 | function DELETE: TResponse; overload; 185 | 186 | /// 187 | /// Deletes an object/entity from the server. 188 | /// 189 | function DELETE(AObject: TObject): TResponse; overload; 190 | 191 | /// 192 | /// Deletes an object/entity from the server. 193 | /// 194 | function DELETE(aValue: TValue): TResponse; overload; 195 | 196 | /// 197 | /// Patches an object/entity on the server, by only updating the pairs that are sent within that PATCH body. 198 | /// 199 | function PATCH: TResponse; overload; 200 | 201 | /// 202 | /// Patches an object/entity on the server, by only updating the pairs that are sent within that PATCH body. 203 | /// 204 | function PATCH(AObject: TObject): TResponse; overload; 205 | 206 | /// 207 | /// Patches an object/entity on the server, by only updating the pairs that are sent within that PATCH body. 208 | /// 209 | function PATCH(aValue: TValue; AObject: TObject): TResponse; overload; 210 | 211 | function BaseApiURL: string; 212 | end; 213 | 214 | function NewRequest(aBaseApiURL: string; aOwnsObjects: Boolean = True): IRestRequest; overload; 215 | function NewRequestAsync(aBaseApiURL: string; aCompletionHandler: TOrmCompletionHandler; aOwnsObjects: Boolean = True): IRestRequest; overload; 216 | 217 | implementation 218 | 219 | uses 220 | DelphiRestOrm.ORM.Request; 221 | 222 | function NewRequest(aBaseApiURL: string; aOwnsObjects: Boolean = True): IRestRequest; overload; 223 | begin 224 | Result := TRestRequest.Create(aBaseApiURL).OwnsObjects(aOwnsObjects); 225 | end; 226 | 227 | function NewRequestAsync(aBaseApiURL: string; aCompletionHandler: TOrmCompletionHandler; aOwnsObjects: Boolean = True): IRestRequest; overload; 228 | begin 229 | Result := TRestRequest.Create(aBaseApiURL, aCompletionHandler).OwnsObjects(aOwnsObjects); 230 | end; 231 | 232 | end. 233 | -------------------------------------------------------------------------------- /ORM/DelphiRestOrm.ORM.Request.pas: -------------------------------------------------------------------------------- 1 | unit DelphiRestOrm.ORM.Request; 2 | 3 | interface 4 | 5 | uses 6 | System.Classes, System.Rtti, System.Generics.Collections, System.Threading, REST.Client, REST.Types, 7 | 8 | DelphiRestOrm.ORM.Helper.ObjectContainer, DelphiRestOrm.ORM.Helper.Azure, DelphiRestOrm.ORM.IRequest, DelphiRestOrm.ORM.Response; 9 | {$M+} 10 | 11 | type 12 | TRestRequest = class(TInterfacedObject, IRestRequest) 13 | private 14 | FObjectContainer: TObjectContainer; 15 | FExternalObjectContainer: TObjectContainer; 16 | FDTOObjectContainer: TObjectContainer; 17 | FClient: TRESTClient; 18 | FRequest: TCustomRESTRequest; 19 | FResponse: TRESTResponse; 20 | FBaseURL: string; 21 | FAsyncCall: Boolean; 22 | 23 | FQueryParams: TDictionary; 24 | FCompletionHandler: TOrmCompletionHandler; 25 | function SetAuthenticator(aCustomAuthenticator: TCustomAuthenticator): IRestRequest; 26 | function DoExecute(aRESTRequestMethod: TRESTRequestMethod): TResponse; overload; 27 | function DoExecute(aObject: TObject; aRESTRequestMethod: TRESTRequestMethod): TResponse; overload; 28 | function InternalParam(aValue: TValue): TRestRequest; 29 | public 30 | constructor Create(const aBaseApiURL: string; aCompletionHandler: TOrmCompletionHandler = nil); reintroduce; 31 | destructor Destroy; override; 32 | function Param(aValue: TValue): IRestRequest; 33 | 34 | function AddAuthParameter(const AName, aValue: string; const AKind: TRESTRequestParameterKind = TRESTRequestParameterKind.pkGetOrPost; const AOptions: TRESTRequestParameterOptions = []) 35 | : IRestRequest; 36 | function AddBody(const aBodyContent: string; aContentType: TRESTContentType = ctNone): IRestRequest; overload; 37 | function AddBody(aObject: TObject; aContentType: TRESTContentType = ctDefault): IRestRequest; overload; 38 | function AddBody(aBodyContent: TStream; aContentType: TRESTContentType = ctNone; AOwnsStream: TRESTObjectOwnership = ooCopy): IRestRequest; overload; 39 | function AddBody(aObject: TObject): IRestRequest; overload; 40 | function ClearBody: IRestRequest; 41 | function AddFile(const AName, AFileName: string; aContentType: TRESTContentType = TRESTContentType.ctNone): IRestRequest; overload; 42 | function AddFile(const AFileName: string; aContentType: TRESTContentType = TRESTContentType.ctNone): IRestRequest; overload; 43 | function AddHeader(const AName, aValue: string; const AOptions: TRESTRequestParameterOptions = [poDoNotEncode]): IRestRequest; overload; 44 | function ResetToDefaults: IRestRequest; 45 | function Cancel: IRestRequest; 46 | function Accept(aContentType: TRESTContentType = ctDefault; const AOptions: TRESTRequestParameterOptions = [poDoNotEncode]): IRestRequest; 47 | function ContentType(aContentType: TRESTContentType = ctDefault; const AOptions: TRESTRequestParameterOptions = [poDoNotEncode]): IRestRequest; 48 | function AzureCredentials(const aTennantID, aClientID, aClientSecret, aResourceId: string): IRestRequest; overload; 49 | function AzureCredentials(const aAzureAuthenticator: TAzureAuthenticator): IRestRequest; overload; 50 | function Credentials(const aUsername, aPassword: string): IRestRequest; 51 | function ObjectContainer(var aObjectContainer: TObjectContainer): IRestRequest; 52 | function IsCancelled: Boolean; 53 | function Post: TResponse; overload; 54 | function Post(aObject: TObject): TResponse; overload; 55 | function Post(aValue: TValue; aObject: TObject): TResponse; overload; 56 | function Put: TResponse; overload; 57 | function Put(aObject: TObject): TResponse; overload; 58 | function Put(aValue: TValue; aObject: TObject): TResponse; overload; 59 | function GET: TResponse; overload; 60 | function GET(aObject: TObject): TResponse; overload; 61 | function GET(aValue: TValue): TResponse; overload; 62 | function DELETE: TResponse; overload; 63 | function DELETE(aObject: TObject): TResponse; overload; 64 | function DELETE(aValue: TValue): TResponse; overload; 65 | function PATCH: TResponse; overload; 66 | function PATCH(aObject: TObject): TResponse; overload; 67 | function PATCH(aValue: TValue; aObject: TObject): TResponse; overload; 68 | 69 | function BaseApiURL: string; 70 | function ConnectionTimeOut(const aValue: Integer = 30000): IRestRequest; 71 | function OwnsObjects(const aValue: Boolean = True): IRestRequest; 72 | function QueryParam(const AName: string; aValue: TValue): IRestRequest; 73 | end; 74 | 75 | implementation 76 | 77 | uses 78 | System.SysUtils, System.IOUtils, 79 | 80 | REST.Authenticator.Basic, DelphiRestOrm.ORM.Helper.ThreadingEx; 81 | 82 | { TRestRequest } 83 | 84 | function TRestRequest.Accept(aContentType: TRESTContentType = ctDefault; const AOptions: TRESTRequestParameterOptions = [poDoNotEncode]): IRestRequest; 85 | begin 86 | AddHeader('Accept', ContentTypeToString(aContentType), AOptions); 87 | Result := Self; 88 | end; 89 | 90 | function TRestRequest.AddBody(aBodyContent: TStream; aContentType: TRESTContentType; AOwnsStream: TRESTObjectOwnership): IRestRequest; 91 | begin 92 | FRequest.AddBody(aBodyContent, aContentType, AOwnsStream); 93 | Result := Self; 94 | end; 95 | 96 | function TRestRequest.AddBody(aObject: TObject; aContentType: TRESTContentType): IRestRequest; 97 | begin 98 | AddBody(aObject.ToString, aContentType); 99 | Result := Self; 100 | end; 101 | 102 | function TRestRequest.AddBody(const aBodyContent: string; aContentType: TRESTContentType): IRestRequest; 103 | begin 104 | FRequest.AddBody(aBodyContent, aContentType); 105 | Result := Self; 106 | end; 107 | 108 | function TRestRequest.AddFile(const AFileName: string; aContentType: TRESTContentType): IRestRequest; 109 | begin 110 | FRequest.AddFile(AFileName, aContentType); 111 | Result := Self; 112 | end; 113 | 114 | function TRestRequest.AddHeader(const AName, aValue: string; const AOptions: TRESTRequestParameterOptions): IRestRequest; 115 | begin 116 | FRequest.AddParameter(AName, aValue, TRESTRequestParameterKind.pkHTTPHEADER, AOptions); 117 | Result := Self; 118 | end; 119 | 120 | function TRestRequest.AzureCredentials(const aAzureAuthenticator: TAzureAuthenticator): IRestRequest; 121 | begin 122 | Result := SetAuthenticator(aAzureAuthenticator); 123 | end; 124 | 125 | function TRestRequest.Param(aValue: TValue): IRestRequest; 126 | begin 127 | Result := QueryParam('', aValue); 128 | end; 129 | 130 | function TRestRequest.AzureCredentials(const aTennantID, aClientID, aClientSecret, aResourceId: string): IRestRequest; 131 | begin 132 | Result := SetAuthenticator(TAzureAuthenticator.Create(aTennantID, aClientID, aClientSecret, aResourceId)); 133 | end; 134 | 135 | function TRestRequest.BaseApiURL: string; 136 | begin 137 | Result := FBaseURL; 138 | end; 139 | 140 | function TRestRequest.AddFile(const AName, AFileName: string; aContentType: TRESTContentType): IRestRequest; 141 | begin 142 | FRequest.AddFile(AName, AFileName, aContentType); 143 | Result := Self; 144 | end; 145 | 146 | function TRestRequest.Cancel: IRestRequest; 147 | begin 148 | FRequest.Cancel; 149 | Result := Self; 150 | end; 151 | 152 | function TRestRequest.ClearBody: IRestRequest; 153 | begin 154 | FRequest.ClearBody; 155 | Result := Self; 156 | end; 157 | 158 | constructor TRestRequest.Create(const aBaseApiURL: string; aCompletionHandler: TOrmCompletionHandler = nil); 159 | begin 160 | FObjectContainer := TObjectContainer.Create; 161 | FBaseURL := aBaseApiURL; 162 | 163 | FClient := TRESTClient.Create(aBaseApiURL); 164 | FRequest := TCustomRESTRequest.Create(FClient); 165 | FResponse := TRESTResponse.Create(FClient); 166 | FRequest.Response := FResponse; 167 | 168 | FDTOObjectContainer := FObjectContainer.Add(TObjectContainer.Create(False)); 169 | FQueryParams := FObjectContainer.Add(TDictionary.Create); 170 | FCompletionHandler := aCompletionHandler; 171 | FAsyncCall := Assigned(aCompletionHandler); 172 | 173 | FExternalObjectContainer := nil; 174 | end; 175 | 176 | function TRestRequest.DELETE: TResponse; 177 | begin 178 | Result := DoExecute(rmDELETE); 179 | end; 180 | 181 | function TRestRequest.DELETE(aObject: TObject): TResponse; 182 | begin 183 | Result := DoExecute(aObject, rmDELETE); 184 | end; 185 | 186 | function TRestRequest.DELETE(aValue: TValue): TResponse; 187 | begin 188 | Result := InternalParam(aValue).DoExecute(rmDELETE); 189 | end; 190 | 191 | destructor TRestRequest.Destroy; 192 | begin 193 | if not FAsyncCall then 194 | FreeAndNil(FClient); 195 | 196 | FreeAndNil(FObjectContainer); 197 | inherited; 198 | end; 199 | 200 | function TRestRequest.DoExecute(aObject: TObject; aRESTRequestMethod: TRESTRequestMethod): TResponse; 201 | begin 202 | FDTOObjectContainer.Add(aObject); 203 | AddBody(aObject.ToString, ctDefault); 204 | Result := DoExecute(aRESTRequestMethod); 205 | end; 206 | 207 | function TRestRequest.DoExecute(aRESTRequestMethod: TRESTRequestMethod): TResponse; 208 | function GetURL: string; 209 | var 210 | Value: string; 211 | Pair: TPair; 212 | begin 213 | Result := FBaseURL; 214 | if not Result.EndsWith('/') then 215 | Result := Result + '/'; 216 | 217 | if FQueryParams.TryGetValue('', Value) then 218 | begin 219 | Result := Result + Value; 220 | FQueryParams.Remove(''); 221 | end 222 | else 223 | Result := Result.Substring(0, Length(Result) - 1); 224 | 225 | if FQueryParams.Count > 0 then 226 | Result := Result + '?'; 227 | 228 | for Pair in FQueryParams do 229 | Result := Result + Pair.Key + '=' + Pair.Value; 230 | 231 | end; 232 | 233 | var 234 | LCLient: TRESTClient; 235 | LRequest: TCustomRESTRequest; 236 | LCompletionHandler: TOrmCompletionHandler; 237 | 238 | begin 239 | LCLient := FClient; 240 | LRequest := FRequest; 241 | LCompletionHandler := FCompletionHandler; 242 | 243 | FClient.BaseURL := GetURL; 244 | FRequest.Method := aRESTRequestMethod; 245 | 246 | if not FAsyncCall then 247 | begin 248 | LRequest.Execute; 249 | Exit(FObjectContainer.Add(TResponse.Create(LRequest.Response, FExternalObjectContainer))); 250 | end; 251 | 252 | Result := nil; 253 | 254 | LRequest.ExecuteAsync( 255 | procedure 256 | var 257 | Response: TResponse; 258 | ObjectContainer: TObjectContainer; 259 | begin 260 | ObjectContainer := TObjectContainer.Create; 261 | ObjectContainer.Add(FClient); 262 | 263 | if Assigned(LCompletionHandler) then 264 | begin 265 | Response := ObjectContainer.Add(TResponse.Create(LRequest.Response, ObjectContainer)); 266 | LCompletionHandler(Response); 267 | end; 268 | 269 | FreeAndNil(ObjectContainer); 270 | end, True, True, 271 | procedure(aErrorObject: TObject) 272 | begin 273 | FreeAndNil(LCLient); 274 | end); 275 | 276 | end; 277 | 278 | function TRestRequest.GET(aObject: TObject): TResponse; 279 | begin 280 | Result := DoExecute(aObject, rmGET); 281 | end; 282 | 283 | function TRestRequest.GET: TResponse; 284 | begin 285 | Result := DoExecute(rmGET); 286 | end; 287 | 288 | function TRestRequest.InternalParam(aValue: TValue): TRestRequest; 289 | begin 290 | Result := Param(aValue) as TRestRequest; 291 | end; 292 | 293 | function TRestRequest.IsCancelled: Boolean; 294 | begin 295 | Result := FRequest.IsCancelled; 296 | end; 297 | 298 | function TRestRequest.ObjectContainer(var aObjectContainer: TObjectContainer): IRestRequest; 299 | begin 300 | if not Assigned(aObjectContainer) then 301 | aObjectContainer := TObjectContainer.Create; 302 | 303 | FExternalObjectContainer := aObjectContainer; 304 | Result := Self; 305 | end; 306 | 307 | function TRestRequest.OwnsObjects(const aValue: Boolean): IRestRequest; 308 | begin 309 | FDTOObjectContainer.OwnsObjects := aValue; 310 | Result := Self; 311 | end; 312 | 313 | function TRestRequest.SetAuthenticator(aCustomAuthenticator: TCustomAuthenticator): IRestRequest; 314 | begin 315 | FObjectContainer.Remove; 316 | FClient.Authenticator := FObjectContainer.Add(aCustomAuthenticator); 317 | Result := Self; 318 | end; 319 | 320 | function TRestRequest.PATCH: TResponse; 321 | begin 322 | Result := DoExecute(rmPATCH); 323 | end; 324 | 325 | function TRestRequest.Post: TResponse; 326 | begin 327 | Result := DoExecute(rmPOST); 328 | end; 329 | 330 | function TRestRequest.Put: TResponse; 331 | begin 332 | Result := DoExecute(rmPUT); 333 | end; 334 | 335 | function TRestRequest.ResetToDefaults: IRestRequest; 336 | begin 337 | FRequest.ResetToDefaults; 338 | Result := Self; 339 | end; 340 | 341 | function TRestRequest.Credentials(const aUsername, aPassword: string): IRestRequest; 342 | begin 343 | Result := SetAuthenticator(THTTPBasicAuthenticator.Create(aUsername, aPassword)) 344 | end; 345 | 346 | function TRestRequest.ConnectionTimeOut(const aValue: Integer): IRestRequest; 347 | begin 348 | FClient.ConnectTimeout := aValue; 349 | end; 350 | 351 | function TRestRequest.ContentType(aContentType: TRESTContentType; const AOptions: TRESTRequestParameterOptions): IRestRequest; 352 | begin 353 | AddHeader('Content-Type', ContentTypeToString(aContentType), AOptions); 354 | Result := Self; 355 | end; 356 | 357 | function TRestRequest.PATCH(aObject: TObject): TResponse; 358 | begin 359 | Result := DoExecute(aObject, rmPATCH); 360 | end; 361 | 362 | function TRestRequest.Post(aObject: TObject): TResponse; 363 | begin 364 | Result := DoExecute(aObject, rmPOST); 365 | end; 366 | 367 | function TRestRequest.Put(aObject: TObject): TResponse; 368 | begin 369 | Result := DoExecute(aObject, rmPUT); 370 | end; 371 | 372 | function TRestRequest.AddAuthParameter(const AName, aValue: string; const AKind: TRESTRequestParameterKind; const AOptions: TRESTRequestParameterOptions): IRestRequest; 373 | begin 374 | FRequest.AddAuthParameter(AName, aValue, AKind, AOptions); 375 | Result := Self; 376 | end; 377 | 378 | function TRestRequest.AddBody(aObject: TObject): IRestRequest; 379 | begin 380 | FDTOObjectContainer.Add(aObject); 381 | Result := AddBody(aObject.ToString, ctDefault); 382 | end; 383 | 384 | function TRestRequest.GET(aValue: TValue): TResponse; 385 | begin 386 | Result := InternalParam(aValue).DoExecute(TRESTRequestMethod.rmGET); 387 | end; 388 | 389 | function TRestRequest.PATCH(aValue: TValue; aObject: TObject): TResponse; 390 | begin 391 | Result := InternalParam(aValue).DoExecute(aObject, rmPATCH); 392 | end; 393 | 394 | function TRestRequest.Post(aValue: TValue; aObject: TObject): TResponse; 395 | begin 396 | Result := InternalParam(aValue).DoExecute(aObject, rmPOST); 397 | end; 398 | 399 | function TRestRequest.Put(aValue: TValue; aObject: TObject): TResponse; 400 | begin 401 | Result := InternalParam(aValue).DoExecute(aObject, rmPUT); 402 | end; 403 | 404 | function TRestRequest.QueryParam(const AName: string; aValue: TValue): IRestRequest; 405 | var 406 | s: string; 407 | begin 408 | if FQueryParams.TryGetValue(AName, s) then 409 | FQueryParams[AName] := aValue.ToString 410 | else 411 | FQueryParams.Add(AName, aValue.ToString); 412 | 413 | Result := Self; 414 | end; 415 | 416 | end. 417 | -------------------------------------------------------------------------------- /ORM/DelphiRestOrm.ORM.Response.pas: -------------------------------------------------------------------------------- 1 | unit DelphiRestOrm.ORM.Response; 2 | 3 | interface 4 | 5 | uses 6 | REST.Client, DelphiRestOrm.ORM.Helper.ObjectContainer; 7 | 8 | type 9 | TResponse = class abstract 10 | strict protected 11 | FResponse: TCustomRESTResponse; 12 | FExternalObjectContainer: TObjectContainer; 13 | class function FromString(aContent: string; aObject: TObject): TObject; virtual; 14 | public 15 | constructor Create(aResponse: TCustomRESTResponse; aExternalObjectContainer: TObjectContainer); 16 | destructor Destroy; override; 17 | function AsType: T; overload; 18 | function AsType(aObject: T): T; overload; 19 | function ToString: string; override; 20 | end; 21 | 22 | TJsonResponse = class(TResponse) 23 | public 24 | class function FromString(aContent: string; aObject: TObject): TObject; override; 25 | end; 26 | 27 | implementation 28 | 29 | uses 30 | System.SysUtils, System.Generics.Collections, System.RTTI, System.JSONConsts, System.Json, 31 | REST.JsonReflect, REST.Json, REST.Types, 32 | 33 | DelphiRestOrm.ORM.Helper.GenericListHelper; 34 | 35 | { TResponse } 36 | function TResponse.AsType: T; 37 | begin 38 | Result := AsType(T.Create); 39 | end; 40 | 41 | function TResponse.AsType(aObject: T): T; 42 | begin 43 | if Assigned(FExternalObjectContainer) then 44 | FExternalObjectContainer.Add(aObject); 45 | 46 | case ContentTypeFromString(FResponse.ContentType) of 47 | ctAPPLICATION_JSON, ctAPPLICATION_VND_EMBARCADERO_FIREDAC_JSON: 48 | Result := TJsonResponse.FromString(FResponse.Content, aObject) as T; 49 | else 50 | raise ENotImplemented.Create('AsType are not implemented for content kind ' + FResponse.ContentType) 51 | end 52 | end; 53 | 54 | constructor TResponse.Create(aResponse: TCustomRESTResponse; aExternalObjectContainer: TObjectContainer); 55 | begin 56 | inherited Create; 57 | FResponse := aResponse; 58 | FExternalObjectContainer := aExternalObjectContainer; 59 | end; 60 | 61 | destructor TResponse.Destroy; 62 | begin 63 | inherited; 64 | end; 65 | 66 | class function TResponse.FromString(aContent: string; aObject: TObject): TObject; 67 | begin 68 | raise ENotImplemented.Create('Not Imlemented') 69 | end; 70 | 71 | function TResponse.ToString: string; 72 | begin 73 | Result := FResponse.Content; 74 | end; 75 | 76 | { TJsonResponse } 77 | 78 | class function TJsonResponse.FromString(aContent: string; aObject: TObject): TObject; 79 | const 80 | DefaultOptions = [joDateIsUTC, joDateFormatISO8601]; 81 | var 82 | JSONValue: TJsonValue; 83 | JSONObject: TJSONObject; 84 | RttiProperty: TRttiProperty; 85 | begin 86 | Result := aObject; 87 | 88 | (* 89 | Yes! I favor my own lib Delphi-JsonToDelphiClass. 90 | https://github.com/JensBorrisholt/Delphi-JsonToDelphiClass 91 | *) 92 | RttiProperty := TRttiContext.Create.GetType(aObject.ClassType).GetProperty('AsJson'); 93 | 94 | if (RttiProperty <> nil) and (RttiProperty.IsWritable) then 95 | begin 96 | RttiProperty.SetValue(aObject, aContent); 97 | exit; 98 | end; 99 | 100 | JSONValue := TJSONObject.ParseJSONValue(aContent); 101 | 102 | try 103 | if not Assigned(JSONValue) then 104 | exit; 105 | 106 | if (JSONValue is TJSONArray) then 107 | begin 108 | GenericListHelper.UnMarshall(aObject, JSONValue as TJSONArray); 109 | exit; 110 | end; 111 | 112 | if (JSONValue is TJSONObject) then 113 | JSONObject := JSONValue as TJSONObject 114 | else 115 | begin 116 | if (aContent = '') and not Assigned(JSONValue) or (aContent <> '') and Assigned(JSONValue) and JSONValue.Null then 117 | exit 118 | else 119 | raise EConversionError.Create(SCannotCreateObject); 120 | end; 121 | 122 | TJson.JsonToObject(Result, JSONObject, DefaultOptions); 123 | finally 124 | JSONValue.Free; 125 | end; 126 | end; 127 | 128 | end. 129 | -------------------------------------------------------------------------------- /ORM/Helpers/DelphiRestOrm.ORM.Helper.Azure.pas: -------------------------------------------------------------------------------- 1 | unit DelphiRestOrm.ORM.Helper.Azure; 2 | 3 | interface 4 | 5 | uses 6 | System.SysUtils, System.Classes, Data.Bind.ObjectScope, 7 | REST.Authenticator.Basic, REST.Client, REST.Types, REST.Json.Types; 8 | 9 | {$M+} 10 | 11 | type 12 | TCustomBindAuthenticator = class(TCustomAuthenticator) 13 | private 14 | FBindSource: TSubHTTPBasicAuthenticationBindSource; 15 | protected 16 | function CreateBindSource: TBaseObjectBindSource; override; 17 | published 18 | property BindSource: TSubHTTPBasicAuthenticationBindSource read FBindSource; 19 | end; 20 | 21 | TAccessToken = class 22 | strict private 23 | FContainsToken: Boolean; 24 | [JSONName('token_type')] 25 | FTokenType: string; 26 | [JSONName('expires_in')] 27 | FExpiresIn: Integer; 28 | [JSONName('ext_expires_in')] 29 | FResource: string; 30 | [JSONName('access_token')] 31 | FAccessToken: string; 32 | public 33 | property ContainsToken: Boolean read FContainsToken write FContainsToken; 34 | property TokenType: string read FTokenType write FTokenType; 35 | property ExpiresIn: Integer read FExpiresIn write FExpiresIn; 36 | property Resource: string read FResource write FResource; 37 | property AccessToken: string read FAccessToken write FAccessToken; 38 | end; 39 | 40 | TAzureAuthenticator = class(TCustomBindAuthenticator) 41 | private 42 | FClientId: string; 43 | FClientSecret: string; 44 | FTenantId: string; 45 | FResourceId: string; 46 | FAuthorizeEndPoint: string; 47 | procedure SetTenantId(const Value: string); 48 | procedure SetAuthorizeEndPoint(const Value: string); 49 | procedure SetClientId(const Value: string); 50 | procedure SetClientSecret(const Value: string); 51 | procedure SetResourceId(const Value: string); 52 | function GetAccessToken: TAccessToken; 53 | protected 54 | procedure DoAuthenticate(ARequest: TCustomRESTRequest); override; 55 | published 56 | property AuthorizeEndPoint: string read FAuthorizeEndPoint write SetAuthorizeEndPoint; 57 | property ClientId: string read FClientId write SetClientId; 58 | property TenantId: string read FTenantId write SetTenantId; 59 | property ClientSecret: string read FClientSecret write SetClientSecret; 60 | property ResourceId: string read FResourceId write SetResourceId; 61 | public 62 | constructor Create(const aTenantID, aClientID, aClientSecret, aResourceId: string); reintroduce; overload; 63 | end; 64 | 65 | implementation 66 | 67 | uses 68 | DelphiRestOrm.ORM.IRequest; 69 | 70 | { TAzureAuthenticator } 71 | 72 | constructor TAzureAuthenticator.Create(const aTenantID, aClientID, aClientSecret, aResourceId: string); 73 | begin 74 | inherited Create(nil); 75 | TenantId := aTenantID; 76 | ClientId := aClientID; 77 | ClientSecret := aClientSecret; 78 | ResourceId := aResourceId; 79 | end; 80 | 81 | procedure TAzureAuthenticator.DoAuthenticate(ARequest: TCustomRESTRequest); 82 | var 83 | AccessToken: TAccessToken; 84 | Param: TRESTRequestParameter; 85 | BodyParam: TRESTRequestParameter; 86 | begin 87 | AccessToken := GetAccessToken; 88 | ARequest.Params.AddItem('Authorization', AccessToken.TokenType + ' ' + AccessToken.AccessToken, TRESTRequestParameterKind.pkHTTPHEADER, [poDoNotEncode]); 89 | ARequest.AddParameter('Content-Type', ContentTypeToString(ctAPPLICATION_JSON), TRESTRequestParameterKind.pkHTTPHEADER, [poDoNotEncode]); 90 | ARequest.AddParameter('Accept', ContentTypeToString(ctAPPLICATION_JSON), TRESTRequestParameterKind.pkHTTPHEADER, [poDoNotEncode]); 91 | 92 | BodyParam := nil; 93 | for Param in ARequest.Params do 94 | if Param.Kind = TRESTRequestParameterKind.pkREQUESTBODY then 95 | begin 96 | BodyParam := Param; 97 | Break; 98 | end; 99 | 100 | if (BodyParam <> nil) and (BodyParam.Value <> '') then 101 | ARequest.Params.AddItem('Content-Length', (BodyParam.Value.Length).ToString, TRESTRequestParameterKind.pkHTTPHEADER); 102 | 103 | FreeAndNil(AccessToken); 104 | end; 105 | 106 | function TAzureAuthenticator.GetAccessToken: TAccessToken; 107 | begin 108 | Result := 109 | NewRequest(AuthorizeEndPoint) 110 | .AddAuthParameter('grant_type', 'client_credentials') 111 | .AddAuthParameter('client_id', ClientId) 112 | .AddAuthParameter('client_secret', ClientSecret) 113 | .AddAuthParameter('resource', ResourceId) 114 | .Post.AsType; 115 | 116 | Result.ContainsToken := (Result.TokenType = 'Bearer') and (Result.AccessToken.Length > 0); 117 | end; 118 | 119 | procedure TAzureAuthenticator.SetAuthorizeEndPoint(const Value: string); 120 | begin 121 | if (Value <> FAuthorizeEndPoint) then 122 | begin 123 | FAuthorizeEndPoint := Value; 124 | PropertyValueChanged; 125 | end; 126 | end; 127 | 128 | procedure TAzureAuthenticator.SetClientId(const Value: string); 129 | begin 130 | if (Value <> FClientId) then 131 | begin 132 | FClientId := Value; 133 | PropertyValueChanged; 134 | end; 135 | end; 136 | 137 | procedure TAzureAuthenticator.SetClientSecret(const Value: string); 138 | begin 139 | if (Value <> FClientSecret) then 140 | begin 141 | FClientSecret := Value; 142 | PropertyValueChanged; 143 | end; 144 | end; 145 | 146 | procedure TAzureAuthenticator.SetResourceId(const Value: string); 147 | begin 148 | if (Value <> FResourceId) then 149 | begin 150 | FResourceId := Value; 151 | PropertyValueChanged; 152 | end; 153 | end; 154 | 155 | procedure TAzureAuthenticator.SetTenantId(const Value: string); 156 | const 157 | AuthorizeEndPointTemplate = 'https://login.microsoftonline.com/{tenant}/oauth2/token'; 158 | begin 159 | if (Value <> FTenantId) then 160 | begin 161 | FTenantId := Value; 162 | PropertyValueChanged; 163 | end; 164 | 165 | AuthorizeEndPoint := AuthorizeEndPointTemplate.Replace('{tenant}', FTenantId); 166 | end; 167 | 168 | { TCustomBindAuthenticator } 169 | 170 | function TCustomBindAuthenticator.CreateBindSource: TBaseObjectBindSource; 171 | begin 172 | FBindSource := TSubHTTPBasicAuthenticationBindSource.Create(Self); 173 | FBindSource.Name := 'BindSource'; { Do not localize } 174 | FBindSource.SetSubComponent(True); 175 | FBindSource.Authenticator := Self; 176 | Result := FBindSource; 177 | end; 178 | 179 | end. 180 | -------------------------------------------------------------------------------- /ORM/Helpers/DelphiRestOrm.ORM.Helper.GenericListHelper.pas: -------------------------------------------------------------------------------- 1 | unit DelphiRestOrm.ORM.Helper.GenericListHelper; 2 | 3 | // CREDIT : Some of the code in this unit is heavly inspired by code found in Stefan Glienke DSharp project. 4 | 5 | interface 6 | 7 | uses 8 | System.Types, System.JSON; 9 | 10 | type 11 | GenericListHelper = record 12 | private 13 | {$IFDEF VER210 } 14 | function SplitString(const S: string; const Delimiter: Char): TStringDynArray; 15 | {$ENDIF} 16 | public 17 | class function IsGenericList(aObject: TObject): boolean; static; 18 | class function GetGenericType(aGenericList: TObject): TClass; static; 19 | class procedure UnMarshall(aGenericList: TObject; aJSONArray: TJSONArray); static; 20 | end; 21 | 22 | implementation 23 | 24 | uses 25 | System.Rtti, System.SysUtils, System.Classes, System.StrUtils, System.TypInfo, System.Generics.Collections, 26 | 27 | REST.JsonReflect; 28 | 29 | var 30 | Context: TRttiContext; 31 | 32 | type 33 | TJSONUnMarshallAcess = class(TJSONUnMarshal) 34 | 35 | end; 36 | 37 | TRttiTypeHelper = class helper for TRttiType 38 | private 39 | function ExtractGenericArguments(ATypeInfo: pTypeInfo): string; 40 | function FindType(const AName: string; out aType: TRttiType): boolean; 41 | public 42 | function GetGenericArguments: TArray; 43 | end; 44 | 45 | class function GenericListHelper.GetGenericType(aGenericList: TObject): TClass; 46 | var 47 | RttiTypes: TArray; 48 | begin 49 | if not IsGenericList(aGenericList) then 50 | raise Exception.Create('Provided object is either nil or is not a GenericList'); 51 | 52 | RttiTypes := Context.GetType(aGenericList.ClassType).GetGenericArguments; 53 | if (Length(RttiTypes) = 0) or (not RttiTypes[0].IsInstance) then 54 | raise Exception.Create('Provided object is either nil or is not a GenericList'); 55 | Result := RttiTypes[0].AsInstance.MetaclassType; 56 | end; 57 | 58 | class function GenericListHelper.IsGenericList(aObject: TObject): boolean; 59 | var 60 | RttiType: TRttiType; 61 | RttiField: TRttiField; 62 | begin 63 | Result := False; 64 | 65 | if aObject = nil then 66 | exit; 67 | 68 | RttiType := Context.GetType(aObject.ClassType); 69 | for RttiField in RttiType.GetFields do 70 | if RttiField.Visibility = TMemberVisibility.mvPrivate then 71 | if RttiField.FieldType.IsRecord then 72 | if RttiField.Name = 'FListHelper' then 73 | exit(True); 74 | end; 75 | 76 | class procedure GenericListHelper.UnMarshall(aGenericList: TObject; aJSONArray: TJSONArray); 77 | var 78 | Objects: TListOfObjects; 79 | Obj: TObject; 80 | begin 81 | with TJSONUnMarshallAcess.Create do 82 | try 83 | Objects := GetArgObjects(GetGenericType(aGenericList), aJSONArray); 84 | 85 | for Obj in Objects do 86 | TList(aGenericList).Add(Obj); 87 | finally 88 | Free; 89 | end; 90 | end; 91 | 92 | {$IFDEF VER210 } 93 | 94 | function TGenericListHelper.SplitString(const S: string; const Delimiter: Char): TStringDynArray; 95 | var 96 | Strings: TStrings; 97 | i: Integer; 98 | begin 99 | with TStringList.Create do 100 | try 101 | StrictDelimiter := True; 102 | Delimiter := Delimiter; 103 | DelimitedText := S; 104 | SetLength(Result, Count); 105 | for i := Low(Result) to High(Result) do 106 | Result[i] := Strings[i]; 107 | finally 108 | Free; 109 | end; 110 | end; 111 | {$ENDIF } 112 | { TRttiTypeHelper } 113 | 114 | function TRttiTypeHelper.ExtractGenericArguments(ATypeInfo: pTypeInfo): string; 115 | var 116 | i: Integer; 117 | S: string; 118 | begin 119 | S := UTF8ToString(ATypeInfo.Name); 120 | i := Pos('<', S); 121 | if i > 0 then 122 | Result := Copy(S, Succ(i), Length(S) - Succ(i)) 123 | else 124 | Result := '' 125 | end; 126 | 127 | function TRttiTypeHelper.FindType(const AName: string; out aType: TRttiType): boolean; 128 | var 129 | LType: TRttiType; 130 | begin 131 | Result := True; 132 | 133 | aType := Context.FindType(AName); 134 | if Assigned(aType) then 135 | exit; 136 | 137 | for LType in Context.GetTypes do 138 | if SameText(LType.Name, AName) then 139 | exit; 140 | 141 | Result := False; 142 | end; 143 | 144 | function TRttiTypeHelper.GetGenericArguments: TArray; 145 | var 146 | i: Integer; 147 | args: TStringDynArray; 148 | begin 149 | args := SplitString(ExtractGenericArguments(Handle), ','); 150 | if Length(args) > 0 then 151 | begin 152 | SetLength(Result, Length(args)); 153 | for i := 0 to Pred(Length(args)) do 154 | FindType(args[i], Result[i]); 155 | end 156 | else 157 | begin 158 | if Assigned(BaseType) then 159 | Result := BaseType.GetGenericArguments; 160 | end; 161 | end; 162 | 163 | initialization 164 | 165 | Context := TRttiContext.Create; 166 | 167 | end. 168 | -------------------------------------------------------------------------------- /ORM/Helpers/DelphiRestOrm.ORM.Helper.ObjectContainer.pas: -------------------------------------------------------------------------------- 1 | unit DelphiRestOrm.ORM.Helper.ObjectContainer; 2 | 3 | interface 4 | 5 | uses 6 | System.Classes, System.Generics.Collections; 7 | 8 | type 9 | TObjectContainer = class(TObjectList) 10 | public 11 | function Add: T; reintroduce; overload; 12 | function Add(aObject: T): T; overload; 13 | function Instance: T; 14 | procedure Remove; 15 | end; 16 | 17 | implementation 18 | 19 | uses 20 | System.TypInfo; 21 | { TObjectContainer } 22 | 23 | function TObjectContainer.Add: T; 24 | begin 25 | Result := Add(T.Create); 26 | end; 27 | 28 | function TObjectContainer.Add(aObject: T): T; 29 | begin 30 | inherited Add(aObject); 31 | Result := aObject; 32 | end; 33 | 34 | function TObjectContainer.Instance: T; 35 | var 36 | Obj: TObject; 37 | begin 38 | for Obj in Self do 39 | if (Obj is T) then 40 | exit(Obj as T); 41 | exit(nil); 42 | end; 43 | 44 | procedure TObjectContainer.Remove; 45 | var 46 | Obj: T; 47 | i: Integer; 48 | begin 49 | Obj := Instance; 50 | i := IndexOf(Obj); 51 | if i >= 0 then 52 | Delete(i); 53 | end; 54 | 55 | end. 56 | -------------------------------------------------------------------------------- /ORM/Helpers/DelphiRestOrm.ORM.Helper.ThreadingEx.pas: -------------------------------------------------------------------------------- 1 | unit DelphiRestOrm.ORM.Helper.ThreadingEx; 2 | 3 | interface 4 | 5 | uses 6 | SysUtils, 7 | Threading; 8 | 9 | type 10 | TAction = reference to procedure(const arg: T); 11 | 12 | TTaskContinuationOptions = (NotOnCompleted, NotOnFaulted, NotOnCanceled, OnlyOnCompleted, OnlyOnFaulted, OnlyOnCanceled); 13 | 14 | ITaskEx = interface(ITask) 15 | ['{3AE1A614-27AA-4B5A-BC50-42483650E20D}'] 16 | function GetExceptObj: Exception; 17 | function GetStatus: TTaskStatus; 18 | function ContinueWith(const continuationAction: TAction; continuationOptions: TTaskContinuationOptions): ITaskEx; 19 | function ContinueWithInMainThread(const continuationAction: TAction; continuationOptions: TTaskContinuationOptions): ITaskEx; 20 | property ExceptObj: Exception read GetExceptObj; 21 | property Status: TTaskStatus read GetStatus; 22 | end; 23 | 24 | TTaskEx = class(TTask, ITaskEx) 25 | private 26 | fExceptObj: Exception; 27 | function GetExceptObj: Exception; 28 | function InternalContinueWith(const continuationAction: TAction; continuationOptions: TTaskContinuationOptions; aMainThread: boolean): ITaskEx; 29 | 30 | protected 31 | function ContinueWith(const continuationAction: TAction; continuationOptions: TTaskContinuationOptions): ITaskEx; 32 | function ContinueWithInMainThread(const continuationAction: TAction; continuationOptions: TTaskContinuationOptions): ITaskEx; 33 | public 34 | destructor Destroy; override; 35 | class function Construct(const Proc: TProc): ITaskEx; overload; static; inline; 36 | class function Run(const action: TProc): ITaskEx; static; 37 | class function QueueMainThread(aDelay: Integer; const action: TProc): ITaskEx; overload; 38 | end; 39 | 40 | implementation 41 | 42 | uses 43 | Classes; 44 | 45 | { TTaskEx } 46 | 47 | function TTaskEx.ContinueWith(const continuationAction: TAction; continuationOptions: TTaskContinuationOptions): ITaskEx; 48 | begin 49 | Result := InternalContinueWith(continuationAction, continuationOptions, false); 50 | 51 | end; 52 | 53 | function TTaskEx.ContinueWithInMainThread(const continuationAction: TAction; continuationOptions: TTaskContinuationOptions): ITaskEx; 54 | begin 55 | Result := InternalContinueWith(continuationAction, continuationOptions, true); 56 | end; 57 | 58 | class function TTaskEx.Construct(const Proc: TProc): ITaskEx; 59 | begin 60 | Result := TTaskEx.Create(nil, TNotifyEvent(nil), Proc, TThreadPool.Default, nil); 61 | end; 62 | 63 | destructor TTaskEx.Destroy; 64 | begin 65 | fExceptObj.Free; 66 | inherited; 67 | end; 68 | 69 | function TTaskEx.GetExceptObj: Exception; 70 | begin 71 | Result := fExceptObj; 72 | end; 73 | 74 | function TTaskEx.InternalContinueWith(const continuationAction: TAction; continuationOptions: TTaskContinuationOptions; aMainThread: boolean): ITaskEx; 75 | begin 76 | Result := TTaskEx.Run( 77 | procedure 78 | var 79 | task: ITaskEx; 80 | doContinue: boolean; 81 | begin 82 | task := Self; 83 | if not IsComplete then 84 | DoneEvent.WaitFor; 85 | fExceptObj := GetExceptionObject; 86 | case continuationOptions of 87 | NotOnCompleted: 88 | doContinue := GetStatus <> TTaskStatus.Completed; 89 | NotOnFaulted: 90 | doContinue := GetStatus <> TTaskStatus.Exception; 91 | NotOnCanceled: 92 | doContinue := GetStatus <> TTaskStatus.Canceled; 93 | OnlyOnCompleted: 94 | doContinue := GetStatus = TTaskStatus.Completed; 95 | OnlyOnFaulted: 96 | doContinue := GetStatus = TTaskStatus.Exception; 97 | OnlyOnCanceled: 98 | doContinue := GetStatus = TTaskStatus.Canceled; 99 | else 100 | doContinue := false; 101 | end; 102 | 103 | if doContinue then 104 | if aMainThread then 105 | TThread.Synchronize(nil, 106 | procedure 107 | begin 108 | continuationAction(task); 109 | end) 110 | else 111 | continuationAction(task); 112 | end); 113 | end; 114 | 115 | class function TTaskEx.QueueMainThread(aDelay: Integer; const action: TProc): ITaskEx; 116 | begin 117 | Result := Run( 118 | procedure 119 | begin 120 | TThread.Sleep(aDelay); 121 | TThread.Queue(nil, 122 | procedure 123 | begin 124 | action; 125 | end); 126 | end); 127 | end; 128 | 129 | class function TTaskEx.Run(const action: TProc): ITaskEx; 130 | var 131 | task: TTaskEx; 132 | begin 133 | task := TTaskEx.Create(nil, TNotifyEvent(nil), action, TThreadPool.Default, nil); 134 | Result := task.Start as ITaskEx; 135 | end; 136 | 137 | end. 138 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Delphi-REST-Orm 2 | Delphi Rest Orm - At simple yet powerful ORM to query a WebService 3 | 4 | ## syntax Examples ## 5 | 6 | ``` pascal 7 | 8 | type 9 | TPerson = class 10 | private 11 | ... 12 | FAddress: string; 13 | ... 14 | published 15 | ... 16 | property Address: string read FAddress write FAddress; 17 | ... 18 | end; 19 | 20 | 21 | // Make the GET- requeest to a webservice, singin using basic authentication, and then parses the result into a type strong ObjecttList. 22 | var 23 | Persons: TObjectList; 24 | begin 25 | Persons := NewRequest('https://localhost:44303/Contact').Credentials('test', 'test').GET.AsType>; 26 | Memo1.Lines.Text := '(Random) Person.Address :' + Persons[Random(Persons.Count)].Address; 27 | Persons.Free; 28 | end; 29 | 30 | 31 | // Make a GET request, add a paramater, and return the raw content from the call 32 | var 33 | s: string; 34 | begin 35 | s := NewRequest('https://localhost:44303/Contact').GET(8).ToString; 36 | Memo1.Lines.Add(s); 37 | end; 38 | 39 | 40 | // Make a GET request to a Azure service. Using both a paramater and a query parameter. Returns the ras content 41 | var 42 | s: string; 43 | AzureAuthenticator: TAzureAuthenticator; 44 | begin 45 | AzureAuthenticator := TAzureAuthenticator.Create('', '', '', ''); 46 | s := NewRequest('').Param(17794452).QueryParam('country', 'DK').AzureCredentials(AzureAuthenticator).GET.ToString; 47 | Memo1.Lines.Add(s); 48 | end; 49 | 50 | // Make an async GET request to a Azure service. Using both a paramater and a query parameter. The response are recived inside a callback function 51 | var 52 | AzureAuthenticator: TAzureAuthenticator; 53 | begin 54 | AzureAuthenticator := TAzureAuthenticator.Create('', '', '', ''); 55 | 56 | NewRequestAsync('', 57 | procedure(const Response: TResponse) 58 | begin 59 | Memo1.Lines.Add(Response.ToString) 60 | end).Param(17794452).QueryParam('country', 'DK').AzureCredentials(AzureAuthenticator).GET; 61 | end; 62 | 63 | ``` 64 | 65 | 66 | 67 | Features : 68 | 69 | This OrM can easy query a WEB Service. Featuring automatic serialization and deserialization, request and response type detection, variety of authentications and other useful features- 70 | 71 | ## Serialization ## 72 | JSON serialization and deserialization 73 | 74 | ## Authentication ## 75 | Basic, Azure are supported. Not enough? Write your own! 76 | 77 | ## Sync and Async ## 78 | Variety of overloads to make synchronous and asynchronous HTTP calls 79 | 80 | ## Parameters ## 81 | Add query, URL segment, body, form or header parameter using an easy and fluent API 82 | -------------------------------------------------------------------------------- /RestServer/.vs/RestServer/DesignTimeBuild/.dtbcache.v2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JensBorrisholt/Delphi-REST-Orm/01cbb0ead71d3a98125e7613058e226ce76f330f/RestServer/.vs/RestServer/DesignTimeBuild/.dtbcache.v2 -------------------------------------------------------------------------------- /RestServer/.vs/RestServer/v16/.suo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JensBorrisholt/Delphi-REST-Orm/01cbb0ead71d3a98125e7613058e226ce76f330f/RestServer/.vs/RestServer/v16/.suo -------------------------------------------------------------------------------- /RestServer/Demo Data/Create Table.sql: -------------------------------------------------------------------------------- 1 | drop table Persons 2 | drop table #Persons 3 | 4 | Declare @JSON varchar(max) 5 | SELECT @JSON=BulkColumn 6 | FROM OPENROWSET (BULK 'C:\aa\DemoData.json', SINGLE_CLOB) import 7 | SELECT * 8 | into #Persons 9 | FROM OPENJSON (@JSON) 10 | WITH 11 | ( 12 | [id] NVARCHAR(24), 13 | [index] INT, 14 | [guid] NVARCHAR(36), 15 | [isActive] NVARCHAR(10), 16 | [balance] NVARCHAR(15), 17 | [picture] NVARCHAR(25), 18 | [age] INT, 19 | [eyeColor] NVARCHAR(5), 20 | [name] NVARCHAR(21), 21 | [gender] NVARCHAR(10), 22 | [company] NVARCHAR(7), 23 | [email] NVARCHAR(22), 24 | [phone] NVARCHAR(17), 25 | [address] NVARCHAR(100), 26 | [about] NVARCHAR(MAX), 27 | [registered] NVARCHAR(26), 28 | [latitude] float, 29 | [longitude] float, 30 | [greeting] NVARCHAR(47), 31 | [favoriteFruit] NVARCHAR(10) 32 | ) 33 | 34 | 35 | 36 | create table Persons 37 | ( 38 | [id] NVARCHAR(24) not null, 39 | [index] INT, 40 | [guid] NVARCHAR(36), 41 | [isActive] NVARCHAR(10), 42 | [balance] NVARCHAR(15), 43 | [picture] NVARCHAR(25), 44 | [age] INT, 45 | [eyeColor] NVARCHAR(5), 46 | [name] NVARCHAR(21), 47 | [gender] NVARCHAR(10), 48 | [company] NVARCHAR(7), 49 | [email] NVARCHAR(22), 50 | [phone] NVARCHAR(17), 51 | [address] NVARCHAR(100), 52 | [about] NVARCHAR(MAX), 53 | [registered] NVARCHAR(26), 54 | [latitude] float, 55 | [longitude] float, 56 | [greeting] NVARCHAR(47), 57 | [favoriteFruit] NVARCHAR(10), 58 | CONSTRAINT PK_Person PRIMARY KEY (ID) 59 | ) 60 | 61 | 62 | insert into Persons 63 | select * from #Persons -------------------------------------------------------------------------------- /RestServer/Demo Data/DemoData.json: -------------------------------------------------------------------------------- 1 | [{"id":"5fe30f37d7c04c9673a2c2a2","index":1,"guid":"2e71b858-a141-4ac6-8c7f-1d0c9d02d76d","isActive":"false","balance":"$2,464.88","picture":"http://placehold.it/32x32","age":34,"eyeColor":"green","name":"Mai Cherry","gender":"female","company":"QUORDAT","email":"maicherry@quordate.com","phone":"+1 (998) 403-3581","address":"934 Moore Street, Cucumber, Michigan, 182","about":"Laborum duis anim irure magna do laborum qui eu nisi. Nulla labore amet ullamco qui laborum. Labore dolor incididunt in tempor consequat eu laborum minim cillum fugiat ipsum aliqua ipsum. Proident cillum ex ex labore ad enim ipsum occaecat id in. Culpa sunt ullamco labore dolore quis eiusmod id. Aliqua ea consequat voluptate et do.\r\n","registered":"2015-08-26T08:36:30 -02:00","latitude":-51.445388,"longitude":-91.834623,"greeting":"Hello, Mai Cherry! You have 4 unread messages.","favoriteFruit":"apple"}] 2 | -------------------------------------------------------------------------------- /RestServer/RestServer.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30320.27 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RestServer", "RestServer\RestServer.csproj", "{35A89729-461B-48BD-A944-360D4B271EB8}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {35A89729-461B-48BD-A944-360D4B271EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {35A89729-461B-48BD-A944-360D4B271EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {35A89729-461B-48BD-A944-360D4B271EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {35A89729-461B-48BD-A944-360D4B271EB8}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {BA6514C3-4C16-4ABE-BA8E-36B088D87A1F} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /RestServer/RestServer/Controllers/ContactController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text.Json; 5 | using Microsoft.AspNetCore.Authorization; 6 | using Microsoft.AspNetCore.Mvc; 7 | using Microsoft.AspNetCore.Mvc.Infrastructure; 8 | using Newtonsoft.Json; 9 | using RestServer.Models; 10 | 11 | // For more information on enabling Web API for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860 12 | 13 | namespace RestServer.Controllers 14 | { 15 | [Route("[controller]")] 16 | [ApiController] 17 | public class ContactController : ControllerBase 18 | { 19 | private static readonly Random _random = new Random(); 20 | private static string RandomString(int length) 21 | { 22 | const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; 23 | return new string(Enumerable.Repeat(chars, length) 24 | .Select(s => s[_random.Next(s.Length)]).ToArray()); 25 | } 26 | 27 | // GET: 28 | [Authorize, RequireHttps, HttpGet] 29 | public IEnumerable Get() => new DemoDataContext().Persons.ToList(); 30 | 31 | 32 | // GET /5 33 | [HttpGet("{id}")] 34 | public Person Get(int id) => new DemoDataContext().Persons.SingleOrDefault(e => e.Index == id); 35 | 36 | 37 | // POST 38 | [HttpPost] 39 | public Person Post([FromBody] JsonElement value) 40 | { 41 | var context = new DemoDataContext(); 42 | var person = JsonConvert.DeserializeObject(value.ToString()); 43 | person.Id = RandomString(24); 44 | person.Index = (context.Persons.Max(e => e.Index) ?? 0) + 1; 45 | context.Add(person); 46 | context.SaveChanges(); 47 | return person; 48 | } 49 | 50 | // PUT /5 51 | [HttpPut("{id}")] 52 | public Person Put(int id, [FromBody] Person value) 53 | { 54 | var context = new DemoDataContext(); 55 | var dbPerson = context.Persons.SingleOrDefault(e => e.Index == id); 56 | 57 | if (dbPerson == default(Person)) 58 | throw new AmbiguousActionException($"No person by that id {id} found in the database"); 59 | 60 | dbPerson.UpdateValues(value); 61 | context.Update(dbPerson); 62 | context.SaveChanges(); 63 | return dbPerson; 64 | } 65 | 66 | // DELETE /5 67 | [HttpDelete("{id}")] 68 | public void Delete(int id) 69 | { 70 | var context = new DemoDataContext(); 71 | var dbPerson = context.Persons.SingleOrDefault(e => e.Index == id); 72 | 73 | if (dbPerson == default(Person)) 74 | throw new AmbiguousActionException($"No person by that id {id} found in the database"); 75 | 76 | context.Persons.Remove(dbPerson); 77 | context.SaveChanges(); 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /RestServer/RestServer/Helpers/BasicAuthenticationHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net.Http.Headers; 3 | using System.Security.Claims; 4 | using System.Text; 5 | using System.Text.Encodings.Web; 6 | using System.Threading.Tasks; 7 | using Microsoft.AspNetCore.Authentication; 8 | using Microsoft.AspNetCore.Authorization; 9 | using Microsoft.AspNetCore.Http; 10 | using Microsoft.Extensions.Logging; 11 | using Microsoft.Extensions.Options; 12 | using RestServer.Models; 13 | using RestServer.Services; 14 | 15 | namespace RestServer.Helpers 16 | { 17 | public class BasicAuthenticationHandler : AuthenticationHandler 18 | { 19 | private readonly IUserService _userService; 20 | 21 | public BasicAuthenticationHandler( 22 | IOptionsMonitor options, 23 | ILoggerFactory logger, 24 | UrlEncoder encoder, 25 | ISystemClock clock, 26 | IUserService userService) 27 | : base(options, logger, encoder, clock) 28 | { 29 | _userService = userService; 30 | } 31 | 32 | protected override async Task HandleAuthenticateAsync() 33 | { 34 | // skip authentication if endpoint has [AllowAnonymous] attribute 35 | var endpoint = Context.GetEndpoint(); 36 | if (endpoint?.Metadata?.GetMetadata() != null) 37 | return AuthenticateResult.NoResult(); 38 | 39 | if (!Request.Headers.ContainsKey("Authorization")) 40 | return AuthenticateResult.Fail("Missing Authorization Header"); 41 | 42 | User user = null; 43 | try 44 | { 45 | var authHeader = AuthenticationHeaderValue.Parse(Request.Headers["Authorization"]); 46 | var credentialBytes = Convert.FromBase64String(authHeader.Parameter); 47 | var credentials = Encoding.UTF8.GetString(credentialBytes).Split(new[] { ':' }, 2); 48 | var username = credentials[0]; 49 | var password = credentials[1]; 50 | user = await _userService.Authenticate(username, password); 51 | } 52 | catch 53 | { 54 | return AuthenticateResult.Fail("Invalid Authorization Header"); 55 | } 56 | 57 | if (user == null) 58 | return AuthenticateResult.Fail("Invalid Username or Password"); 59 | 60 | var claims = new[] { 61 | new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()), 62 | new Claim(ClaimTypes.Name, user.Username), 63 | }; 64 | var identity = new ClaimsIdentity(claims, Scheme.Name); 65 | var principal = new ClaimsPrincipal(identity); 66 | var ticket = new AuthenticationTicket(principal, Scheme.Name); 67 | 68 | return AuthenticateResult.Success(ticket); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /RestServer/RestServer/Helpers/ExtensionMethods.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using RestServer.Models; 4 | 5 | namespace RestServer.Helpers 6 | { 7 | public static class ExtensionMethods 8 | { 9 | public static IEnumerable WithoutPasswords(this IEnumerable users) => users.Select(x => x.WithoutPassword()); 10 | 11 | public static User WithoutPassword(this User user) 12 | { 13 | user.Password = null; 14 | return user; 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /RestServer/RestServer/Models/DemoDataContext.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.EntityFrameworkCore; 3 | using Microsoft.EntityFrameworkCore.Metadata; 4 | 5 | #nullable disable 6 | 7 | namespace RestServer.Models 8 | { 9 | public partial class DemoDataContext : DbContext 10 | { 11 | public DemoDataContext() 12 | { 13 | } 14 | 15 | public DemoDataContext(DbContextOptions options) 16 | : base(options) 17 | { 18 | } 19 | 20 | public virtual DbSet Persons { get; set; } 21 | 22 | protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) 23 | { 24 | if (!optionsBuilder.IsConfigured) 25 | { 26 | #warning To protect potentially sensitive information in your connection string, you should move it out of source code. You can avoid scaffolding the connection string by using the Name= syntax to read it from configuration - see https://go.microsoft.com/fwlink/?linkid=2131148. For more guidance on storing connection strings, see http://go.microsoft.com/fwlink/?LinkId=723263. 27 | optionsBuilder.UseSqlServer("Server=Dellserver;Database=DemoData;Integrated Security=True;"); 28 | } 29 | } 30 | 31 | protected override void OnModelCreating(ModelBuilder modelBuilder) 32 | { 33 | modelBuilder.HasAnnotation("Relational:Collation", "Danish_Norwegian_CI_AS"); 34 | 35 | modelBuilder.Entity(entity => 36 | { 37 | entity.Property(e => e.Id) 38 | .HasMaxLength(24) 39 | .HasColumnName("id"); 40 | 41 | entity.Property(e => e.About).HasColumnName("about"); 42 | 43 | entity.Property(e => e.Address) 44 | .HasMaxLength(43) 45 | .HasColumnName("address"); 46 | 47 | entity.Property(e => e.Age).HasColumnName("age"); 48 | 49 | entity.Property(e => e.Balance) 50 | .HasMaxLength(9) 51 | .HasColumnName("balance"); 52 | 53 | entity.Property(e => e.Company) 54 | .HasMaxLength(7) 55 | .HasColumnName("company"); 56 | 57 | entity.Property(e => e.Email) 58 | .HasMaxLength(22) 59 | .HasColumnName("email"); 60 | 61 | entity.Property(e => e.EyeColor) 62 | .HasMaxLength(5) 63 | .HasColumnName("eyeColor"); 64 | 65 | entity.Property(e => e.FavoriteFruit) 66 | .HasMaxLength(6) 67 | .HasColumnName("favoriteFruit"); 68 | 69 | entity.Property(e => e.Gender) 70 | .HasMaxLength(10) 71 | .HasColumnName("gender"); 72 | 73 | entity.Property(e => e.Greeting) 74 | .HasMaxLength(47) 75 | .HasColumnName("greeting"); 76 | 77 | entity.Property(e => e.Guid) 78 | .HasMaxLength(36) 79 | .HasColumnName("guid"); 80 | 81 | entity.Property(e => e.Index).HasColumnName("index"); 82 | 83 | entity.Property(e => e.IsActive) 84 | .HasMaxLength(4) 85 | .HasColumnName("isActive"); 86 | 87 | entity.Property(e => e.Latitude).HasColumnName("latitude"); 88 | 89 | entity.Property(e => e.Longitude).HasColumnName("longitude"); 90 | 91 | entity.Property(e => e.Name) 92 | .HasMaxLength(21) 93 | .HasColumnName("name"); 94 | 95 | entity.Property(e => e.Phone) 96 | .HasMaxLength(17) 97 | .HasColumnName("phone"); 98 | 99 | entity.Property(e => e.Picture) 100 | .HasMaxLength(25) 101 | .HasColumnName("picture"); 102 | 103 | entity.Property(e => e.Registered) 104 | .HasMaxLength(26) 105 | .HasColumnName("registered"); 106 | }); 107 | 108 | OnModelCreatingPartial(modelBuilder); 109 | } 110 | 111 | partial void OnModelCreatingPartial(ModelBuilder modelBuilder); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /RestServer/RestServer/Models/Person.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Microsoft.EntityFrameworkCore; 4 | 5 | #nullable disable 6 | 7 | namespace RestServer.Models 8 | { 9 | public partial class Person 10 | { 11 | public string Id { get; set; } 12 | public int? Index { get; set; } 13 | public string Guid { get; set; } 14 | public string IsActive { get; set; } 15 | public string Balance { get; set; } 16 | public string Picture { get; set; } 17 | public int? Age { get; set; } 18 | public string EyeColor { get; set; } 19 | public string Name { get; set; } 20 | public string Gender { get; set; } 21 | public string Company { get; set; } 22 | public string Email { get; set; } 23 | public string Phone { get; set; } 24 | public string Address { get; set; } 25 | public string About { get; set; } 26 | public string Registered { get; set; } 27 | public double? Latitude { get; set; } 28 | public double? Longitude { get; set; } 29 | public string Greeting { get; set; } 30 | public string FavoriteFruit { get; set; } 31 | 32 | public Person UpdateValues(Person from) 33 | { 34 | if (Id != from.Id) 35 | throw new ApplicationException(); 36 | 37 | foreach (var property in from.GetType().GetProperties()) 38 | property.SetValue(this, property.GetValue(from)); 39 | 40 | return this; 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /RestServer/RestServer/Models/User.cs: -------------------------------------------------------------------------------- 1 | namespace RestServer.Models 2 | { 3 | public class User 4 | { 5 | public int Id { get; set; } 6 | public string FirstName { get; set; } 7 | public string LastName { get; set; } 8 | public string Username { get; set; } 9 | public string Password { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /RestServer/RestServer/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Hosting; 6 | using Microsoft.Extensions.Configuration; 7 | using Microsoft.Extensions.Hosting; 8 | using Microsoft.Extensions.Logging; 9 | 10 | namespace RestServer 11 | { 12 | public class Program 13 | { 14 | public static void Main(string[] args) 15 | { 16 | CreateHostBuilder(args).Build().Run(); 17 | } 18 | 19 | public static IHostBuilder CreateHostBuilder(string[] args) => 20 | Host.CreateDefaultBuilder(args) 21 | .ConfigureWebHostDefaults(webBuilder => 22 | { 23 | webBuilder.UseStartup(); 24 | }); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /RestServer/RestServer/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/launchsettings.json", 3 | "iisSettings": { 4 | "windowsAuthentication": false, 5 | "anonymousAuthentication": true, 6 | "iisExpress": { 7 | "applicationUrl": "http://localhost:51155", 8 | "sslPort": 44303 9 | } 10 | }, 11 | "profiles": { 12 | "IIS Express": { 13 | "commandName": "IISExpress", 14 | "launchBrowser": true, 15 | "launchUrl": "Contact/1", 16 | "environmentVariables": { 17 | "ASPNETCORE_ENVIRONMENT": "Development" 18 | } 19 | }, 20 | "RestServer": { 21 | "commandName": "Project", 22 | "launchBrowser": true, 23 | "launchUrl": "Contact/1", 24 | "applicationUrl": "https://localhost:5001;http://localhost:5000", 25 | "environmentVariables": { 26 | "ASPNETCORE_ENVIRONMENT": "Development" 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /RestServer/RestServer/RestServer.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | all 13 | runtime; build; native; contentfiles; analyzers; buildtransitive 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /RestServer/RestServer/RestServer.csproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | MvcControllerEmptyScaffolder 5 | root/Common/MVC/Controller 6 | 7 | -------------------------------------------------------------------------------- /RestServer/RestServer/Services/UserService.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | using RestServer.Helpers; 5 | using RestServer.Models; 6 | 7 | namespace RestServer.Services 8 | { 9 | public interface IUserService 10 | { 11 | Task Authenticate(string username, string password); 12 | } 13 | 14 | public class UserService : IUserService 15 | { 16 | // users hardcoded for simplicity, store in a db with hashed passwords in production applications 17 | private readonly List _users = new List 18 | { 19 | new User { Id = 1, FirstName = "Test", LastName = "User", Username = "test", Password = "test" } 20 | }; 21 | 22 | public async Task Authenticate(string username, string password) 23 | { 24 | var user = await Task.Run(() => _users.SingleOrDefault(x => x.Username == username && x.Password == password)); 25 | 26 | // return null if user not found 27 | return user?.WithoutPassword(); 28 | 29 | // authentication successful so return user details without password 30 | } 31 | 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /RestServer/RestServer/Startup.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Authentication; 2 | using Microsoft.AspNetCore.Builder; 3 | using Microsoft.AspNetCore.Hosting; 4 | using Microsoft.Extensions.Configuration; 5 | using Microsoft.Extensions.DependencyInjection; 6 | using Microsoft.Extensions.Hosting; 7 | using RestServer.Helpers; 8 | using RestServer.Services; 9 | 10 | namespace RestServer 11 | { 12 | public class Startup 13 | { 14 | public Startup(IConfiguration configuration) 15 | { 16 | Configuration = configuration; 17 | } 18 | 19 | public IConfiguration Configuration { get; } 20 | 21 | // This method gets called by the runtime. Use this method to add services to the container. 22 | public void ConfigureServices(IServiceCollection services) 23 | { 24 | // configure basic authentication 25 | services.AddAuthentication("BasicAuthentication") 26 | .AddScheme("BasicAuthentication", null); 27 | 28 | // configure DI for application services 29 | services.AddScoped(); 30 | 31 | services.AddControllers(); 32 | } 33 | 34 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 35 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 36 | { 37 | if (env.IsDevelopment()) 38 | { 39 | app.UseDeveloperExceptionPage(); 40 | } 41 | 42 | app.UseHttpsRedirection(); 43 | 44 | app.UseRouting(); 45 | 46 | app.UseAuthentication(); 47 | app.UseAuthorization(); 48 | 49 | app.UseEndpoints(endpoints => 50 | { 51 | endpoints.MapControllers(); 52 | }); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /RestServer/RestServer/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /RestServer/RestServer/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "AllowedHosts": "*" 10 | } 11 | --------------------------------------------------------------------------------