├── .gitattributes ├── .gitignore ├── 01 - What's RAD Server - Introduction ├── 01 - What's RAD Server - Introduction.pdf └── README.md ├── 02 - Using the RAD Wizard to Create a “hello world” ├── C++ │ ├── HelloWorldPackage.cbproj │ ├── HelloWorldPackage.cpp │ ├── MyDM.cpp │ ├── MyDM.dfm │ └── MyDM.h ├── Delphi │ ├── HelloWorldPackage.dpk │ ├── HelloWorldPackage.dproj │ ├── MyDM.dfm │ ├── MyDM.pas │ └── MyHelloWorldDelphiRADServerPackage.res └── README.md ├── 03 - Creating your first CRUD Application ├── C++ │ ├── FirstCRUDPackage.cbproj │ ├── FirstCRUDPackage.cpp │ ├── MyDM.cpp │ ├── MyDM.dfm │ └── MyDM.h ├── Delphi │ ├── FirstCRUDPackage.dpk │ ├── FirstCRUDPackage.dproj │ ├── FirstCRUDPackage.res │ ├── MyDM.dfm │ ├── MyDM.pas │ └── MyFirstCRUDDelphiRADServerPackage.res └── README.md ├── 04 - REST Debugger ├── C++ │ ├── ComponentsFromRESTDebugger.cbproj │ ├── ComponentsFromRESTDebugger.cpp │ ├── ComponentsFromRESTDebuggerPCH1.h │ ├── MyForm01.cpp │ ├── MyForm01.fmx │ ├── MyForm01.h │ └── MyForm01.vlb ├── Delphi │ ├── ComponentsFromRESTDebugger.dpr │ ├── ComponentsFromRESTDebugger.dproj │ ├── ComponentsFromRESTDebugger.res │ ├── GroupProject.groupproj │ ├── MyForm01.fmx │ ├── MyForm01.pas │ └── MyForm01.vlb └── README.md ├── 05 - Using FireDAC Batch Move and JSONWriter ├── C++ │ ├── FireDACPackage.cbproj │ ├── FireDACPackage.cpp │ ├── MyDM.cpp │ ├── MyDM.dfm │ └── MyDM.h ├── Delphi │ ├── FireDACPackage.dpk │ ├── FireDACPackage.dproj │ ├── FireDACPackage.res │ ├── MyDM.dfm │ ├── MyDM.pas │ └── RADServerFireDAC.res └── README.md ├── 06 - JSONValue, JSONWriter and JSONBuilder ├── C++ │ ├── JSONPackage.cbproj │ ├── JSONPackage.cpp │ ├── MyDM.cpp │ ├── MyDM.dfm │ └── MyDM.h ├── Delphi │ ├── JSONPackage.dpk │ ├── JSONPackage.dproj │ ├── MyDM.dfm │ ├── MyDM.pas │ └── RADServerJSON.res └── README.md ├── 07 - Creating your own customized endpoints ├── C++ │ ├── CustomEndpointsPackage.cbproj │ ├── CustomEndpointsPackage.cpp │ ├── MyDMUnit.cpp │ ├── MyDMUnit.dfm │ └── MyDMUnit.h ├── Delphi │ ├── CustomEndpointsPackage.dpk │ ├── CustomEndpointsPackage.dproj │ ├── CustomEndpointsPackage.res │ ├── MyDM.dfm │ └── MyDM.pas └── README.md ├── 08 - Accessing the built-in analytics └── README.md ├── 09 - Deploying RAD Server └── README.md ├── 10 - RAD Server Lite └── README.md ├── 11 - Authentication and Authorization ├── C++ │ ├── CustomLoginPackageCpp.cbproj │ ├── CustomLoginPackageCpp.cpp │ ├── CustomLoginPackageCpp.res │ ├── CustomLoginResourceU.cpp │ └── CustomLoginResourceU.h ├── Delphi │ ├── CustomLoginPackage.dpk │ ├── CustomLoginPackage.dproj │ ├── CustomLoginPackage.res │ ├── CustomLoginResourceU.pas │ └── readme.md └── README.md ├── 12 - Documenting and testing your endpoints using OpenAPI ├── C++ │ ├── APIDocSpecs.cpp │ ├── APIDocSpecs.h │ ├── Common.cpp │ ├── Common.h │ ├── CustomAPIDocPackage.cbproj │ ├── CustomAPIDocPackage.cpp │ ├── MyDM.cpp │ ├── MyDM.dfm │ └── MyDM.h ├── Delphi │ ├── APIDocSpecs.pas │ ├── Common.pas │ ├── CustomAPIDocPackage.dpk │ ├── CustomAPIDocPackage.dproj │ ├── CustomAPIDocPackage.res │ ├── FirstCRUDPackage.res │ ├── MyDM.dfm │ ├── MyDM.pas │ └── MyFirstCRUDDelphiRADServerPackage.res └── README.md ├── 13 - File management and storage ├── C++ │ ├── FileManagement.cbproj │ ├── FileManagement.cpp │ ├── MyDM.cpp │ ├── MyDM.dfm │ └── MyDM.h ├── Delphi │ ├── FileManagement.delphilsp.json │ ├── FileManagement.dpk │ ├── FileManagement.dproj │ ├── FileManagement.res │ ├── MyDM.dfm │ ├── MyDM.pas │ └── Project1.res └── README.md ├── LICENSE ├── README.md └── resources └── employee.gdb /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.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 | # 72 | .DS_Store 73 | Android64/ 74 | Android32/ 75 | Windows32/ 76 | Windows64/ 77 | iOSDevice64/ 78 | Android/ 79 | iOSSimARM64/ 80 | OSXARM64/ 81 | OSX64/ 82 | Win32/ 83 | Win64/ 84 | __astcache/ -------------------------------------------------------------------------------- /01 - What's RAD Server - Introduction/01 - What's RAD Server - Introduction.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Embarcadero/RADServer-Docs/b6cecd34bb509284d5f7d8d0d3aed5352fecc529/01 - What's RAD Server - Introduction/01 - What's RAD Server - Introduction.pdf -------------------------------------------------------------------------------- /01 - What's RAD Server - Introduction/README.md: -------------------------------------------------------------------------------- 1 | ## Chapter 1: What's RAD Server - Introduction 2 | 3 | Don't forget to [watch the video](https://youtu.be/jWdt-5Z3lrY) of this chapter. 4 | 5 | In this first chapter we will do a brief introduction to REST API as well as what RAD Server is. This documentation will show you how to quickly design, build, debug and deploy service based multi-tier applications using RAD Server’s REST based API hosting engine, components and technologies that are available in RAD Studio Enterprise and Architect editions. 6 | 7 | Here you will find the first chapter of the tecnical guide. You can download for free the whole guide [on this link](https://lp.embarcadero.com/RADServerGuide). -------------------------------------------------------------------------------- /02 - Using the RAD Wizard to Create a “hello world”/C++/HelloWorldPackage.cpp: -------------------------------------------------------------------------------- 1 | // EMS Package 2 | //--------------------------------------------------------------------------- 3 | 4 | #include 5 | #pragma hdrstop 6 | USEFORM("MyDM.cpp", TestResource1); /* TDataModule: File Type */ 7 | //--------------------------------------------------------------------------- 8 | #pragma package(smart_init) 9 | //--------------------------------------------------------------------------- 10 | 11 | // Package source. 12 | //--------------------------------------------------------------------------- 13 | 14 | 15 | #pragma argsused 16 | extern "C" int _libmain(unsigned long reason) 17 | { 18 | return 1; 19 | } 20 | //--------------------------------------------------------------------------- 21 | 22 | 23 | -------------------------------------------------------------------------------- /02 - Using the RAD Wizard to Create a “hello world”/C++/MyDM.cpp: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------- 2 | #pragma hdrstop 3 | 4 | #include "MyDM.h" 5 | #include 6 | //--------------------------------------------------------------------------- 7 | #pragma package(smart_init) 8 | #pragma classgroup "System.Classes.TPersistent" 9 | #pragma resource "*.dfm" 10 | //--------------------------------------------------------------------------- 11 | __fastcall TTestResource1::TTestResource1(TComponent* Owner) 12 | : TDataModule(Owner) 13 | { 14 | } 15 | 16 | void TTestResource1::Get(TEndpointContext* AContext, TEndpointRequest* ARequest, TEndpointResponse* AResponse) 17 | { 18 | AResponse->Body->SetValue(new TJSONString("Hello World"), true); 19 | } 20 | 21 | void TTestResource1::GetItem(TEndpointContext* AContext, TEndpointRequest* ARequest, TEndpointResponse* AResponse) 22 | { 23 | String item; 24 | item = ARequest->Params->Values["item"]; 25 | AResponse->Body->SetValue(new TJSONString("Hello World " + item), true); 26 | } 27 | 28 | static void Register() 29 | { 30 | std::unique_ptr attributes(new TEMSResourceAttributes()); 31 | attributes->ResourceName = "test"; 32 | attributes->ResourceSuffix["GetItem"] = "{item}"; 33 | RegisterResource(__typeinfo(TTestResource1), attributes.release()); 34 | } 35 | 36 | #pragma startup Register 32 37 | -------------------------------------------------------------------------------- /02 - Using the RAD Wizard to Create a “hello world”/C++/MyDM.dfm: -------------------------------------------------------------------------------- 1 | object TestResource1: TTestResource1 2 | Height = 375 3 | Width = 750 4 | PixelsPerInch = 120 5 | end 6 | -------------------------------------------------------------------------------- /02 - Using the RAD Wizard to Create a “hello world”/C++/MyDM.h: -------------------------------------------------------------------------------- 1 | // EMS Resource Modules 2 | //--------------------------------------------------------------------------- 3 | 4 | #ifndef MyDMH 5 | #define MyDMH 6 | //--------------------------------------------------------------------------- 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | //--------------------------------------------------------------------------- 13 | #pragma explicit_rtti methods (public) 14 | class TTestResource1 : public TDataModule 15 | { 16 | __published: 17 | private: 18 | public: 19 | __fastcall TTestResource1(TComponent* Owner); 20 | void Get(TEndpointContext* AContext, TEndpointRequest* ARequest, TEndpointResponse* AResponse); 21 | void GetItem(TEndpointContext* AContext, TEndpointRequest* ARequest, TEndpointResponse* AResponse); 22 | }; 23 | #endif 24 | -------------------------------------------------------------------------------- /02 - Using the RAD Wizard to Create a “hello world”/Delphi/HelloWorldPackage.dpk: -------------------------------------------------------------------------------- 1 | package HelloWorldPackage; 2 | 3 | {$R *.res} 4 | {$IFDEF IMPLICITBUILDING This IFDEF should not be used by users} 5 | {$ALIGN 8} 6 | {$ASSERTIONS ON} 7 | {$BOOLEVAL OFF} 8 | {$DEBUGINFO OFF} 9 | {$EXTENDEDSYNTAX ON} 10 | {$IMPORTEDDATA ON} 11 | {$IOCHECKS ON} 12 | {$LOCALSYMBOLS ON} 13 | {$LONGSTRINGS ON} 14 | {$OPENSTRINGS ON} 15 | {$OPTIMIZATION OFF} 16 | {$OVERFLOWCHECKS ON} 17 | {$RANGECHECKS ON} 18 | {$REFERENCEINFO ON} 19 | {$SAFEDIVIDE OFF} 20 | {$STACKFRAMES ON} 21 | {$TYPEDADDRESS OFF} 22 | {$VARSTRINGCHECKS ON} 23 | {$WRITEABLECONST OFF} 24 | {$MINENUMSIZE 1} 25 | {$IMAGEBASE $400000} 26 | {$DEFINE DEBUG} 27 | {$ENDIF IMPLICITBUILDING} 28 | {$RUNONLY} 29 | {$IMPLICITBUILD ON} 30 | 31 | requires 32 | rtl, 33 | emsserverapi; 34 | 35 | contains 36 | MyDM in 'MyDM.pas' {TestResource1: TDataModule}; 37 | 38 | end. 39 | -------------------------------------------------------------------------------- /02 - Using the RAD Wizard to Create a “hello world”/Delphi/MyDM.dfm: -------------------------------------------------------------------------------- 1 | object TestResource1: TTestResource1 2 | Height = 375 3 | Width = 750 4 | PixelsPerInch = 120 5 | end 6 | -------------------------------------------------------------------------------- /02 - Using the RAD Wizard to Create a “hello world”/Delphi/MyDM.pas: -------------------------------------------------------------------------------- 1 | unit MyDM; 2 | 3 | // EMS Resource Module 4 | 5 | interface 6 | 7 | uses 8 | System.SysUtils, 9 | System.Classes, 10 | System.JSON, 11 | EMS.Services, 12 | EMS.ResourceAPI, 13 | EMS.ResourceTypes; 14 | 15 | type 16 | 17 | [ResourceName('Test')] 18 | TTestResource1 = class(TDataModule) 19 | published 20 | procedure Get(const AContext: TEndpointContext; const ARequest: TEndpointRequest; 21 | const AResponse: TEndpointResponse); 22 | [ResourceSuffix('{item}')] 23 | procedure GetItem(const AContext: TEndpointContext; const ARequest: TEndpointRequest; 24 | const AResponse: TEndpointResponse); 25 | end; 26 | 27 | implementation 28 | 29 | {%CLASSGROUP 'System.Classes.TPersistent'} 30 | {$R *.dfm} 31 | 32 | procedure TTestResource1.Get(const AContext: TEndpointContext; const ARequest: TEndpointRequest; 33 | const AResponse: TEndpointResponse); 34 | begin 35 | AResponse.Body.SetValue(TJSONString.Create('Hello World'), True); 36 | end; 37 | 38 | procedure TTestResource1.GetItem(const AContext: TEndpointContext; const ARequest: TEndpointRequest; 39 | const AResponse: TEndpointResponse); 40 | var LItem: string; 41 | begin 42 | LItem := ARequest.Params.Values['item']; 43 | AResponse.Body.SetValue(TJSONString.Create('Hello World ' + LItem), True); 44 | end; 45 | 46 | procedure Register; 47 | begin 48 | RegisterResource(TypeInfo(TTestResource1)); 49 | end; 50 | 51 | initialization 52 | 53 | Register; 54 | 55 | end. 56 | -------------------------------------------------------------------------------- /02 - Using the RAD Wizard to Create a “hello world”/Delphi/MyHelloWorldDelphiRADServerPackage.res: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Embarcadero/RADServer-Docs/b6cecd34bb509284d5f7d8d0d3aed5352fecc529/02 - Using the RAD Wizard to Create a “hello world”/Delphi/MyHelloWorldDelphiRADServerPackage.res -------------------------------------------------------------------------------- /02 - Using the RAD Wizard to Create a “hello world”/README.md: -------------------------------------------------------------------------------- 1 | ## Chapter 2: Using the RAD Wizard to Create a “hello world” 2 | 3 | Don't forget to [watch the video](https://youtu.be/CVQh-CDnGEA) of this chapter. 4 | 5 | It’s time to start programming. In this chapter, you’ll learn how to build your first RAD Server based service applications using Delphi and C++Builder. 6 | 7 | In the demos folders you will find the examples of this chapter. The project has no dependencies or any other extra configuration. 8 | 9 | -------------------------------------------------------------------------------- /03 - Creating your first CRUD Application/C++/FirstCRUDPackage.cpp: -------------------------------------------------------------------------------- 1 | // EMS Package 2 | //--------------------------------------------------------------------------- 3 | 4 | #include 5 | #pragma hdrstop 6 | USEFORM("MyDM.cpp", TestResource1); /* TDataModule: File Type */ 7 | //--------------------------------------------------------------------------- 8 | #pragma package(smart_init) 9 | //--------------------------------------------------------------------------- 10 | 11 | // Package source. 12 | //--------------------------------------------------------------------------- 13 | 14 | 15 | #pragma argsused 16 | extern "C" int _libmain(unsigned long reason) 17 | { 18 | return 1; 19 | } 20 | //--------------------------------------------------------------------------- 21 | 22 | 23 | -------------------------------------------------------------------------------- /03 - Creating your first CRUD Application/C++/MyDM.cpp: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------- 2 | #pragma hdrstop 3 | 4 | #include "MyDM.h" 5 | #include 6 | //--------------------------------------------------------------------------- 7 | #pragma package(smart_init) 8 | #pragma classgroup "System.Classes.TPersistent" 9 | #pragma resource "*.dfm" 10 | //--------------------------------------------------------------------------- 11 | __fastcall TTestResource1::TTestResource1(TComponent* Owner) 12 | : TDataModule(Owner) 13 | { 14 | } 15 | 16 | static void Register() 17 | { 18 | std::unique_ptr attributes(new TEMSResourceAttributes()); 19 | attributes->ResourceName = "test"; 20 | attributes->ResourceSuffix["dsrCUSTOMER"] = "customers"; 21 | attributes->ResourceSuffix["dsrSALES"] = "sales"; 22 | 23 | RegisterResource(__typeinfo(TTestResource1), attributes.release()); 24 | } 25 | 26 | #pragma startup Register 32 27 | -------------------------------------------------------------------------------- /03 - Creating your first CRUD Application/C++/MyDM.dfm: -------------------------------------------------------------------------------- 1 | object TestResource1: TTestResource1 2 | Height = 375 3 | Width = 750 4 | PixelsPerInch = 120 5 | object FDConnection1: TFDConnection 6 | Params.Strings = ( 7 | 'ConnectionDef=EMPLOYEE') 8 | LoginPrompt = False 9 | Left = 38 10 | Top = 20 11 | end 12 | object qryCUSTOMER: TFDQuery 13 | Connection = FDConnection1 14 | SQL.Strings = ( 15 | 'select * from CUSTOMER' 16 | '{if !SORT}order by !SORT{fi}') 17 | Left = 163 18 | Top = 20 19 | MacroData = < 20 | item 21 | Value = Null 22 | Name = 'SORT' 23 | end> 24 | end 25 | object dsrCUSTOMER: TEMSDataSetResource 26 | AllowedActions = [List, Get, Post, Put, Delete] 27 | DataSet = qryCUSTOMER 28 | Left = 163 29 | Top = 80 30 | end 31 | object qrySALES: TFDQuery 32 | Connection = FDConnection1 33 | SQL.Strings = ( 34 | 'select * from SALES' 35 | '{if !SORT}order by !SORT{fi}') 36 | Left = 288 37 | Top = 20 38 | MacroData = < 39 | item 40 | Value = Null 41 | Name = 'SORT' 42 | end> 43 | end 44 | object dsrSALES: TEMSDataSetResource 45 | AllowedActions = [List, Get, Post, Put, Delete] 46 | DataSet = qrySALES 47 | Left = 288 48 | Top = 80 49 | end 50 | end 51 | -------------------------------------------------------------------------------- /03 - Creating your first CRUD Application/C++/MyDM.h: -------------------------------------------------------------------------------- 1 | // EMS Resource Modules 2 | //--------------------------------------------------------------------------- 3 | 4 | #ifndef MyDMH 5 | #define MyDMH 6 | //--------------------------------------------------------------------------- 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | //--------------------------------------------------------------------------- 33 | #pragma explicit_rtti methods (public) 34 | class TTestResource1 : public TDataModule 35 | { 36 | __published: 37 | TFDConnection *FDConnection1; 38 | TFDQuery *qryCUSTOMER; 39 | TEMSDataSetResource *dsrCUSTOMER; 40 | TFDQuery *qrySALES; 41 | TEMSDataSetResource *dsrSALES; 42 | 43 | private: 44 | public: 45 | __fastcall TTestResource1(TComponent* Owner); 46 | }; 47 | #endif 48 | -------------------------------------------------------------------------------- /03 - Creating your first CRUD Application/Delphi/FirstCRUDPackage.dpk: -------------------------------------------------------------------------------- 1 | package FirstCRUDPackage; 2 | 3 | {$R *.res} 4 | {$IFDEF IMPLICITBUILDING This IFDEF should not be used by users} 5 | {$ALIGN 8} 6 | {$ASSERTIONS ON} 7 | {$BOOLEVAL OFF} 8 | {$DEBUGINFO OFF} 9 | {$EXTENDEDSYNTAX ON} 10 | {$IMPORTEDDATA ON} 11 | {$IOCHECKS ON} 12 | {$LOCALSYMBOLS ON} 13 | {$LONGSTRINGS ON} 14 | {$OPENSTRINGS ON} 15 | {$OPTIMIZATION OFF} 16 | {$OVERFLOWCHECKS ON} 17 | {$RANGECHECKS ON} 18 | {$REFERENCEINFO ON} 19 | {$SAFEDIVIDE OFF} 20 | {$STACKFRAMES ON} 21 | {$TYPEDADDRESS OFF} 22 | {$VARSTRINGCHECKS ON} 23 | {$WRITEABLECONST OFF} 24 | {$MINENUMSIZE 1} 25 | {$IMAGEBASE $400000} 26 | {$DEFINE DEBUG} 27 | {$ENDIF IMPLICITBUILDING} 28 | {$RUNONLY} 29 | {$IMPLICITBUILD ON} 30 | 31 | requires 32 | rtl, 33 | emsserverapi, 34 | dbrtl, 35 | emsserverresource, 36 | FireDACCommonDriver, 37 | FireDACCommon, 38 | FireDAC, 39 | FireDACIBDriver; 40 | 41 | contains 42 | MyDM in 'MyDM.pas' {TestResource1: TDataModule}; 43 | 44 | end. 45 | -------------------------------------------------------------------------------- /03 - Creating your first CRUD Application/Delphi/FirstCRUDPackage.res: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Embarcadero/RADServer-Docs/b6cecd34bb509284d5f7d8d0d3aed5352fecc529/03 - Creating your first CRUD Application/Delphi/FirstCRUDPackage.res -------------------------------------------------------------------------------- /03 - Creating your first CRUD Application/Delphi/MyDM.dfm: -------------------------------------------------------------------------------- 1 | object TestResource1: TTestResource1 2 | Height = 311 3 | Width = 486 4 | PixelsPerInch = 120 5 | object FDConnection1: TFDConnection 6 | Params.Strings = ( 7 | 'ConnectionDef=EMPLOYEE') 8 | LoginPrompt = False 9 | Left = 38 10 | Top = 20 11 | end 12 | object qryCUSTOMER: TFDQuery 13 | Connection = FDConnection1 14 | SQL.Strings = ( 15 | 'select * from CUSTOMER' 16 | '{if !SORT}order by !SORT{fi}') 17 | Left = 163 18 | Top = 20 19 | MacroData = < 20 | item 21 | Value = Null 22 | Name = 'SORT' 23 | end> 24 | end 25 | object dsrCUSTOMER: TEMSDataSetResource 26 | AllowedActions = [List, Get, Post, Put, Delete] 27 | DataSet = qryCUSTOMER 28 | Left = 163 29 | Top = 80 30 | end 31 | object qrySALES: TFDQuery 32 | Connection = FDConnection1 33 | SQL.Strings = ( 34 | 'select * from SALES' 35 | '{if !SORT}order by !SORT{fi}') 36 | Left = 288 37 | Top = 20 38 | MacroData = < 39 | item 40 | Value = Null 41 | Name = 'SORT' 42 | end> 43 | end 44 | object dsrSALES: TEMSDataSetResource 45 | AllowedActions = [List, Get, Post, Put, Delete] 46 | DataSet = qrySALES 47 | Left = 288 48 | Top = 80 49 | end 50 | end 51 | -------------------------------------------------------------------------------- /03 - Creating your first CRUD Application/Delphi/MyDM.pas: -------------------------------------------------------------------------------- 1 | unit MyDM; 2 | 3 | // EMS Resource Module 4 | 5 | interface 6 | 7 | uses 8 | System.SysUtils, 9 | System.Classes, 10 | System.JSON, 11 | EMS.Services, 12 | EMS.ResourceAPI, 13 | EMS.ResourceTypes, 14 | FireDAC.Stan.Intf, 15 | FireDAC.Stan.Option, 16 | FireDAC.Stan.Error, 17 | FireDAC.UI.Intf, 18 | FireDAC.Phys.Intf, 19 | FireDAC.Stan.Def, 20 | FireDAC.Stan.Pool, 21 | FireDAC.Stan.Async, 22 | FireDAC.Phys, 23 | FireDAC.Phys.IB, 24 | FireDAC.Phys.IBDef, 25 | FireDAC.ConsoleUI.Wait, 26 | FireDAC.Stan.Param, 27 | FireDAC.DatS, 28 | FireDAC.DApt.Intf, 29 | FireDAC.DApt, 30 | EMS.DataSetResource, 31 | Data.DB, 32 | FireDAC.Comp.DataSet, 33 | FireDAC.Comp.Client; 34 | 35 | type 36 | 37 | [ResourceName('test')] 38 | TTestResource1 = class(TDataModule) 39 | FDConnection1: TFDConnection; 40 | qryCUSTOMER: TFDQuery; 41 | [ResourceSuffix('customers')] 42 | dsrCUSTOMER: TEMSDataSetResource; 43 | qrySALES: TFDQuery; 44 | [ResourceSuffix('sales')] 45 | dsrSALES: TEMSDataSetResource; 46 | 47 | published 48 | end; 49 | 50 | implementation 51 | 52 | {%CLASSGROUP 'System.Classes.TPersistent'} 53 | {$R *.dfm} 54 | 55 | procedure Register; 56 | begin 57 | RegisterResource(TypeInfo(TTestResource1)); 58 | end; 59 | 60 | initialization 61 | 62 | Register; 63 | 64 | end. 65 | -------------------------------------------------------------------------------- /03 - Creating your first CRUD Application/Delphi/MyFirstCRUDDelphiRADServerPackage.res: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Embarcadero/RADServer-Docs/b6cecd34bb509284d5f7d8d0d3aed5352fecc529/03 - Creating your first CRUD Application/Delphi/MyFirstCRUDDelphiRADServerPackage.res -------------------------------------------------------------------------------- /03 - Creating your first CRUD Application/README.md: -------------------------------------------------------------------------------- 1 | ## Chapter 3: Creating your first CRUD Application 2 | 3 | Don't forget to [watch the video](https://youtu.be/ud3KFTq7vr8) of this chapter. 4 | 5 | RAD Studio provides multiple ready to use components but one of the most useful ones when it comes to create CRUD APIs is **EMSDataSetResource**. This component allows you to link a FireDAC query to it and expose not only the data but also manipulate it. The component creates automatically all the required endpoints for CRUD and provides extra functionality like pagination, sorting and more. 6 | 7 | The **EMSDataSetResource** can be created in any of your current units, or even easier, use the RAD Server Wizard to create all the required components automatically linked to a FDConnection. 8 | 9 | ### Steps before running the demo 10 | 11 | To configure the demo included in this chapter correctly you will find the **employee.gdb** database in the folder **resources** in the root path of this repository. You just need to add it as one of your available databases in the **Data Explorer** tab and name it **employee**. Another option is modifying the **FDConnection** component to point to the database. Remember you will need **InterBase** dev installed in your machine. 12 | 13 | -------------------------------------------------------------------------------- /04 - REST Debugger/C++/ComponentsFromRESTDebugger.cpp: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------- 2 | 3 | #include 4 | #ifdef _WIN32 5 | #include 6 | #endif 7 | #pragma hdrstop 8 | #include 9 | //--------------------------------------------------------------------------- 10 | USEFORM("MyForm01.cpp", Form1); 11 | //--------------------------------------------------------------------------- 12 | extern "C" int FMXmain() 13 | { 14 | try 15 | { 16 | Application->Initialize(); 17 | Application->CreateForm(__classid(TForm1), &Form1); 18 | Application->Run(); 19 | } 20 | catch (Exception &exception) 21 | { 22 | Application->ShowException(&exception); 23 | } 24 | catch (...) 25 | { 26 | try 27 | { 28 | throw Exception(""); 29 | } 30 | catch (Exception &exception) 31 | { 32 | Application->ShowException(&exception); 33 | } 34 | } 35 | return 0; 36 | } 37 | //--------------------------------------------------------------------------- 38 | -------------------------------------------------------------------------------- /04 - REST Debugger/C++/ComponentsFromRESTDebuggerPCH1.h: -------------------------------------------------------------------------------- 1 | #include 2 | #ifdef _WIN32 3 | #include 4 | #endif 5 | 6 | -------------------------------------------------------------------------------- /04 - REST Debugger/C++/MyForm01.cpp: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------- 2 | 3 | #include 4 | #pragma hdrstop 5 | 6 | #include "MyForm01.h" 7 | //--------------------------------------------------------------------------- 8 | #pragma package(smart_init) 9 | #pragma resource "*.fmx" 10 | TForm1 *Form1; 11 | //--------------------------------------------------------------------------- 12 | __fastcall TForm1::TForm1(TComponent* Owner) 13 | : TForm(Owner) 14 | { 15 | } 16 | //--------------------------------------------------------------------------- 17 | void __fastcall TForm1::ActSendRequestExecute(TObject *Sender) 18 | { 19 | RESTRequest1->Execute(); 20 | } 21 | //--------------------------------------------------------------------------- 22 | 23 | -------------------------------------------------------------------------------- /04 - REST Debugger/C++/MyForm01.fmx: -------------------------------------------------------------------------------- 1 | object Form1: TForm1 2 | Left = 0 3 | Top = 0 4 | Caption = 'Form1' 5 | ClientHeight = 480 6 | ClientWidth = 977 7 | Position = ScreenCenter 8 | FormFactor.Width = 320 9 | FormFactor.Height = 480 10 | FormFactor.Devices = [Desktop] 11 | DesignerMasterStyle = 0 12 | object Layout1: TLayout 13 | Align = Top 14 | Size.Width = 977.000000000000000000 15 | Size.Height = 50.000000000000000000 16 | Size.PlatformDefault = False 17 | TabOrder = 1 18 | object Button1: TButton 19 | Action = ActSendRequest 20 | Align = Left 21 | Enabled = True 22 | ImageIndex = -1 23 | Size.Width = 97.000000000000000000 24 | Size.Height = 50.000000000000000000 25 | Size.PlatformDefault = False 26 | TabOrder = 0 27 | end 28 | end 29 | object Grid1: TGrid 30 | Align = Client 31 | CanFocus = True 32 | ClipChildren = True 33 | Size.Width = 977.000000000000000000 34 | Size.Height = 430.000000000000000000 35 | Size.PlatformDefault = False 36 | TabOrder = 2 37 | RowCount = 0 38 | Viewport.Width = 973.000000000000000000 39 | Viewport.Height = 405.000000000000000000 40 | end 41 | object ActionList1: TActionList 42 | Left = 640 43 | Top = 8 44 | object ActSendRequest: TAction 45 | Text = 'Send Request' 46 | OnExecute = ActSendRequestExecute 47 | end 48 | end 49 | object RESTClient1: TRESTClient 50 | Accept = 'application/json, text/plain; q=0.9, text/html;q=0.8,' 51 | AcceptCharset = 'utf-8, *;q=0.8' 52 | BaseURL = 'http://localhost:8080' 53 | ContentType = 'application/json' 54 | Params = <> 55 | SynchronizedEvents = False 56 | Left = 640 57 | Top = 144 58 | end 59 | object RESTRequest1: TRESTRequest 60 | AssignedValues = [rvConnectTimeout, rvReadTimeout] 61 | Client = RESTClient1 62 | Params = < 63 | item 64 | Kind = pkREQUESTBODY 65 | Name = 'body073A70D6B8134D9C9CE713375EC7B611' 66 | Value = 67 | '{'#13#10' "CUSTOMER": "Bank of Manchster",'#13#10' "PHONE_NO": "+446121199' + 68 | '88"'#13#10'}' 69 | ContentTypeStr = 'application/json' 70 | end> 71 | Resource = 'test/customers/' 72 | Response = RESTResponse1 73 | SynchronizedEvents = False 74 | Left = 640 75 | Top = 200 76 | end 77 | object RESTResponse1: TRESTResponse 78 | ContentType = 'application/json' 79 | Left = 640 80 | Top = 256 81 | end 82 | object RESTResponseDataSetAdapter1: TRESTResponseDataSetAdapter 83 | Active = True 84 | Dataset = FDMemTable1 85 | FieldDefs = <> 86 | Response = RESTResponse1 87 | TypesMode = JSONOnly 88 | Left = 640 89 | Top = 312 90 | end 91 | object FDMemTable1: TFDMemTable 92 | Active = True 93 | FieldDefs = < 94 | item 95 | Name = 'CUST_NO' 96 | DataType = ftFloat 97 | end 98 | item 99 | Name = 'CUSTOMER' 100 | DataType = ftWideString 101 | Size = 16 102 | end 103 | item 104 | Name = 'CONTACT_FIRST' 105 | DataType = ftWideString 106 | Size = 7 107 | end 108 | item 109 | Name = 'CONTACT_LAST' 110 | DataType = ftWideString 111 | Size = 6 112 | end 113 | item 114 | Name = 'PHONE_NO' 115 | DataType = ftWideString 116 | Size = 14 117 | end 118 | item 119 | Name = 'ADDRESS_LINE1' 120 | DataType = ftWideString 121 | Size = 27 122 | end 123 | item 124 | Name = 'CITY' 125 | DataType = ftWideString 126 | Size = 9 127 | end 128 | item 129 | Name = 'STATE_PROVINCE' 130 | DataType = ftWideString 131 | Size = 2 132 | end 133 | item 134 | Name = 'COUNTRY' 135 | DataType = ftWideString 136 | Size = 3 137 | end 138 | item 139 | Name = 'POSTAL_CODE' 140 | DataType = ftWideString 141 | Size = 5 142 | end> 143 | IndexDefs = <> 144 | FetchOptions.AssignedValues = [evMode] 145 | FetchOptions.Mode = fmAll 146 | ResourceOptions.AssignedValues = [rvSilentMode] 147 | ResourceOptions.SilentMode = True 148 | UpdateOptions.AssignedValues = [uvUpdateChngFields, uvUpdateMode, uvLockMode, uvLockPoint, uvLockWait, uvRefreshMode, uvFetchGeneratorsPoint, uvCheckRequired, uvCheckReadOnly, uvCheckUpdatable] 149 | UpdateOptions.LockWait = True 150 | UpdateOptions.FetchGeneratorsPoint = gpNone 151 | UpdateOptions.CheckRequired = False 152 | StoreDefs = True 153 | Left = 640 154 | Top = 368 155 | end 156 | object BindSourceDB1: TBindSourceDB 157 | DataSet = FDMemTable1 158 | ScopeMappings = <> 159 | Left = 472 160 | Top = 368 161 | end 162 | object BindingsList1: TBindingsList 163 | Methods = <> 164 | OutputConverters = <> 165 | Left = 340 166 | Top = 365 167 | object LinkGridToDataSourceBindSourceDB1: TLinkGridToDataSource 168 | Category = 'Quick Bindings' 169 | DataSource = BindSourceDB1 170 | GridControl = Grid1 171 | Columns = <> 172 | end 173 | end 174 | end 175 | -------------------------------------------------------------------------------- /04 - REST Debugger/C++/MyForm01.h: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------- 2 | 3 | #ifndef MyForm01H 4 | #define MyForm01H 5 | //--------------------------------------------------------------------------- 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | //--------------------------------------------------------------------------- 42 | class TForm1 : public TForm 43 | { 44 | __published: // IDE-managed Components 45 | TLayout *Layout1; 46 | TButton *Button1; 47 | TGrid *Grid1; 48 | TActionList *ActionList1; 49 | TAction *ActSendRequest; 50 | TRESTClient *RESTClient1; 51 | TRESTRequest *RESTRequest1; 52 | TRESTResponse *RESTResponse1; 53 | TRESTResponseDataSetAdapter *RESTResponseDataSetAdapter1; 54 | TFDMemTable *FDMemTable1; 55 | TBindSourceDB *BindSourceDB1; 56 | TBindingsList *BindingsList1; 57 | TLinkGridToDataSource *LinkGridToDataSourceBindSourceDB1; 58 | void __fastcall ActSendRequestExecute(TObject *Sender); 59 | private: // User declarations 60 | public: // User declarations 61 | __fastcall TForm1(TComponent* Owner); 62 | }; 63 | //--------------------------------------------------------------------------- 64 | extern PACKAGE TForm1 *Form1; 65 | //--------------------------------------------------------------------------- 66 | #endif 67 | -------------------------------------------------------------------------------- /04 - REST Debugger/C++/MyForm01.vlb: -------------------------------------------------------------------------------- 1 | [Layout1] 2 | Coordinates=89,470,53,33 3 | 4 | [Button1] 5 | Coordinates=10,470,53,51 6 | 7 | [] 8 | Coordinates=163,1,70,51 9 | 10 | [Grid1] 11 | Coordinates=311,109,41,51 12 | 13 | [ActionList1] 14 | Coordinates=163,1,70,33 15 | 16 | [ActSendRequest] 17 | Coordinates=163,1,96,33 18 | 19 | [FDMemTable1] 20 | Coordinates=446,1,84,33 21 | Visible=False 22 | 23 | [RESTClient1] 24 | Coordinates=300,339,76,69 25 | 26 | [RESTResponse1] 27 | Coordinates=10,10,96,303 28 | 29 | [RESTResponseDataSetAdapter1] 30 | Coordinates=427,71,174,33 31 | 32 | [RESTRequest1] 33 | Coordinates=10,339,264,105 34 | 35 | [BindSourceDB1] 36 | Coordinates=132,10,99,249 37 | 38 | [BindingsList1] 39 | Coordinates=357,144,82,33 40 | 41 | -------------------------------------------------------------------------------- /04 - REST Debugger/Delphi/ComponentsFromRESTDebugger.dpr: -------------------------------------------------------------------------------- 1 | program ComponentsFromRESTDebugger; 2 | 3 | uses 4 | System.StartUpCopy, 5 | FMX.Forms, 6 | MyForm01 in 'MyForm01.pas' {Form1}; 7 | 8 | {$R *.res} 9 | 10 | begin 11 | Application.Initialize; 12 | Application.CreateForm(TForm1, Form1); 13 | Application.Run; 14 | 15 | end. 16 | -------------------------------------------------------------------------------- /04 - REST Debugger/Delphi/ComponentsFromRESTDebugger.res: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Embarcadero/RADServer-Docs/b6cecd34bb509284d5f7d8d0d3aed5352fecc529/04 - REST Debugger/Delphi/ComponentsFromRESTDebugger.res -------------------------------------------------------------------------------- /04 - REST Debugger/Delphi/GroupProject.groupproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | {DF6473DF-F074-4DD1-AFB9-B31B99514E04} 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | Default.Personality.12 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /04 - REST Debugger/Delphi/MyForm01.fmx: -------------------------------------------------------------------------------- 1 | object Form1: TForm1 2 | Left = 0 3 | Top = 0 4 | Caption = 'Form1' 5 | ClientHeight = 480 6 | ClientWidth = 820 7 | Position = ScreenCenter 8 | FormFactor.Width = 320 9 | FormFactor.Height = 480 10 | FormFactor.Devices = [Desktop] 11 | DesignerMasterStyle = 0 12 | object Layout1: TLayout 13 | Align = Top 14 | Size.Width = 820.000000000000000000 15 | Size.Height = 50.000000000000000000 16 | Size.PlatformDefault = False 17 | TabOrder = 1 18 | object Button1: TButton 19 | Action = ActSendGETRequest 20 | Align = Left 21 | Enabled = True 22 | ImageIndex = -1 23 | Size.Width = 121.000000000000000000 24 | Size.Height = 50.000000000000000000 25 | Size.PlatformDefault = False 26 | TabOrder = 0 27 | end 28 | end 29 | object Grid1: TGrid 30 | Align = Client 31 | CanFocus = True 32 | ClipChildren = True 33 | Size.Width = 820.000000000000000000 34 | Size.Height = 430.000000000000000000 35 | Size.PlatformDefault = False 36 | TabOrder = 0 37 | RowCount = 0 38 | Viewport.Width = 816.000000000000000000 39 | Viewport.Height = 405.000000000000000000 40 | end 41 | object ActionList1: TActionList 42 | Left = 544 43 | Top = 8 44 | object ActSendGETRequest: TAction 45 | Text = 'Send Request' 46 | Visible = False 47 | OnExecute = ActSendGETRequestExecute 48 | end 49 | end 50 | object RESTClient1: TRESTClient 51 | Accept = 'application/json, text/plain; q=0.9, text/html;q=0.8,' 52 | AcceptCharset = 'utf-8, *;q=0.8' 53 | BaseURL = 'http://localhost:8080' 54 | ContentType = 'application/json' 55 | Params = <> 56 | SynchronizedEvents = False 57 | Left = 544 58 | Top = 136 59 | end 60 | object RESTRequest1: TRESTRequest 61 | AssignedValues = [rvConnectTimeout, rvReadTimeout] 62 | Client = RESTClient1 63 | Params = < 64 | item 65 | Kind = pkREQUESTBODY 66 | Name = 'bodyF7CCF69DCF884E7A847419065113613C' 67 | Value = 68 | '{'#13#10' "CUSTOMER": "Bank of Manchster",'#13#10' "PHONE_NO": "+446121199' + 69 | '88"'#13#10'}' 70 | ContentTypeStr = 'application/json' 71 | end> 72 | Resource = 'test/customers/' 73 | Response = RESTResponse1 74 | SynchronizedEvents = False 75 | Left = 544 76 | Top = 192 77 | end 78 | object RESTResponse1: TRESTResponse 79 | ContentType = 'application/json' 80 | Left = 544 81 | Top = 248 82 | end 83 | object RESTResponseDataSetAdapter1: TRESTResponseDataSetAdapter 84 | Active = True 85 | Dataset = FDMemTable1 86 | FieldDefs = <> 87 | Response = RESTResponse1 88 | TypesMode = JSONOnly 89 | Left = 544 90 | Top = 304 91 | end 92 | object FDMemTable1: TFDMemTable 93 | Active = True 94 | FieldDefs = < 95 | item 96 | Name = 'CUST_NO' 97 | DataType = ftFloat 98 | end 99 | item 100 | Name = 'CUSTOMER' 101 | DataType = ftWideString 102 | Size = 16 103 | end 104 | item 105 | Name = 'CONTACT_FIRST' 106 | DataType = ftWideString 107 | Size = 7 108 | end 109 | item 110 | Name = 'CONTACT_LAST' 111 | DataType = ftWideString 112 | Size = 6 113 | end 114 | item 115 | Name = 'PHONE_NO' 116 | DataType = ftWideString 117 | Size = 14 118 | end 119 | item 120 | Name = 'ADDRESS_LINE1' 121 | DataType = ftWideString 122 | Size = 27 123 | end 124 | item 125 | Name = 'CITY' 126 | DataType = ftWideString 127 | Size = 9 128 | end 129 | item 130 | Name = 'STATE_PROVINCE' 131 | DataType = ftWideString 132 | Size = 2 133 | end 134 | item 135 | Name = 'COUNTRY' 136 | DataType = ftWideString 137 | Size = 3 138 | end 139 | item 140 | Name = 'POSTAL_CODE' 141 | DataType = ftWideString 142 | Size = 5 143 | end> 144 | IndexDefs = <> 145 | FetchOptions.AssignedValues = [evMode] 146 | FetchOptions.Mode = fmAll 147 | ResourceOptions.AssignedValues = [rvSilentMode] 148 | ResourceOptions.SilentMode = True 149 | UpdateOptions.AssignedValues = [uvUpdateChngFields, uvUpdateMode, uvLockMode, uvLockPoint, uvLockWait, uvRefreshMode, uvFetchGeneratorsPoint, uvCheckRequired, uvCheckReadOnly, uvCheckUpdatable] 150 | UpdateOptions.LockWait = True 151 | UpdateOptions.FetchGeneratorsPoint = gpNone 152 | UpdateOptions.CheckRequired = False 153 | StoreDefs = True 154 | Left = 544 155 | Top = 360 156 | end 157 | object BindSourceDB1: TBindSourceDB 158 | DataSet = FDMemTable1 159 | ScopeMappings = <> 160 | Left = 368 161 | Top = 360 162 | end 163 | object BindingsList1: TBindingsList 164 | Methods = <> 165 | OutputConverters = <> 166 | Left = 260 167 | Top = 357 168 | object LinkGridToDataSourceBindSourceDB1: TLinkGridToDataSource 169 | Category = 'Quick Bindings' 170 | DataSource = BindSourceDB1 171 | GridControl = Grid1 172 | Columns = <> 173 | end 174 | end 175 | end 176 | -------------------------------------------------------------------------------- /04 - REST Debugger/Delphi/MyForm01.pas: -------------------------------------------------------------------------------- 1 | unit MyForm01; 2 | 3 | interface 4 | 5 | uses 6 | System.SysUtils, 7 | System.Types, 8 | System.UITypes, 9 | System.Classes, 10 | System.Variants, 11 | FMX.Types, 12 | FMX.Controls, 13 | FMX.Forms, 14 | FMX.Graphics, 15 | FMX.Dialogs, 16 | System.Rtti, 17 | FMX.Grid.Style, 18 | System.Actions, 19 | FMX.ActnList, 20 | FMX.StdCtrls, 21 | FMX.Controls.Presentation, 22 | FMX.ScrollBox, 23 | FMX.Grid, 24 | FMX.Layouts, 25 | REST.Types, 26 | FireDAC.Stan.Intf, 27 | FireDAC.Stan.Option, 28 | FireDAC.Stan.Param, 29 | FireDAC.Stan.Error, 30 | FireDAC.DatS, 31 | FireDAC.Phys.Intf, 32 | FireDAC.DApt.Intf, 33 | Data.Bind.EngExt, 34 | FMX.Bind.DBEngExt, 35 | FMX.Bind.Grid, 36 | System.Bindings.Outputs, 37 | FMX.Bind.Editors, 38 | Data.Bind.Components, 39 | Data.Bind.Grid, 40 | Data.Bind.DBScope, 41 | Data.DB, 42 | FireDAC.Comp.DataSet, 43 | FireDAC.Comp.Client, 44 | REST.Response.Adapter, 45 | REST.Client, 46 | Data.Bind.ObjectScope; 47 | 48 | type 49 | TForm1 = class(TForm) 50 | Layout1: TLayout; 51 | Grid1: TGrid; 52 | ActionList1: TActionList; 53 | ActSendGETRequest: TAction; 54 | Button1: TButton; 55 | RESTClient1: TRESTClient; 56 | RESTRequest1: TRESTRequest; 57 | RESTResponse1: TRESTResponse; 58 | RESTResponseDataSetAdapter1: TRESTResponseDataSetAdapter; 59 | FDMemTable1: TFDMemTable; 60 | BindSourceDB1: TBindSourceDB; 61 | BindingsList1: TBindingsList; 62 | LinkGridToDataSourceBindSourceDB1: TLinkGridToDataSource; 63 | procedure ActSendGETRequestExecute(Sender: TObject); 64 | private 65 | { Private declarations } 66 | public 67 | { Public declarations } 68 | end; 69 | 70 | var Form1: TForm1; 71 | 72 | implementation 73 | 74 | {$R *.fmx} 75 | 76 | procedure TForm1.ActSendGETRequestExecute(Sender: TObject); 77 | begin 78 | RESTRequest1.Execute; 79 | end; 80 | 81 | end. 82 | -------------------------------------------------------------------------------- /04 - REST Debugger/Delphi/MyForm01.vlb: -------------------------------------------------------------------------------- 1 | [RESTRequest1] 2 | Coordinates=149,10,314,124 3 | 4 | [Layout1] 5 | Coordinates=10,508,60,36 6 | 7 | [RESTResponseDataSetAdapter1] 8 | Coordinates=210,144,205,36 9 | 10 | [Grid1] 11 | Coordinates=196,413,46,58 12 | Visible=True 13 | 14 | [RESTClient1] 15 | Coordinates=268,402,89,80 16 | 17 | [RESTResponse1] 18 | Coordinates=10,10,113,366 19 | 20 | [ActSendGETRequest] 21 | Coordinates=210,331,137,36 22 | 23 | [ActionList1] 24 | Coordinates=113,385,78,36 25 | 26 | [Button1] 27 | Coordinates=383,402,59,58 28 | 29 | [FDMemTable1] 30 | Coordinates=165,386,99,36 31 | Visible=False 32 | 33 | [] 34 | Coordinates=482,386,160,36 35 | 36 | [BindSourceDB1] 37 | Coordinates=10,402,118,300 38 | 39 | [BindingsList1] 40 | Coordinates=142,153,91,36 41 | 42 | -------------------------------------------------------------------------------- /04 - REST Debugger/README.md: -------------------------------------------------------------------------------- 1 | ## Chapter 4: REST Debugger 2 | 3 | Don't forget to [watch the video](https://youtu.be/c7KFvP8nF7Q) of this chapter. 4 | 5 | [REST Debugger](https://www.embarcadero.com/free-tools/rest-debugger) is Embarcadero's free solution for exploring, understanding, and integrating RESTful web services with Delphi and C++Builder apps. It empowers developers to explore, test, and ultimately understand how a RESTful web service works with features such as filterable JSON blobs, streamlined OAuth 1.0/2.0 authentication, and configurable request/resource parameters. Not only that, but also offers you the possibility to copy and paste REST components directly into your projects in just a few clicks. 6 | 7 | This chapter uses the REST Debugger that comes with RAD Studio and you can find under Tools/REST Debugger. To test some endpoints the RAD Server example from chapter 3 will be used. 8 | 9 | -------------------------------------------------------------------------------- /05 - Using FireDAC Batch Move and JSONWriter/C++/FireDACPackage.cpp: -------------------------------------------------------------------------------- 1 | // EMS Package 2 | //--------------------------------------------------------------------------- 3 | 4 | #include 5 | #pragma hdrstop 6 | USEFORM("MyDM.cpp", TestResource1); /* TDataModule: File Type */ 7 | //--------------------------------------------------------------------------- 8 | #pragma package(smart_init) 9 | //--------------------------------------------------------------------------- 10 | 11 | // Package source. 12 | //--------------------------------------------------------------------------- 13 | 14 | 15 | #pragma argsused 16 | extern "C" int _libmain(unsigned long reason) 17 | { 18 | return 1; 19 | } 20 | //--------------------------------------------------------------------------- 21 | 22 | 23 | -------------------------------------------------------------------------------- /05 - Using FireDAC Batch Move and JSONWriter/C++/MyDM.cpp: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------- 2 | #pragma hdrstop 3 | 4 | #include "MyDM.h" 5 | #include 6 | //--------------------------------------------------------------------------- 7 | #pragma package(smart_init) 8 | #pragma classgroup "System.Classes.TPersistent" 9 | #pragma resource "*.dfm" 10 | //--------------------------------------------------------------------------- 11 | __fastcall TTestResource1::TTestResource1(TComponent* Owner) 12 | : TDataModule(Owner) 13 | { 14 | } 15 | 16 | void TTestResource1::Get(TEndpointContext* AContext, TEndpointRequest* ARequest, TEndpointResponse* AResponse) 17 | { 18 | TMemoryStream * mStream = new TMemoryStream; 19 | AResponse->Body->SetStream(mStream,"application/json", True); 20 | EmployeeQuery->Open(); 21 | EmployeeQuery->SaveToStream(mStream, sfJSON); 22 | } 23 | 24 | void TTestResource1::GetBatchMove(TEndpointContext* AContext, TEndpointRequest* ARequest, TEndpointResponse* AResponse) 25 | { 26 | FDBatchMoveJSONWriter1->JsonWriter = AResponse->Body->JSONWriter; 27 | FDBatchMove1->Execute(); 28 | } 29 | 30 | static void Register() 31 | { 32 | std::unique_ptr attributes(new TEMSResourceAttributes()); 33 | attributes->ResourceName = "FireDAC"; 34 | attributes->ResourceSuffix["GetBatchMove"] = "BatchMove"; 35 | RegisterResource(__typeinfo(TTestResource1), attributes.release()); 36 | } 37 | 38 | #pragma startup Register 32 39 | -------------------------------------------------------------------------------- /05 - Using FireDAC Batch Move and JSONWriter/C++/MyDM.dfm: -------------------------------------------------------------------------------- 1 | object TestResource1: TTestResource1 2 | Height = 289 3 | Width = 940 4 | PixelsPerInch = 120 5 | object EmployeeConnection: TFDConnection 6 | Params.Strings = ( 7 | 'ConnectionDef=EMPLOYEE') 8 | Connected = True 9 | Left = 120 10 | Top = 50 11 | end 12 | object EmployeeQuery: TFDQuery 13 | Connection = EmployeeConnection 14 | SQL.Strings = ( 15 | 'SELECT *' 16 | 'FROM employee') 17 | Left = 120 18 | Top = 150 19 | end 20 | object FDStanStorageJSONLink1: TFDStanStorageJSONLink 21 | Left = 340 22 | Top = 110 23 | end 24 | object FDBatchMove1: TFDBatchMove 25 | Reader = FDBatchMoveDataSetReader1 26 | Writer = FDBatchMoveJSONWriter1 27 | Mappings = <> 28 | LogFileName = 'Data.log' 29 | Left = 570 30 | Top = 50 31 | end 32 | object FDBatchMoveDataSetReader1: TFDBatchMoveDataSetReader 33 | DataSet = EmployeeQuery 34 | Left = 570 35 | Top = 140 36 | end 37 | object FDBatchMoveJSONWriter1: TFDBatchMoveJSONWriter 38 | DataDef.Fields = <> 39 | Left = 780 40 | Top = 110 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /05 - Using FireDAC Batch Move and JSONWriter/C++/MyDM.h: -------------------------------------------------------------------------------- 1 | // EMS Resource Modules 2 | //--------------------------------------------------------------------------- 3 | 4 | #ifndef MyDMH 5 | #define MyDMH 6 | //--------------------------------------------------------------------------- 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | //--------------------------------------------------------------------------- 36 | #pragma explicit_rtti methods (public) 37 | class TTestResource1 : public TDataModule 38 | { 39 | __published: 40 | TFDConnection *EmployeeConnection; 41 | TFDQuery *EmployeeQuery; 42 | TFDStanStorageJSONLink *FDStanStorageJSONLink1; 43 | TFDBatchMove *FDBatchMove1; 44 | TFDBatchMoveDataSetReader *FDBatchMoveDataSetReader1; 45 | TFDBatchMoveJSONWriter *FDBatchMoveJSONWriter1; 46 | private: 47 | public: 48 | __fastcall TTestResource1(TComponent* Owner); 49 | void Get(TEndpointContext* AContext, TEndpointRequest* ARequest, TEndpointResponse* AResponse); 50 | void GetBatchMove(TEndpointContext* AContext, TEndpointRequest* ARequest, TEndpointResponse* AResponse); 51 | }; 52 | #endif 53 | -------------------------------------------------------------------------------- /05 - Using FireDAC Batch Move and JSONWriter/Delphi/FireDACPackage.dpk: -------------------------------------------------------------------------------- 1 | package FireDACPackage; 2 | 3 | {$R *.res} 4 | {$IFDEF IMPLICITBUILDING This IFDEF should not be used by users} 5 | {$ALIGN 8} 6 | {$ASSERTIONS ON} 7 | {$BOOLEVAL OFF} 8 | {$DEBUGINFO OFF} 9 | {$EXTENDEDSYNTAX ON} 10 | {$IMPORTEDDATA ON} 11 | {$IOCHECKS ON} 12 | {$LOCALSYMBOLS ON} 13 | {$LONGSTRINGS ON} 14 | {$OPENSTRINGS ON} 15 | {$OPTIMIZATION OFF} 16 | {$OVERFLOWCHECKS ON} 17 | {$RANGECHECKS ON} 18 | {$REFERENCEINFO ON} 19 | {$SAFEDIVIDE OFF} 20 | {$STACKFRAMES ON} 21 | {$TYPEDADDRESS OFF} 22 | {$VARSTRINGCHECKS ON} 23 | {$WRITEABLECONST OFF} 24 | {$MINENUMSIZE 1} 25 | {$IMAGEBASE $400000} 26 | {$DEFINE DEBUG} 27 | {$ENDIF IMPLICITBUILDING} 28 | {$RUNONLY} 29 | {$IMPLICITBUILD ON} 30 | 31 | requires 32 | rtl, 33 | emsserverapi, 34 | dbrtl, 35 | FireDAC, 36 | FireDACCommonDriver, 37 | FireDACCommon, 38 | FireDACIBDriver; 39 | 40 | contains 41 | MyDM in 'MyDM.pas' {FireDACResource1: TDataModule}; 42 | 43 | end. 44 | -------------------------------------------------------------------------------- /05 - Using FireDAC Batch Move and JSONWriter/Delphi/FireDACPackage.res: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Embarcadero/RADServer-Docs/b6cecd34bb509284d5f7d8d0d3aed5352fecc529/05 - Using FireDAC Batch Move and JSONWriter/Delphi/FireDACPackage.res -------------------------------------------------------------------------------- /05 - Using FireDAC Batch Move and JSONWriter/Delphi/MyDM.dfm: -------------------------------------------------------------------------------- 1 | object FireDACResource1: TFireDACResource1 2 | Height = 315 3 | Width = 919 4 | PixelsPerInch = 120 5 | object EmployeeConnection: TFDConnection 6 | Params.Strings = ( 7 | 'ConnectionDef=EMPLOYEE') 8 | Left = 120 9 | Top = 80 10 | end 11 | object EmployeeQuery: TFDQuery 12 | Connection = EmployeeConnection 13 | SQL.Strings = ( 14 | 'SELECT *' 15 | 'FROM employee') 16 | Left = 120 17 | Top = 180 18 | end 19 | object FDBatchMoveJSONWriter1: TFDBatchMoveJSONWriter 20 | DataDef.Fields = <> 21 | Left = 750 22 | Top = 130 23 | end 24 | object FDBatchMove1: TFDBatchMove 25 | Reader = FDBatchMoveDataSetReader1 26 | Writer = FDBatchMoveJSONWriter1 27 | Mappings = <> 28 | LogFileName = 'Data.log' 29 | Left = 550 30 | Top = 80 31 | end 32 | object FDBatchMoveDataSetReader1: TFDBatchMoveDataSetReader 33 | DataSet = EmployeeQuery 34 | Left = 550 35 | Top = 180 36 | end 37 | object FDStanStorageJSONLink1: TFDStanStorageJSONLink 38 | Left = 310 39 | Top = 140 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /05 - Using FireDAC Batch Move and JSONWriter/Delphi/MyDM.pas: -------------------------------------------------------------------------------- 1 | unit MyDM; 2 | 3 | // EMS Resource Module 4 | 5 | interface 6 | 7 | uses 8 | System.SysUtils, 9 | System.Classes, 10 | System.JSON, 11 | EMS.Services, 12 | EMS.ResourceAPI, 13 | EMS.ResourceTypes, 14 | FireDAC.Stan.Intf, 15 | FireDAC.Stan.Option, 16 | FireDAC.Stan.Error, 17 | FireDAC.UI.Intf, 18 | FireDAC.Phys.Intf, 19 | FireDAC.Stan.Def, 20 | FireDAC.Stan.Pool, 21 | FireDAC.Stan.Async, 22 | FireDAC.Phys, 23 | FireDAC.Phys.IB, 24 | FireDAC.Phys.IBDef, 25 | FireDAC.ConsoleUI.Wait, 26 | FireDAC.Stan.Param, 27 | FireDAC.DatS, 28 | FireDAC.DApt.Intf, 29 | FireDAC.DApt, 30 | FireDAC.Comp.BatchMove, 31 | FireDAC.Comp.BatchMove.JSON, 32 | Data.DB, 33 | FireDAC.Comp.DataSet, 34 | FireDAC.Comp.Client, 35 | FireDAC.Comp.BatchMove.DataSet, 36 | FireDAC.Stan.StorageJSON; 37 | 38 | type 39 | 40 | [ResourceName('FireDAC')] 41 | TFireDACResource1 = class(TDataModule) 42 | EmployeeConnection: TFDConnection; 43 | EmployeeQuery: TFDQuery; 44 | FDBatchMoveJSONWriter1: TFDBatchMoveJSONWriter; 45 | FDBatchMove1: TFDBatchMove; 46 | FDBatchMoveDataSetReader1: TFDBatchMoveDataSetReader; 47 | FDStanStorageJSONLink1: TFDStanStorageJSONLink; 48 | published 49 | 50 | procedure Get(const AContext: TEndpointContext; const ARequest: TEndpointRequest; 51 | const AResponse: TEndpointResponse); 52 | 53 | [ResourceSuffix('BatchMove')] 54 | procedure GetBatchMove(const AContext: TEndpointContext; const ARequest: TEndpointRequest; 55 | const AResponse: TEndpointResponse); 56 | end; 57 | 58 | implementation 59 | 60 | {%CLASSGROUP 'System.Classes.TPersistent'} 61 | {$R *.dfm} 62 | 63 | procedure TFireDACResource1.Get(const AContext: TEndpointContext; const ARequest: TEndpointRequest; 64 | const AResponse: TEndpointResponse); 65 | begin 66 | var mStream := TMemoryStream.Create; 67 | AResponse.Body.SetStream(mStream, 'application/json', True); 68 | EmployeeQuery.Open; 69 | EmployeeQuery.SaveToStream(mStream, sfJSON); 70 | end; 71 | 72 | procedure TFireDACResource1.GetBatchMove(const AContext: TEndpointContext; const ARequest: TEndpointRequest; 73 | const AResponse: TEndpointResponse); 74 | begin 75 | FDBatchMoveJSONWriter1.JsonWriter := AResponse.Body.JsonWriter; 76 | FDBatchMove1.Execute; 77 | end; 78 | 79 | procedure Register; 80 | begin 81 | RegisterResource(TypeInfo(TFireDACResource1)); 82 | end; 83 | 84 | initialization 85 | 86 | Register; 87 | 88 | end. 89 | -------------------------------------------------------------------------------- /05 - Using FireDAC Batch Move and JSONWriter/Delphi/RADServerFireDAC.res: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Embarcadero/RADServer-Docs/b6cecd34bb509284d5f7d8d0d3aed5352fecc529/05 - Using FireDAC Batch Move and JSONWriter/Delphi/RADServerFireDAC.res -------------------------------------------------------------------------------- /05 - Using FireDAC Batch Move and JSONWriter/README.md: -------------------------------------------------------------------------------- 1 | ## Chapter 5: Using FireDAC Batch Move and JSONWriter 2 | 3 | Don't forget to [watch the video](https://youtu.be/Rs3QpUGjrW4) of this chapter. 4 | 5 | Depending on the requirements for your projects or what technology you are more familiar with, RAD Studio allows you to use even more tools to create your REST API. 6 | 7 | FireDAC components are available to produce and consume a stream containing database metadata and data encoded in JSON for a response from one of your RAD Server endpoints. This approach is great if your client applications are going to be VCL or FMX. You can use MemoryTables to automatically map all the database information and metadata to map it automatically. Other client applications that use languages, like JavaScript, could have a problem dealing with the database information and data that would be included in the response, but RAD Studio provides a way to generate clean JSON that JavaScript or other languages would expect to receive. 8 | 9 | In the demo files you'll find different examples of using this set of components and methods. -------------------------------------------------------------------------------- /06 - JSONValue, JSONWriter and JSONBuilder/C++/JSONPackage.cpp: -------------------------------------------------------------------------------- 1 | // EMS Package 2 | //--------------------------------------------------------------------------- 3 | 4 | #include 5 | #pragma hdrstop 6 | USEFORM("MyDM.cpp", TestResource1); /* TDataModule: File Type */ 7 | //--------------------------------------------------------------------------- 8 | #pragma package(smart_init) 9 | //--------------------------------------------------------------------------- 10 | 11 | // Package source. 12 | //--------------------------------------------------------------------------- 13 | 14 | 15 | #pragma argsused 16 | extern "C" int _libmain(unsigned long reason) 17 | { 18 | return 1; 19 | } 20 | //--------------------------------------------------------------------------- 21 | 22 | 23 | -------------------------------------------------------------------------------- /06 - JSONValue, JSONWriter and JSONBuilder/C++/MyDM.cpp: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------- 2 | #pragma hdrstop 3 | 4 | #include "MyDM.h" 5 | #include 6 | //--------------------------------------------------------------------------- 7 | #pragma package(smart_init) 8 | #pragma classgroup "System.Classes.TPersistent" 9 | #pragma resource "*.dfm" 10 | //--------------------------------------------------------------------------- 11 | __fastcall TTestResource1::TTestResource1(TComponent* Owner) 12 | : TDataModule(Owner) 13 | { 14 | } 15 | 16 | void TTestResource1::GetJSON(TEndpointContext* AContext, TEndpointRequest* ARequest, TEndpointResponse* AResponse) 17 | { 18 | // create some JSON objects 19 | TJSONObject* JSONRed = new TJSONObject(); 20 | JSONRed->AddPair("color", "red"); 21 | JSONRed->AddPair("hex", "#ff0000"); 22 | JSONRed->AddPair("default", True); 23 | JSONRed->AddPair("customId", new TJSONNull()); 24 | TJSONObject* JSONBlue = new TJSONObject(); 25 | JSONBlue->AddPair("color", "blue"); 26 | JSONBlue->AddPair("hex", "#0000ff"); 27 | JSONBlue->AddPair("default", False); 28 | JSONBlue->AddPair("customId", 653992); 29 | // create an array and assign the previous objects to it 30 | TJSONArray* JSONArray = new TJSONArray(); 31 | JSONArray->Add(JSONRed); 32 | JSONArray->Add(JSONBlue); 33 | // create an extra object that will contain the array of colors 34 | TJSONObject* JSONObject = new TJSONObject(); 35 | JSONObject->AddPair("colors", JSONArray); 36 | AResponse->Body->SetValue(JSONObject, True); 37 | } 38 | 39 | void TTestResource1::GetJSONWriter(TEndpointContext* AContext, TEndpointRequest* ARequest, TEndpointResponse* AResponse) 40 | { 41 | // to avoid typing AResponse.Body.JSONWriter on every line we store it in a variable 42 | TJsonWriter* Writer = AResponse->Body->JSONWriter; 43 | // start the JSON object 44 | Writer->WriteStartObject(); 45 | Writer->WritePropertyName("colors"); 46 | // start the JSON Array 47 | Writer->WriteStartArray(); 48 | Writer->WriteStartObject(); 49 | Writer->WritePropertyName("name"); 50 | Writer->WriteValue("red"); 51 | // add WritePropertyName and WriteValue statements as often as needed 52 | Writer->WritePropertyName("hex"); 53 | Writer->WriteValue("#ff0000"); 54 | Writer->WritePropertyName("default"); 55 | Writer->WriteValue(False); 56 | Writer->WritePropertyName("customId"); 57 | Writer->WriteNull(); 58 | Writer->WriteEndObject(); 59 | // write as many additional JSON objects as you need 60 | Writer->WriteStartObject(); 61 | Writer->WritePropertyName("name"); 62 | Writer->WriteValue("blue"); 63 | Writer->WritePropertyName("hex"); 64 | Writer->WriteValue("#0000ff"); 65 | Writer->WritePropertyName("default"); 66 | Writer->WriteValue(True); 67 | Writer->WritePropertyName("customId"); 68 | Writer->WriteValue(653992); 69 | // end the JSON object 70 | Writer->WriteEndObject(); 71 | // end the JSON array 72 | Writer->WriteEndArray(); 73 | Writer->WriteEndObject(); 74 | } 75 | 76 | void TTestResource1::GetJSONBuilder(TEndpointContext* AContext, TEndpointRequest* ARequest, TEndpointResponse* AResponse) 77 | { 78 | TJsonWriter* Writer = AResponse->Body->JSONWriter; 79 | // link the JSONWriter from the response to the builder 80 | TJSONObjectBuilder* Builder = new TJSONObjectBuilder(Writer); 81 | try { 82 | Builder 83 | ->BeginObject() 84 | ->BeginArray("colors") 85 | ->BeginObject() 86 | ->Add("name", "red") 87 | ->Add("hex", "#ff0000") 88 | ->Add("default", False) 89 | ->AddNull("customId") 90 | ->EndObject() 91 | ->BeginObject() 92 | ->Add("name", "blue") 93 | ->Add("hex", "#0000ff") 94 | ->Add("default", True) 95 | ->Add("customId", 653992) 96 | ->EndObject() 97 | ->EndArray() 98 | ->EndObject(); 99 | } __finally { 100 | delete Builder; 101 | } 102 | } 103 | 104 | static void Register() 105 | { 106 | std::unique_ptr attributes(new TEMSResourceAttributes()); 107 | attributes->ResourceName = "Test"; 108 | attributes->ResourceSuffix["GetJSON"] = "JSON"; 109 | attributes->ResourceSuffix["GetJSONWriter"] = "JSONWriter"; 110 | attributes->ResourceSuffix["GetJSONBuilder"] = "JSONBuilder"; 111 | RegisterResource(__typeinfo(TTestResource1), attributes.release()); 112 | } 113 | 114 | #pragma startup Register 32 115 | -------------------------------------------------------------------------------- /06 - JSONValue, JSONWriter and JSONBuilder/C++/MyDM.dfm: -------------------------------------------------------------------------------- 1 | object TestResource1: TTestResource1 2 | Height = 375 3 | Width = 750 4 | PixelsPerInch = 120 5 | end 6 | -------------------------------------------------------------------------------- /06 - JSONValue, JSONWriter and JSONBuilder/C++/MyDM.h: -------------------------------------------------------------------------------- 1 | // EMS Resource Modules 2 | //--------------------------------------------------------------------------- 3 | 4 | #ifndef MyDMH 5 | #define MyDMH 6 | //--------------------------------------------------------------------------- 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | //--------------------------------------------------------------------------- 16 | #pragma explicit_rtti methods (public) 17 | class TTestResource1 : public TDataModule 18 | { 19 | __published: 20 | private: 21 | public: 22 | __fastcall TTestResource1(TComponent* Owner); 23 | void GetJSON(TEndpointContext* AContext, TEndpointRequest* ARequest, TEndpointResponse* AResponse); 24 | void GetJSONWriter(TEndpointContext* AContext, TEndpointRequest* ARequest, TEndpointResponse* AResponse); 25 | void GetJSONBuilder(TEndpointContext* AContext, TEndpointRequest* ARequest, TEndpointResponse* AResponse); 26 | }; 27 | #endif 28 | -------------------------------------------------------------------------------- /06 - JSONValue, JSONWriter and JSONBuilder/Delphi/JSONPackage.dpk: -------------------------------------------------------------------------------- 1 | package JSONPackage; 2 | 3 | {$R *.res} 4 | {$IFDEF IMPLICITBUILDING This IFDEF should not be used by users} 5 | {$ALIGN 8} 6 | {$ASSERTIONS ON} 7 | {$BOOLEVAL OFF} 8 | {$DEBUGINFO OFF} 9 | {$EXTENDEDSYNTAX ON} 10 | {$IMPORTEDDATA ON} 11 | {$IOCHECKS ON} 12 | {$LOCALSYMBOLS ON} 13 | {$LONGSTRINGS ON} 14 | {$OPENSTRINGS ON} 15 | {$OPTIMIZATION OFF} 16 | {$OVERFLOWCHECKS ON} 17 | {$RANGECHECKS ON} 18 | {$REFERENCEINFO ON} 19 | {$SAFEDIVIDE OFF} 20 | {$STACKFRAMES ON} 21 | {$TYPEDADDRESS OFF} 22 | {$VARSTRINGCHECKS ON} 23 | {$WRITEABLECONST OFF} 24 | {$MINENUMSIZE 1} 25 | {$IMAGEBASE $400000} 26 | {$DEFINE DEBUG} 27 | {$ENDIF IMPLICITBUILDING} 28 | {$RUNONLY} 29 | {$IMPLICITBUILD ON} 30 | 31 | requires 32 | rtl, 33 | emsserverapi; 34 | 35 | contains 36 | MyDM in 'MyDM.pas' {TestResource1: TDataModule}; 37 | 38 | end. 39 | -------------------------------------------------------------------------------- /06 - JSONValue, JSONWriter and JSONBuilder/Delphi/MyDM.dfm: -------------------------------------------------------------------------------- 1 | object TestResource1: TTestResource1 2 | Height = 354 3 | Width = 543 4 | PixelsPerInch = 120 5 | end 6 | -------------------------------------------------------------------------------- /06 - JSONValue, JSONWriter and JSONBuilder/Delphi/MyDM.pas: -------------------------------------------------------------------------------- 1 | unit MyDM; 2 | 3 | // EMS Resource Module 4 | 5 | interface 6 | 7 | uses 8 | System.SysUtils, 9 | System.Classes, 10 | System.JSON, 11 | System.JSON.Types, 12 | System.JSON.Writers, 13 | System.JSON.Builders, 14 | EMS.Services, 15 | EMS.ResourceAPI, 16 | EMS.ResourceTypes; 17 | 18 | type 19 | 20 | [ResourceName('test')] 21 | TTestResource1 = class(TDataModule) 22 | published 23 | [ResourceSuffix('./JSON')] 24 | procedure GetJSON(const AContext: TEndpointContext; const ARequest: TEndpointRequest; 25 | const AResponse: TEndpointResponse); 26 | [ResourceSuffix('./JSONWriter')] 27 | procedure GetJSONWriter(const AContext: TEndpointContext; const ARequest: TEndpointRequest; 28 | const AResponse: TEndpointResponse); 29 | [ResourceSuffix('./JSONBuilder')] 30 | procedure GetJSONBuilder(const AContext: TEndpointContext; const ARequest: TEndpointRequest; 31 | const AResponse: TEndpointResponse); 32 | end; 33 | 34 | implementation 35 | 36 | {%CLASSGROUP 'System.Classes.TPersistent'} 37 | {$R *.dfm} 38 | 39 | procedure TTestResource1.GetJSON(const AContext: TEndpointContext; const ARequest: TEndpointRequest; 40 | const AResponse: TEndpointResponse); 41 | begin 42 | // create some JSON objects 43 | var JSONRed := TJSONObject.Create; 44 | JSONRed.AddPair('name', 'red'); 45 | JSONRed.AddPair('hex', '#ff0000'); 46 | JSONRed.AddPair('default', False); 47 | JSONRed.AddPair('customId', TJSONNull.Create); 48 | var JSONBlue := TJSONObject.Create; 49 | JSONBlue.AddPair('name', 'blue'); 50 | JSONBlue.AddPair('hex', '#0000ff'); 51 | JSONBlue.AddPair('default', True); 52 | JSONBlue.AddPair('customId', 653992); 53 | // create an array and assign the previous objects to it 54 | var JSONArray := TJSONArray.Create; 55 | JSONArray.Add(JSONRed); 56 | JSONarray.Add(JSONBlue); 57 | // create an extra object that will contain the array of colors 58 | var JSONObject := TJSONObject.Create; 59 | JSONObject.AddPair('colors', JSONArray); 60 | AResponse.Body.SetValue(JSONObject, True); 61 | end; 62 | 63 | procedure TTestResource1.GetJSONWriter(const AContext: TEndpointContext; const ARequest: TEndpointRequest; 64 | const AResponse: TEndpointResponse); 65 | begin 66 | // to avoid typing AResponse.Body.JSONWriter on every line we store it in a variable 67 | var Writer := AResponse.Body.JSONWriter; 68 | // start the JSON object 69 | Writer.WriteStartObject; 70 | Writer.WritePropertyName('colors'); 71 | // start the JSON Array 72 | Writer.WriteStartArray; 73 | Writer.WriteStartObject; 74 | Writer.WritePropertyName('name'); 75 | Writer.WriteValue('red'); 76 | // add WritePropertyName and WriteValue statements as required 77 | Writer.WritePropertyName('hex'); 78 | Writer.WriteValue('#ff0000'); 79 | Writer.WritePropertyName('default'); 80 | Writer.WriteValue(False); 81 | Writer.WritePropertyName('customId'); 82 | Writer.WriteNull; 83 | Writer.WriteEndObject; 84 | // write as many additional JSON objects as you need 85 | Writer.WriteStartObject; 86 | Writer.WritePropertyName('name'); 87 | Writer.WriteValue('blue'); 88 | Writer.WritePropertyName('hex'); 89 | Writer.WriteValue('#0000ff'); 90 | Writer.WritePropertyName('default'); 91 | Writer.WriteValue(True); 92 | Writer.WritePropertyName('customId'); 93 | Writer.WriteValue(653992); 94 | // end the JSON object 95 | Writer.WriteEndObject; 96 | // end the JSON array 97 | Writer.WriteEndArray; 98 | Writer.WriteEndObject; 99 | end; 100 | 101 | procedure TTestResource1.GetJSONBuilder(const AContext: TEndpointContext; const ARequest: TEndpointRequest; 102 | const AResponse: TEndpointResponse); 103 | begin 104 | var Writer := Aresponse.Body.JSONWriter; 105 | // link the JSONWriter from the response to the builder 106 | var Builder := TJSONObjectBuilder.Create(Writer); 107 | try 108 | Builder 109 | .BeginObject 110 | .BeginArray('colors') 111 | .BeginObject 112 | .Add('name', 'red') 113 | .Add('hex', '#ff0000') 114 | .Add('default', False) 115 | .AddNull('customId') 116 | .EndObject 117 | .BeginObject 118 | .Add('name', 'blue') 119 | .Add('hex', '#0000ff') 120 | .Add('default', True) 121 | .Add('customId', 653992) 122 | .EndObject 123 | .EndArray 124 | .EndObject; 125 | finally 126 | Builder.Free; 127 | end; 128 | end; 129 | 130 | procedure Register; 131 | begin 132 | RegisterResource(TypeInfo(TTestResource1)); 133 | end; 134 | 135 | initialization 136 | 137 | Register; 138 | 139 | end. 140 | -------------------------------------------------------------------------------- /06 - JSONValue, JSONWriter and JSONBuilder/Delphi/RADServerJSON.res: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Embarcadero/RADServer-Docs/b6cecd34bb509284d5f7d8d0d3aed5352fecc529/06 - JSONValue, JSONWriter and JSONBuilder/Delphi/RADServerJSON.res -------------------------------------------------------------------------------- /06 - JSONValue, JSONWriter and JSONBuilder/README.md: -------------------------------------------------------------------------------- 1 | ## Chapter 6: JSONValue, JSONWriter and JSON builder 2 | 3 | Don't forget to [watch the video](https://youtu.be/DQukKQg0V2M) of this chapter. 4 | 5 | RAD Server provides support for handling JSON data that can be consumed by different programming languages and tools. Creating a JSON string, transmitting the string as a response, and having the client application code process the return is okay for smaller amounts of data. Imagine how large a JSON array response would be for an entire database or a complex data structure? RAD Studio provides three main frameworks for working with JSON data. This chapter covers a few of the many ways RAD Server applications can return JSON to a calling application. 6 | 7 | ### Frameworks for Handling JSON Data 8 | 9 | RAD Studio provides multiple frameworks to handle JSON data. The three most common are: 10 | - JSON Objects Framework – creates temporary objects to read and write JSON data. 11 | - Readers and Writers JSON Framework – allows you to read and write JSON data directly. 12 | - JSONBuilder – using writers, create complex structures in a more maintainable way. 13 | 14 | In the demo files you'll find different examples of using this set of frameworks. 15 | -------------------------------------------------------------------------------- /07 - Creating your own customized endpoints/C++/CustomEndpointsPackage.cpp: -------------------------------------------------------------------------------- 1 | // EMS Package 2 | //--------------------------------------------------------------------------- 3 | 4 | #include 5 | #pragma hdrstop 6 | USEFORM("MyDMUnit.cpp", TestResource1); /* TDataModule: File Type */ 7 | //--------------------------------------------------------------------------- 8 | #pragma package(smart_init) 9 | //--------------------------------------------------------------------------- 10 | 11 | // Package source. 12 | //--------------------------------------------------------------------------- 13 | 14 | 15 | #pragma argsused 16 | extern "C" int _libmain(unsigned long reason) 17 | { 18 | return 1; 19 | } 20 | //--------------------------------------------------------------------------- 21 | 22 | 23 | -------------------------------------------------------------------------------- /07 - Creating your own customized endpoints/C++/MyDMUnit.cpp: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------- 2 | #pragma hdrstop 3 | 4 | #include "MyDMUnit.h" 5 | #include 6 | #include "Data.DBJson.hpp" 7 | //--------------------------------------------------------------------------- 8 | #pragma package(smart_init) 9 | #pragma classgroup "System.Classes.TPersistent" 10 | #pragma resource "*.dfm" 11 | //--------------------------------------------------------------------------- 12 | __fastcall TTestResource1::TTestResource1(TComponent* Owner) 13 | : TDataModule(Owner) 14 | { 15 | } 16 | 17 | void TTestResource1::GetCustomersDetails(TEndpointContext* AContext, TEndpointRequest* ARequest, TEndpointResponse* AResponse) 18 | { 19 | qryCUSTOMER->Open(); 20 | qryCUSTOMER->First(); 21 | qrySALES->Open(); 22 | 23 | TStringList* lFields = ExcludeMasterFieldFromFields(qrySALES); 24 | 25 | // this array will store all the objects returned from the ConverToMasterDetail loop 26 | TJSONArray* lJSONArray = new TJSONArray; 27 | try { 28 | while (!qryCUSTOMER->Eof) { 29 | lJSONArray->Add(SerializeMasterDetail(qryCUSTOMER, qrySALES, "SALES", lFields)); 30 | qryCUSTOMER->Next(); 31 | } 32 | 33 | AResponse->Body->SetValue(lJSONArray, true); 34 | } __finally { 35 | lFields->Free(); 36 | qrySALES->Close(); 37 | qryCUSTOMER->Close(); 38 | } 39 | } 40 | 41 | void TTestResource1::GetCustomerDetails(TEndpointContext* AContext, TEndpointRequest* ARequest, TEndpointResponse* AResponse) 42 | { 43 | int lCustomerNo = ARequest->Params->Values["CUST_NO"].ToInt(); 44 | // We use a parameter instead of concatenating the CustomerNo to avoid SQL injection 45 | qryCUSTOMER->MacroByName("MacroWhere")->AsRaw = "WHERE CUST_NO = :CUST_NO"; 46 | qryCUSTOMER->ParamByName("CUST_NO")->AsInteger = lCustomerNo; 47 | qryCUSTOMER->Open(); 48 | try { 49 | if (qryCUSTOMER->RecordCount == 0) { 50 | AResponse->RaiseNotFound("Not found", "Customer ID not found"); 51 | } 52 | qrySALES->ParamByName("CUST_NO")->AsInteger = lCustomerNo; 53 | qrySALES->Open(); 54 | 55 | TStringList* lFields = ExcludeMasterFieldFromFields(qrySALES); 56 | try { 57 | AResponse->Body->SetValue( 58 | SerializeMasterDetail(qryCUSTOMER, qrySALES, "SALES", lFields), 59 | true 60 | ); 61 | } __finally { 62 | lFields->Free(); 63 | } 64 | } __finally { 65 | qryCUSTOMER->Close(); 66 | qryCUSTOMER->MacroByName("MacroWhere")->Clear(); 67 | } 68 | } 69 | 70 | void TTestResource1::PostCustomEndPoint(TEndpointContext* AContext, TEndpointRequest* ARequest, TEndpointResponse* AResponse) 71 | { 72 | TJSONObject *lJSON; 73 | System::UnicodeString lName; 74 | if (!ARequest->Body->TryGetObject(lJSON) && lJSON->TryGetValue("name", lName)) { 75 | AResponse->RaiseBadRequest("Bad Request", "Missing Data"); 76 | } 77 | int lID = ARequest->Params->Values["ID"].ToInt(); 78 | // Add your extra business logic 79 | lName = "The name is " & lName; 80 | AResponse->Body->JSONWriter->WriteStartObject(); 81 | AResponse->Body->JSONWriter->WritePropertyName("id"); 82 | AResponse->Body->JSONWriter->WriteValue(lID); 83 | AResponse->Body->JSONWriter->WritePropertyName("name"); 84 | AResponse->Body->JSONWriter->WriteValue(lName); 85 | AResponse->Body->JSONWriter->WriteEndObject(); 86 | } 87 | 88 | // given 2 queries with a master/detail relationship, returns 1 JSON object with a nested array with the detail query 89 | TJSONObject* TTestResource1::SerializeMasterDetail(TFDQuery* AMasterDataset, TFDQuery* ADetailDataset, System::UnicodeString APropertyName, TStringList* AFields) 90 | { 91 | TDataSetToJSONBridge *lBridge = new TDataSetToJSONBridge; 92 | try { 93 | // takes the current record of the master query and converts it to a JSON object 94 | lBridge->Dataset = AMasterDataset; 95 | lBridge->IncludeNulls = True; 96 | // specifies that the we only require to process the current record 97 | lBridge->Area = TJSONDataSetArea::Current; 98 | TJSONObject* lJSONObject = new TJSONObject; 99 | // adds the master record as an object in the JSON result 100 | lJSONObject = (TJSONObject*) lBridge->Produce(); 101 | 102 | // in case we passed a list of fields we want to export we assign them to the bridge, otherwise the default behaviour is exporting all fields in the query 103 | if (AFields != NULL) { 104 | lBridge->FieldNames->Assign(AFields); 105 | } 106 | // the same bridge is being reused, but now the detail dataset is being assigned 107 | lBridge->Dataset = ADetailDataset; 108 | // in this case all the records from the query will be processed 109 | lBridge->Area = TJSONDataSetArea::All; 110 | TJSONArray* lJSONArray = new TJSONArray; 111 | // stores the detail array in a temp array to add it afterwards in the main object 112 | lJSONArray = (TJSONArray*) lBridge->Produce(); 113 | // the array is being added to the main object as an array with the propertyname passed in the argument 114 | lJSONObject->AddPair(APropertyName, lJSONArray); 115 | return lJSONObject; 116 | } __finally { 117 | lBridge->Free(); 118 | } 119 | } 120 | 121 | // if a query has a masterfield assigned, it retuns a stringlist with all the fields but that masterfield 122 | TStringList* TTestResource1::ExcludeMasterFieldFromFields(TFDQuery* ADataset) 123 | { 124 | System::UnicodeString lMasterField = ADataset->MasterFields; 125 | TStringList* fields = new TStringList; 126 | fields->Assign(ADataset->FieldList); 127 | int i = fields->IndexOf(lMasterField); 128 | if (i > -1) { 129 | fields->Delete(i); 130 | } 131 | return fields; 132 | } 133 | 134 | 135 | static void Register() 136 | { 137 | std::unique_ptr attributes(new TEMSResourceAttributes()); 138 | attributes->ResourceName = "test"; 139 | attributes->ResourceSuffix["dsrCUSTOMER"] = "customers"; 140 | attributes->ResourceSuffix["dsrSALES"] = "customers/{CUST_NO}/sales"; 141 | attributes->ResourceSuffix["dsrSALES.List"] = "./"; 142 | attributes->ResourceSuffix["dsrSALES.Get"] = "./{PO_NUMBER}"; 143 | attributes->ResourceSuffix["dsrSALES.Post"] = "./"; 144 | attributes->ResourceSuffix["dsrSALES.Put"] = "./{PO_NUMBER}"; 145 | attributes->ResourceSuffix["dsrSALES.Delete"] = "./{PO_NUMBER}"; 146 | attributes->ResourceSuffix["GetCustomersDetails"] = "./customers-details/"; 147 | attributes->ResourceSuffix["GetCustomerDetails"] = "./customers-details/{CUST_NO}"; 148 | attributes->ResourceSuffix["PostCustomEndPoint"] = "./custom/{ID}"; 149 | 150 | RegisterResource(__typeinfo(TTestResource1), attributes.release()); 151 | } 152 | 153 | #pragma startup Register 32 154 | 155 | -------------------------------------------------------------------------------- /07 - Creating your own customized endpoints/C++/MyDMUnit.dfm: -------------------------------------------------------------------------------- 1 | object TestResource1: TTestResource1 2 | Height = 375 3 | Width = 750 4 | PixelsPerInch = 120 5 | object FDConnection1: TFDConnection 6 | Params.Strings = ( 7 | 'ConnectionDef=EMPLOYEE') 8 | LoginPrompt = False 9 | Left = 38 10 | Top = 20 11 | end 12 | object qryCUSTOMER: TFDQuery 13 | Connection = FDConnection1 14 | SQL.Strings = ( 15 | 'select * from CUSTOMER' 16 | '&MacroWhere' 17 | '{if !SORT}order by !SORT{fi}') 18 | Left = 163 19 | Top = 20 20 | MacroData = < 21 | item 22 | Value = Null 23 | Name = 'MACROWHERE' 24 | DataType = mdIdentifier 25 | end 26 | item 27 | Value = Null 28 | Name = 'SORT' 29 | end> 30 | end 31 | object dsrCUSTOMER: TEMSDataSetResource 32 | AllowedActions = [List, Get, Post, Put, Delete] 33 | DataSet = qryCUSTOMER 34 | Left = 163 35 | Top = 96 36 | end 37 | object qrySALES: TFDQuery 38 | MasterSource = dsCUSTOMER 39 | MasterFields = 'CUST_NO' 40 | Connection = FDConnection1 41 | SQL.Strings = ( 42 | 'select * from SALES' 43 | 'WHERE CUST_NO = :CUST_NO' 44 | '{if !SORT}order by !SORT{fi}') 45 | Left = 288 46 | Top = 20 47 | ParamData = < 48 | item 49 | Name = 'CUST_NO' 50 | ParamType = ptInput 51 | end> 52 | MacroData = < 53 | item 54 | Value = Null 55 | Name = 'SORT' 56 | end> 57 | end 58 | object dsrSALES: TEMSDataSetResource 59 | AllowedActions = [List, Get, Post, Put, Delete] 60 | DataSet = qrySALES 61 | Left = 288 62 | Top = 96 63 | end 64 | object dsCUSTOMER: TDataSource 65 | DataSet = qryCUSTOMER 66 | Left = 160 67 | Top = 176 68 | end 69 | end 70 | -------------------------------------------------------------------------------- /07 - Creating your own customized endpoints/C++/MyDMUnit.h: -------------------------------------------------------------------------------- 1 | // EMS Resource Modules 2 | //--------------------------------------------------------------------------- 3 | 4 | #ifndef MyDMUnitH 5 | #define MyDMUnitH 6 | //--------------------------------------------------------------------------- 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | //--------------------------------------------------------------------------- 33 | #pragma explicit_rtti methods (public) 34 | class TTestResource1 : public TDataModule 35 | { 36 | __published: 37 | TFDConnection *FDConnection1; 38 | TFDQuery *qryCUSTOMER; 39 | TEMSDataSetResource *dsrCUSTOMER; 40 | TFDQuery *qrySALES; 41 | TEMSDataSetResource *dsrSALES; 42 | TDataSource *dsCUSTOMER; 43 | 44 | private: 45 | 46 | public: 47 | __fastcall TTestResource1(TComponent* Owner); 48 | // Returns an array with all the customers and a nested array with all the customer's sales 49 | void GetCustomersDetails(TEndpointContext* AContext, TEndpointRequest* ARequest, TEndpointResponse* AResponse); 50 | // Returns a JSON object with the specified customer number and a nested array with all the customer's sales 51 | void GetCustomerDetails(TEndpointContext* AContext, TEndpointRequest* ARequest, TEndpointResponse* AResponse); 52 | // Custom mock POST method 53 | void PostCustomEndPoint(TEndpointContext* AContext, TEndpointRequest* ARequest, TEndpointResponse* AResponse); 54 | // If the query has a masterfield defined, it retuns a StringList of all the fields but the masterfield one 55 | TStringList* ExcludeMasterFieldFromFields(TFDQuery* ADataset); 56 | // Serializes a Master/Detail relationship into a JSON Object with the detail query nested as a JSON array in the same object 57 | TJSONObject* SerializeMasterDetail(TFDQuery* AMasterDataset, TFDQuery* ADetailDataset, System::UnicodeString APropertyName, TStringList* AFields = NULL); 58 | }; 59 | #endif 60 | -------------------------------------------------------------------------------- /07 - Creating your own customized endpoints/Delphi/CustomEndpointsPackage.dpk: -------------------------------------------------------------------------------- 1 | package CustomEndpointsPackage; 2 | 3 | {$R *.res} 4 | {$IFDEF IMPLICITBUILDING This IFDEF should not be used by users} 5 | {$ALIGN 8} 6 | {$ASSERTIONS ON} 7 | {$BOOLEVAL OFF} 8 | {$DEBUGINFO OFF} 9 | {$EXTENDEDSYNTAX ON} 10 | {$IMPORTEDDATA ON} 11 | {$IOCHECKS ON} 12 | {$LOCALSYMBOLS ON} 13 | {$LONGSTRINGS ON} 14 | {$OPENSTRINGS ON} 15 | {$OPTIMIZATION OFF} 16 | {$OVERFLOWCHECKS ON} 17 | {$RANGECHECKS ON} 18 | {$REFERENCEINFO ON} 19 | {$SAFEDIVIDE OFF} 20 | {$STACKFRAMES ON} 21 | {$TYPEDADDRESS OFF} 22 | {$VARSTRINGCHECKS ON} 23 | {$WRITEABLECONST OFF} 24 | {$MINENUMSIZE 1} 25 | {$IMAGEBASE $400000} 26 | {$DEFINE DEBUG} 27 | {$ENDIF IMPLICITBUILDING} 28 | {$RUNONLY} 29 | {$IMPLICITBUILD ON} 30 | 31 | requires 32 | rtl, 33 | emsserverapi, 34 | dbrtl, 35 | emsserverresource, 36 | FireDACCommonDriver, 37 | FireDACCommon, 38 | FireDAC, 39 | FireDACIBDriver, 40 | vcl, 41 | vclFireDAC; 42 | 43 | contains 44 | MyDM in 'MyDM.pas' {TestResource1: TDataModule}; 45 | 46 | end. 47 | -------------------------------------------------------------------------------- /07 - Creating your own customized endpoints/Delphi/CustomEndpointsPackage.res: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Embarcadero/RADServer-Docs/b6cecd34bb509284d5f7d8d0d3aed5352fecc529/07 - Creating your own customized endpoints/Delphi/CustomEndpointsPackage.res -------------------------------------------------------------------------------- /07 - Creating your own customized endpoints/Delphi/MyDM.dfm: -------------------------------------------------------------------------------- 1 | object TestResource1: TTestResource1 2 | Height = 267 3 | Width = 348 4 | object FDConnection1: TFDConnection 5 | Params.Strings = ( 6 | 'ConnectionDef=EMPLOYEE') 7 | LoginPrompt = False 8 | Left = 30 9 | Top = 16 10 | end 11 | object qryCUSTOMER: TFDQuery 12 | Connection = FDConnection1 13 | SQL.Strings = ( 14 | 'select * from CUSTOMER' 15 | '&MacroWhere' 16 | '{if !SORT}order by !SORT{fi}') 17 | Left = 130 18 | Top = 16 19 | MacroData = < 20 | item 21 | Value = Null 22 | Name = 'MACROWHERE' 23 | DataType = mdIdentifier 24 | end 25 | item 26 | Value = Null 27 | Name = 'SORT' 28 | end> 29 | end 30 | object dsrCUSTOMER: TEMSDataSetResource 31 | AllowedActions = [List, Get, Post, Put, Delete] 32 | DataSet = qryCUSTOMER 33 | KeyFields = 'CUST_NO' 34 | PageSize = 5 35 | Left = 130 36 | Top = 80 37 | end 38 | object qrySALES: TFDQuery 39 | MasterSource = dsCUSTOMER 40 | MasterFields = 'CUST_NO' 41 | Connection = FDConnection1 42 | SQL.Strings = ( 43 | 'select * from SALES' 44 | 'where cust_no = :CUST_NO' 45 | '{if !SORT}order by !SORT{fi}') 46 | Left = 230 47 | Top = 16 48 | ParamData = < 49 | item 50 | Name = 'CUST_NO' 51 | DataType = ftInteger 52 | ParamType = ptInput 53 | Value = Null 54 | end> 55 | MacroData = < 56 | item 57 | Value = Null 58 | Name = 'SORT' 59 | end> 60 | end 61 | object dsrSALES: TEMSDataSetResource 62 | AllowedActions = [List, Get, Post, Put, Delete] 63 | DataSet = qrySALES 64 | KeyFields = 'PO_NUMBER' 65 | Left = 230 66 | Top = 80 67 | end 68 | object dsCUSTOMER: TDataSource 69 | DataSet = qryCUSTOMER 70 | Left = 128 71 | Top = 141 72 | end 73 | end 74 | -------------------------------------------------------------------------------- /07 - Creating your own customized endpoints/Delphi/MyDM.pas: -------------------------------------------------------------------------------- 1 | unit MyDM; 2 | 3 | // EMS Resource Module 4 | 5 | interface 6 | 7 | uses 8 | System.SysUtils, 9 | System.Classes, 10 | System.JSON, 11 | EMS.Services, 12 | EMS.ResourceAPI, 13 | EMS.ResourceTypes, 14 | FireDAC.Stan.Intf, 15 | FireDAC.Stan.Option, 16 | FireDAC.Stan.Error, 17 | FireDAC.UI.Intf, 18 | FireDAC.Phys.Intf, 19 | FireDAC.Stan.Def, 20 | FireDAC.Stan.Pool, 21 | FireDAC.Stan.Async, 22 | FireDAC.Phys, 23 | FireDAC.Phys.IB, 24 | FireDAC.Phys.IBDef, 25 | FireDAC.ConsoleUI.Wait, 26 | FireDAC.Stan.Param, 27 | FireDAC.DatS, 28 | FireDAC.DApt.Intf, 29 | FireDAC.DApt, 30 | EMS.DataSetResource, 31 | Data.DB, 32 | FireDAC.Comp.DataSet, 33 | FireDAC.Comp.Client, 34 | FireDAC.VCLUI.Wait; 35 | 36 | type 37 | 38 | [ResourceName('test')] 39 | TTestResource1 = class(TDataModule) 40 | FDConnection1: TFDConnection; 41 | qryCUSTOMER: TFDQuery; 42 | [ResourceSuffix('customers')] 43 | dsrCUSTOMER: TEMSDataSetResource; 44 | qrySALES: TFDQuery; 45 | [ResourceSuffix('customers/{CUST_NO}/sales')] 46 | [ResourceSuffix('List', './')] 47 | [ResourceSuffix('Get', './{PO_NUMBER}')] 48 | [ResourceSuffix('Post', './')] 49 | [ResourceSuffix('Put', './{PO_NUMBER}')] 50 | [ResourceSuffix('Delete', './{PO_NUMBER}')] 51 | dsrSALES: TEMSDataSetResource; 52 | dsCUSTOMER: TDataSource; 53 | private 54 | // If the query has a masterfield defined, it retuns a StringList of all the fields but the masterfield one 55 | function ExcludeMasterFieldFromFields(ADataset: TFDQuery): TStringList; 56 | // Serializes a Master/Detail relationship into a JSON Object with the detail query nested as a JSON array in the same object 57 | function SerializeMasterDetail(AMasterDataset: TFDQuery; ADetailDataset: TFDQuery; APropertyName: string; AFields: TStringList = nil): TJSONObject; 58 | published 59 | [ResourceSuffix('./customers-details/')] 60 | // Returns an array with all the customers and a nested array with all the customer's sales 61 | procedure GetCustomersDetails(const AContext: TEndpointContext; const ARequest: TEndpointRequest; 62 | const AResponse: TEndpointResponse); 63 | [ResourceSuffix('./customers-details/{CUST_NO}')] 64 | // Returns a JSON object with the specified customer number and a nested array with all the customer's sales 65 | procedure GetCustomerDetails(const AContext: TEndpointContext; const ARequest: TEndpointRequest; 66 | const AResponse: TEndpointResponse); 67 | [ResourceSuffix('./custom/{ID}')] 68 | // Custom mock POST method 69 | procedure PostCustomEndPoint(const AContext: TEndpointContext; const ARequest: TEndpointRequest; 70 | const AResponse: TEndpointResponse); 71 | end; 72 | 73 | implementation 74 | 75 | uses 76 | Data.DBJson; 77 | 78 | {%CLASSGROUP 'System.Classes.TPersistent'} 79 | {$R *.dfm} 80 | 81 | { TTestResource1 } 82 | 83 | procedure TTestResource1.GetCustomersDetails(const AContext: TEndpointContext; const ARequest: TEndpointRequest; 84 | const AResponse: TEndpointResponse); 85 | begin 86 | qryCUSTOMER.Open; 87 | qryCUSTOMER.First; 88 | qrySALES.Open; 89 | 90 | var lFields := ExcludeMasterFieldFromFields(qrySALES); 91 | 92 | // this array will store all the objects returned from the ConverToMasterDetail loop 93 | var lJSONArray := TJSONArray.Create; 94 | try 95 | while not(qryCUSTOMER.Eof) do 96 | begin 97 | lJSONArray.Add(SerializeMasterDetail(qryCUSTOMER, qrySALES, 'SALES', lFields)); 98 | qryCUSTOMER.Next; 99 | end; 100 | AResponse.Body.SetValue(lJSONArray, true); 101 | finally 102 | lFields.Free; 103 | qrySALES.Close; 104 | qryCUSTOMER.Close; 105 | end; 106 | end; 107 | 108 | procedure TTestResource1.GetCustomerDetails(const AContext: TEndpointContext; const ARequest: TEndpointRequest; 109 | const AResponse: TEndpointResponse); 110 | begin 111 | var lCustomerNo := ARequest.Params.Values['CUST_NO'].ToInteger; 112 | // We use a parameter instead of concatenating the CustomerNo to avoid SQL injection 113 | qryCUSTOMER.MacroByName('MacroWhere').AsRaw := 'WHERE CUST_NO = :CUST_NO'; 114 | qryCUSTOMER.ParamByName('CUST_NO').AsInteger := lCustomerNo; 115 | qryCUSTOMER.Open; 116 | try 117 | if qryCUSTOMER.RecordCount = 0 then 118 | AResponse.RaiseNotFound('Not found', 'Customer ID not found'); 119 | 120 | qrySALES.ParamByName('CUST_NO').asInteger := lCustomerNo; 121 | qrySALES.Open; 122 | var lFields := ExcludeMasterFieldFromFields(qrySALES); 123 | try 124 | AResponse.Body.SetValue( 125 | SerializeMasterDetail(qryCUSTOMER, qrySALES, 'SALES', lFields) 126 | , True); 127 | qrySALES.Close; 128 | finally 129 | lFields.Free; 130 | end; 131 | finally 132 | qryCUSTOMER.Close; 133 | qryCUSTOMER.MacroByName('MacroWhere').Clear; 134 | end; 135 | end; 136 | 137 | 138 | procedure TTestResource1.PostCustomEndPoint(const AContext: TEndpointContext; 139 | const ARequest: TEndpointRequest; const AResponse: TEndpointResponse); 140 | var 141 | lId: integer; 142 | lName: string; 143 | lJSON: TJSONObject; 144 | begin 145 | if not(ARequest.Body.TryGetObject(lJSON) and lJSON.TryGetValue('name', lName)) then 146 | AResponse.RaiseBadRequest('Bad request', 'Missing data'); 147 | lID := ARequest.Params.Values['ID'].ToInteger; 148 | // Add your extra business logic 149 | lName := 'The name is ' + lName; 150 | AResponse.Body.JSONWriter.WriteStartObject; 151 | AResponse.Body.JSONWriter.WritePropertyName('id'); 152 | AResponse.Body.JSONWriter.WriteValue(lId); 153 | AResponse.Body.JSONWriter.WritePropertyName('name'); 154 | AResponse.Body.JSONWriter.WriteValue(lName); 155 | AResponse.Body.JSONWriter.WriteEndObject; 156 | end; 157 | 158 | // given 2 queries with a master/detail relationship, returns 1 JSON object with a nested array with the detail query 159 | function TTestResource1.SerializeMasterDetail(AMasterDataset: TFDQuery; ADetailDataset: TFDQuery; APropertyName: string; AFields: TStringList = nil): TJSONObject; 160 | begin 161 | var lBridge := TDataSetToJSONBridge.Create; 162 | try 163 | // takes the current record of the master query and converts it to a JSON object 164 | lBridge.Dataset := AMasterDataset; 165 | lBridge.IncludeNulls := True; 166 | // specifies that the we only require to process the current record 167 | lBridge.Area := TJSONDataSetArea.Current; 168 | // adds the master record as an object in the JSON result 169 | Result := TJSONObject(lBridge.Produce); 170 | 171 | // in case we passed a list of fields we want to export we assign them to the bridge, otherwise the default behaviour is exporting all fields in the query 172 | if Assigned(AFields) then 173 | lBridge.FieldNames.Assign(AFields); 174 | // the same bridge is being reused, but now the detail dataset is being assigned 175 | lBridge.Dataset := ADetailDataset; 176 | // in this case all the records from the query will be processed 177 | lBridge.Area := TJSONDataSetArea.All; 178 | // stores the detail array in a temp array to add it afterwards in the main object 179 | var lJSONarray := TJSONArray(lBridge.Produce); 180 | // the array is being added to the main object as an array with the propertyname passed in the argument 181 | Result.AddPair(APropertyName, lJSONarray); 182 | finally 183 | lBridge.Free; 184 | end; 185 | end; 186 | 187 | // if a query has a masterfield assigned, it retuns a stringlist with all the fields but that masterfield 188 | function TTestResource1.ExcludeMasterFieldFromFields(ADataset: TFDQuery): TStringList; 189 | begin 190 | var lMasterField := ADataset.MasterFields; 191 | Result := TStringList.Create; 192 | Result.Assign(ADataset.FieldList); 193 | var i := Result.IndexOf(lMasterField); 194 | if i > -1 then 195 | Result.Delete(i); 196 | end; 197 | 198 | procedure Register; 199 | begin 200 | RegisterResource(TypeInfo(TTestResource1)); 201 | end; 202 | 203 | initialization 204 | 205 | Register; 206 | 207 | end. 208 | -------------------------------------------------------------------------------- /07 - Creating your own customized endpoints/README.md: -------------------------------------------------------------------------------- 1 | ## Chapter 7: Creating your own customized endpoints 2 | 3 | Don't forget to [watch the video](https://youtu.be/oBPzvrRK1Kw) of this chapter. 4 | 5 | Until this chapter we have seen basic JSON structures: arrays, objects… with fairly simple URIs as well: /customers, /sales… but it’s very common when it comes to REST API best practices to find sub-resources URIs and also nested arrays/objects inside other objets in the JSON responses. In this chapter we will talk about how to accomplish those kind of structures with RAD Server as well as creating your own GET, POST, PUT or DELETE methods. -------------------------------------------------------------------------------- /08 - Accessing the built-in analytics/README.md: -------------------------------------------------------------------------------- 1 | ## Chapter 8: Accessing the built-in analytics 2 | 3 | Don't forget to [watch the video](https://youtu.be/uDntdmokIyE) of this chapter. 4 | 5 | The RAD Server Console is a service that provides a pre-configured web application which displays multiple data as well as analytics from the RAD Server Engine. It allows you to have a more in-depth view of the activity on your RAD Server instances and make decisions based on real data. Analyze user, API, and services activity to gain insight into how your application is being utilized. 6 | 7 | This chapter has no demo files because you can access to the dev console from any RAD Server project. In the dev server window click on the **Open Console** button and an instance of the console will be run on, by default, port 8081. -------------------------------------------------------------------------------- /09 - Deploying RAD Server/README.md: -------------------------------------------------------------------------------- 1 | ## Chapter 9: Deploying RAD Server 2 | 3 | - Deployment on Windows: [watch the video](https://youtu.be/QtdV4m_0Ubk). 4 | - Deployment on Linux: [watch the video](https://youtu.be/ZzPfvOgS-Zk). 5 | - Deployment on Docker: [watch the video](https://youtu.be/YFem4Tmdxag). 6 | 7 | This chapter covers the multiple platforms where you can deploy RAD Server in production: Windows, Linux and Docker. If you are interested in RAD Server Lite, jump to the next chapter. 8 | 9 | The easiest way to deploy RAD Server in production is using the installers you can find on GetIt. You will find one for Windows and another one for Linux. 10 | 11 | For manual deployment, even though conceptually speaking all the platforms use the same foundation, the deployments can differ a bit. For this chapter you will find 3 different videos, one for each platform. 12 | 13 | -------------------------------------------------------------------------------- /10 - RAD Server Lite/README.md: -------------------------------------------------------------------------------- 1 | ## Chapter 10: RAD Server Lite 2 | 3 | Don't forget to [watch the video](https://youtu.be/NMEC1Urcdv4) of this chapter. 4 | 5 | **RAD Server Lite** (RSLite) offers a simpler deployment model for test servers and scenarios not requiring a lot of throughputs, and it offers this by using the InterBase embedded database engine, **IBToGo**, instead of the full-blown server and combines it with a simplified licensing model. 6 | 7 | RSLite uses the same binary of the development edition (that ships with RAD Studio) along with IBToGo binaries and a license slip file you can deploy with your solution (requiring no registration on the computer you deploy it to). Because it uses an embedded database and because it uses the Indy HTTP Server component, it cannot serve the same number of requests per second of a regular full-blown RAD Server installation, and it cannot scale with multiple RAD Server front ends. 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /11 - Authentication and Authorization/C++/CustomLoginPackageCpp.cpp: -------------------------------------------------------------------------------- 1 | // EMS Package 2 | //--------------------------------------------------------------------------- 3 | 4 | #include 5 | #pragma hdrstop 6 | #pragma package(smart_init) 7 | //--------------------------------------------------------------------------- 8 | 9 | // Package source. 10 | //--------------------------------------------------------------------------- 11 | 12 | 13 | #pragma argsused 14 | extern "C" int _libmain(unsigned long reason) 15 | { 16 | return 1; 17 | } 18 | //--------------------------------------------------------------------------- 19 | 20 | 21 | -------------------------------------------------------------------------------- /11 - Authentication and Authorization/C++/CustomLoginPackageCpp.res: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Embarcadero/RADServer-Docs/b6cecd34bb509284d5f7d8d0d3aed5352fecc529/11 - Authentication and Authorization/C++/CustomLoginPackageCpp.res -------------------------------------------------------------------------------- /11 - Authentication and Authorization/C++/CustomLoginResourceU.cpp: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------- 2 | 3 | // This software is Copyright (c) 2016 Embarcadero Technologies, Inc. 4 | // You may only use this software if you are an authorized licensee 5 | // of an Embarcadero developer tools product. 6 | // This software is considered a Redistributable as defined under 7 | // the software license agreement that comes with the Embarcadero Products 8 | // and is subject to that software license agreement. 9 | 10 | //--------------------------------------------------------------------------- 11 | #pragma hdrstop 12 | 13 | #include "CustomLoginResourceU.h" 14 | #include 15 | //--------------------------------------------------------------------------- 16 | #pragma package(smart_init) 17 | //--------------------------------------------------------------------------- 18 | 19 | // Customer external credentials validation 20 | String TCustomLoginResource::ValidateExternalCredentials(const String userName, 21 | const String Password) 22 | { 23 | // Integrate your own logic for verifying the credentials 24 | bool lUserAuthorized = true; 25 | GUID lUserGUID; 26 | CreateGUID(lUserGUID); 27 | if (!lUserAuthorized) { 28 | UnicodeString lError = "A more descriptive error"; 29 | EEMSHTTPError::RaiseUnauthorized("unauthorized user", lError); 30 | } 31 | return GUIDToString(lUserGUID); 32 | } 33 | 34 | // Custom external signup 35 | String TCustomLoginResource::CreateExternalUser(const String AUserName, 36 | const String APassword) 37 | { 38 | GUID lUserGUID; 39 | CreateGUID(lUserGUID); 40 | // Integrate your own logic for creating the user. This example will never fail 41 | if (IsEqualGUID(lUserGUID, GUID_NULL)) { 42 | UnicodeString lError = "A more descriptive error"; 43 | EEMSHTTPError::RaiseBadRequest("Bad Request", lError); 44 | } 45 | return GUIDToString(lUserGUID); 46 | } 47 | 48 | // This method gererates a hashed version of the user name to use it as password for RAD Server 49 | // Using this approach it will allow the user to change their password on the third party auth service 50 | // and still allow to login automatically in RAD Server. This sytem won't allow change of username though 51 | String TCustomLoginResource::GenerateHashedPassword(const String APassword) 52 | { 53 | const String SALT = "my-super-secret-salt"; 54 | return System::Hash::THashSHA2::GetHashString(APassword + SALT); 55 | } 56 | 57 | void TCustomLoginResource::PostLogin(TEndpointContext* AContext, 58 | TEndpointRequest* ARequest, 59 | TEndpointResponse* AResponse) 60 | { 61 | AResponse->RaiseUnauthorized("Unauthorized access", ""); 62 | // Create in-process EMS API 63 | std::unique_ptr lEMSAPI(new TEMSInternalAPI(AContext)); 64 | // Extract credentials from request 65 | TJSONObject* lValue; 66 | String lUserName; 67 | String lPassword; 68 | 69 | if (!(ARequest->Body->TryGetObject(lValue) && 70 | (lValue->GetValue(TEMSInternalAPI_TJSONNames_UserName) != NULL) && 71 | (lValue->GetValue(TEMSInternalAPI_TJSONNames_Password) != NULL))) 72 | AResponse->RaiseBadRequest("", "Missing credentials"); 73 | 74 | lUserName = lValue->Get(TEMSInternalAPI_TJSONNames_UserName)->JsonValue->Value(); 75 | lPassword = lValue->Get(TEMSInternalAPI_TJSONNames_Password)->JsonValue->Value(); 76 | 77 | String lExternalUserGUID = ValidateExternalCredentials(lUserName, lPassword); 78 | 79 | _di_IEMSResourceResponseContent lResponse; 80 | if (!lEMSAPI->QueryUserName(lUserName)) { 81 | // Add user when there is no user for these credentials 82 | // in-process call to actual Users/Signup endpoint 83 | std::unique_ptr lUserFields; 84 | lUserFields->AddPair("ExternalUserGUID", lExternalUserGUID); 85 | lUserFields->AddPair("comment", "This user added by CustomResource.CustomLoginUser"); 86 | lResponse = lEMSAPI->SignupUser(lUserName, 87 | GenerateHashedPassword(lUserName), 88 | lUserFields.get()); 89 | } else 90 | // in-process call to actual Users/Login endpoint 91 | lResponse = lEMSAPI->LoginUser(lUserName, GenerateHashedPassword(lUserName)); 92 | if (lResponse->TryGetObject(lValue)) { 93 | AResponse->Body->SetValue(lValue, false); 94 | } 95 | } 96 | 97 | // Custom EMS signup 98 | void TCustomLoginResource::PostSignup(TEndpointContext* AContext, 99 | TEndpointRequest* ARequest, 100 | TEndpointResponse* AResponse) 101 | { 102 | // Create in-process EMS API 103 | std::unique_ptr lEMSAPI(new TEMSInternalAPI(AContext)); 104 | // Extract credentials from request 105 | TJSONObject* lValue; 106 | String lUserName, lPassword; 107 | if (!(ARequest->Body->TryGetObject(lValue) && 108 | (lValue->GetValue(TEMSInternalAPI_TJSONNames_UserName) != NULL) && 109 | (lValue->GetValue(TEMSInternalAPI_TJSONNames_Password) != NULL))) 110 | AResponse->RaiseBadRequest("", "Missing credentials"); 111 | lUserName = lValue->Get(TEMSInternalAPI_TJSONNames_UserName)->JsonValue->Value(); 112 | lPassword = lValue->Get(TEMSInternalAPI_TJSONNames_Password)->JsonValue->Value(); 113 | 114 | String lExternalUserGUID = CreateExternalUser(lUserName, lPassword); 115 | std::unique_ptr lUserFields(static_cast(lValue->Clone())); 116 | 117 | // Remove metadata 118 | lUserFields->RemovePair(TEMSInternalAPI_TJSONNames_UserName); 119 | lUserFields->RemovePair(TEMSInternalAPI_TJSONNames_Password); 120 | // Add another fields, for example 121 | lUserFields->AddPair("ExternalUserGUID", lExternalUserGUID); 122 | lUserFields->AddPair("comment", "This user added by CustomResource.CustomSignupUser"); 123 | // in-process call to actual Users/Signup endpoint 124 | _di_IEMSResourceResponseContent lResponse; 125 | lResponse = lEMSAPI->SignupUser(lUserName, 126 | GenerateHashedPassword(lUserName), 127 | lUserFields.get()); 128 | if (lResponse->TryGetObject(lValue)) { 129 | AResponse->Body->SetValue(lValue, false); 130 | } 131 | } 132 | 133 | static void Register() 134 | { 135 | std::auto_ptr attributes(new TEMSResourceAttributes()); 136 | attributes->ResourceName = "CustomLogin"; 137 | attributes->ResourceSuffix["PostSignup"] = "signup"; 138 | attributes->EndPointName["PostSignup"] = "CustomSignupUser"; 139 | attributes->ResourceSuffix["PostLogin"] = "login"; 140 | attributes->EndPointName["PostLogin"] = "CustomLoginUser"; 141 | RegisterResource(__typeinfo(TCustomLoginResource), attributes.release()); 142 | } 143 | 144 | #pragma startup Register 32 145 | 146 | -------------------------------------------------------------------------------- /11 - Authentication and Authorization/C++/CustomLoginResourceU.h: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------- 2 | 3 | // This software is Copyright (c) 2016 Embarcadero Technologies, Inc. 4 | // You may only use this software if you are an authorized licensee 5 | // of an Embarcadero developer tools product. 6 | // This software is considered a Redistributable as defined under 7 | // the software license agreement that comes with the Embarcadero Products 8 | // and is subject to that software license agreement. 9 | 10 | //--------------------------------------------------------------------------- 11 | // EMS Resource Modules 12 | //--------------------------------------------------------------------------- 13 | 14 | #ifndef CustomLoginResourceUH 15 | #define CustomLoginResourceUH 16 | //--------------------------------------------------------------------------- 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | //--------------------------------------------------------------------------- 25 | #pragma explicit_rtti methods (public, private) 26 | class TCustomLoginResource : public TObject 27 | { 28 | __published: 29 | private: 30 | String ValidateExternalCredentials(const String userName, const String Password); 31 | String CreateExternalUser(const String AUserName, const String APassword); 32 | String GenerateHashedPassword(const String APassword); 33 | public: 34 | __fastcall ~TCustomLoginResource(){} 35 | void PostSignup(TEndpointContext* AContext, TEndpointRequest* ARequest, TEndpointResponse* AResponse); 36 | void PostLogin(TEndpointContext* AContext, TEndpointRequest* ARequest, TEndpointResponse* AResponse); 37 | }; 38 | #endif 39 | 40 | 41 | -------------------------------------------------------------------------------- /11 - Authentication and Authorization/Delphi/CustomLoginPackage.dpk: -------------------------------------------------------------------------------- 1 | package CustomLoginPackage; 2 | 3 | {$R *.res} 4 | {$IFDEF IMPLICITBUILDING This IFDEF should not be used by users} 5 | {$ALIGN 8} 6 | {$ASSERTIONS ON} 7 | {$BOOLEVAL OFF} 8 | {$DEBUGINFO OFF} 9 | {$EXTENDEDSYNTAX ON} 10 | {$IMPORTEDDATA ON} 11 | {$IOCHECKS ON} 12 | {$LOCALSYMBOLS ON} 13 | {$LONGSTRINGS ON} 14 | {$OPENSTRINGS ON} 15 | {$OPTIMIZATION OFF} 16 | {$OVERFLOWCHECKS OFF} 17 | {$RANGECHECKS OFF} 18 | {$REFERENCEINFO ON} 19 | {$SAFEDIVIDE OFF} 20 | {$STACKFRAMES ON} 21 | {$TYPEDADDRESS OFF} 22 | {$VARSTRINGCHECKS ON} 23 | {$WRITEABLECONST OFF} 24 | {$MINENUMSIZE 1} 25 | {$IMAGEBASE $400000} 26 | {$DEFINE DEBUG} 27 | {$ENDIF IMPLICITBUILDING} 28 | {$RUNONLY} 29 | {$IMPLICITBUILD ON} 30 | 31 | requires 32 | rtl, 33 | emsserverapi; 34 | 35 | contains 36 | CustomLoginResourceU in 'CustomLoginResourceU.pas'; 37 | 38 | end. 39 | -------------------------------------------------------------------------------- /11 - Authentication and Authorization/Delphi/CustomLoginPackage.res: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Embarcadero/RADServer-Docs/b6cecd34bb509284d5f7d8d0d3aed5352fecc529/11 - Authentication and Authorization/Delphi/CustomLoginPackage.res -------------------------------------------------------------------------------- /11 - Authentication and Authorization/Delphi/CustomLoginResourceU.pas: -------------------------------------------------------------------------------- 1 | unit CustomLoginResourceU; 2 | 3 | // EMS Resource Unit 4 | 5 | interface 6 | 7 | uses 8 | System.SysUtils, 9 | System.Classes, 10 | System.JSON, 11 | EMS.Services, 12 | EMS.ResourceAPI, 13 | EMS.ResourceTypes; 14 | 15 | type 16 | {$METHODINFO ON} 17 | [ResourceName('CustomLogin')] 18 | TCustomLogonResource = class 19 | private 20 | function ValidateExternalCredentials(AUserName, APassword: string): string; 21 | function CreateExternalUser(AUserName, APassword: string): string; 22 | function GenerateHashedPassword(AUserName: string): string; 23 | public 24 | [EndpointName('CustomSignupUser')] 25 | // Declare endpoint that matches signature of Users.SignupUser 26 | [ResourceSuffix('signup')] 27 | procedure PostSignup(const AContext: TEndpointContext; const ARequest: TEndpointRequest; const AResponse: TEndpointResponse); 28 | 29 | [EndpointName('CustomLoginUser')] 30 | // Declare endpoint that matches signature of Users.LoginUser 31 | [ResourceSuffix('login')] 32 | procedure PostLogin(const AContext: TEndpointContext; const ARequest: TEndpointRequest; const AResponse: TEndpointResponse); 33 | end; 34 | {$METHODINFO OFF} 35 | 36 | procedure Register; 37 | 38 | implementation 39 | 40 | uses System.Hash; 41 | 42 | procedure Register; 43 | begin 44 | RegisterResource(TypeInfo(TCustomLogonResource)); 45 | end; 46 | 47 | { TCustomLogonResource } 48 | 49 | // Customer external credentials validation 50 | function TCustomLogonResource.ValidateExternalCredentials(AUserName, APassword: string): string; 51 | var 52 | lUserGUID: TGUID; 53 | lUserAuthorized: boolean; 54 | lError: string; 55 | begin 56 | // Integrate your own logic for verifying the credentials 57 | lUserAuthorized := true; 58 | CreateGUID(lUserGUID); 59 | if not(lUserAuthorized) then 60 | begin 61 | lError := 'A more descriptive error'; 62 | EEMSHTTPError.RaiseUnauthorized('unathorized user', lError); 63 | end; 64 | Result := lUserGUID.ToString; 65 | end; 66 | 67 | // Custom external signup 68 | function TCustomLogonResource.CreateExternalUser(AUserName, APassword: string): string; 69 | var 70 | lUserGUID: TGUID; 71 | lError: string; 72 | begin 73 | CreateGUID(lUserGUID); 74 | // Integrate your own logic for creating the user. This example will never fail 75 | if lUserGUID.IsEmpty then 76 | begin 77 | lError := 'A more descriptive error'; 78 | EEMSHTTPError.RaiseBadRequest('Bad Request', lError); 79 | end; 80 | Result := lUserGUID.ToString; 81 | end; 82 | 83 | // This method gererates a hashed version of the user name to use it as password for RAD Server 84 | // Using this approach it will allow the user to change their password on the third party auth service 85 | // and still allow to login automatically in RAD Server. This sytem won't allow change of username though 86 | function TCustomLogonResource.GenerateHashedPassword(AUserName: string): string; 87 | begin 88 | const SALT = 'my-super-secret-salt'; 89 | Result := System.Hash.THashSHA2.GetHashString(AUserName + SALT); 90 | end; 91 | 92 | procedure TCustomLogonResource.PostLogin(const AContext: TEndpointContext; 93 | const ARequest: TEndpointRequest; const AResponse: TEndpointResponse); 94 | var 95 | lEMSAPI: TEMSInternalAPI; 96 | lResponse: IEMSResourceResponseContent; 97 | lValue: TJSONValue; 98 | lUserName: string; 99 | lPassword: string; 100 | begin 101 | // Create in-process EMS API 102 | lEMSAPI := TEMSInternalAPI.Create(AContext); 103 | try 104 | // Extract credentials from request 105 | if not (ARequest.Body.TryGetValue(LValue) and 106 | lValue.TryGetValue(TEMSInternalAPI.TJSONNames.UserName, lUserName) and 107 | lValue.TryGetValue(TEMSInternalAPI.TJSONNames.Password, lPassword)) then 108 | AResponse.RaiseBadRequest('', 'Missing credentials'); 109 | 110 | var lExternalUserGUID := ValidateExternalCredentials(lUserName, lPassword); 111 | 112 | if not LEMSAPI.QueryUserName(lUserName) then 113 | begin 114 | // Add user when there is no user for these credentials 115 | // in-process call to actual Users/Signup endpoint 116 | var lUserFields := TJSONObject.Create; 117 | lUserFields.AddPair('ExternalUserGUID', lExternalUserGUID); 118 | lUserFields.AddPair('comment', 'This user added by RAD Server CustomLoginUser'); 119 | lResponse := lEMSAPI.SignupUser(LUserName, GenerateHashedPassword(lUserName), lUserFields); 120 | end 121 | else 122 | // in-process call to actual Users/Login endpoint 123 | lResponse := lEMSAPI.LoginUser(lUserName, GenerateHashedPassword(lUserName)); 124 | if lResponse.TryGetValue(lValue) then 125 | AResponse.Body.SetValue(lValue, False); 126 | finally 127 | LEMSAPI.Free; 128 | end; 129 | end; 130 | 131 | // Custom EMS signup 132 | procedure TCustomLogonResource.PostSignup(const AContext: TEndpointContext; 133 | const ARequest: TEndpointRequest; const AResponse: TEndpointResponse); 134 | var 135 | lEMSAPI: TEMSInternalAPI; 136 | lResponse: IEMSResourceResponseContent; 137 | lValue: TJSONObject; 138 | lUserName: string; 139 | lPassword: string; 140 | lUserFields: TJSONObject; 141 | begin 142 | // Create in-process EMS API 143 | lEMSAPI := TEMSInternalAPI.Create(AContext); 144 | try 145 | // Extract credentials from request 146 | if not (ARequest.Body.TryGetObject(lValue) and 147 | lValue.TryGetValue(TEMSInternalAPI.TJSONNames.UserName, lUserName) and 148 | lValue.TryGetValue(TEMSInternalAPI.TJSONNames.Password, lPassword)) then 149 | AResponse.RaiseBadRequest('', 'Missing credentials'); 150 | 151 | var lExternalUserGUID := CreateExternalUser(lUserName, lPassword); 152 | lUserFields := lValue.Clone as TJSONObject; 153 | try 154 | // Remove meta data 155 | lUserFields.RemovePair(TEMSInternalAPI.TJSONNames.UserName); 156 | lUserFields.RemovePair(TEMSInternalAPI.TJSONNames.Password); 157 | // Add another fields, for example 158 | lUserFields.AddPair('ExternalUserGUID', lExternalUserGUID); 159 | lUserFields.AddPair('comment', 'This user added by RAD Server CustomSignupUser'); 160 | // in-process call to actual Users/Signup endpoint 161 | lResponse := LEMSAPI.SignupUser(LUserName, GenerateHashedPassword(lUserName), lUserFields) 162 | finally 163 | lUserFields.Free; 164 | end; 165 | if lResponse.TryGetObject(lValue) then 166 | AResponse.Body.SetValue(lValue, False); 167 | finally 168 | lEMSAPI.Free; 169 | end; 170 | end; 171 | 172 | 173 | end. 174 | 175 | 176 | -------------------------------------------------------------------------------- /11 - Authentication and Authorization/Delphi/readme.md: -------------------------------------------------------------------------------- 1 | EMS.CustomLogin Sample[]() 2 | # EMS.CustomLogin Sample 3 | 4 | 5 | 6 | ## Contents 7 | 8 | 9 | 10 | * [1 Location](#Location) 11 | * [2 Description](#Description) 12 | * [3 How to Use the Sample](#How_to_Use_the_Sample) 13 | 14 | * [3.1 Run the EMS CustomLogin Package](#Run_the_EMS_CustomLogin_Package) 15 | * [3.2 Run the Client Application](#Run_the_Client_Application) 16 | 17 | * [4 Implementation](#Implementation) 18 | 19 | * [4.1 EMS Custom Package](#EMS_Custom_Package) 20 | * [4.2 Client Application](#Client_Application) 21 | 22 | * [5 Uses](#Uses) 23 | * [6 See Also](#See_Also) 24 | 25 | The **CustomLogin** sample is a server-client EMS demo, that demonstrates how to implement custom Login and Signup endpoints in a custom resource.It requires InterBase to be installed on the machine to connect to the EMS server. Make sure that the EMS Server is running before you run the client project. 26 | 27 | ## Location 28 | 29 | You can find the **CustomLogin** sample project at: 30 | * **Start | Programs | Embarcadero RAD Studio Rio | Samples** and then navigate to either: 31 | 32 | * `Object Pascal\Database\EMS\CustomLogin` 33 | * `CPP\Database\EMS\CustomLogin` 34 | 35 | * **GitHub Repository:** 36 | 37 | * [https://github.com/Embarcadero/RADStudio11Demos/tree/main/Object%20Pascal/Database/EMS/CustomLogin/](https://github.com/Embarcadero/RADStudio11Demos/tree/main/Object%20Pascal/Database/EMS/CustomLogin/) 38 | * [https://github.com/Embarcadero/RADStudio11Demos/tree/main/CPP/Database/EMS/CustomLogin/](https://github.com/Embarcadero/RADStudio11Demos/tree/main/CPP/Database/EMS/CustomLogin/) 39 | 40 | ## Description 41 | 42 | This sample demonstrates how to implement a custom resource EMS package to extend the EMS Server. The **CustomLogin** resource is used to log on and sign up to the EMS Server by using Windows credentials.The first part consists of creating an EMS Package with a new resource (**CustomLogin**), that implements custom Login and Signup endpoints for your EMS Server. Once you run the package, the CustomLogin resource is registered on the EMS server, and the EMS Resource endpoints (Login and Signup) can be accessed by an EMS client application using REST call. 43 | The client application allows you to use the custom Login and Signup endpoints (from the CustomLogin package) and the standard [Users.SignupUser](http://docwiki.embarcadero.com/RADStudio/en/RAD_Server_Users_Resource#SignupUser_Endpoint) and [Users.LoginUser](http://docwiki.embarcadero.com/RADStudio/en/RAD_Server_Users_Resource#LoginUser_Endpoint) endpoints from the [Users resource](http://docwiki.embarcadero.com/RADStudio/en/RAD_Server_Users_Resource). 44 | 45 | ## How to Use the Sample 46 | 47 | 48 | ### Run the EMS CustomLogin Package 49 | 50 | When you run the CustomLoginPackage project, the EMSDevServer starts automatically. The supported platforms for this sample are: 32-bit and 64-bit Windows. 51 | 52 | 1. Navigate to one of the locations given above and open: 53 | 54 | * Delphi: **CustomLoginPackage.dpk**. 55 | * C++: **CustomLoginPackageCpp.cbproj**. 56 | 57 | 2. Press **Shift+Ctrl+F9** or choose **Run > Run Without Debugging**. 58 | 3. The **EMS Development Server** opens. If it is the first time you are using EMS on the machine, you need to run the configuration wizard in order to create the EMS server configuration file. 59 | 60 | * When the **Confirm** dialog opens, click **Yes**. 61 | * Click **Next** until the installation finishes. 62 | 63 | 4. The EMS Development Server starts automatically. 64 | In the [EMS Development Server window](http://docwiki.embarcadero.com/RADStudio/en/RAD_Server_Engine_Window) you can see the **CustomLogin** resource loaded to the server: 65 | ``` 66 | {"RegResource":{"Resource":"CustomLogin","Endpoints":["CustomSignupUser","CustomLoginUser"],"Thread":6444}} 67 | 68 | ``` 69 | 70 | 71 | ### Run the Client Application 72 | 73 | The client expects the EMSDevServer to be running at `localhost:8080`. If you are running the server at a different address, modify the properties of the [TEMSProvider](http://docwiki.embarcadero.com/Libraries/en/REST.Backend.EMSProvider.TEMSProvider) component.To run the sample on a different machine that the EMS Server, change the **URLHost** property of the TEMSProvider component to the IP address from the machine where the EMS Server runs. 74 | **Note:** The TEMSProvider component is placed in the CustomLoginClientU unit. 75 | 1. On the Project Manager, right-click on **ProjectGroup1**. 76 | 2. Click **Add Existing Project...**. 77 | 3. Navigate to one of the locations given above and open: 78 | 79 | * Delphi: **CustomLoginClient.dproj**. 80 | 81 | 4. C++: **CustomLoginClientCpp.cbproj**. 82 | 5. Press **F9** or choose **Run > Run**. 83 | 6. Select **Use Custom Resource** (CustomLogin) to use your Window credentials to authenticate in the EMS Server. 84 | 7. In the **Signup** or **Login** tabs, insert a **UserName** and **Password** and click the buttons: 85 | 86 | * Use the **Signup** tab to sign up a new user and log on to the EMS Server with that user. 87 | * Use the **Login** tab if you previously created an account. Click the **Logout** button to log off from the EMS Server. 88 | 89 | 8. In the **User** tab you can manage your user data: 90 | 91 | * Click the **Delete `user`** to delete the user from the EMS Server. 92 | * Click the **Retrieve Fields** button to get the custom description field from the EMS Server. 93 | 94 | ``` 95 | { 96 | "description":"New info" 97 | } 98 | 99 | ``` 100 | 101 | 102 | 103 | 104 | * Click the **Update Fields** button to modify and update the custom description field in the EMS Server. 105 | **Note:** The custom Login and Signup endpoints validate the **UserName** and **Password** against Windows users by calling [WinApi.Windows.LogonUser](https://msdn.microsoft.com/en-us/library/windows/desktop/aa378184%28v=vs.85%29.aspx). You need to sign up with valid Windows credentials. 106 | ## Implementation 107 | 108 | 109 | ### EMS Custom Package 110 | 111 | The custom login package implements custom Login and Signup endpoints, by matching the signature of the [Users.LoginUser](http://docwiki.embarcadero.com/RADStudio/en/RAD_Server_Users_Resource#LoginUser_Endpoint) and [Users.SignupUser](http://docwiki.embarcadero.com/RADStudio/en/RAD_Server_Users_Resource#SignupUser_Endpoint) and implementing these methods. The custom public endpoints are: 112 | * **CustomSignupUser** to sign up a new EMS user. 113 | * **CustomLoginUser** to log in an existing EMS user. 114 | The custom package uses the [TEMSInternalAPI](http://docwiki.embarcadero.com/Libraries/en/EMS.Services.TEMSInternalAPI) to call the endpoints of the [Users](http://docwiki.embarcadero.com/RADStudio/en/RAD_Server_Users_Resource) resource in the EMS Server.The custom package validates credentials using [Windows Logon API](https://msdn.microsoft.com/en-us/library/windows/desktop/aa378184%28v=vs.85%29.aspx). 115 | 116 | ### Client Application 117 | 118 | The client application has a [TEMSProvider](http://docwiki.embarcadero.com/Libraries/en/REST.Backend.EMSProvider.TEMSProvider) component, that: 119 | * Identifies the address of the [RAD Server Engine (EMS Server)](http://docwiki.embarcadero.com/RADStudio/en/RAD_Server_Engine_(EMS_Server)): http://localhost:8080. 120 | * Set the value of [TEMSProvider.LoginResource](http://docwiki.embarcadero.com/Libraries/en/REST.Backend.EMSProvider.TEMSProvider.LoginResource) property to **CustomLogin** (the custom resource from the sample). 121 | The client application also has the following components: 122 | * A [TBackendAuth](http://docwiki.embarcadero.com/Libraries/en/REST.Backend.ServiceComponents.TBackendAuth) component to standard log in and sign up users. 123 | * A [ActionList](http://docwiki.embarcadero.com/Libraries/en/FMX.ActnList.TActionList) for the actions the client application can do against the EMS Server (Login, Logout, Signup, DeleteUser, RetrieveUserFields, UpdateUserFields, UseCustomResource). 124 | You can authenticate in the EMS Server by using: 125 | * If **Use Custom Resource** is selected, the EMS client application uses the **CustomLogin** resource to authenticate in the EMS Server. Your **Windows credentials** are used to authenticate in this sample. 126 | * If **Use Custom Resource** is not selected, standard Login and Signup is used to authenticate in the EMS Server. 127 | The information is managed with TJSONObject and TJSONArray. 128 | ## Uses 129 | 130 | 131 | * [REST.Backend.EMSProvider.TEMSProvider](http://docwiki.embarcadero.com/Libraries/en/REST.Backend.EMSProvider.TEMSProvider) 132 | * [REST.Backend.ServiceComponents.TBackendAuth](http://docwiki.embarcadero.com/Libraries/en/REST.Backend.ServiceComponents.TBackendAuth) 133 | * [REST.Backend.EndPoint.TBackendEndpoint](http://docwiki.embarcadero.com/Libraries/en/REST.Backend.EndPoint.TBackendEndpoint) 134 | * [FMX.ActnList.TActionList](http://docwiki.embarcadero.com/Libraries/en/FMX.ActnList.TActionList) 135 | 136 | ## See Also 137 | 138 | 139 | * [RAD Server (EMS)](http://docwiki.embarcadero.com/RADStudio/en/RAD_Server_(EMS)) 140 | * [RAD Server External Credentials Support](http://docwiki.embarcadero.com/RADStudio/en/RAD_Server_External_Credentials_Support) 141 | * [Installing a RAD Server Package](http://docwiki.embarcadero.com/RADStudio/en/Installing_a_RAD_Server_Package) 142 | * [RAD Server Console](http://docwiki.embarcadero.com/RADStudio/en/RAD_Server_Console) 143 | * [EMS.NotesResource Sample](http://docwiki.embarcadero.com/CodeExamples/en/EMS.NotesResource_Sample) 144 | * [EMS.FireDACResource Sample](http://docwiki.embarcadero.com/CodeExamples/en/EMS.FireDACResource_Sample) 145 | 146 | 147 | 148 | 149 | 150 | -------------------------------------------------------------------------------- /11 - Authentication and Authorization/README.md: -------------------------------------------------------------------------------- 1 | ## Chapter 11: Authentication and Authorization 2 | 3 | Authentication and authorization are a crucial topic when it comes to securizing your services. RAD Server comes with a built-in solution for handling users and groups but sometimes it's required to integrate a third-party authorization system. In this example it's shown how to customize the login and signup endpoints leveraging the internal RAD Server API though the class TEMSInternalAPI. -------------------------------------------------------------------------------- /12 - Documenting and testing your endpoints using OpenAPI/C++/APIDocSpecs.cpp: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------- 2 | 3 | #pragma hdrstop 4 | 5 | #include 6 | 7 | #include "APIDocSpecs.h" 8 | 9 | //--------------------------------------------------------------------------- 10 | #pragma package(smart_init) 11 | 12 | 13 | namespace APISpecs { 14 | 15 | System::String initYamlDefinitions() 16 | { 17 | return "# " sLineBreak 18 | " customer:" sLineBreak 19 | " type: object" sLineBreak 20 | " properties:" sLineBreak 21 | " CUST_NO:" sLineBreak 22 | " type: integer" sLineBreak 23 | " format: int32" sLineBreak 24 | " description: Primary key" sLineBreak 25 | " readOnly: true" sLineBreak 26 | " CUSTOMER:" sLineBreak 27 | " type: string" sLineBreak 28 | " CONTACT_FIRST:" sLineBreak 29 | " type: string" sLineBreak 30 | " CONTACT_LAST:" sLineBreak 31 | " type: string" sLineBreak 32 | " PHONE_NO:" sLineBreak 33 | " type: string" sLineBreak 34 | " ADDRESS_LINE1:" sLineBreak 35 | " type: string" sLineBreak 36 | " ADDRESS_LINE2:" sLineBreak 37 | " type: string" sLineBreak 38 | " CITY:" sLineBreak 39 | " type: string" sLineBreak 40 | " STATE_PROVINCE:" sLineBreak 41 | " type: string" sLineBreak 42 | " COUNTRY:" sLineBreak 43 | " type: string" sLineBreak 44 | " POSTAL_CODE:" sLineBreak 45 | " type: string" sLineBreak 46 | " ON_HOLD:" sLineBreak 47 | " type: string" sLineBreak 48 | " sale:" sLineBreak 49 | " type: object" sLineBreak 50 | " properties:" sLineBreak 51 | " PO_NUMBER:" sLineBreak 52 | " type: string" sLineBreak 53 | " description: Primary key" sLineBreak 54 | " readOnly: true" sLineBreak 55 | " CUST_NO:" sLineBreak 56 | " type: integer" sLineBreak 57 | " description: Customer numer" sLineBreak 58 | " SALES_REP:" sLineBreak 59 | " type: integer" sLineBreak 60 | " ORDER_STATUS:" sLineBreak 61 | " type: string" sLineBreak 62 | " ORDER_DATE:" sLineBreak 63 | " type: string" sLineBreak 64 | " format: date" sLineBreak 65 | " SHIP_DATE:" sLineBreak 66 | " type: string" sLineBreak 67 | " format: date-time" sLineBreak 68 | " example: 2024-07-21T17:32:28Z" sLineBreak 69 | " DATE_NEEDED:" sLineBreak 70 | " type: string" sLineBreak 71 | " format: date-time" sLineBreak 72 | " PAID:" sLineBreak 73 | " type: string" sLineBreak 74 | " example: Y or N" sLineBreak 75 | " QTY_ORDERED:" sLineBreak 76 | " type: integer" sLineBreak 77 | " TOTAL_VALUE:" sLineBreak 78 | " type: number" sLineBreak 79 | " format: currency" sLineBreak 80 | " DISCOUNT:" sLineBreak 81 | " type: number" sLineBreak 82 | " ITEM_TYPE:" sLineBreak 83 | " type: string" sLineBreak 84 | " AGED:" sLineBreak 85 | " type: number" sLineBreak 86 | "#" sLineBreak 87 | " ItemPostedResponseObject:" sLineBreak 88 | " type: object" sLineBreak 89 | " properties:" sLineBreak 90 | " PostedData:" sLineBreak 91 | " type: array" sLineBreak 92 | " items:" sLineBreak 93 | " type: string" sLineBreak 94 | "#" sLineBreak 95 | " ItemPutResponseObject:" sLineBreak 96 | " type: object" sLineBreak 97 | " properties:" sLineBreak 98 | " PathItem:" sLineBreak 99 | " type: string" sLineBreak 100 | " PostedData:" sLineBreak 101 | " type: array" sLineBreak 102 | " items:" sLineBreak 103 | " type: string" sLineBreak 104 | ""; 105 | } 106 | 107 | 108 | System::String initJSONDefinitions() 109 | { 110 | return "{" sLineBreak 111 | " \"customer\": {" sLineBreak 112 | " \"type\": \"object\"," sLineBreak 113 | " \"properties\": {" sLineBreak 114 | " \"CUST_NO\": {" sLineBreak 115 | " \"type\": \"integer\"," sLineBreak 116 | " \"format\": \"int32\"," sLineBreak 117 | " \"description\": \"Primary key\"," sLineBreak 118 | " \"readOnly\": true" sLineBreak 119 | " }," sLineBreak 120 | " \"CUSTOMER\": {" sLineBreak 121 | " \"type\": \"string\"" sLineBreak 122 | " }," sLineBreak 123 | " \"CONTACT_FIRST\": {" sLineBreak 124 | " \"type\": \"string\"" sLineBreak 125 | " }," sLineBreak 126 | " \"CONTACT_LAST\": {" sLineBreak 127 | " \"type\": \"string\"" sLineBreak 128 | " }," sLineBreak 129 | " \"PHONE_NO\": {" sLineBreak 130 | " \"type\": \"string\"" sLineBreak 131 | " }," sLineBreak 132 | " \"ADDRESS_LINE1\": {" sLineBreak 133 | " \"type\": \"string\"" sLineBreak 134 | " }," sLineBreak 135 | " \"ADDRESS_LINE2\": {" sLineBreak 136 | " \"type\": \"string\"" sLineBreak 137 | " }," sLineBreak 138 | " \"CITY\": {" sLineBreak 139 | " \"type\": \"string\"" sLineBreak 140 | " }," sLineBreak 141 | " \"STATE_PROVINCE\": {" sLineBreak 142 | " \"type\": \"string\"" sLineBreak 143 | " }," sLineBreak 144 | " \"COUNTRY\": {" sLineBreak 145 | " \"type\": \"string\"" sLineBreak 146 | " }," sLineBreak 147 | " \"POSTAL_CODE\": {" sLineBreak 148 | " \"type\": \"string\"" sLineBreak 149 | " }," sLineBreak 150 | " \"ON_HOLD\": {" sLineBreak 151 | " \"type\": \"string\"" sLineBreak 152 | " }" sLineBreak 153 | " }" sLineBreak 154 | " }," sLineBreak 155 | " \"sale\": {" sLineBreak 156 | " \"type\": \"object\"," sLineBreak 157 | " \"properties\": {" sLineBreak 158 | " \"PO_NUMBER\": {" sLineBreak 159 | " \"type\": \"string\"," sLineBreak 160 | " \"description\": \"Primary key\"," sLineBreak 161 | " \"readOnly\": true" sLineBreak 162 | " }," sLineBreak 163 | " \"CUST_NO\": {" sLineBreak 164 | " \"type\": \"integer\"," sLineBreak 165 | " \"description\": \"Customer number\"" sLineBreak 166 | " }," sLineBreak 167 | " \"SALES_REP\": {" sLineBreak 168 | " \"type\": \"integer\"" sLineBreak 169 | " }," sLineBreak 170 | " \"ORDER_STATUS\": {" sLineBreak 171 | " \"type\": \"string\"" sLineBreak 172 | " }," sLineBreak 173 | " \"ORDER_DATE\": {" sLineBreak 174 | " \"type\": \"string\"," sLineBreak 175 | " \"format\": \"date\"" sLineBreak 176 | " }," sLineBreak 177 | " \"SHIP_DATE\": {" sLineBreak 178 | " \"type\": \"string\"," sLineBreak 179 | " \"format\": \"date-time\"," sLineBreak 180 | " \"example\": \"2024-07-21T17:32:28Z\"" sLineBreak 181 | " }," sLineBreak 182 | " \"DATE_NEEDED\": {" sLineBreak 183 | " \"type\": \"string\"," sLineBreak 184 | " \"format\": \"date-time\"" sLineBreak 185 | " }," sLineBreak 186 | " \"PAID\": {" sLineBreak 187 | " \"type\": \"string\"," sLineBreak 188 | " \"example\": \"Y or N\"" sLineBreak 189 | " }," sLineBreak 190 | " \"QTY_ORDERED\": {" sLineBreak 191 | " \"type\": \"integer\"" sLineBreak 192 | " }," sLineBreak 193 | " \"TOTAL_VALUE\": {" sLineBreak 194 | " \"type\": \"number\"," sLineBreak 195 | " \"format\": \"currency\"" sLineBreak 196 | " }," sLineBreak 197 | " \"DISCOUNT\": {" sLineBreak 198 | " \"type\": \"number\"" sLineBreak 199 | " }," sLineBreak 200 | " \"ITEM_TYPE\": {" sLineBreak 201 | " \"type\": \"string\"" sLineBreak 202 | " }," sLineBreak 203 | " \"AGED\": {" sLineBreak 204 | " \"type\": \"number\"" sLineBreak 205 | " }" sLineBreak 206 | " }" sLineBreak 207 | " }," sLineBreak 208 | " \"ItemPostedResponseObject\": {" sLineBreak 209 | " \"type\": \"object\"," sLineBreak 210 | " \"properties\": {" sLineBreak 211 | " \"PostedData\": {" sLineBreak 212 | " \"type\": \"array\"," sLineBreak 213 | " \"items\": {" sLineBreak 214 | " \"type\": \"string\"" sLineBreak 215 | " }" sLineBreak 216 | " }" sLineBreak 217 | " }" sLineBreak 218 | " }," sLineBreak 219 | " \"ItemPutResponseObject\": {" sLineBreak 220 | " \"type\": \"object\"," sLineBreak 221 | " \"properties\": {" sLineBreak 222 | " \"PathItem\": {" sLineBreak 223 | " \"type\": \"string\"" sLineBreak 224 | " }," sLineBreak 225 | " \"PostedData\": {" sLineBreak 226 | " \"type\": \"array\"," sLineBreak 227 | " \"items\": {" sLineBreak 228 | " \"type\": \"string\"" sLineBreak 229 | " }" sLineBreak 230 | " }" sLineBreak 231 | " }" sLineBreak 232 | " }" sLineBreak 233 | "}" sLineBreak 234 | ""; 235 | } 236 | } 237 | -------------------------------------------------------------------------------- /12 - Documenting and testing your endpoints using OpenAPI/C++/APIDocSpecs.h: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------- 2 | 3 | #ifndef APIDocSpecsH 4 | #define APIDocSpecsH 5 | //--------------------------------------------------------------------------- 6 | #endif 7 | -------------------------------------------------------------------------------- /12 - Documenting and testing your endpoints using OpenAPI/C++/Common.cpp: -------------------------------------------------------------------------------- 1 | // This unit defines the most used values for the OpenAPI definitions 2 | // It's not required to use this approach but for readability, simplicity 3 | // and avoid human mistakes it's recomended to abstract the raw values. 4 | 5 | //--------------------------------------------------------------------------- 6 | 7 | #pragma hdrstop 8 | 9 | #include "Common.h" 10 | //--------------------------------------------------------------------------- 11 | #pragma package(smart_init) 12 | 13 | 14 | class TSpecs { 15 | public: 16 | using ParamIn = TAPIDocParameter::TParameterIn; 17 | using Types = TAPIDoc::TPrimitiveType; 18 | using Formats = TAPIDoc::TPrimitiveFormat; 19 | }; 20 | 21 | class TMethods { 22 | public: 23 | static const char* List; 24 | static const char* Get; 25 | static const char* Post; 26 | static const char* Put; 27 | static const char* Delete; 28 | }; 29 | 30 | const char* TMethods::List = "List"; 31 | const char* TMethods::Get = "Get"; 32 | const char* TMethods::Post = "Post"; 33 | const char* TMethods::Put = "Put"; 34 | const char* TMethods::Delete = "Delete"; 35 | 36 | 37 | class TStatus { 38 | public: 39 | struct Ok { 40 | static const int Code = 200; 41 | static const char* Reason; 42 | }; 43 | 44 | struct Created { 45 | static const int Code = 201; 46 | static const char* Reason; 47 | }; 48 | 49 | struct Accepted { 50 | static const int Code = 202; 51 | static const char* Reason; 52 | }; 53 | 54 | struct NoContent { 55 | static const int Code = 204; 56 | static const char* Reason; 57 | }; 58 | 59 | struct BadRequest { 60 | static const int Code = 400; 61 | static const char* Reason; 62 | }; 63 | 64 | struct Forbidden { 65 | static const int Code = 403; 66 | static const char* Reason; 67 | }; 68 | 69 | struct Unauthorized { 70 | static const int Code = 401; 71 | static const char* Reason; 72 | }; 73 | 74 | struct NotFound { 75 | static const int Code = 404; 76 | static const char* Reason; 77 | }; 78 | 79 | struct InternalError { 80 | static const int Code = 500; 81 | static const char* Reason; 82 | }; 83 | }; 84 | 85 | const char* TStatus::Ok::Reason = "Ok"; 86 | const char* TStatus::Created::Reason = "Created"; 87 | const char* TStatus::Accepted::Reason = "Accepted"; 88 | const char* TStatus::NoContent::Reason = "No Content"; 89 | const char* TStatus::BadRequest::Reason = "Bad Request"; 90 | const char* TStatus::Forbidden::Reason = "Forbidden"; 91 | const char* TStatus::Unauthorized::Reason = "Unauthorized"; 92 | const char* TStatus::NotFound::Reason = "Not Found"; 93 | const char* TStatus::InternalError::Reason = "Internal Server Error"; 94 | 95 | UnicodeString generateMethod(const UnicodeString& resource, const char* method) { 96 | UnicodeString n = UnicodeString(resource) + "." + UnicodeString(method); 97 | return UnicodeString(resource) + "." + UnicodeString(method); 98 | } 99 | -------------------------------------------------------------------------------- /12 - Documenting and testing your endpoints using OpenAPI/C++/Common.h: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------- 2 | 3 | #include 4 | 5 | #ifndef CommonH 6 | #define CommonH 7 | //--------------------------------------------------------------------------- 8 | #endif 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /12 - Documenting and testing your endpoints using OpenAPI/C++/CustomAPIDocPackage.cpp: -------------------------------------------------------------------------------- 1 | // EMS Package 2 | //--------------------------------------------------------------------------- 3 | 4 | #include 5 | #pragma hdrstop 6 | USEFORM("MyDM.cpp", TestResource1); /* TDataModule: File Type */ 7 | //--------------------------------------------------------------------------- 8 | #pragma package(smart_init) 9 | //--------------------------------------------------------------------------- 10 | 11 | // Package source. 12 | //--------------------------------------------------------------------------- 13 | 14 | 15 | #pragma argsused 16 | extern "C" int _libmain(unsigned long reason) 17 | { 18 | return 1; 19 | } 20 | //--------------------------------------------------------------------------- 21 | 22 | 23 | -------------------------------------------------------------------------------- /12 - Documenting and testing your endpoints using OpenAPI/C++/MyDM.cpp: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------- 2 | #pragma hdrstop 3 | 4 | //#include "APIDocCppAttributesUnit.h" 5 | #include "MyDM.h" 6 | #include 7 | #include "APIDocSpecs.cpp" 8 | #include "Common.cpp" 9 | //--------------------------------------------------------------------------- 10 | #pragma package(smart_init) 11 | #pragma classgroup "System.Classes.TPersistent" 12 | #pragma resource "*.dfm" 13 | //--------------------------------------------------------------------------- 14 | __fastcall TTestResource1::TTestResource1(TComponent* Owner) 15 | : TDataModule(Owner) 16 | { 17 | } 18 | 19 | static void Register() 20 | { 21 | std::auto_ptr attributes(new TEMSResourceAttributes()); 22 | attributes->ResourceName = "test"; 23 | //YAML definitions 24 | attributes->YAMLDefinitions["test"] = APISpecs::initYamlDefinitions(); 25 | //JSON definitions 26 | attributes->JSONDefinitions["test"] = APISpecs::initJSONDefinitions(); 27 | 28 | //Customer endpoint 29 | std::unique_ptr RequestSummary(new EndPointRequestSummaryAttribute( 30 | "Customers", // Tags 31 | "Customers", // Summary 32 | "Description of the Customers endpoint", // Description 33 | "application/json", // Produces 34 | "")); // Consumes 35 | attributes->RequestSummary["dsrCUSTOMER"] = RequestSummary.get(); 36 | 37 | //Add Parameters 38 | std::unique_ptr ResponseParameter(new EndPointRequestParameterAttribute( 39 | TSpecs::ParamIn::Path, 40 | "CUST_NO", // Param name 41 | "Customer number", // desc 42 | true, // required 43 | TSpecs::Types::spInteger, 44 | TSpecs::Formats::Int64, 45 | TSpecs::Types::spInteger, 46 | "", // Schema 47 | "")); // Reference 48 | attributes->AddRequestParameter(generateMethod("dsrCUSTOMER", TMethods::Get), ResponseParameter.get()); 49 | ResponseParameter.reset(new EndPointRequestParameterAttribute( 50 | TSpecs::ParamIn::Body, 51 | "Name", // Param name 52 | "Description", // desc 53 | true, // required 54 | TSpecs::Types::spObject, 55 | TSpecs::Formats::None, 56 | TSpecs::Types::spObject, 57 | "#/definitions/customer", // Schema 58 | "#/definitions/customer")); // Reference 59 | attributes->AddRequestParameter("dsrCUSTOMER.Post", ResponseParameter.get()); 60 | ResponseParameter.reset(new EndPointRequestParameterAttribute( 61 | TSpecs::ParamIn::Path, 62 | "CUST_NO", // Param name 63 | "Customer number", // desc 64 | true, // required 65 | TSpecs::Types::spInteger, 66 | TSpecs::Formats::Int64, 67 | TSpecs::Types::spInteger, 68 | "", // Schema 69 | "")); // Reference 70 | attributes->AddRequestParameter(generateMethod("dsrCUSTOMER", TMethods::Delete), ResponseParameter.get()); 71 | 72 | //Add Responses 73 | std::unique_ptr ResponseDetail(new EndPointResponseDetailsAttribute(TStatus::Ok::Code, TStatus::Ok::Reason, TSpecs::Types::spArray, TSpecs::Formats::None, "", "#/definitions/customer")); 74 | attributes->AddResponseDetail(generateMethod("dsrCUSTOMER", TMethods::List), ResponseDetail.get()); 75 | ResponseDetail.reset(new EndPointResponseDetailsAttribute(TStatus::Ok::Code, TStatus::Ok::Reason, TSpecs::Types::spObject, TSpecs::Formats::None, "", "#/definitions/customer")); 76 | attributes->AddResponseDetail(generateMethod("dsrCUSTOMER", TMethods::Get), ResponseDetail.get()); 77 | ResponseDetail.reset(new EndPointResponseDetailsAttribute(TStatus::Created::Code, TStatus::Created::Reason, TSpecs::Types::spObject, TSpecs::Formats::None, "", "#/definitions/customer")); 78 | attributes->AddResponseDetail(generateMethod("dsrCUSTOMER", TMethods::Post), ResponseDetail.get()); 79 | ResponseDetail.reset(new EndPointResponseDetailsAttribute(TStatus::Ok::Code, TStatus::Ok::Reason, TSpecs::Types::spObject, TSpecs::Formats::None, "", "#/definitions/customer")); 80 | attributes->AddResponseDetail(generateMethod("dsrCUSTOMER", TMethods::Put), ResponseDetail.get()); 81 | ResponseDetail.reset(new EndPointResponseDetailsAttribute(TStatus::BadRequest::Code, TStatus::BadRequest::Reason, TSpecs::Types::spNull, TSpecs::Formats::None, "", "")); 82 | attributes->AddResponseDetail(generateMethod("dsrCUSTOMER", TMethods::Post), ResponseDetail.get()); 83 | ResponseDetail.reset(new EndPointResponseDetailsAttribute(TStatus::NotFound::Code, TStatus::NotFound::Reason, TSpecs::Types::spNull, TSpecs::Formats::None, "", "")); 84 | attributes->AddResponseDetail(generateMethod("dsrCUSTOMER", TMethods::Post), ResponseDetail.get()); 85 | 86 | attributes->ResourceSuffix["dsrCUSTOMER"] = "customers"; 87 | attributes->ResourceSuffix["dsrCUSTOMER.List"] = "./"; 88 | attributes->ResourceSuffix["dsrCUSTOMER.Get"] = "./{CUST_NO}"; 89 | attributes->ResourceSuffix["dsrCUSTOMER.Post"] = "./"; 90 | attributes->ResourceSuffix["dsrCUSTOMER.Put"] = "./{CUST_NO}"; 91 | attributes->ResourceSuffix["dsrCUSTOMER.Delete"] = "./{CUST_NO}"; 92 | 93 | // Sales endpoint 94 | RequestSummary.reset(new EndPointRequestSummaryAttribute("Sales", "Sales", "Description of the Sales endpoint", "application/json", "")); 95 | attributes->RequestSummary["dsrSALES"] = RequestSummary.get(); 96 | 97 | // Add Parameters 98 | ResponseParameter.reset(new EndPointRequestParameterAttribute(TSpecs::ParamIn::Path, "PO_NUMBER", "Sale number", true, TSpecs::Types::spString, TSpecs::Formats::None, TSpecs::Types::spString,"", "")); 99 | attributes->AddRequestParameter(generateMethod("dsrSALES", TMethods::Get), ResponseParameter.get()); 100 | ResponseParameter.reset(new EndPointRequestParameterAttribute(TMethods::Post, TSpecs::ParamIn::Body, "Name", "Description", true, TSpecs::Types::spObject, TSpecs::Formats::None, TSpecs::Types::spObject, "#/definitions/sale", "#/definitions/sale")); 101 | attributes->AddRequestParameter(generateMethod("dsrSALES", TMethods::Post), ResponseParameter.get()); 102 | ResponseParameter.reset(new EndPointRequestParameterAttribute(TMethods::Delete, TSpecs::ParamIn::Path, "PO_NUMBER", "Sale number", true, TSpecs::Types::spString, TSpecs::Formats::None, TSpecs::Types::spString, "", "")); 103 | attributes->AddRequestParameter(generateMethod("dsrSALES", TMethods::Delete), ResponseParameter.get()); 104 | 105 | // Add Response Details 106 | ResponseDetail.reset(new EndPointResponseDetailsAttribute(TMethods::List, TStatus::Ok::Code, TStatus::Ok::Reason, TSpecs::Types::spArray, TSpecs::Formats::None, "", "#/definitions/sale")); 107 | attributes->AddResponseDetail(generateMethod("dsrSALES", TMethods::List), ResponseDetail.get()); 108 | ResponseDetail.reset(new EndPointResponseDetailsAttribute(TMethods::Get, TStatus::Ok::Code, TStatus::Ok::Reason, TSpecs::Types::spObject, TSpecs::Formats::None, "", "#/definitions/sale")); 109 | attributes->AddResponseDetail(generateMethod("dsrSALES", TMethods::Get), ResponseDetail.get()); 110 | ResponseDetail.reset(new EndPointResponseDetailsAttribute(TMethods::Post, TStatus::Created::Code, TStatus::Created::Reason, TSpecs::Types::spObject, TSpecs::Formats::None, "", "#/definitions/sale")); 111 | attributes->AddResponseDetail(generateMethod("dsrSALES", TMethods::Post), ResponseDetail.get()); 112 | ResponseDetail.reset(new EndPointResponseDetailsAttribute(TMethods::Put, TStatus::Ok::Code, TStatus::Ok::Reason, TSpecs::Types::spObject, TSpecs::Formats::None, "", "#/definitions/sale")); 113 | attributes->AddResponseDetail(generateMethod("dsrSALES", TMethods::Put), ResponseDetail.get()); 114 | ResponseDetail.reset(new EndPointResponseDetailsAttribute(TMethods::Post, TStatus::BadRequest::Code, TStatus::BadRequest::Reason, TSpecs::Types::spNull, TSpecs::Formats::None, "", "")); 115 | attributes->AddResponseDetail(generateMethod("dsrSALES", TMethods::Post), ResponseDetail.get()); 116 | ResponseDetail.reset(new EndPointResponseDetailsAttribute(TMethods::Get, TStatus::NotFound::Code, TStatus::NotFound::Reason, TSpecs::Types::spNull, TSpecs::Formats::None, "", "")); 117 | attributes->AddResponseDetail(generateMethod("dsrSALES", TMethods::Get), ResponseDetail.get()); 118 | 119 | attributes->ResourceSuffix["dsrSALES"] = "sales"; 120 | attributes->ResourceSuffix["dsrSALES.List"] = "./"; 121 | attributes->ResourceSuffix["dsrSALES.Get"] = "./{PO_NUMBER}"; 122 | attributes->ResourceSuffix["dsrSALES.Post"] = "./"; 123 | attributes->ResourceSuffix["dsrSALES.Put"] = "./{PO_NUMBER}"; 124 | attributes->ResourceSuffix["dsrSALES.Delete"] = "./{PO_NUMBER}"; 125 | 126 | RegisterResource(__typeinfo(TTestResource1), attributes.release()); 127 | } 128 | 129 | #pragma startup Register 32 130 | -------------------------------------------------------------------------------- /12 - Documenting and testing your endpoints using OpenAPI/C++/MyDM.dfm: -------------------------------------------------------------------------------- 1 | object TestResource1: TTestResource1 2 | Height = 300 3 | Width = 600 4 | object FDConnection1: TFDConnection 5 | Params.Strings = ( 6 | 'ConnectionDef=EMPLOYEE') 7 | LoginPrompt = False 8 | Left = 30 9 | Top = 16 10 | end 11 | object qryCUSTOMER: TFDQuery 12 | Connection = FDConnection1 13 | SQL.Strings = ( 14 | 'select * from CUSTOMER' 15 | '{if !SORT}order by !SORT{fi}') 16 | Left = 130 17 | Top = 16 18 | MacroData = < 19 | item 20 | Value = Null 21 | Name = 'SORT' 22 | end> 23 | end 24 | object dsrCUSTOMER: TEMSDataSetResource 25 | AllowedActions = [List, Get, Post, Put, Delete] 26 | DataSet = qryCUSTOMER 27 | Left = 130 28 | Top = 64 29 | end 30 | object qrySALES: TFDQuery 31 | Connection = FDConnection1 32 | SQL.Strings = ( 33 | 'select * from SALES' 34 | '{if !SORT}order by !SORT{fi}') 35 | Left = 230 36 | Top = 16 37 | MacroData = < 38 | item 39 | Value = Null 40 | Name = 'SORT' 41 | end> 42 | end 43 | object dsrSALES: TEMSDataSetResource 44 | AllowedActions = [List, Get, Post, Put, Delete] 45 | DataSet = qrySALES 46 | Left = 230 47 | Top = 64 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /12 - Documenting and testing your endpoints using OpenAPI/C++/MyDM.h: -------------------------------------------------------------------------------- 1 | // EMS Resource Modules 2 | //--------------------------------------------------------------------------- 3 | 4 | #ifndef MyDMH 5 | #define MyDMH 6 | //--------------------------------------------------------------------------- 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | //--------------------------------------------------------------------------- 33 | #pragma explicit_rtti methods (public) 34 | class TTestResource1 : public TDataModule 35 | { 36 | __published: 37 | TFDConnection *FDConnection1; 38 | TFDQuery *qryCUSTOMER; 39 | TEMSDataSetResource *dsrCUSTOMER; 40 | TFDQuery *qrySALES; 41 | TEMSDataSetResource *dsrSALES; 42 | 43 | private: 44 | public: 45 | __fastcall TTestResource1(TComponent* Owner); 46 | }; 47 | #endif 48 | -------------------------------------------------------------------------------- /12 - Documenting and testing your endpoints using OpenAPI/Delphi/APIDocSpecs.pas: -------------------------------------------------------------------------------- 1 | unit APIDocSpecs; 2 | 3 | interface 4 | 5 | const 6 | cYamlDefinitions =''' 7 | customer: 8 | type: object 9 | properties: 10 | CUST_NO: 11 | type: integer 12 | format: int32 13 | description: Primary key 14 | readOnly: true 15 | CUSTOMER: 16 | type: string 17 | CONTACT_FIRST: 18 | type: string 19 | CONTACT_LAST: 20 | type: string 21 | PHONE_NO: 22 | type: string 23 | ADDRESS_LINE1: 24 | type: string 25 | ADDRESS_LINE2: 26 | type: string 27 | CITY: 28 | type: string 29 | STATE_PROVINCE: 30 | type: string 31 | COUNTRY: 32 | type: string 33 | POSTAL_CODE: 34 | type: string 35 | ON_HOLD: 36 | type: string 37 | sale: 38 | type: object 39 | properties: 40 | PO_NUMBER: 41 | type: string 42 | description: Primary key 43 | readOnly: true 44 | CUST_NO: 45 | type: integer 46 | description: Customer number 47 | SALES_REP: 48 | type: integer 49 | ORDER_STATUS: 50 | type: string 51 | ORDER_DATE: 52 | type: string 53 | format: date 54 | SHIP_DATE: 55 | type: string 56 | format: date-time 57 | example: 2024-07-21T17:32:28Z 58 | DATE_NEEDED: 59 | type: string 60 | format: date-time 61 | PAID: 62 | type: string 63 | example: Y or N 64 | QTY_ORDERED: 65 | type: integer 66 | TOTAL_VALUE: 67 | type: number 68 | format: currency 69 | DISCOUNT: 70 | type: number 71 | ITEM_TYPE: 72 | type: string 73 | AGED: 74 | type: number 75 | # 76 | ItemPostedResponseObject: 77 | type: object 78 | properties: 79 | PostedData: 80 | type: array 81 | items: 82 | type: string 83 | # 84 | ItemPutResponseObject: 85 | type: object 86 | properties: 87 | PathItem: 88 | type: string 89 | PostedData: 90 | type: array 91 | items: 92 | type: string 93 | '''; 94 | 95 | cJSONDefinitions = ''' 96 | { 97 | "customer": { 98 | "type": "object", 99 | "properties": { 100 | "CUST_NO": { 101 | "type": "integer", 102 | "format": "int32", 103 | "description": "Primary key", 104 | "readOnly": true 105 | }, 106 | "CUSTOMER": { 107 | "type": "string" 108 | }, 109 | "CONTACT_FIRST": { 110 | "type": "string" 111 | }, 112 | "CONTACT_LAST": { 113 | "type": "string" 114 | }, 115 | "PHONE_NO": { 116 | "type": "string" 117 | }, 118 | "ADDRESS_LINE1": { 119 | "type": "string" 120 | }, 121 | "ADDRESS_LINE2": { 122 | "type": "string" 123 | }, 124 | "CITY": { 125 | "type": "string" 126 | }, 127 | "STATE_PROVINCE": { 128 | "type": "string" 129 | }, 130 | "COUNTRY": { 131 | "type": "string" 132 | }, 133 | "POSTAL_CODE": { 134 | "type": "string" 135 | }, 136 | "ON_HOLD": { 137 | "type": "string" 138 | } 139 | } 140 | }, 141 | "sale": { 142 | "type": "object", 143 | "properties": { 144 | "PO_NUMBER": { 145 | "type": "string", 146 | "description": "Primary key", 147 | "readOnly": true 148 | }, 149 | "CUST_NO": { 150 | "type": "integer", 151 | "description": "Customer number" 152 | }, 153 | "SALES_REP": { 154 | "type": "integer" 155 | }, 156 | "ORDER_STATUS": { 157 | "type": "string" 158 | }, 159 | "ORDER_DATE": { 160 | "type": "string", 161 | "format": "date" 162 | }, 163 | "SHIP_DATE": { 164 | "type": "string", 165 | "format": "date-time", 166 | "example": "2024-07-21T17:32:28Z" 167 | }, 168 | "DATE_NEEDED": { 169 | "type": "string", 170 | "format": "date-time" 171 | }, 172 | "PAID": { 173 | "type": "string", 174 | "example": "Y or N" 175 | }, 176 | "QTY_ORDERED": { 177 | "type": "integer" 178 | }, 179 | "TOTAL_VALUE": { 180 | "type": "number", 181 | "format": "currency" 182 | }, 183 | "DISCOUNT": { 184 | "type": "number" 185 | }, 186 | "ITEM_TYPE": { 187 | "type": "string" 188 | }, 189 | "AGED": { 190 | "type": "number" 191 | } 192 | } 193 | }, 194 | "ItemPostedResponseObject": { 195 | "type": "object", 196 | "properties": { 197 | "PostedData": { 198 | "type": "array", 199 | "items": { 200 | "type": "string" 201 | } 202 | } 203 | } 204 | }, 205 | "ItemPutResponseObject": { 206 | "type": "object", 207 | "properties": { 208 | "PathItem": { 209 | "type": "string" 210 | }, 211 | "PostedData": { 212 | "type": "array", 213 | "items": { 214 | "type": "string" 215 | } 216 | } 217 | } 218 | } 219 | } 220 | '''; 221 | 222 | implementation 223 | 224 | end. 225 | -------------------------------------------------------------------------------- /12 - Documenting and testing your endpoints using OpenAPI/Delphi/Common.pas: -------------------------------------------------------------------------------- 1 | // This unit defines the most used values for the OpenAPI definitions 2 | // It's not required to use this approach but for readability, simplicity 3 | // and avoid human mistakes it's recomended to abstract the raw values. 4 | 5 | unit Common; 6 | 7 | interface 8 | 9 | uses EMS.ResourceTypes; 10 | 11 | type 12 | 13 | TSpecs = class 14 | type 15 | ParamIn = TAPIDocParameter.TParameterIn; 16 | Types = TAPIDoc.TPrimitiveType; 17 | Formats = TAPIDoc.TPrimitiveFormat; 18 | end; 19 | 20 | TMethods = record 21 | const 22 | List = 'List'; 23 | Get = 'Get'; 24 | Post = 'Post'; 25 | Put = 'Put'; 26 | Delete = 'Delete'; 27 | end; 28 | 29 | // These are just some of the most common HTTP Status codes. 30 | TStatus = class 31 | type 32 | Ok = record 33 | const 34 | Code = 200; 35 | Reason = 'Ok'; 36 | end; 37 | Created = record 38 | const 39 | Code = 201; 40 | Reason = 'Created'; 41 | end; 42 | Accepted = record 43 | const 44 | Code = 202; 45 | Reason = 'Accepted'; 46 | end; 47 | NoContent = record 48 | const 49 | Code = 204; 50 | Reason = 'No Content'; 51 | end; 52 | BadRequest = record 53 | const 54 | Code = 400; 55 | Reason = 'Bad Request'; 56 | end; 57 | Forbidden = record 58 | const 59 | Code = 403; 60 | Reason = 'Forbidden'; 61 | end; 62 | Unauthorized = record 63 | const 64 | Code = 401; 65 | Reason = 'Unauthorized'; 66 | end; 67 | NotFound = record 68 | const 69 | Code = 404; 70 | Reason = 'Not Found'; 71 | end; 72 | InternalError = record 73 | const 74 | Code = 500; 75 | Reason = 'Internal Server Error'; 76 | end; 77 | end; 78 | 79 | implementation 80 | 81 | 82 | end. 83 | 84 | -------------------------------------------------------------------------------- /12 - Documenting and testing your endpoints using OpenAPI/Delphi/CustomAPIDocPackage.dpk: -------------------------------------------------------------------------------- 1 | package CustomAPIDocPackage; 2 | 3 | {$R *.res} 4 | {$IFDEF IMPLICITBUILDING This IFDEF should not be used by users} 5 | {$ALIGN 8} 6 | {$ASSERTIONS ON} 7 | {$BOOLEVAL OFF} 8 | {$DEBUGINFO OFF} 9 | {$EXTENDEDSYNTAX ON} 10 | {$IMPORTEDDATA ON} 11 | {$IOCHECKS ON} 12 | {$LOCALSYMBOLS ON} 13 | {$LONGSTRINGS ON} 14 | {$OPENSTRINGS ON} 15 | {$OPTIMIZATION OFF} 16 | {$OVERFLOWCHECKS ON} 17 | {$RANGECHECKS ON} 18 | {$REFERENCEINFO ON} 19 | {$SAFEDIVIDE OFF} 20 | {$STACKFRAMES ON} 21 | {$TYPEDADDRESS OFF} 22 | {$VARSTRINGCHECKS ON} 23 | {$WRITEABLECONST OFF} 24 | {$MINENUMSIZE 1} 25 | {$IMAGEBASE $400000} 26 | {$DEFINE DEBUG} 27 | {$ENDIF IMPLICITBUILDING} 28 | {$RUNONLY} 29 | {$IMPLICITBUILD ON} 30 | 31 | requires 32 | rtl, 33 | emsserverapi, 34 | dbrtl, 35 | emsserverresource, 36 | FireDACCommonDriver, 37 | FireDACCommon, 38 | FireDAC, 39 | FireDACIBDriver; 40 | 41 | contains 42 | MyDM in 'MyDM.pas' {TestResource1: TDataModule}, 43 | APIDocSpecs in 'APIDocSpecs.pas', 44 | Common in 'Common.pas'; 45 | 46 | end. 47 | -------------------------------------------------------------------------------- /12 - Documenting and testing your endpoints using OpenAPI/Delphi/CustomAPIDocPackage.res: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Embarcadero/RADServer-Docs/b6cecd34bb509284d5f7d8d0d3aed5352fecc529/12 - Documenting and testing your endpoints using OpenAPI/Delphi/CustomAPIDocPackage.res -------------------------------------------------------------------------------- /12 - Documenting and testing your endpoints using OpenAPI/Delphi/FirstCRUDPackage.res: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Embarcadero/RADServer-Docs/b6cecd34bb509284d5f7d8d0d3aed5352fecc529/12 - Documenting and testing your endpoints using OpenAPI/Delphi/FirstCRUDPackage.res -------------------------------------------------------------------------------- /12 - Documenting and testing your endpoints using OpenAPI/Delphi/MyDM.dfm: -------------------------------------------------------------------------------- 1 | object TestResource1: TTestResource1 2 | Height = 249 3 | Width = 389 4 | object FDConnection1: TFDConnection 5 | Params.Strings = ( 6 | 'ConnectionDef=EMPLOYEE') 7 | Connected = True 8 | LoginPrompt = False 9 | Left = 30 10 | Top = 16 11 | end 12 | object qryCUSTOMER: TFDQuery 13 | Connection = FDConnection1 14 | SQL.Strings = ( 15 | 'select * from CUSTOMER' 16 | '{if !SORT}order by !SORT{fi}') 17 | Left = 130 18 | Top = 16 19 | MacroData = < 20 | item 21 | Value = Null 22 | Name = 'SORT' 23 | end> 24 | end 25 | object dsrCUSTOMER: TEMSDataSetResource 26 | AllowedActions = [List, Get, Post, Put, Delete] 27 | DataSet = qryCUSTOMER 28 | Left = 130 29 | Top = 64 30 | end 31 | object qrySALES: TFDQuery 32 | Connection = FDConnection1 33 | SQL.Strings = ( 34 | 'select * from SALES' 35 | '{if !SORT}order by !SORT{fi}') 36 | Left = 230 37 | Top = 16 38 | MacroData = < 39 | item 40 | Value = Null 41 | Name = 'SORT' 42 | end> 43 | end 44 | object dsrSALES: TEMSDataSetResource 45 | AllowedActions = [List, Get, Post, Put, Delete] 46 | DataSet = qrySALES 47 | Left = 230 48 | Top = 64 49 | end 50 | end 51 | -------------------------------------------------------------------------------- /12 - Documenting and testing your endpoints using OpenAPI/Delphi/MyDM.pas: -------------------------------------------------------------------------------- 1 | unit MyDM; 2 | 3 | // EMS Resource Module 4 | 5 | interface 6 | 7 | {$REGION 'uses'} 8 | uses 9 | System.SysUtils, 10 | System.Classes, 11 | System.JSON, 12 | EMS.Services, 13 | EMS.ResourceAPI, 14 | EMS.ResourceTypes, 15 | FireDAC.Stan.Intf, 16 | FireDAC.Stan.Option, 17 | FireDAC.Stan.Error, 18 | FireDAC.UI.Intf, 19 | FireDAC.Phys.Intf, 20 | FireDAC.Stan.Def, 21 | FireDAC.Stan.Pool, 22 | FireDAC.Stan.Async, 23 | FireDAC.Phys, 24 | FireDAC.Phys.IB, 25 | FireDAC.Phys.IBDef, 26 | FireDAC.ConsoleUI.Wait, 27 | FireDAC.Stan.Param, 28 | FireDAC.DatS, 29 | FireDAC.DApt.Intf, 30 | FireDAC.DApt, 31 | EMS.DataSetResource, 32 | Data.DB, 33 | FireDAC.Comp.DataSet, 34 | FireDAC.Comp.Client, 35 | ApiDocSpecs, 36 | Common; 37 | {$ENDREGION} 38 | 39 | type 40 | [ResourceName('test')] 41 | [EndPointObjectsYAMLDefinitions(cYamlDefinitions)] 42 | [EndPointObjectsJSONDefinitions(cJSONDefinitions)] 43 | TTestResource1 = class(TDataModule) 44 | FDConnection1: TFDConnection; 45 | qryCUSTOMER: TFDQuery; 46 | {$REGION 'Customers OpenAPI specs'} 47 | [EndPointRequestSummary( 48 | 'Customers', // Tags 49 | 'Customers', // Summary 50 | 'Description of the Customers endpoint', // Description 51 | 'application/json', // Produces 52 | '')] // Consumes 53 | [EndPointRequestParameter( 54 | TMethods.Get, 55 | TSpecs.ParamIn.Path, // equivalent to TAPIDocParameter.TParameterIn.Path 56 | 'CUST_NO', // Param name 57 | 'Customer number', //desc 58 | true, // required 59 | TSpecs.Types.spInteger, // equivalent to TAPIDoc.TPrimitiveType.spInteger 60 | TSpecs.Formats.Int64, // equivalent to TAPIDoc.TPrimitiveFormat.Int64 61 | TSpecs.Types.spInteger, 62 | '', // Schema 63 | '')] // Reference 64 | [EndPointRequestParameter( 65 | TMethods.Post, 66 | TSpecs.ParamIn.Body, 67 | 'Name', // Param name 68 | 'Description', //desc 69 | true, // required 70 | TSpecs.Types.spObject, 71 | TSpecs.Formats.None, 72 | TSpecs.Types.spObject, 73 | '#/definitions/customer', // Schema 74 | '#/definitions/customer')] // Reference 75 | [EndPointRequestParameter( 76 | TMethods.Delete, 77 | TSpecs.ParamIn.Path, 78 | 'CUST_NO', // Param name 79 | 'Customer number', //desc 80 | true, // required 81 | TSpecs.Types.spInteger, 82 | TSpecs.Formats.Int64, 83 | TSpecs.Types.spInteger, 84 | '', // Schema 85 | '')] // Reference 86 | [EndPointResponseDetails(TMethods.List, TStatus.Ok.Code, TStatus.Ok.Reason, TSpecs.Types.spArray, TSpecs.Formats.None, '', '#/definitions/customer')] 87 | [EndPointResponseDetails(TMethods.Get, TStatus.Ok.Code, TStatus.Ok.Reason, TSpecs.Types.spObject, TSpecs.Formats.None, '', '#/definitions/customer')] 88 | [EndPointResponseDetails(TMethods.Post, TStatus.Created.Code, TStatus.Created.Reason, TSpecs.Types.spObject, TSpecs.Formats.None, '', '#/definitions/customer')] 89 | [EndPointResponseDetails(TMethods.Put, TStatus.Ok.Code, TStatus.Ok.Reason, TSpecs.Types.spObject, TSpecs.Formats.None, '', '#/definitions/customer')] 90 | [EndPointResponseDetails(TMethods.Post, TStatus.BadRequest.Code, TStatus.BadRequest.Reason, TSpecs.Types.spNull, TSpecs.Formats.None, '', '')] 91 | [EndPointResponseDetails(TMethods.Get, TStatus.NotFound.Code, TStatus.NotFound.Reason, TSpecs.Types.spNull, TSpecs.Formats.None, '', '')] 92 | [ResourceSuffix('customers')] 93 | [ResourceSuffix(TMethods.List, './')] 94 | [ResourceSuffix(TMethods.Get, './{CUST_NO}')] 95 | [ResourceSuffix(TMethods.Post, './')] 96 | [ResourceSuffix(TMethods.Put, './{CUST_NO}')] 97 | [ResourceSuffix(TMethods.Delete, './{CUST_NO}')] 98 | {$ENDREGION} 99 | dsrCUSTOMER: TEMSDataSetResource; 100 | qrySALES: TFDQuery; 101 | {$REGION 'Sales OpenAPI specs'} 102 | [EndPointRequestSummary('Sales', 'Sales', 'Description of the Sales endpoint', 'application/json', '')] 103 | [EndPointRequestParameter(TMethods.Get, TSpecs.ParamIn.Path, 'PO_NUMBER', 'Sale number', true, TSpecs.Types.spString, TSpecs.Formats.None, TSpecs.Types.spString, '', '')] 104 | [EndPointRequestParameter(TMethods.Post, TSpecs.ParamIn.Body, 'Name', 'Description', true, TSpecs.Types.spObject, TSpecs.Formats.None, TSpecs.Types.spObject, '#/definitions/sale', '#/definitions/sale')] 105 | [EndPointRequestParameter(TMethods.Delete, TSpecs.ParamIn.Path, 'PO_NUMBER', 'Sale number', true, TSpecs.Types.spString, TSpecs.Formats.None, TSpecs.Types.spString, '', '')] 106 | [EndPointResponseDetails(TMethods.List, TStatus.Ok.Code, TStatus.Ok.Reason, TSpecs.Types.spArray, TSpecs.Formats.None, '', '#/definitions/sale')] 107 | [EndPointResponseDetails(TMethods.Get, TStatus.Ok.Code, TStatus.Ok.Reason, TSpecs.Types.spObject, TSpecs.Formats.None, '', '#/definitions/sale')] 108 | [EndPointResponseDetails(TMethods.Post, TStatus.Created.Code, TStatus.Created.Reason, TSpecs.Types.spObject, TSpecs.Formats.None, '', '#/definitions/sale')] 109 | [EndPointResponseDetails(TMethods.Put, TStatus.Ok.Code, TStatus.Ok.Reason, TSpecs.Types.spObject, TSpecs.Formats.None, '', '#/definitions/sale')] 110 | [EndPointResponseDetails(TMethods.Post, TStatus.BadRequest.Code, TStatus.BadRequest.Reason, TSpecs.Types.spNull, TSpecs.Formats.None, '', '')] 111 | [EndPointResponseDetails(TMethods.Get, TStatus.NotFound.Code, TStatus.NotFound.Reason, TSpecs.Types.spNull, TSpecs.Formats.None, '', '')] 112 | [ResourceSuffix('sales')] 113 | [ResourceSuffix('List', './')] 114 | [ResourceSuffix('Get', './{PO_NUMBER}')] 115 | [ResourceSuffix('Post', './')] 116 | [ResourceSuffix('Put', './{PO_NUMBER}')] 117 | [ResourceSuffix('Delete', './{PO_NUMBER}')] 118 | {$ENDREGION} 119 | dsrSALES: TEMSDataSetResource; 120 | 121 | published 122 | end; 123 | 124 | implementation 125 | 126 | {%CLASSGROUP 'System.Classes.TPersistent'} 127 | {$R *.dfm} 128 | 129 | procedure Register; 130 | begin 131 | RegisterResource(TypeInfo(TTestResource1)); 132 | end; 133 | 134 | initialization 135 | 136 | Register; 137 | 138 | end. 139 | -------------------------------------------------------------------------------- /12 - Documenting and testing your endpoints using OpenAPI/Delphi/MyFirstCRUDDelphiRADServerPackage.res: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Embarcadero/RADServer-Docs/b6cecd34bb509284d5f7d8d0d3aed5352fecc529/12 - Documenting and testing your endpoints using OpenAPI/Delphi/MyFirstCRUDDelphiRADServerPackage.res -------------------------------------------------------------------------------- /12 - Documenting and testing your endpoints using OpenAPI/README.md: -------------------------------------------------------------------------------- 1 | ## Chapter 12 - Documenting and testing your endpoints using OpenAPI 2 | 3 | This sample is an EMS package that contains a new resource with API documentation that can be accessed via HTTP. It displays the server responses in YAML and JSON formats.This sample uses an EMS package to extend the EMS Server to connect to Interbase (employee.gdb) using FireDAC components. 4 | 5 | ### Notes about this sample 6 | 7 | To simply the process of reusing HTTP status codes, Specs etc the unit _Common_ was created. In this unit you can find multiple classes and records to reference these instead of raw strings or long pre-defined values from EMS.ResourceTypes. This is not mandatory and these can be still referenced normally directly in the attributes but readability, simplicity and avoid human mistakes, it's recommended to abstract the raw values. 8 | 9 | Keep in mind that this sample only shows some of the standard OpenAPI specifications and it's not fully documented. Follow the standards from OpenAPI to define the most standardized spec possible. 10 | 11 | The generated YAML and JSON specs can be access through the endpoints: 12 | 13 | * `http://localhost:8080/api/apidoc.yaml` 14 | * `http://localhost:8080/api/apidoc.json` 15 | 16 | ## See Also 17 | 18 | * [Custom API Documentation](http://docwiki.embarcadero.com/RADStudio/en/Custom_API_Documentation) 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /13 - File management and storage/C++/FileManagement.cpp: -------------------------------------------------------------------------------- 1 | // EMS Package 2 | //--------------------------------------------------------------------------- 3 | 4 | #include 5 | #pragma hdrstop 6 | USEFORM("MyDM.cpp", DataResource1); /* TDataModule: File Type */ 7 | //--------------------------------------------------------------------------- 8 | #pragma package(smart_init) 9 | //--------------------------------------------------------------------------- 10 | 11 | // Package source. 12 | //--------------------------------------------------------------------------- 13 | 14 | 15 | #pragma argsused 16 | extern "C" int _libmain(unsigned long reason) 17 | { 18 | return 1; 19 | } 20 | //--------------------------------------------------------------------------- 21 | 22 | 23 | -------------------------------------------------------------------------------- /13 - File management and storage/C++/MyDM.cpp: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------- 2 | #pragma hdrstop 3 | 4 | #include "MyDM.h" 5 | #include 6 | #include 7 | //--------------------------------------------------------------------------- 8 | #pragma package(smart_init) 9 | #pragma classgroup "System.Classes.TPersistent" 10 | #pragma resource "*.dfm" 11 | //--------------------------------------------------------------------------- 12 | __fastcall TDataResource1::TDataResource1(TComponent* Owner) 13 | : TDataModule(Owner) 14 | { 15 | } 16 | 17 | void TDataResource1::PostUpload(TEndpointContext* AContext, TEndpointRequest* ARequest, TEndpointResponse* AResponse) 18 | { 19 | const System::UnicodeString UPLOAD_PATH = "c:\\uploads"; // change this with the local path you prefer 20 | TDirectory::CreateDirectory(UPLOAD_PATH); 21 | AResponse->Body->JSONWriter->WriteStartArray(); 22 | for (int i = 0; i < ARequest->Body->PartCount; ++i) // check how many files/parts are in the body 23 | { 24 | System::UnicodeString lFileName = ARequest->Body->Parts[i]->FileName; // accessing the original file name 25 | // In case of multi platform development it is important to verify if the file name complies with the OS standards 26 | if (!TPath::HasValidFileNameChars(lFileName, false)) { 27 | AResponse->RaiseError(415, "invalid file name: ", lFileName); 28 | } 29 | TStream* lFile = new TStream; 30 | // The file is created in the local path 31 | lFile = TFile::Create(TPath::Combine(UPLOAD_PATH, lFileName)); 32 | try { 33 | // The stream from the body is added to the file 34 | lFile->CopyFrom(ARequest->Body->Parts[i]->GetStream(), 0); 35 | // In this example we return a status code of 201 and as extra info the file name and the file size 36 | AResponse->StatusCode = 201; 37 | AResponse->Body->JSONWriter->WriteStartObject(); 38 | AResponse->Body->JSONWriter->WritePropertyName("fileName"); 39 | AResponse->Body->JSONWriter->WriteValue(lFileName); 40 | AResponse->Body->JSONWriter->WritePropertyName("size"); 41 | AResponse->Body->JSONWriter->WriteValue(lFile->Size); 42 | AResponse->Body->JSONWriter->WriteEndObject(); 43 | } __finally { 44 | lFile->Free(); 45 | } 46 | } 47 | AResponse->Body->JSONWriter->WriteEndArray(); 48 | } 49 | 50 | static void Register() 51 | { 52 | std::unique_ptr attributes(new TEMSResourceAttributes()); 53 | attributes->ResourceName = "test"; 54 | attributes->ResourceSuffix["PostUpload"] = "./upload"; 55 | attributes->ResourceSuffix["EMSFileResource1"] = "./fileResource"; 56 | attributes->ResourceSuffix["EMSFileResource1.List"] = "./"; 57 | attributes->ResourceSuffix["EMSFileResource1.Get"] = "./{id}"; 58 | attributes->ResourceSuffix["EMSFileResource1.Post"] = "./"; 59 | RegisterResource(__typeinfo(TDataResource1), attributes.release()); 60 | } 61 | 62 | #pragma startup Register 32 63 | -------------------------------------------------------------------------------- /13 - File management and storage/C++/MyDM.dfm: -------------------------------------------------------------------------------- 1 | object DataResource1: TDataResource1 2 | Height = 119 3 | Width = 131 4 | object EMSFileResource1: TEMSFileResource 5 | PathTemplate = 'c:\uploads\{id}' 6 | Left = 48 7 | Top = 32 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /13 - File management and storage/C++/MyDM.h: -------------------------------------------------------------------------------- 1 | // EMS Resource Modules 2 | //--------------------------------------------------------------------------- 3 | 4 | #ifndef MyDMH 5 | #define MyDMH 6 | //--------------------------------------------------------------------------- 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | //--------------------------------------------------------------------------- 14 | #pragma explicit_rtti methods (public) 15 | class TDataResource1 : public TDataModule 16 | { 17 | __published: 18 | TEMSFileResource *EMSFileResource1; 19 | private: 20 | public: 21 | __fastcall TDataResource1(TComponent* Owner); 22 | void PostUpload(TEndpointContext* AContext, TEndpointRequest* ARequest, TEndpointResponse* AResponse); 23 | }; 24 | #endif 25 | -------------------------------------------------------------------------------- /13 - File management and storage/Delphi/FileManagement.delphilsp.json: -------------------------------------------------------------------------------- 1 | { "settings": { "project": "file:///C%3A/Users/azapa/Documents/RADServer-docs-antonio/13%20-%20File%20management%20and%20storage/Delphi/FileManagement.dpk", "dllname": "dcc32280.dll", "dccOptions": "-$O- -$W+ -$R+ -$Q+ --no-config -Q -TX.bpl -AGenerics.Collections=System.Generics.Collections;Generics.Defaults=System.Generics.Defaults;WinTypes=Winapi.Windows;WinProcs=Winapi.Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE -DDEBUG -E.\\Win32\\Debug -I\"c:\\program files (x86)\\embarcadero\\studio\\22.0\\lib\\Win32\\debug\";\"c:\\program files (x86)\\embarcadero\\studio\\22.0\\lib\\Win32\\release\";C:\\Users\\azapa\\Documents\\Embarcadero\\Studio\\22.0\\Imports;C:\\Users\\azapa\\Documents\\Embarcadero\\Studio\\22.0\\Imports\\Win32;\"c:\\program files (x86)\\embarcadero\\studio\\22.0\\Imports\";C:\\Users\\Public\\Documents\\Embarcadero\\Studio\\22.0\\Dcp;\"c:\\program files (x86)\\embarcadero\\studio\\22.0\\include\";\"C:\\Program Files (x86)\\FastReports\\LibD28\";C:\\Users\\azapa\\Documents\\Embarcadero\\Studio\\22.0\\CatalogRepository\\Skia4Delphi-5.0.0\\Source;C:\\Users\\azapa\\Documents\\Embarcadero\\Studio\\22.0\\CatalogRepository\\Skia4Delphi-5.0.0\\Source\\FMX;C:\\Users\\azapa\\Documents\\Embarcadero\\Studio\\22.0\\CatalogRepository\\Skia4Delphi-5.0.0\\Source\\VCL;C:\\Users\\azapa\\Documents\\Embarcadero\\Studio\\22.0\\CatalogRepository\\Skia4Delphi-5.0.0\\Source\\FMX\\Designtime;C:\\Users\\azapa\\Documents\\Embarcadero\\Studio\\22.0\\CatalogRepository\\Skia4Delphi-5.0.0\\Source\\VCL\\Designtime;\"C:\\Users\\azapa\\Documents\\Embarcadero\\Studio\\22.0\\CatalogRepository\\Skia4Delphi-5.0.0\\Library\\RAD Studio 11 Alexandria\\Win32\\Release\";C:\\Users\\azapa\\Documents\\Embarcadero\\Studio\\22.0\\CatalogRepository\\TFrameStand-1.8-11\\Src\\source;C:\\Users\\azapa\\Documents\\Embarcadero\\Studio\\22.0\\CatalogRepository\\AWSSDKforDelphi\\Source;C:\\Users\\azapa\\Documents\\Embarcadero\\Studio\\22.0\\CatalogRepository\\AWSSDKforDelphi\\Source\\AWS;C:\\Users\\azapa\\Documents\\Embarcadero\\Studio\\22.0\\CatalogRepository\\AWSSDKforDelphi\\Source\\Components;C:\\Users\\azapa\\.boss\\modules\\internal.4e7eb35756869019c9263ab22556beb6\\modules\\.dcp;C:\\Users\\azapa\\.boss\\modules\\internal.4e7eb35756869019c9263ab22556beb6\\modules\\.dcu;C:\\Users\\azapa\\.boss\\modules\\internal.4e7eb35756869019c9263ab22556beb6\\modules;C:\\Users\\azapa\\Documents\\Embarcadero\\Studio\\22.0\\CatalogRepository\\BeaconFence-280-1.3For11.3\\lib\\Win32\\Debug;C:\\Users\\azapa\\Documents\\Embarcadero\\Studio\\22.0\\CatalogRepository\\SempareTemplateEngine-1.7.2\\Src -LEC:\\Users\\Public\\Documents\\Embarcadero\\Studio\\22.0\\Bpl -LNC:\\Users\\Public\\Documents\\Embarcadero\\Studio\\22.0\\Dcp -NU.\\Win32\\Debug -NSWinapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;System;Xml;Data;Datasnap;Web;Soap; -O\"c:\\program files (x86)\\embarcadero\\studio\\22.0\\lib\\Win32\\release\";C:\\Users\\azapa\\Documents\\Embarcadero\\Studio\\22.0\\Imports;C:\\Users\\azapa\\Documents\\Embarcadero\\Studio\\22.0\\Imports\\Win32;\"c:\\program files (x86)\\embarcadero\\studio\\22.0\\Imports\";C:\\Users\\Public\\Documents\\Embarcadero\\Studio\\22.0\\Dcp;\"c:\\program files (x86)\\embarcadero\\studio\\22.0\\include\";\"C:\\Program Files (x86)\\FastReports\\LibD28\";C:\\Users\\azapa\\Documents\\Embarcadero\\Studio\\22.0\\CatalogRepository\\Skia4Delphi-5.0.0\\Source;C:\\Users\\azapa\\Documents\\Embarcadero\\Studio\\22.0\\CatalogRepository\\Skia4Delphi-5.0.0\\Source\\FMX;C:\\Users\\azapa\\Documents\\Embarcadero\\Studio\\22.0\\CatalogRepository\\Skia4Delphi-5.0.0\\Source\\VCL;C:\\Users\\azapa\\Documents\\Embarcadero\\Studio\\22.0\\CatalogRepository\\Skia4Delphi-5.0.0\\Source\\FMX\\Designtime;C:\\Users\\azapa\\Documents\\Embarcadero\\Studio\\22.0\\CatalogRepository\\Skia4Delphi-5.0.0\\Source\\VCL\\Designtime;\"C:\\Users\\azapa\\Documents\\Embarcadero\\Studio\\22.0\\CatalogRepository\\Skia4Delphi-5.0.0\\Library\\RAD Studio 11 Alexandria\\Win32\\Release\";C:\\Users\\azapa\\Documents\\Embarcadero\\Studio\\22.0\\CatalogRepository\\TFrameStand-1.8-11\\Src\\source;C:\\Users\\azapa\\Documents\\Embarcadero\\Studio\\22.0\\CatalogRepository\\AWSSDKforDelphi\\Source;C:\\Users\\azapa\\Documents\\Embarcadero\\Studio\\22.0\\CatalogRepository\\AWSSDKforDelphi\\Source\\AWS;C:\\Users\\azapa\\Documents\\Embarcadero\\Studio\\22.0\\CatalogRepository\\AWSSDKforDelphi\\Source\\Components;C:\\Users\\azapa\\.boss\\modules\\internal.4e7eb35756869019c9263ab22556beb6\\modules\\.dcp;C:\\Users\\azapa\\.boss\\modules\\internal.4e7eb35756869019c9263ab22556beb6\\modules\\.dcu;C:\\Users\\azapa\\.boss\\modules\\internal.4e7eb35756869019c9263ab22556beb6\\modules;C:\\Users\\azapa\\Documents\\Embarcadero\\Studio\\22.0\\CatalogRepository\\BeaconFence-280-1.3For11.3\\lib\\Win32\\Debug;C:\\Users\\azapa\\Documents\\Embarcadero\\Studio\\22.0\\CatalogRepository\\SempareTemplateEngine-1.7.2\\Src -R\"c:\\program files (x86)\\embarcadero\\studio\\22.0\\lib\\Win32\\release\";C:\\Users\\azapa\\Documents\\Embarcadero\\Studio\\22.0\\Imports;C:\\Users\\azapa\\Documents\\Embarcadero\\Studio\\22.0\\Imports\\Win32;\"c:\\program files (x86)\\embarcadero\\studio\\22.0\\Imports\";C:\\Users\\Public\\Documents\\Embarcadero\\Studio\\22.0\\Dcp;\"c:\\program files (x86)\\embarcadero\\studio\\22.0\\include\";\"C:\\Program Files (x86)\\FastReports\\LibD28\";C:\\Users\\azapa\\Documents\\Embarcadero\\Studio\\22.0\\CatalogRepository\\Skia4Delphi-5.0.0\\Source;C:\\Users\\azapa\\Documents\\Embarcadero\\Studio\\22.0\\CatalogRepository\\Skia4Delphi-5.0.0\\Source\\FMX;C:\\Users\\azapa\\Documents\\Embarcadero\\Studio\\22.0\\CatalogRepository\\Skia4Delphi-5.0.0\\Source\\VCL;C:\\Users\\azapa\\Documents\\Embarcadero\\Studio\\22.0\\CatalogRepository\\Skia4Delphi-5.0.0\\Source\\FMX\\Designtime;C:\\Users\\azapa\\Documents\\Embarcadero\\Studio\\22.0\\CatalogRepository\\Skia4Delphi-5.0.0\\Source\\VCL\\Designtime;\"C:\\Users\\azapa\\Documents\\Embarcadero\\Studio\\22.0\\CatalogRepository\\Skia4Delphi-5.0.0\\Library\\RAD Studio 11 Alexandria\\Win32\\Release\";C:\\Users\\azapa\\Documents\\Embarcadero\\Studio\\22.0\\CatalogRepository\\TFrameStand-1.8-11\\Src\\source;C:\\Users\\azapa\\Documents\\Embarcadero\\Studio\\22.0\\CatalogRepository\\AWSSDKforDelphi\\Source;C:\\Users\\azapa\\Documents\\Embarcadero\\Studio\\22.0\\CatalogRepository\\AWSSDKforDelphi\\Source\\AWS;C:\\Users\\azapa\\Documents\\Embarcadero\\Studio\\22.0\\CatalogRepository\\AWSSDKforDelphi\\Source\\Components;C:\\Users\\azapa\\.boss\\modules\\internal.4e7eb35756869019c9263ab22556beb6\\modules\\.dcp;C:\\Users\\azapa\\.boss\\modules\\internal.4e7eb35756869019c9263ab22556beb6\\modules\\.dcu;C:\\Users\\azapa\\.boss\\modules\\internal.4e7eb35756869019c9263ab22556beb6\\modules;C:\\Users\\azapa\\Documents\\Embarcadero\\Studio\\22.0\\CatalogRepository\\BeaconFence-280-1.3For11.3\\lib\\Win32\\Debug;C:\\Users\\azapa\\Documents\\Embarcadero\\Studio\\22.0\\CatalogRepository\\SempareTemplateEngine-1.7.2\\Src -U\"c:\\program files (x86)\\embarcadero\\studio\\22.0\\lib\\Win32\\debug\";\"c:\\program files (x86)\\embarcadero\\studio\\22.0\\lib\\Win32\\release\";C:\\Users\\azapa\\Documents\\Embarcadero\\Studio\\22.0\\Imports;C:\\Users\\azapa\\Documents\\Embarcadero\\Studio\\22.0\\Imports\\Win32;\"c:\\program files (x86)\\embarcadero\\studio\\22.0\\Imports\";C:\\Users\\Public\\Documents\\Embarcadero\\Studio\\22.0\\Dcp;\"c:\\program files (x86)\\embarcadero\\studio\\22.0\\include\";\"C:\\Program Files (x86)\\FastReports\\LibD28\";C:\\Users\\azapa\\Documents\\Embarcadero\\Studio\\22.0\\CatalogRepository\\Skia4Delphi-5.0.0\\Source;C:\\Users\\azapa\\Documents\\Embarcadero\\Studio\\22.0\\CatalogRepository\\Skia4Delphi-5.0.0\\Source\\FMX;C:\\Users\\azapa\\Documents\\Embarcadero\\Studio\\22.0\\CatalogRepository\\Skia4Delphi-5.0.0\\Source\\VCL;C:\\Users\\azapa\\Documents\\Embarcadero\\Studio\\22.0\\CatalogRepository\\Skia4Delphi-5.0.0\\Source\\FMX\\Designtime;C:\\Users\\azapa\\Documents\\Embarcadero\\Studio\\22.0\\CatalogRepository\\Skia4Delphi-5.0.0\\Source\\VCL\\Designtime;\"C:\\Users\\azapa\\Documents\\Embarcadero\\Studio\\22.0\\CatalogRepository\\Skia4Delphi-5.0.0\\Library\\RAD Studio 11 Alexandria\\Win32\\Release\";C:\\Users\\azapa\\Documents\\Embarcadero\\Studio\\22.0\\CatalogRepository\\TFrameStand-1.8-11\\Src\\source;C:\\Users\\azapa\\Documents\\Embarcadero\\Studio\\22.0\\CatalogRepository\\AWSSDKforDelphi\\Source;C:\\Users\\azapa\\Documents\\Embarcadero\\Studio\\22.0\\CatalogRepository\\AWSSDKforDelphi\\Source\\AWS;C:\\Users\\azapa\\Documents\\Embarcadero\\Studio\\22.0\\CatalogRepository\\AWSSDKforDelphi\\Source\\Components;C:\\Users\\azapa\\.boss\\modules\\internal.4e7eb35756869019c9263ab22556beb6\\modules\\.dcp;C:\\Users\\azapa\\.boss\\modules\\internal.4e7eb35756869019c9263ab22556beb6\\modules\\.dcu;C:\\Users\\azapa\\.boss\\modules\\internal.4e7eb35756869019c9263ab22556beb6\\modules;C:\\Users\\azapa\\Documents\\Embarcadero\\Studio\\22.0\\CatalogRepository\\BeaconFence-280-1.3For11.3\\lib\\Win32\\Debug;C:\\Users\\azapa\\Documents\\Embarcadero\\Studio\\22.0\\CatalogRepository\\SempareTemplateEngine-1.7.2\\Src -V -VN -NBC:\\Users\\Public\\Documents\\Embarcadero\\Studio\\22.0\\Dcp -NHC:\\Users\\Public\\Documents\\Embarcadero\\Studio\\22.0\\hpp\\Win32 -NO.\\Win32\\Debug -LUrtl;emsserverapi;emsserverresource;" , "projectFiles":[ { "name": "MyDM", "file": "file:///C%3A/Users/azapa/Documents/RADServer-docs-antonio/13%20-%20File%20management%20and%20storage/Delphi/MyDM.pas" } ] , "includeDCUsInUsesCompletion": true, "browsingPaths": [ "file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/22.0/OCX/Servers","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/22.0/SOURCE/VCL","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/22.0/source/rtl/common","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/22.0/SOURCE/RTL/SYS","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/22.0/source/rtl/win","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/22.0/source/rtl/win/winrt","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/22.0/source/ToolsAPI","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/22.0/SOURCE/IBX","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/22.0/source/Internet","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/22.0/SOURCE/PROPERTY%20EDITORS","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/22.0/source/soap","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/22.0/SOURCE/XML","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/22.0/source/Indy10/Core","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/22.0/source/Indy10/System","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/22.0/source/Indy10/Protocols","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/22.0/source/fmx","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/22.0/source/databinding/components","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/22.0/source/databinding/engine","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/22.0/source/databinding/graph","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/22.0/source/data","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/22.0/source/data/ado","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/22.0/source/data/cloud","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/22.0/source/data/datasnap","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/22.0/source/data/dbx","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/22.0/source/data/dsnap","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/22.0/source/data/vclctrls","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/22.0/source/data/datasnap/connectors","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/22.0/source/data/datasnap/proxygen","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/22.0/source/DataExplorer","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/22.0/source/DUnit/Contrib/DUnitWizard/Source/Common","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/22.0/source/DUnit/Contrib/DUnitWizard/Source/Common/dunit","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/22.0/source/DUnit/Contrib/DUnitWizard/Source/DelphiExperts/Common","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/22.0/source/DUnit/Contrib/DUnitWizard/Source/DelphiExperts/DUnitProject","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/22.0/source/DUnit/Contrib/DUnitWizard/Source/DelphiExperts/DUnitProject/dunit","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/22.0/source/DUnit/src","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/22.0/source/DUnit/tests","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/22.0/source/Experts","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/22.0/source/indy/abstraction","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/22.0/source/indy/implementation","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/22.0/source/indyimpl","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/22.0/source/Property%20Editors/Indy10","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/22.0/source/soap/wsdlimporter","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/22.0/source/Visualizers","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/22.0/source/DUnit/Contrib/XMLReporting","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/22.0/source/DUnit/Contrib/XPGen","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/22.0/source/data/rest","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/22.0/source/data/firedac","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/22.0/source/tethering","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/22.0/source/DUnitX","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/22.0/source/data/ems","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/22.0/source/rtl/net","file:///c%3A/program%20files%20%28x86%29/embarcadero/studio/22.0/source/FlatBox2D","file:///C%3A/Program%20Files%20%28x86%29/FastReports/LibD28","file:///C%3A/Users/azapa/Documents/Embarcadero/Studio/22.0/CatalogRepository/BeaconFence-280-1.3For11.3/source" ] } } -------------------------------------------------------------------------------- /13 - File management and storage/Delphi/FileManagement.dpk: -------------------------------------------------------------------------------- 1 | package FileManagement; 2 | 3 | {$R *.res} 4 | {$IFDEF IMPLICITBUILDING This IFDEF should not be used by users} 5 | {$ALIGN 8} 6 | {$ASSERTIONS ON} 7 | {$BOOLEVAL OFF} 8 | {$DEBUGINFO OFF} 9 | {$EXTENDEDSYNTAX ON} 10 | {$IMPORTEDDATA ON} 11 | {$IOCHECKS ON} 12 | {$LOCALSYMBOLS ON} 13 | {$LONGSTRINGS ON} 14 | {$OPENSTRINGS ON} 15 | {$OPTIMIZATION OFF} 16 | {$OVERFLOWCHECKS ON} 17 | {$RANGECHECKS ON} 18 | {$REFERENCEINFO ON} 19 | {$SAFEDIVIDE OFF} 20 | {$STACKFRAMES ON} 21 | {$TYPEDADDRESS OFF} 22 | {$VARSTRINGCHECKS ON} 23 | {$WRITEABLECONST OFF} 24 | {$MINENUMSIZE 1} 25 | {$IMAGEBASE $400000} 26 | {$DEFINE DEBUG} 27 | {$ENDIF IMPLICITBUILDING} 28 | {$RUNONLY} 29 | {$IMPLICITBUILD ON} 30 | 31 | requires 32 | rtl, 33 | emsserverapi, 34 | emsserverresource; 35 | 36 | contains 37 | MyDM in 'MyDM.pas' {DataResource1: TDataModule}; 38 | 39 | end. 40 | -------------------------------------------------------------------------------- /13 - File management and storage/Delphi/FileManagement.res: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Embarcadero/RADServer-Docs/b6cecd34bb509284d5f7d8d0d3aed5352fecc529/13 - File management and storage/Delphi/FileManagement.res -------------------------------------------------------------------------------- /13 - File management and storage/Delphi/MyDM.dfm: -------------------------------------------------------------------------------- 1 | object DataResource1: TDataResource1 2 | Height = 136 3 | Width = 145 4 | object EMSFileResource1: TEMSFileResource 5 | AllowedActions = [List, Get, Post, Put, Delete] 6 | PathTemplate = 'c:\uploads\{id}' 7 | DefaultFile = 'index.html' 8 | Left = 48 9 | Top = 32 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /13 - File management and storage/Delphi/MyDM.pas: -------------------------------------------------------------------------------- 1 | unit MyDM; 2 | 3 | // EMS Resource Module 4 | 5 | interface 6 | 7 | uses 8 | System.SysUtils, 9 | System.Classes, 10 | System.JSON, 11 | EMS.Services, 12 | EMS.ResourceAPI, 13 | EMS.ResourceTypes, 14 | EMS.FileResource; 15 | 16 | type 17 | 18 | [ResourceName('test')] 19 | TDataResource1 = class(TDataModule) 20 | [ResourceSuffix('./fileResource')] 21 | [ResourceSuffix('list', './')] 22 | [ResourceSuffix('get', './{id}')] 23 | [ResourceSuffix('post', './')] 24 | EMSFileResource1: TEMSFileResource; 25 | published 26 | [ResourceSuffix('./upload')] 27 | procedure PostUpload(const AContext: TEndpointContext; const ARequest: TEndpointRequest; 28 | const AResponse: TEndpointResponse); 29 | end; 30 | 31 | implementation 32 | 33 | {%CLASSGROUP 'System.Classes.TPersistent'} 34 | {$R *.dfm} 35 | 36 | uses 37 | System.IOUtils; 38 | 39 | procedure TDataResource1.PostUpload(const AContext: TEndpointContext; const ARequest: TEndpointRequest; 40 | const AResponse: TEndpointResponse); 41 | begin 42 | const UPLOAD_PATH = 'c:\uploads'; // change this with the local path you prefer 43 | TDirectory.CreateDirectory(UPLOAD_PATH); 44 | AResponse.Body.JSONWriter.WriteStartArray; 45 | for var i := 0 to ARequest.Body.PartCount - 1 do // check how many files/parts are in the body 46 | begin 47 | var lFileName := ARequest.Body.Parts[i].FileName; // accessing the original file name 48 | 49 | // In case of multi platform development it is important to verify if the file name complies with the OS standards 50 | if not TPath.HasValidFileNameChars(lFileName, false) then 51 | AResponse.RaiseError(415, 'invalid file name: ', lFileName); 52 | 53 | // The file is created in the local path 54 | var lFile := TFile.Create(TPath.Combine(UPLOAD_PATH, lFileName)); 55 | try 56 | // The stream from the body is added to the file 57 | lFile.CopyFrom(ARequest.Body.Parts[i].GetStream, 0); 58 | 59 | // In this example we return a status code of 201 and as extra info the file name and the file size 60 | AResponse.StatusCode := 201; 61 | with AResponse.Body.JSONWriter do 62 | begin 63 | WriteStartObject; 64 | WritePropertyName('fileName'); 65 | WriteValue(lFile.FileName); 66 | WritePropertyName('size'); 67 | WriteValue(lFile.Size); 68 | WriteEndObject; 69 | end; 70 | finally 71 | lFile.free; 72 | end; 73 | end; 74 | AResponse.Body.JSONWriter.WriteEndArray; 75 | end; 76 | 77 | procedure Register; 78 | begin 79 | RegisterResource(TypeInfo(TDataResource1)); 80 | end; 81 | 82 | initialization 83 | 84 | Register; 85 | 86 | end. 87 | -------------------------------------------------------------------------------- /13 - File management and storage/Delphi/Project1.res: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Embarcadero/RADServer-Docs/b6cecd34bb509284d5f7d8d0d3aed5352fecc529/13 - File management and storage/Delphi/Project1.res -------------------------------------------------------------------------------- /13 - File management and storage/README.md: -------------------------------------------------------------------------------- 1 | ## Chapter 13: File management and storage 2 | 3 | In this chapter, we will analyze multiple ways that RAD Server offers to manage files and access infrastructure storage. Also, we will see how we can specify the types of files that each endpoint can produce or consume globally or define it in a granular way on each endpoint. 4 | 5 | This sample shows how to use the component TEMSFileResource as well as how to access files sent directly in the body of a request. -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Embarcadero Technologies 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 | Logo 5 | 6 | 7 |

RAD Server documentation

8 | 9 |

10 | Download Documentation 11 |
12 |
13 | More info 14 | 18 |

19 |
20 | 21 | ## What is RAD Server? 22 | 23 | [![RAD Server Screen Shot][product-screenshot]][radserver-url] 24 | 25 | RAD Server is a turn-key application foundation for rapidly building and deploying services based applications. RAD Server enables developers to quickly build new application back-ends or migrate existing Delphi or C++ client/server business logic to a modern services based architecture that is open, stateless, secure and scalable. 26 | 27 | RAD Server is easy to develop, deploy and operate making it ideally suited for ISVs and OEMs building re-deployable solutions. RAD Server allows you to take your existing code and convert it to REST API endpoints. 28 | 29 | ## About this documentation 30 | 31 | This repository is the **part 1** of an updated version of the original white paper about RAD Server written by David Intersimone. The whole document is beeing updated and some brand new chapters have been introduced. 32 | 33 | For a more concise and aprocheable learning process, each chapter has been split in different folders. Inside each one you will find: 34 | 35 | - `README.md` file with a summary of the chapter and a video link to YouTube (each chapter has a explaining video) 36 | - `Demo projects` for Delphi and C++ (if the chapter requires demos) 37 | 38 | ## Prerequisites 39 | 40 | For following this documentation correctly you will need: 41 | 42 | - Delphi or C++ Enterprise/Architect 43 | - InterBase Developer edition 44 | 45 | ## How to download the whole documentation 46 | 47 | You can find chapter 1 of the documentation in the folder in this repository. If you want to download for free all the chapters, you just need to access [this link][new-docs-url]. 48 | 49 | ## How to download the examples 50 | 51 | You can just click the ![code button][code-button] button on this repository and download the whole project, but we recommend you to clone the repository to get future updates in a faster and simpler way. 52 | 53 | #### Clone the repo 54 | 55 | ```sh 56 | git clone https://github.com/embarcadero/radserver-docs.git 57 | ``` 58 | 59 | ## Videos 60 | 61 | We have created a video for each chapter. You can find the link to every specific chapter inside its README.md in the folder, but you can also access the whole playlist [here][yt-playlist-url]. 62 | 63 | ## Roadmap 64 | 65 | Currently we have launched a first batch of 10 chapters that will help to understand and start using RAD Server. Our plan is to keep releasing new chapters in a regular basis, update the documentation and include the examples in this repository. 66 | 67 | ## FAQ 68 | 69 | ### How do I get a license of RAD Server? 70 | 71 | Depending on the RAD Studio license you own maybe you are elegible for a RAD Server license already. Also, RAD Server Lite is free for any Enterprise/Architect owner. Contact your sales rep for more info. 72 | 73 | ### Can I suggest a topic for a future chapter? 74 | 75 | Absolutely. We are constantly working in uplading more content to this repository with extra chapters. If you'd like to see anything in particular, [open an issue][issues-url] and suggest it. 76 | 77 | ### I have a problem with RAD Server. Can I report it in this repo? 78 | 79 | No, this is no the right place for that. If you have any questions or requests regarding the product, you should [contact support][support-url]. 80 | 81 | ### I've came up with an idea that would be interesting for the demos. Can I send a pull request? 82 | 83 | We would prefer if you open an issue first, but if for example, you detect a bug on the code or you think that a demo could be improved, feel free to send a PR and we will analyze it. 84 | 85 | ## Contact 86 | 87 | - Twitter - [@EmbarcaderoTech](https://x.com/EmbarcaderoTech) 88 | - Facebook - [@EmbarcaderoTech](https://www.facebook.com/embarcaderotech) 89 | - LinkedIn - [@Embarcadero-technologies](https://www.linkedin.com/company/embarcadero-technologies) 90 | - Website - [Contact us](https://www.embarcadero.com/company/contact-us) 91 | 92 | Repo Link: [https://github.com/embarcadero/radserver-docs](https://github.com/embarcadero/radserver-docs) 93 | 94 | 95 | 96 | ## See also 97 | 98 | - [Embarcadero](https://embarcadero.com) 99 | - [RAD Studio](https://www.embarcadero.com/products/rad-studio) 100 | - [InterBase](https://interbase.com) 101 | - [DocWiki](https://docwiki.embarcadero.com) 102 | - [Quality Portal](https://quality.embarcadero.com) 103 | - [Previous RAD Server WP](https://blogs.embarcadero.com/learn-how-to-create-a-rad-server-with-david-i-intersimone-in-delphi-and-c/) 104 | 105 | 106 | 107 | 108 | [yt-playlist-url]: https://www.youtube.com/playlist?list=PLwUPJvR9mZHj6Ixqd53dREQ-c4CN51RVF 109 | [new-docs-url]: https://lp.embarcadero.com/RADServerGuide 110 | [radserver-url]: https://www.embarcadero.com/products/rad-server 111 | [issues-url]: https://github.com/Embarcadero/RADServer-docs/issues 112 | [support-url]: https://www.embarcadero.com/support 113 | [product-screenshot]: https://d2ohlsp9gwqc7h.cloudfront.net/images/rad-server/n-tier-architecture.webp 114 | [code-button]: https://img.shields.io/badge/%3C%3E_code-darkgreen 115 | -------------------------------------------------------------------------------- /resources/employee.gdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Embarcadero/RADServer-Docs/b6cecd34bb509284d5f7d8d0d3aed5352fecc529/resources/employee.gdb --------------------------------------------------------------------------------