Merge branch 'master' into feature_642_functions_as_action

This commit is contained in:
Daniele Teti 2023-05-30 12:52:10 +02:00
commit 23927c806c
34 changed files with 2428 additions and 211 deletions

View File

@ -0,0 +1,77 @@
object MainForm: TMainForm
Left = 0
Top = 0
Caption = 'dotEnv :: ShowCase'
ClientHeight = 442
ClientWidth = 824
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -12
Font.Name = 'Segoe UI'
Font.Style = []
DesignSize = (
824
442)
TextHeight = 15
object Shape1: TShape
Left = 8
Top = 181
Width = 169
Height = 4
Brush.Color = clGray
end
object btnSimple: TButton
Left = 8
Top = 16
Width = 169
Height = 49
Caption = 'default ENV'
TabOrder = 0
OnClick = btnSimpleClick
end
object mmVars: TMemo
Left = 183
Top = 16
Width = 633
Height = 418
Anchors = [akLeft, akTop, akRight, akBottom]
Font.Charset = ANSI_CHARSET
Font.Color = clWindowText
Font.Height = -15
Font.Name = 'Consolas'
Font.Style = []
ParentFont = False
TabOrder = 1
ExplicitWidth = 433
ExplicitHeight = 417
end
object btnTestEnv: TButton
Left = 8
Top = 71
Width = 169
Height = 49
Caption = 'Test ENV (default + test)'
TabOrder = 2
OnClick = btnTestEnvClick
end
object btnProdEnv: TButton
Left = 8
Top = 126
Width = 169
Height = 49
Caption = 'Prod ENV (default + prod)'
TabOrder = 3
OnClick = btnProdEnvClick
end
object btnSingleEnv: TButton
Left = 8
Top = 191
Width = 169
Height = 49
Caption = 'Single ENV without inheritance (only prod)'
TabOrder = 4
WordWrap = True
OnClick = btnSingleEnvClick
end
end

View File

@ -0,0 +1,81 @@
unit MainFormU;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls,
MVCFramework.DotEnv;
type
TMainForm = class(TForm)
btnSimple: TButton;
mmVars: TMemo;
btnTestEnv: TButton;
btnProdEnv: TButton;
Shape1: TShape;
btnSingleEnv: TButton;
procedure btnSimpleClick(Sender: TObject);
procedure btnTestEnvClick(Sender: TObject);
procedure btnProdEnvClick(Sender: TObject);
procedure btnSingleEnvClick(Sender: TObject);
private
procedure UpdateUI(dotEnv: IMVCDotEnv);
public
{ Public declarations }
end;
var
MainForm: TMainForm;
implementation
{$R *.dfm}
procedure TMainForm.btnProdEnvClick(Sender: TObject);
begin
var dotEnv := NewDotEnv
.WithStrategy(TMVCDotEnvPriority.EnvThenFile)
.UseProfile('prod')
.Build();
mmVars.Clear;
mmVars.Lines.AddStrings(dotEnv.ToArray);
UpdateUI(dotEnv);
end;
procedure TMainForm.btnSimpleClick(Sender: TObject);
begin
var dotEnv := NewDotEnv.WithStrategy(TMVCDotEnvPriority.EnvThenFile).Build();
mmVars.Clear;
mmVars.Lines.AddStrings(dotEnv.ToArray);
UpdateUI(dotEnv);
end;
procedure TMainForm.btnSingleEnvClick(Sender: TObject);
begin
var dotEnv := NewDotEnv
.WithStrategy(TMVCDotEnvPriority.EnvThenFile)
.UseProfile('prod')
.Build('env1');
mmVars.Clear;
mmVars.Lines.AddStrings(dotEnv.ToArray);
UpdateUI(dotEnv);
end;
procedure TMainForm.btnTestEnvClick(Sender: TObject);
begin
var dotEnv := NewDotEnv
.WithStrategy(TMVCDotEnvPriority.EnvThenFile)
.UseProfile('test')
.Build();
mmVars.Clear;
mmVars.Lines.AddStrings(dotEnv.ToArray);
UpdateUI(dotEnv);
end;
procedure TMainForm.UpdateUI(dotEnv: IMVCDotEnv);
begin
Caption := 'dotEnv ShowCase :: MODE = ' + dotEnv.Env('mode');
end;
end.

View File

@ -0,0 +1,25 @@
############
# ENV FILE #
############
mode=dev
#DB Name
dbhostname=my_product_db_dev
#The DB username
dbuser=my_user
#The DB password (in this example is read from an EnvVariable)
dbpassword="XYZ${USERNAME}!$"
#DB Hostname
dbhostname="127.0.0.1"
#user preferences
user_preferences_path=${APPDATA}
email_template="This is a ${mode} email template
second template email line
third template email line"

View File

@ -0,0 +1,22 @@
############
# ENV FILE #
############
mode=prod
#DB Name
dbhostname=my_product_db
#The DB username
dbuser=${USERNAME}
#The DB password
#in this example is read from an EnvVariable
dbpassword=${dbuser}!$%
#DB Hostname
dbhostname="192.168.3.10"
#user preferences
#read from env var and internal defined var
user_preferences_path=${APPDATA}_${mode}

View File

@ -0,0 +1,12 @@
############
# ENV FILE #
############
mode=test
#DB Name
dbhostname=my_product_db_test
#DB Hostname
dbhostname=192.168.1.100

View File

@ -0,0 +1,22 @@
############
# ENV FILE #
############
mode=prod
#DB Name
dbhostname=my_product_db
#The DB username
dbuser=${USERNAME}
#The DB password
#in this example is read from an EnvVariable
dbpassword=${dbuser}!$%
#DB Hostname
dbhostname="192.168.3.10"
#user preferences
#read from env var and internal defined var
user_preferences_path=${APPDATA}_${mode}

View File

@ -0,0 +1,14 @@
program dotEnv_ShowCase;
uses
Vcl.Forms,
MainFormU in 'MainFormU.pas' {MainForm};
{$R *.res}
begin
Application.Initialize;
Application.MainFormOnTaskbar := True;
Application.CreateForm(TMainForm, MainForm);
Application.Run;
end.

View File

@ -0,0 +1,976 @@
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ProjectGuid>{5BD4812E-15AA-4F9D-B25F-5C84C8B1998F}</ProjectGuid>
<ProjectVersion>19.5</ProjectVersion>
<FrameworkType>VCL</FrameworkType>
<Base>True</Base>
<Config Condition="'$(Config)'==''">Debug</Config>
<Platform Condition="'$(Platform)'==''">Win32</Platform>
<TargetedPlatforms>1</TargetedPlatforms>
<AppType>Application</AppType>
<MainSource>dotEnv_ShowCase.dpr</MainSource>
</PropertyGroup>
<PropertyGroup Condition="'$(Config)'=='Base' or '$(Base)'!=''">
<Base>true</Base>
</PropertyGroup>
<PropertyGroup Condition="('$(Platform)'=='Win32' and '$(Base)'=='true') or '$(Base_Win32)'!=''">
<Base_Win32>true</Base_Win32>
<CfgParent>Base</CfgParent>
<Base>true</Base>
</PropertyGroup>
<PropertyGroup Condition="('$(Platform)'=='Win64' and '$(Base)'=='true') or '$(Base_Win64)'!=''">
<Base_Win64>true</Base_Win64>
<CfgParent>Base</CfgParent>
<Base>true</Base>
</PropertyGroup>
<PropertyGroup Condition="'$(Config)'=='Debug' or '$(Cfg_1)'!=''">
<Cfg_1>true</Cfg_1>
<CfgParent>Base</CfgParent>
<Base>true</Base>
</PropertyGroup>
<PropertyGroup Condition="('$(Platform)'=='Win32' and '$(Cfg_1)'=='true') or '$(Cfg_1_Win32)'!=''">
<Cfg_1_Win32>true</Cfg_1_Win32>
<CfgParent>Cfg_1</CfgParent>
<Cfg_1>true</Cfg_1>
<Base>true</Base>
</PropertyGroup>
<PropertyGroup Condition="'$(Config)'=='Release' or '$(Cfg_2)'!=''">
<Cfg_2>true</Cfg_2>
<CfgParent>Base</CfgParent>
<Base>true</Base>
</PropertyGroup>
<PropertyGroup Condition="('$(Platform)'=='Win32' and '$(Cfg_2)'=='true') or '$(Cfg_2_Win32)'!=''">
<Cfg_2_Win32>true</Cfg_2_Win32>
<CfgParent>Cfg_2</CfgParent>
<Cfg_2>true</Cfg_2>
<Base>true</Base>
</PropertyGroup>
<PropertyGroup Condition="'$(Base)'!=''">
<DCC_DcuOutput>.\$(Platform)\$(Config)</DCC_DcuOutput>
<DCC_ExeOutput>.\bin</DCC_ExeOutput>
<DCC_E>false</DCC_E>
<DCC_N>false</DCC_N>
<DCC_S>false</DCC_S>
<DCC_F>false</DCC_F>
<DCC_K>false</DCC_K>
<DCC_Namespace>System;Xml;Data;Datasnap;Web;Soap;Vcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples;Vcl.Shell;$(DCC_Namespace)</DCC_Namespace>
<Icon_MainIcon>$(BDS)\bin\delphi_PROJECTICON.ico</Icon_MainIcon>
<UWP_DelphiLogo44>$(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png</UWP_DelphiLogo44>
<UWP_DelphiLogo150>$(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png</UWP_DelphiLogo150>
<SanitizedProjectName>dotEnv_ShowCase</SanitizedProjectName>
<VerInfo_Locale>1040</VerInfo_Locale>
<VerInfo_Keys>CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=</VerInfo_Keys>
</PropertyGroup>
<PropertyGroup Condition="'$(Base_Win32)'!=''">
<DCC_UsePackage>RaizeComponentsVcl;JvNet;vclwinx;DataSnapServer;fmx;emshosting;vclie;DbxCommonDriver;bindengine;IndyIPCommon;VCLRESTComponents;DBXMSSQLDriver;FireDACCommonODBC;emsclient;FireDACCommonDriver;appanalytics;IndyProtocols;vclx;Skia.Package.RTL;RadiantShapesFmx_Design;IndyIPClient;dbxcds;vcledge;frxe28;frxTee28;bindcompvclwinx;Prometheus.Client.Core;FmxTeeUI;emsedge;bindcompfmx;DBXFirebirdDriver;JvBands;inetdb;JvAppFrm;ibmonitor;FireDACSqliteDriver;DbxClientDriver;FireDACASADriver;Tee;soapmidas;JclVcl;SVGIconImageListFMX;vclactnband;TeeUI;fmxFireDAC;dbexpress;Jcl;JvManagedThreads;FireDACInfxDriver;DBXMySQLDriver;VclSmp;inet;DataSnapCommon;JvPascalInterpreter;vcltouch;fmxase;frx28;JvPluginSystem;DBXOdbcDriver;DzHTMLText_FMX;dbrtl;JvDB;FireDACDBXDriver;FireDACOracleDriver;ComPortDrv;fmxdae;TeeDB;Skia.Package.FMX;JvTimeFramework;FireDACMSAccDriver;JvCustom;CustomIPTransport;FireDACMSSQLDriver;JvSystem;SVGIconPackage;DataSnapIndy10ServerTransport;JclDeveloperTools;JvControls;DataSnapConnectors;vcldsnap;DBXInterBaseDriver;JvCrypt;FireDACMongoDBDriver;JvJans;JvMM;IndySystem;JvWizards;frxDB28;RadiantShapesFmx;FireDACTDataDriver;Skia.Package.VCL;JvGlobus;vcldb;ibxbindings;IconFontsImageList;JclContainers;JvPageComps;vclFireDAC;JvCore;bindcomp;FireDACCommon;DataSnapServerMidas;FireDACODBCDriver;emsserverresource;IndyCore;RESTBackendComponents;dmvcframeworkDT;bindcompdbx;DzHTMLText_VCL;rtl;FireDACMySQLDriver;FireDACADSDriver;RaizeComponentsVclDb;RESTComponents;DBXSqliteDriver;vcl;IndyIPServer;dsnapxml;dsnapcon;DataSnapClient;DataSnapProviderClient;adortl;JvDotNetCtrls;JvHMI;DBXSybaseASEDriver;JvRuntimeDesign;DBXDb2Driver;JvXPCtrls;vclimg;DataSnapFireDAC;emsclientfiredac;FireDACPgDriver;FireDAC;FireDACDSDriver;inetdbxpress;xmlrtl;tethering;JvStdCtrls;ibxpress;JvDlgs;JvDocking;bindcompvcl;dsnap;JvPrintPreview;JvCmp;dmvcframeworkRT;CloudService;DBXSybaseASADriver;DBXOracleDriver;FireDACDb2Driver;DBXInformixDriver;vclib;IconFontsImageListFMX;fmxobj;bindcompvclsmp;FMXTee;DataSnapNativeClient;DatasnapConnectorsFreePascal;soaprtl;SVGIconImageList;soapserver;FireDACIBDriver;$(DCC_UsePackage)</DCC_UsePackage>
<DCC_Namespace>Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace)</DCC_Namespace>
<BT_BuildType>Debug</BT_BuildType>
<VerInfo_IncludeVerInfo>true</VerInfo_IncludeVerInfo>
<VerInfo_Keys>CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=</VerInfo_Keys>
<VerInfo_Locale>1033</VerInfo_Locale>
<Manifest_File>$(BDS)\bin\default_app.manifest</Manifest_File>
</PropertyGroup>
<PropertyGroup Condition="'$(Base_Win64)'!=''">
<DCC_UsePackage>RaizeComponentsVcl;vclwinx;DataSnapServer;fmx;emshosting;vclie;DbxCommonDriver;bindengine;IndyIPCommon;VCLRESTComponents;DBXMSSQLDriver;FireDACCommonODBC;emsclient;FireDACCommonDriver;appanalytics;IndyProtocols;vclx;Skia.Package.RTL;RadiantShapesFmx_Design;IndyIPClient;dbxcds;vcledge;bindcompvclwinx;FmxTeeUI;emsedge;bindcompfmx;DBXFirebirdDriver;inetdb;ibmonitor;FireDACSqliteDriver;DbxClientDriver;FireDACASADriver;Tee;soapmidas;SVGIconImageListFMX;vclactnband;TeeUI;fmxFireDAC;dbexpress;FireDACInfxDriver;DBXMySQLDriver;VclSmp;inet;DataSnapCommon;vcltouch;fmxase;DBXOdbcDriver;DzHTMLText_FMX;dbrtl;FireDACDBXDriver;FireDACOracleDriver;ComPortDrv;fmxdae;TeeDB;Skia.Package.FMX;FireDACMSAccDriver;CustomIPTransport;FireDACMSSQLDriver;SVGIconPackage;DataSnapIndy10ServerTransport;DataSnapConnectors;vcldsnap;DBXInterBaseDriver;FireDACMongoDBDriver;IndySystem;RadiantShapesFmx;FireDACTDataDriver;Skia.Package.VCL;vcldb;ibxbindings;vclFireDAC;bindcomp;FireDACCommon;DataSnapServerMidas;FireDACODBCDriver;emsserverresource;IndyCore;RESTBackendComponents;bindcompdbx;DzHTMLText_VCL;rtl;FireDACMySQLDriver;FireDACADSDriver;RaizeComponentsVclDb;RESTComponents;DBXSqliteDriver;vcl;IndyIPServer;dsnapxml;dsnapcon;DataSnapClient;DataSnapProviderClient;adortl;DBXSybaseASEDriver;DBXDb2Driver;vclimg;DataSnapFireDAC;emsclientfiredac;FireDACPgDriver;FireDAC;FireDACDSDriver;inetdbxpress;xmlrtl;tethering;ibxpress;bindcompvcl;dsnap;CloudService;DBXSybaseASADriver;DBXOracleDriver;FireDACDb2Driver;DBXInformixDriver;vclib;fmxobj;bindcompvclsmp;FMXTee;DataSnapNativeClient;DatasnapConnectorsFreePascal;soaprtl;SVGIconImageList;soapserver;FireDACIBDriver;$(DCC_UsePackage)</DCC_UsePackage>
</PropertyGroup>
<PropertyGroup Condition="'$(Cfg_1)'!=''">
<DCC_Define>DEBUG;$(DCC_Define)</DCC_Define>
<DCC_DebugDCUs>true</DCC_DebugDCUs>
<DCC_Optimize>false</DCC_Optimize>
<DCC_GenerateStackFrames>true</DCC_GenerateStackFrames>
<DCC_DebugInfoInExe>true</DCC_DebugInfoInExe>
<DCC_RemoteDebug>true</DCC_RemoteDebug>
<DCC_IntegerOverflowCheck>true</DCC_IntegerOverflowCheck>
<DCC_RangeChecking>true</DCC_RangeChecking>
</PropertyGroup>
<PropertyGroup Condition="'$(Cfg_1_Win32)'!=''">
<DCC_RemoteDebug>false</DCC_RemoteDebug>
<AppDPIAwarenessMode>PerMonitorV2</AppDPIAwarenessMode>
<VerInfo_IncludeVerInfo>true</VerInfo_IncludeVerInfo>
<VerInfo_Locale>1033</VerInfo_Locale>
</PropertyGroup>
<PropertyGroup Condition="'$(Cfg_2)'!=''">
<DCC_LocalDebugSymbols>false</DCC_LocalDebugSymbols>
<DCC_Define>RELEASE;$(DCC_Define)</DCC_Define>
<DCC_SymbolReferenceInfo>0</DCC_SymbolReferenceInfo>
<DCC_DebugInformation>0</DCC_DebugInformation>
</PropertyGroup>
<PropertyGroup Condition="'$(Cfg_2_Win32)'!=''">
<AppDPIAwarenessMode>PerMonitorV2</AppDPIAwarenessMode>
</PropertyGroup>
<ItemGroup>
<DelphiCompile Include="$(MainSource)">
<MainSource>MainSource</MainSource>
</DelphiCompile>
<DCCReference Include="MainFormU.pas">
<Form>MainForm</Form>
<FormType>dfm</FormType>
</DCCReference>
<BuildConfiguration Include="Base">
<Key>Base</Key>
</BuildConfiguration>
<BuildConfiguration Include="Debug">
<Key>Cfg_1</Key>
<CfgParent>Base</CfgParent>
</BuildConfiguration>
<BuildConfiguration Include="Release">
<Key>Cfg_2</Key>
<CfgParent>Base</CfgParent>
</BuildConfiguration>
</ItemGroup>
<ProjectExtensions>
<Borland.Personality>Delphi.Personality.12</Borland.Personality>
<Borland.ProjectType>Application</Borland.ProjectType>
<BorlandProject>
<Delphi.Personality>
<Source>
<Source Name="MainSource">dotEnv_ShowCase.dpr</Source>
</Source>
<Excluded_Packages>
<Excluded_Packages Name="$(BDSBIN)\bcboffice2k280.bpl">Embarcadero C++Builder Office 2000 Servers Package</Excluded_Packages>
<Excluded_Packages Name="$(BDSBIN)\bcbofficexp280.bpl">Embarcadero C++Builder Office XP Servers Package</Excluded_Packages>
<Excluded_Packages Name="$(BDSBIN)\dcloffice2k280.bpl">Microsoft Office 2000 Sample Automation Server Wrapper Components</Excluded_Packages>
<Excluded_Packages Name="$(BDSBIN)\dclofficexp280.bpl">Microsoft Office XP Sample Automation Server Wrapper Components</Excluded_Packages>
</Excluded_Packages>
</Delphi.Personality>
<Deployment Version="4">
<DeployFile LocalName="Win32\Debug\dotEnv_ShowCase.exe" Configuration="Debug" Class="ProjectOutput">
<Platform Name="Win32">
<RemoteName>dotEnv_ShowCase.exe</RemoteName>
<Overwrite>true</Overwrite>
</Platform>
</DeployFile>
<DeployClass Name="AdditionalDebugSymbols">
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
<Platform Name="OSX32">
<RemoteDir>Contents\MacOS</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Win32">
<Operation>0</Operation>
</Platform>
</DeployClass>
<DeployClass Name="AndroidClasses">
<Platform Name="Android">
<RemoteDir>classes</RemoteDir>
<Operation>64</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>classes</RemoteDir>
<Operation>64</Operation>
</Platform>
</DeployClass>
<DeployClass Name="AndroidFileProvider">
<Platform Name="Android">
<RemoteDir>res\xml</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\xml</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="AndroidGDBServer">
<Platform Name="Android">
<RemoteDir>library\lib\armeabi-v7a</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="AndroidLibnativeArmeabiFile">
<Platform Name="Android">
<RemoteDir>library\lib\armeabi</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>library\lib\armeabi</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="AndroidLibnativeArmeabiv7aFile">
<Platform Name="Android64">
<RemoteDir>library\lib\armeabi-v7a</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="AndroidLibnativeMipsFile">
<Platform Name="Android">
<RemoteDir>library\lib\mips</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>library\lib\mips</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="AndroidServiceOutput">
<Platform Name="Android">
<RemoteDir>library\lib\armeabi-v7a</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>library\lib\arm64-v8a</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="AndroidServiceOutput_Android32">
<Platform Name="Android64">
<RemoteDir>library\lib\armeabi-v7a</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="AndroidSplashImageDef">
<Platform Name="Android">
<RemoteDir>res\drawable</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="AndroidSplashStyles">
<Platform Name="Android">
<RemoteDir>res\values</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\values</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="AndroidSplashStylesV21">
<Platform Name="Android">
<RemoteDir>res\values-v21</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\values-v21</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_Colors">
<Platform Name="Android">
<RemoteDir>res\values</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\values</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_DefaultAppIcon">
<Platform Name="Android">
<RemoteDir>res\drawable</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_LauncherIcon144">
<Platform Name="Android">
<RemoteDir>res\drawable-xxhdpi</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable-xxhdpi</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_LauncherIcon192">
<Platform Name="Android">
<RemoteDir>res\drawable-xxxhdpi</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable-xxxhdpi</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_LauncherIcon36">
<Platform Name="Android">
<RemoteDir>res\drawable-ldpi</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable-ldpi</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_LauncherIcon48">
<Platform Name="Android">
<RemoteDir>res\drawable-mdpi</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable-mdpi</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_LauncherIcon72">
<Platform Name="Android">
<RemoteDir>res\drawable-hdpi</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable-hdpi</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_LauncherIcon96">
<Platform Name="Android">
<RemoteDir>res\drawable-xhdpi</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable-xhdpi</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_NotificationIcon24">
<Platform Name="Android">
<RemoteDir>res\drawable-mdpi</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable-mdpi</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_NotificationIcon36">
<Platform Name="Android">
<RemoteDir>res\drawable-hdpi</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable-hdpi</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_NotificationIcon48">
<Platform Name="Android">
<RemoteDir>res\drawable-xhdpi</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable-xhdpi</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_NotificationIcon72">
<Platform Name="Android">
<RemoteDir>res\drawable-xxhdpi</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable-xxhdpi</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_NotificationIcon96">
<Platform Name="Android">
<RemoteDir>res\drawable-xxxhdpi</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable-xxxhdpi</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_SplashImage426">
<Platform Name="Android">
<RemoteDir>res\drawable-small</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable-small</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_SplashImage470">
<Platform Name="Android">
<RemoteDir>res\drawable-normal</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable-normal</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_SplashImage640">
<Platform Name="Android">
<RemoteDir>res\drawable-large</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable-large</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_SplashImage960">
<Platform Name="Android">
<RemoteDir>res\drawable-xlarge</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable-xlarge</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_Strings">
<Platform Name="Android">
<RemoteDir>res\values</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\values</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="DebugSymbols">
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
<Platform Name="OSX32">
<RemoteDir>Contents\MacOS</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Win32">
<Operation>0</Operation>
</Platform>
</DeployClass>
<DeployClass Name="DependencyFramework">
<Platform Name="OSX32">
<RemoteDir>Contents\MacOS</RemoteDir>
<Operation>1</Operation>
<Extensions>.framework</Extensions>
</Platform>
<Platform Name="OSX64">
<RemoteDir>Contents\MacOS</RemoteDir>
<Operation>1</Operation>
<Extensions>.framework</Extensions>
</Platform>
<Platform Name="OSXARM64">
<RemoteDir>Contents\MacOS</RemoteDir>
<Operation>1</Operation>
<Extensions>.framework</Extensions>
</Platform>
<Platform Name="Win32">
<Operation>0</Operation>
</Platform>
</DeployClass>
<DeployClass Name="DependencyModule">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
<Extensions>.dylib</Extensions>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
<Extensions>.dylib</Extensions>
</Platform>
<Platform Name="iOSSimARM64">
<Operation>1</Operation>
<Extensions>.dylib</Extensions>
</Platform>
<Platform Name="OSX32">
<RemoteDir>Contents\MacOS</RemoteDir>
<Operation>1</Operation>
<Extensions>.dylib</Extensions>
</Platform>
<Platform Name="OSX64">
<RemoteDir>Contents\MacOS</RemoteDir>
<Operation>1</Operation>
<Extensions>.dylib</Extensions>
</Platform>
<Platform Name="OSXARM64">
<RemoteDir>Contents\MacOS</RemoteDir>
<Operation>1</Operation>
<Extensions>.dylib</Extensions>
</Platform>
<Platform Name="Win32">
<Operation>0</Operation>
<Extensions>.dll;.bpl</Extensions>
</Platform>
</DeployClass>
<DeployClass Required="true" Name="DependencyPackage">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
<Extensions>.dylib</Extensions>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
<Extensions>.dylib</Extensions>
</Platform>
<Platform Name="iOSSimARM64">
<Operation>1</Operation>
<Extensions>.dylib</Extensions>
</Platform>
<Platform Name="OSX32">
<RemoteDir>Contents\MacOS</RemoteDir>
<Operation>1</Operation>
<Extensions>.dylib</Extensions>
</Platform>
<Platform Name="OSX64">
<RemoteDir>Contents\MacOS</RemoteDir>
<Operation>1</Operation>
<Extensions>.dylib</Extensions>
</Platform>
<Platform Name="OSXARM64">
<RemoteDir>Contents\MacOS</RemoteDir>
<Operation>1</Operation>
<Extensions>.dylib</Extensions>
</Platform>
<Platform Name="Win32">
<Operation>0</Operation>
<Extensions>.bpl</Extensions>
</Platform>
</DeployClass>
<DeployClass Name="File">
<Platform Name="Android">
<Operation>0</Operation>
</Platform>
<Platform Name="Android64">
<Operation>0</Operation>
</Platform>
<Platform Name="iOSDevice32">
<Operation>0</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>0</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<Operation>0</Operation>
</Platform>
<Platform Name="OSX32">
<RemoteDir>Contents\Resources\StartUp\</RemoteDir>
<Operation>0</Operation>
</Platform>
<Platform Name="OSX64">
<RemoteDir>Contents\Resources\StartUp\</RemoteDir>
<Operation>0</Operation>
</Platform>
<Platform Name="OSXARM64">
<RemoteDir>Contents\Resources\StartUp\</RemoteDir>
<Operation>0</Operation>
</Platform>
<Platform Name="Win32">
<Operation>0</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectAndroidManifest">
<Platform Name="Android">
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectOSXDebug">
<Platform Name="OSX64">
<RemoteDir>..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="OSXARM64">
<RemoteDir>..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectOSXEntitlements">
<Platform Name="OSX32">
<RemoteDir>..\</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="OSX64">
<RemoteDir>..\</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="OSXARM64">
<RemoteDir>..\</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectOSXInfoPList">
<Platform Name="OSX32">
<RemoteDir>Contents</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="OSX64">
<RemoteDir>Contents</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="OSXARM64">
<RemoteDir>Contents</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectOSXResource">
<Platform Name="OSX32">
<RemoteDir>Contents\Resources</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="OSX64">
<RemoteDir>Contents\Resources</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="OSXARM64">
<RemoteDir>Contents\Resources</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Required="true" Name="ProjectOutput">
<Platform Name="Android">
<RemoteDir>library\lib\armeabi-v7a</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>library\lib\arm64-v8a</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<Operation>1</Operation>
</Platform>
<Platform Name="Linux64">
<Operation>1</Operation>
</Platform>
<Platform Name="OSX32">
<RemoteDir>Contents\MacOS</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="OSX64">
<RemoteDir>Contents\MacOS</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="OSXARM64">
<RemoteDir>Contents\MacOS</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Win32">
<Operation>0</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectOutput_Android32">
<Platform Name="Android64">
<RemoteDir>library\lib\armeabi-v7a</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectUWPManifest">
<Platform Name="Win32">
<Operation>1</Operation>
</Platform>
<Platform Name="Win64">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectiOSDeviceDebug">
<Platform Name="iOSDevice32">
<RemoteDir>..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<RemoteDir>..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<RemoteDir>..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectiOSEntitlements">
<Platform Name="iOSDevice32">
<RemoteDir>..\</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<RemoteDir>..\</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<RemoteDir>..\</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectiOSInfoPList">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectiOSLaunchScreen">
<Platform Name="iOSDevice64">
<RemoteDir>..\$(PROJECTNAME).launchscreen</RemoteDir>
<Operation>64</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<RemoteDir>..\$(PROJECTNAME).launchscreen</RemoteDir>
<Operation>64</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectiOSResource">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="UWP_DelphiLogo150">
<Platform Name="Win32">
<RemoteDir>Assets</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Win64">
<RemoteDir>Assets</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="UWP_DelphiLogo44">
<Platform Name="Win32">
<RemoteDir>Assets</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Win64">
<RemoteDir>Assets</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iOS_AppStore1024">
<Platform Name="iOSDevice64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPad_AppIcon152">
<Platform Name="iOSDevice64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPad_AppIcon167">
<Platform Name="iOSDevice64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPad_Launch2x">
<Platform Name="iOSDevice64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPad_LaunchDark2x">
<Platform Name="iOSDevice64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPad_Notification40">
<Platform Name="iOSDevice64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPad_Setting58">
<Platform Name="iOSDevice64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPad_SpotLight80">
<Platform Name="iOSDevice64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPhone_AppIcon120">
<Platform Name="iOSDevice64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPhone_AppIcon180">
<Platform Name="iOSDevice64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPhone_Launch2x">
<Platform Name="iOSDevice64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPhone_Launch3x">
<Platform Name="iOSDevice64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPhone_LaunchDark2x">
<Platform Name="iOSDevice64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPhone_LaunchDark3x">
<Platform Name="iOSDevice64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPhone_Notification40">
<Platform Name="iOSDevice64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPhone_Notification60">
<Platform Name="iOSDevice64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPhone_Setting58">
<Platform Name="iOSDevice64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPhone_Setting87">
<Platform Name="iOSDevice64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPhone_Spotlight120">
<Platform Name="iOSDevice64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPhone_Spotlight80">
<Platform Name="iOSDevice64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<ProjectRoot Platform="Android" Name="$(PROJECTNAME)"/>
<ProjectRoot Platform="Android64" Name="$(PROJECTNAME)"/>
<ProjectRoot Platform="iOSDevice32" Name="$(PROJECTNAME).app"/>
<ProjectRoot Platform="iOSDevice64" Name="$(PROJECTNAME).app"/>
<ProjectRoot Platform="iOSSimARM64" Name="$(PROJECTNAME).app"/>
<ProjectRoot Platform="Linux64" Name="$(PROJECTNAME)"/>
<ProjectRoot Platform="OSX32" Name="$(PROJECTNAME).app"/>
<ProjectRoot Platform="OSX64" Name="$(PROJECTNAME).app"/>
<ProjectRoot Platform="OSXARM64" Name="$(PROJECTNAME).app"/>
<ProjectRoot Platform="Win32" Name="$(PROJECTNAME)"/>
<ProjectRoot Platform="Win64" Name="$(PROJECTNAME)"/>
</Deployment>
<Platforms>
<Platform value="Win32">True</Platform>
<Platform value="Win64">False</Platform>
</Platforms>
</BorlandProject>
<ProjectFileVersion>12</ProjectFileVersion>
</ProjectExtensions>
<Import Project="$(BDS)\Bin\CodeGear.Delphi.Targets" Condition="Exists('$(BDS)\Bin\CodeGear.Delphi.Targets')"/>
<Import Project="$(APPDATA)\Embarcadero\$(BDSAPPDATABASEDIR)\$(PRODUCTVERSION)\UserTools.proj" Condition="Exists('$(APPDATA)\Embarcadero\$(BDSAPPDATABASEDIR)\$(PRODUCTVERSION)\UserTools.proj')"/>
<Import Project="$(MSBuildProjectName).deployproj" Condition="Exists('$(MSBuildProjectName).deployproj')"/>
</Project>

View File

@ -7,7 +7,6 @@ uses
MVCFramework.Logger, MVCFramework.Logger,
MVCFramework.Commons, MVCFramework.Commons,
MVCFramework.Console, MVCFramework.Console,
MVCFramework.REPLCommandsHandlerU,
Web.ReqMulti, Web.ReqMulti,
Web.WebReq, Web.WebReq,
Web.WebBroker, Web.WebBroker,

View File

@ -183,17 +183,17 @@
</Excluded_Packages> </Excluded_Packages>
</Delphi.Personality> </Delphi.Personality>
<Deployment Version="4"> <Deployment Version="4">
<DeployFile LocalName="$(BDS)\Redist\iossimulator\libcgunwind.1.0.dylib" Class="DependencyModule"/>
<DeployFile LocalName="$(BDS)\Redist\iossimulator\libPCRE.dylib" Class="DependencyModule"/> <DeployFile LocalName="$(BDS)\Redist\iossimulator\libPCRE.dylib" Class="DependencyModule"/>
<DeployFile LocalName="$(BDS)\Redist\iossimulator\libcgunwind.1.0.dylib" Class="DependencyModule"/>
<DeployFile LocalName="$(BDS)\Redist\osx32\libcgsqlite3.dylib" Class="DependencyModule"/> <DeployFile LocalName="$(BDS)\Redist\osx32\libcgsqlite3.dylib" Class="DependencyModule"/>
<DeployFile LocalName="$(BDS)\Redist\osx32\libcgunwind.1.0.dylib" Class="DependencyModule"/> <DeployFile LocalName="$(BDS)\Redist\osx32\libcgunwind.1.0.dylib" Class="DependencyModule"/>
<DeployFile LocalName="Win32\Debug\jsonrpcserverwithobjects.exe" Configuration="Debug" Class="ProjectOutput"/>
<DeployFile LocalName="bin\jsonrpcserverwithobjects.exe" Configuration="Debug" Class="ProjectOutput"> <DeployFile LocalName="bin\jsonrpcserverwithobjects.exe" Configuration="Debug" Class="ProjectOutput">
<Platform Name="Win32"> <Platform Name="Win32">
<RemoteName>jsonrpcserverwithobjects.exe</RemoteName> <RemoteName>jsonrpcserverwithobjects.exe</RemoteName>
<Overwrite>true</Overwrite> <Overwrite>true</Overwrite>
</Platform> </Platform>
</DeployFile> </DeployFile>
<DeployFile LocalName="Win32\Debug\jsonrpcserverwithobjects.exe" Configuration="Debug" Class="ProjectOutput"/>
<DeployClass Name="AdditionalDebugSymbols"> <DeployClass Name="AdditionalDebugSymbols">
<Platform Name="OSX32"> <Platform Name="OSX32">
<Operation>1</Operation> <Operation>1</Operation>
@ -585,6 +585,127 @@
<Operation>0</Operation> <Operation>0</Operation>
</Platform> </Platform>
</DeployClass> </DeployClass>
<DeployClass Name="ProjectAndroidManifest">
<Platform Name="Android">
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectOSXDebug"/>
<DeployClass Name="ProjectOSXEntitlements"/>
<DeployClass Name="ProjectOSXInfoPList"/>
<DeployClass Name="ProjectOSXResource">
<Platform Name="OSX32">
<RemoteDir>Contents\Resources</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="OSX64">
<RemoteDir>Contents\Resources</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="OSXARM64">
<RemoteDir>Contents\Resources</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Required="true" Name="ProjectOutput">
<Platform Name="Android">
<RemoteDir>library\lib\armeabi-v7a</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>library\lib\arm64-v8a</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<Operation>1</Operation>
</Platform>
<Platform Name="Linux64">
<Operation>1</Operation>
</Platform>
<Platform Name="OSX32">
<Operation>1</Operation>
</Platform>
<Platform Name="OSX64">
<Operation>1</Operation>
</Platform>
<Platform Name="OSXARM64">
<Operation>1</Operation>
</Platform>
<Platform Name="Win32">
<Operation>0</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectOutput_Android32">
<Platform Name="Android64">
<RemoteDir>library\lib\armeabi-v7a</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectUWPManifest">
<Platform Name="Win32">
<Operation>1</Operation>
</Platform>
<Platform Name="Win64">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectiOSDeviceDebug">
<Platform Name="iOSDevice32">
<RemoteDir>..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<RemoteDir>..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<RemoteDir>..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectiOSEntitlements"/>
<DeployClass Name="ProjectiOSInfoPList"/>
<DeployClass Name="ProjectiOSLaunchScreen"/>
<DeployClass Name="ProjectiOSResource">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="UWP_DelphiLogo150">
<Platform Name="Win32">
<RemoteDir>Assets</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Win64">
<RemoteDir>Assets</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="UWP_DelphiLogo44">
<Platform Name="Win32">
<RemoteDir>Assets</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Win64">
<RemoteDir>Assets</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iOS_AppStore1024"> <DeployClass Name="iOS_AppStore1024">
<Platform Name="iOSDevice64"> <Platform Name="iOSDevice64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset</RemoteDir> <RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset</RemoteDir>
@ -785,127 +906,6 @@
<Operation>1</Operation> <Operation>1</Operation>
</Platform> </Platform>
</DeployClass> </DeployClass>
<DeployClass Name="ProjectAndroidManifest">
<Platform Name="Android">
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectiOSDeviceDebug">
<Platform Name="iOSDevice32">
<RemoteDir>..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<RemoteDir>..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<RemoteDir>..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectiOSEntitlements"/>
<DeployClass Name="ProjectiOSInfoPList"/>
<DeployClass Name="ProjectiOSLaunchScreen"/>
<DeployClass Name="ProjectiOSResource">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectOSXDebug"/>
<DeployClass Name="ProjectOSXEntitlements"/>
<DeployClass Name="ProjectOSXInfoPList"/>
<DeployClass Name="ProjectOSXResource">
<Platform Name="OSX32">
<RemoteDir>Contents\Resources</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="OSX64">
<RemoteDir>Contents\Resources</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="OSXARM64">
<RemoteDir>Contents\Resources</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Required="true" Name="ProjectOutput">
<Platform Name="Android">
<RemoteDir>library\lib\armeabi-v7a</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>library\lib\arm64-v8a</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<Operation>1</Operation>
</Platform>
<Platform Name="Linux64">
<Operation>1</Operation>
</Platform>
<Platform Name="OSX32">
<Operation>1</Operation>
</Platform>
<Platform Name="OSX64">
<Operation>1</Operation>
</Platform>
<Platform Name="OSXARM64">
<Operation>1</Operation>
</Platform>
<Platform Name="Win32">
<Operation>0</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectOutput_Android32">
<Platform Name="Android64">
<RemoteDir>library\lib\armeabi-v7a</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectUWPManifest">
<Platform Name="Win32">
<Operation>1</Operation>
</Platform>
<Platform Name="Win64">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="UWP_DelphiLogo150">
<Platform Name="Win32">
<RemoteDir>Assets</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Win64">
<RemoteDir>Assets</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="UWP_DelphiLogo44">
<Platform Name="Win32">
<RemoteDir>Assets</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Win64">
<RemoteDir>Assets</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<ProjectRoot Platform="Android" Name="$(PROJECTNAME)"/> <ProjectRoot Platform="Android" Name="$(PROJECTNAME)"/>
<ProjectRoot Platform="Android64" Name="$(PROJECTNAME)"/> <ProjectRoot Platform="Android64" Name="$(PROJECTNAME)"/>
<ProjectRoot Platform="iOSDevice32" Name="$(PROJECTNAME).app"/> <ProjectRoot Platform="iOSDevice32" Name="$(PROJECTNAME).app"/>

View File

@ -0,0 +1,307 @@
// *************************************************************************** }
//
// Delphi MVC Framework
//
// Copyright (c) 2010-2023 Daniele Teti and the DMVCFramework Team
//
// https://github.com/danieleteti/delphimvcframework
//
// ***************************************************************************
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// ***************************************************************************
unit MVCFramework.DotEnv.Parser;
interface
uses System.Generics.Collections, System.SysUtils;
type
{$SCOPEDENUMS ON}
TMVCDotEnvParserState = (FileThenEnv, EnvThenFile, OnlyFile, OnlyEnv);
TMVCDotEnvDictionary = class(TDictionary<String, String>)
public
constructor Create; virtual;
end;
EMVCDotEnvParser = class(Exception)
end;
TLineBreakStyle = (MSWindows, Linux { MacOS too } );
TStringQuotedStyle = (SingleQuoted, DoublyQuoted, UnQuoted);
{
For Windows, it is CRLF
For UNIX, it is LF
For MAC (up through version 9) it was CR
For MAC OS X, it is LF
https://en.wikipedia.org/wiki/Newline
}
{
https://pypi.org/project/python-dotenv/
}
TMVCDotEnvParser = class
private
fCode: string;
fCurrChar: Char;
fIndex: Integer;
fCurLine: Integer;
fLineBreakStyle: TLineBreakStyle;
fLineBreaksStyle: TLineBreakStyle;
fSavedIndex: Integer;
fCodeLength: Integer;
function MatchIdentifier(out Value: String): Boolean;
function MatchKey(out Token: String): Boolean;
function MatchValue(out Token: String): Boolean;
function MatchSymbol(const Symbol: Char): Boolean;
procedure Check(Value: Boolean; Error: String = '');
function MatchString(out Value: String): Boolean;
procedure EatLineBreaks;
procedure EatUpToLineBreak;
function NextChar: Char;
procedure EatSpaces;
function DetectLineBreakStyle(Code: String): TLineBreakStyle;
procedure MatchInLineComment;
public
constructor Create; virtual;
destructor Destroy; override;
procedure Parse(const EnvDictionay: TMVCDotEnvDictionary; const DotEnvCode: String);
end;
implementation
uses
System.IOUtils,
System.TypInfo,
System.Classes;
const
LINE_BREAKS: array [TLineBreakStyle.MSWindows .. TLineBreakStyle.Linux] of AnsiString = (#13#10, #10);
{ TMVCDotEnvParser }
procedure TMVCDotEnvParser.Check(Value: Boolean; Error: String);
begin
if not Value then
begin
raise EMVCDotEnvParser.CreateFmt('Error: %s - got "%s" at line: %d - index: %d',
[Error, fCurrChar, fCurLine, fIndex - fSavedIndex]);
end;
end;
constructor TMVCDotEnvParser.Create;
begin
inherited;
end;
destructor TMVCDotEnvParser.Destroy;
begin
inherited;
end;
function TMVCDotEnvParser.DetectLineBreakStyle(Code: String): TLineBreakStyle;
begin
if Code.Contains(String(LINE_BREAKS[TLineBreakStyle.MSWindows])) then
Exit(TLineBreakStyle.MSWindows);
if Code.Contains(String(LINE_BREAKS[TLineBreakStyle.Linux])) then
Exit(TLineBreakStyle.Linux);
Result := TLineBreakStyle.MSWindows; // just one line or empty file
end;
procedure TMVCDotEnvParser.EatLineBreaks;
begin
while CharInSet(fCode.Chars[fIndex], [#13, #10]) do
begin
NextChar;
if (fCurrChar = String(LINE_BREAKS[fLineBreakStyle])[1]) then
begin
Inc(fCurLine);
fSavedIndex := fIndex;
end;
end;
end;
procedure TMVCDotEnvParser.EatSpaces;
begin
while CharInSet(fCode.Chars[fIndex], [#32, #9]) do
begin
NextChar;
end;
end;
procedure TMVCDotEnvParser.EatUpToLineBreak;
begin
while not CharInSet(fCode.Chars[fIndex], [#13, #10]) do
begin
NextChar;
end;
end;
procedure TMVCDotEnvParser.MatchInLineComment;
begin
EatSpaces;
if MatchSymbol('#') then
begin
EatUpToLineBreak;
end;
end;
procedure TMVCDotEnvParser.Parse(const EnvDictionay: TMVCDotEnvDictionary; const DotEnvCode: String);
var
lKey: string;
lValue: string;
begin
fCode := DotEnvCode;
fCodeLength := Length(fCode);
fLineBreaksStyle := DetectLineBreakStyle(fCode);
fIndex := -1;
fCurLine := 0;
fSavedIndex := 0;
NextChar;
while fIndex < Length(DotEnvCode) do
begin
EatLineBreaks;
EatSpaces;
if MatchKey(lKey) then
begin
EatSpaces;
Check(MatchSymbol('='), 'Expected "="');
EatSpaces;
Check(MatchValue(lValue), 'Expected "Value"');
EnvDictionay.AddOrSetValue(lKey, lValue);
end
else if fCurrChar = #0 then
begin
Break;
end
else if CharInSet(fCurrChar, [';', '#']) then
begin
EatUpToLineBreak;
EatLineBreaks;
end
else
begin
raise EMVCDotEnvParser.CreateFmt('Unexpected char "%s" at %d', [fCurrChar, fIndex - fSavedIndex]);
end;
end;
end;
function TMVCDotEnvParser.MatchKey(out Token: String): Boolean;
var
lTmp: String;
begin
lTmp := '';
if MatchSymbol('''') then
begin
Check(MatchIdentifier(Token));
Check(MatchSymbol(''''));
Result := True;
end
else
begin
Result := MatchIdentifier(Token);
end;
end;
function TMVCDotEnvParser.MatchSymbol(const Symbol: Char): Boolean;
begin
Result := fCode.Chars[fIndex] = Symbol;
if Result then
begin
NextChar;
end;
end;
function TMVCDotEnvParser.MatchIdentifier(out Value: String): Boolean;
begin
Value := '';
while CharInSet(fCode.Chars[fIndex], ['0' .. '9', 'a' .. 'z', 'A' .. 'Z', '_', '.', ':', '$', '%']) do
begin
Value := Value + fCode.Chars[fIndex];
NextChar;
end;
Result := not Value.IsEmpty;
end;
function TMVCDotEnvParser.MatchString(out Value: String): Boolean;
procedure MatchUpToCharacterSingleLine(out Value: String; const Delimiter1: Char);
begin
while (fIndex < fCodeLength) and (fCode.Chars[fIndex] <> Delimiter1) and
(not CharInSet(fCode.Chars[fIndex], [#13, #10])) do
begin
Value := Value + fCode.Chars[fIndex];
NextChar;
end;
end;
procedure MatchUpToCharacterMultiLine(out Value: String; const Delimiter1: Char);
begin
while (fIndex < fCodeLength) and (fCode.Chars[fIndex] <> Delimiter1) do
begin
Value := Value + fCode.Chars[fIndex];
NextChar;
end;
end;
begin
Value := '';
EatSpaces;
if MatchSymbol('"') then
begin
MatchUpToCharacterMultiLine(Value, '"');
Check(MatchSymbol('"'), 'Expected ''"''');
EatSpaces;
MatchInLineComment;
end
else if MatchSymbol('''') then
begin
MatchUpToCharacterMultiLine(Value, '''');
Check(MatchSymbol(''''), 'Expected ''''');
EatSpaces;
MatchInLineComment;
end
else
begin
MatchUpToCharacterSingleLine(Value, '#');
Value := Value.Trim;
end;
Result := not Value.IsEmpty;
end;
function TMVCDotEnvParser.MatchValue(out Token: String): Boolean;
begin
Result := MatchString(Token);
end;
function TMVCDotEnvParser.NextChar: Char;
begin
Inc(fIndex);
Result := fCode.Chars[fIndex];
fCurrChar := Result;
end;
{ TMVCDotEnvDictionary }
constructor TMVCDotEnvDictionary.Create;
begin
inherited Create;
end;
end.

View File

@ -0,0 +1,326 @@
// *************************************************************************** }
//
// Delphi MVC Framework
//
// Copyright (c) 2010-2023 Daniele Teti and the DMVCFramework Team
//
// https://github.com/danieleteti/delphimvcframework
//
// ***************************************************************************
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// ***************************************************************************
unit MVCFramework.DotEnv;
interface
uses System.SysUtils, System.Generics.Collections, MVCFramework.DotEnv.Parser;
type
{$SCOPEDENUMS ON}
TMVCDotEnvPriority = (FileThenEnv, EnvThenFile, OnlyFile, OnlyEnv);
EMVCDotEnv = class(Exception)
end;
IMVCDotEnv = interface
['{5FD2C3CB-0895-4CCD-985F-27394798E4A8}']
function Env(const Name: string): string; overload;
function SaveToFile(const FileName: String): IMVCDotEnv;
function ToArray(): TArray<String>;
end;
IMVCDotEnvBuilder = interface
['{1A5EDD44-7226-40BC-A8EE-789E27522392}']
function WithStrategy(const Strategy: TMVCDotEnvPriority = TMVCDotEnvPriority.EnvThenFile): IMVCDotEnvBuilder;
function UseProfile(const ProfileName: String): IMVCDotEnvBuilder;
function ClearProfiles: IMVCDotEnvBuilder;
function Build(const DotEnvPath: string = ''): IMVCDotEnv; overload;
end;
function NewDotEnv: IMVCDotEnvBuilder;
implementation
uses
System.IOUtils,
System.TypInfo,
System.Classes;
var
gDotEnv: IMVCDotEnvBuilder = nil;
{ TDotEnv }
type
{$SCOPEDENUMS ON}
TdotEnvEngineState = (created, building, built);
TMVCDotEnv = class(TInterfacedObject, IMVCDotEnv, IMVCDotEnvBuilder)
strict private
fState: TdotEnvEngineState;
fPriority: TMVCDotEnvPriority;
fEnvPath: string;
fEnvDict: TMVCDotEnvDictionary;
fProfiles: TList<String>;
procedure ReadEnvFile;
function GetDotEnvVar(const key: string): string;
function ExplodePlaceholders(const Value: string): string;
procedure PopulateDictionary(const EnvDict: TDictionary<string, string>; const EnvFilePath: String);
procedure CheckAlreadyBuilt;
procedure ExplodeReferences;
strict protected
function WithStrategy(const Priority: TMVCDotEnvPriority = TMVCDotEnvPriority.EnvThenFile): IMVCDotEnvBuilder; overload;
function UseProfile(const ProfileName: String): IMVCDotEnvBuilder;
function ClearProfiles: IMVCDotEnvBuilder;
function Build(const DotEnvDirectory: string = ''): IMVCDotEnv; overload;
function Env(const Name: string): string; overload;
function SaveToFile(const FileName: String): IMVCDotEnv;
function ToArray(): TArray<String>;
public
constructor Create;
destructor Destroy; override;
end;
function TMVCDotEnv.GetDotEnvVar(const key: string): string;
begin
fEnvDict.TryGetValue(key, Result);
end;
function TMVCDotEnv.Env(const Name: string): string;
var
lTmp: String;
begin
if fState = TdotEnvEngineState.created then
begin
raise EMVCDotEnv.Create('dotEnv Engine not built');
end;
if fPriority in [TMVCDotEnvPriority.FileThenEnv, TMVCDotEnvPriority.OnlyFile] then
begin
Result := GetDotEnvVar(name);
if Result.Contains('${' + Name + '}') then
begin
raise EMVCDotEnv.CreateFmt('Configuration loop detected with key "%s"', [Name]);
end;
if fPriority = TMVCDotEnvPriority.OnlyFile then
begin
// OnlyFile
Exit;
end;
// FileThenEnv
if Result.IsEmpty then
begin
Exit(ExplodePlaceholders(GetEnvironmentVariable(Name)));
end;
end
else if fPriority in [TMVCDotEnvPriority.EnvThenFile, TMVCDotEnvPriority.OnlyEnv] then
begin
Result := ExplodePlaceholders(GetEnvironmentVariable(Name));
if fPriority = TMVCDotEnvPriority.OnlyEnv then
begin
// OnlyEnv
Exit;
end;
// EnvThenFile
if Result.IsEmpty then
begin
lTmp := GetDotEnvVar(Name);
if lTmp.Contains('${' + Name + '}') then
begin
raise EMVCDotEnv.CreateFmt('Configuration loop detected with key "%s"', [Name]);
end;
Exit(lTmp);
end;
end
else
begin
raise EMVCDotEnv.CreateFmt('Unknown dotEnv Priority: %s', [GetEnumName(TypeInfo(TMVCDotEnvPriority), Ord(fPriority))]);
end;
end;
function TMVCDotEnv.UseProfile(const ProfileName: String): IMVCDotEnvBuilder;
begin
CheckAlreadyBuilt;
fProfiles.Add(ProfileName);
Result := Self;
end;
function TMVCDotEnv.WithStrategy(const Priority: TMVCDotEnvPriority): IMVCDotEnvBuilder;
begin
CheckAlreadyBuilt;
Result := Self;
fPriority := Priority;
end;
function TMVCDotEnv.Build(const DotEnvDirectory: string): IMVCDotEnv;
begin
if fState <> TdotEnvEngineState.created then
begin
raise EMVCDotEnv.Create('dotEnv engine already built');
end;
fState := TdotEnvEngineState.building;
Result := Self;
fEnvPath := TDirectory.GetParent(GetModuleName(HInstance));
if not DotEnvDirectory.IsEmpty then
begin
fEnvPath := TPath.Combine(fEnvPath, DotEnvDirectory);
end;
fEnvDict.Clear;
ReadEnvFile;
ExplodeReferences;
fState := TdotEnvEngineState.built;
end;
procedure TMVCDotEnv.CheckAlreadyBuilt;
begin
if fState in [TdotEnvEngineState.built] then
begin
raise Exception.Create('DotEnv Engine Already Built');
end;
end;
function TMVCDotEnv.ClearProfiles: IMVCDotEnvBuilder;
begin
CheckAlreadyBuilt;
fProfiles.Clear;
Result := Self;
end;
constructor TMVCDotEnv.Create;
begin
inherited;
fState := TdotEnvEngineState.created;
fProfiles := TList<String>.Create;
fEnvDict := TMVCDotEnvDictionary.Create;
fEnvPath := IncludeTrailingPathDelimiter(ExtractFilePath(ParamStr(0)));
fPriority := TMVCDotEnvPriority.EnvThenFile;
end;
destructor TMVCDotEnv.Destroy;
begin
FreeAndNil(fEnvDict);
fProfiles.Free;
inherited;
end;
function TMVCDotEnv.ExplodePlaceholders(const Value: string): string;
var
lStartPos, lEndPos: Integer;
lKey, lValue: string;
begin
Result := Value;
while Result.IndexOf('${') > -1 do
begin
lStartPos := Result.IndexOf('${');
lEndPos := Result.IndexOf('}');
if (lEndPos = -1) or (lEndPos < lStartPos) then
begin
raise EMVCDotEnv.Create('Unclosed expansion (${...}) at: ' + Value);
end;
lKey := Result.Substring(lStartPos + 2, lEndPos - (lStartPos + 2));
lValue := Env(lKey);
Result := StringReplace(Result, '${' + lKey + '}', lValue, [rfReplaceAll]);
end;
end;
procedure TMVCDotEnv.ExplodeReferences;
var
lKey: String;
begin
for lKey in fEnvDict.Keys do
begin
fEnvDict.AddOrSetValue(lKey, ExplodePlaceholders(fEnvDict[lKey]));
end;
end;
function TMVCDotEnv.SaveToFile(const FileName: String): IMVCDotEnv;
var
lKeys: TArray<String>;
lKey: String;
lSL: TStringList;
begin
lKeys := fEnvDict.Keys.ToArray;
TArray.Sort<String>(lKeys);
lSL := TStringList.Create;
try
for lKey in lKeys do
begin
lSL.Values[lKey] := GetDotEnvVar(lKey);
end;
lSL.SaveToFile(FileName);
finally
lSL.Free;
end;
Result := Self;
end;
function TMVCDotEnv.ToArray: TArray<String>;
var
lKeys: TArray<String>;
lKey: String;
I: Integer;
begin
lKeys := fEnvDict.Keys.ToArray;
TArray.Sort<String>(lKeys);
SetLength(Result, Length(lKeys));
I := 0;
for lKey in lKeys do
begin
Result[I] := lKey + '=' + GetDotEnvVar(lKey);
Inc(I);
end;
end;
procedure TMVCDotEnv.PopulateDictionary(const EnvDict: TDictionary<string, string>; const EnvFilePath: String);
var
lDotEnvCode: string;
lParser: TMVCDotEnvParser;
begin
if not TFile.Exists(EnvFilePath) then
begin
Exit;
end;
lDotEnvCode := TFile.ReadAllText(EnvFilePath);
lParser := TMVCDotEnvParser.Create;
try
lParser.Parse(fEnvDict, lDotEnvCode);
finally
lParser.Free;
end;
end;
procedure TMVCDotEnv.ReadEnvFile;
var
lProfileEnvPath: string;
I: Integer;
begin
PopulateDictionary(fEnvDict, IncludeTrailingPathDelimiter(fEnvPath) + '.env');
for I := 0 to fProfiles.Count - 1 do
begin
lProfileEnvPath := TPath.Combine(fEnvPath, '.env') + '.' + fProfiles[I];
PopulateDictionary(fEnvDict, lProfileEnvPath);
end;
end;
function NewDotEnv: IMVCDotEnvBuilder;
begin
Result := TMVCDotEnv.Create;
end;
end.

View File

@ -1813,6 +1813,7 @@ begin
SetLength(lParamsIsRecord, lParamsCount); SetLength(lParamsIsRecord, lParamsCount);
SetLength(lRecordsPointer, lParamsCount); SetLength(lRecordsPointer, lParamsCount);
SetLength(lParamArrayLength, lParamsCount); SetLength(lParamArrayLength, lParamsCount);
try
// scroll json params and rttimethod params and find the best match // scroll json params and rttimethod params and find the best match
if Assigned(lJSONParams) then if Assigned(lJSONParams) then
begin begin
@ -1862,8 +1863,7 @@ begin
raise EMVCJSONRPCInvalidParams.Create(Ex.Message); raise EMVCJSONRPCInvalidParams.Create(Ex.Message);
end; end;
end; end;
finally
for I := 0 to lParamsCount - 1 do for I := 0 to lParamsCount - 1 do
begin begin
if lParamsArray[I].IsObject then if lParamsArray[I].IsObject then
@ -1872,11 +1872,11 @@ begin
end end
else if lParamsIsRecord[I] then else if lParamsIsRecord[I] then
begin begin
//FinalizeRecord(lRecordsPointer[I], lRTTIMethodParams[I].ParamType.Handle);
FreeMem(lRecordsPointer[I], lRTTIMethodParams[I].ParamType.TypeSize); FreeMem(lRecordsPointer[I], lRTTIMethodParams[I].ParamType.TypeSize);
end; end;
end; end;
end; end;
end;
function TMVCJSONRPCController.JSONObjectAs<T>(const JSON: TJDOJsonObject): T; function TMVCJSONRPCController.JSONObjectAs<T>(const JSON: TJDOJsonObject): T;
begin begin

View File

@ -67,7 +67,7 @@ type
/// <summary> /// <summary>
/// Get the response string, if it is of any type of text. /// Get the response string, if it is of any type of text.
/// </summary> /// </summary>
class function GetResponseContentAsString(aContentRawBytes: TArray<Byte>; const aContentType: string): string; class function GetResponseContentAsString(var aContentRawBytes: TArray<Byte>; const aContentType: string): string;
end; end;
EMVCRESTClientException = class(Exception); EMVCRESTClientException = class(Exception);
@ -75,7 +75,7 @@ type
TMVCRESTClientConsts = record TMVCRESTClientConsts = record
public const public const
DEFAULT_ACCEPT_ENCODING = 'gzip,deflate'; DEFAULT_ACCEPT_ENCODING = 'gzip,deflate';
DEFAULT_ACCEPT = TMVCMediaType.APPLICATION_JSON + ', ' + TMVCMediaType.TEXT_PLAIN + ', ' + TMVCMediaType.TEXT_HTML; DEFAULT_ACCEPT = '*/*';
DEFAULT_USER_AGENT = 'DelphiMVCFramework RESTClient/' + DMVCFRAMEWORK_VERSION; DEFAULT_USER_AGENT = 'DelphiMVCFramework RESTClient/' + DMVCFRAMEWORK_VERSION;
DEFAULT_FILE_NAME = 'file'; DEFAULT_FILE_NAME = 'file';
AUTHORIZATION_HEADER = 'Authorization'; AUTHORIZATION_HEADER = 'Authorization';
@ -211,7 +211,7 @@ begin
end; end;
end; end;
class function TMVCRESTClientHelper.GetResponseContentAsString(aContentRawBytes: TArray<Byte>; class function TMVCRESTClientHelper.GetResponseContentAsString(var aContentRawBytes: TArray<Byte>;
const aContentType: string): string; const aContentType: string): string;
var var
lContentIsString: Boolean; lContentIsString: Boolean;

View File

@ -298,6 +298,10 @@ type
/// </param> /// </param>
function AddFile(const aName, aFileName: string; const aContentType: string = ''): IMVCRESTClient; overload; function AddFile(const aName, aFileName: string; const aContentType: string = ''): IMVCRESTClient; overload;
function AddFile(const aFileName: string; const aContentType: string = ''): IMVCRESTClient; overload; function AddFile(const aFileName: string; const aContentType: string = ''): IMVCRESTClient; overload;
{$IF defined(RIOORBETTER)}
function AddFile(const aName: string; aFileStreamValue: TStream; const aFileName: string = '';
const aContentType: string = ''): IMVCRESTClient; overload;
{$ENDIF}
function AddBodyFieldFormData(const aName, aValue: string): IMVCRESTClient; overload; function AddBodyFieldFormData(const aName, aValue: string): IMVCRESTClient; overload;
{$IF defined(RIOORBETTER)} {$IF defined(RIOORBETTER)}
function AddBodyFieldFormData(const aName: string; aStreamValue: TStream; function AddBodyFieldFormData(const aName: string; aStreamValue: TStream;
@ -477,7 +481,7 @@ type
function Headers: TStrings; function Headers: TStrings;
function HeaderValue(const aName: string): string; function HeaderValue(const aName: string): string;
function Cookies: TCookies; function Cookies: TCookies;
function CookieByName(const aName: string): TCookie; function CookieByName(const aName: string; const RaiseExceptionIfNotFound: Boolean = False): TCookie;
function Server: string; function Server: string;
function ContentType: string; function ContentType: string;
function ContentEncoding: string; function ContentEncoding: string;

View File

@ -46,7 +46,8 @@ uses
MVCFramework.RESTClient.Commons, MVCFramework.RESTClient.Commons,
MVCFramework.Serializer.Intf, MVCFramework.Serializer.Intf,
MVCFramework.Serializer.Commons, MVCFramework.Serializer.Commons,
Data.DB, JsonDataObjects; Data.DB,
JsonDataObjects;
type type
/// <summary> /// <summary>
@ -381,6 +382,10 @@ type
/// </param> /// </param>
function AddFile(const aName, aFileName: string; const aContentType: string = ''): IMVCRESTClient; overload; function AddFile(const aName, aFileName: string; const aContentType: string = ''): IMVCRESTClient; overload;
function AddFile(const aFileName: string; const aContentType: string = ''): IMVCRESTClient; overload; function AddFile(const aFileName: string; const aContentType: string = ''): IMVCRESTClient; overload;
{$IF defined(RIOORBETTER)}
function AddFile(const aName: string; aFileStreamValue: TStream; const aFileName: string = '';
const aContentType: string = ''): IMVCRESTClient; overload;
{$ENDIF}
function AddBodyFieldFormData(const aName, aValue: string): IMVCRESTClient; overload; function AddBodyFieldFormData(const aName, aValue: string): IMVCRESTClient; overload;
{$IF defined(RIOORBETTER)} {$IF defined(RIOORBETTER)}
@ -533,7 +538,7 @@ type
function Headers: TStrings; function Headers: TStrings;
function HeaderValue(const aName: string): string; function HeaderValue(const aName: string): string;
function Cookies: TCookies; function Cookies: TCookies;
function CookieByName(const aName: string): TCookie; function CookieByName(const aName: string; const RaiseExceptionIfNotFound: Boolean = False): TCookie;
function Server: string; function Server: string;
function ContentType: string; function ContentType: string;
function ContentEncoding: string; function ContentEncoding: string;
@ -665,7 +670,7 @@ function TMVCRESTClient.AddBodyFieldFormData(const aName: string; aStreamValue:
const aContentType: string): IMVCRESTClient; const aContentType: string): IMVCRESTClient;
begin begin
Result := Self; Result := Self;
GetBodyFormData.AddStream(aName, aStreamValue, aContentType); GetBodyFormData.AddStream(aName, aStreamValue, '', aContentType);
SetContentType(TMVCMediaType.MULTIPART_FORM_DATA); SetContentType(TMVCMediaType.MULTIPART_FORM_DATA);
end; end;
{$ENDIF} {$ENDIF}
@ -696,6 +701,15 @@ begin
SetContentType(TMVCMediaType.MULTIPART_FORM_DATA); SetContentType(TMVCMediaType.MULTIPART_FORM_DATA);
end; end;
{$IF defined(RIOORBETTER)}
function TMVCRESTClient.AddFile(const aName: string; aFileStreamValue: TStream; const aFileName, aContentType: string): IMVCRESTClient;
begin
Result := Self;
GetBodyFormData.AddStream(aName, aFileStreamValue, aFileName, aContentType);
SetContentType(TMVCMediaType.MULTIPART_FORM_DATA);
end;
{$ENDIF}
function TMVCRESTClient.AddHeader(const aName, aValue: string): IMVCRESTClient; function TMVCRESTClient.AddHeader(const aName, aValue: string): IMVCRESTClient;
begin begin
Result := Self; Result := Self;
@ -1914,7 +1928,7 @@ begin
Result := fContentType; Result := fContentType;
end; end;
function TMVCRESTResponse.CookieByName(const aName: string): TCookie; function TMVCRESTResponse.CookieByName(const aName: string; const RaiseExceptionIfNotFound: Boolean): TCookie;
var var
lCookie: TCookie; lCookie: TCookie;
begin begin
@ -1924,6 +1938,11 @@ begin
if SameText(lCookie.Name, aName) then if SameText(lCookie.Name, aName) then
Exit(lCookie); Exit(lCookie);
end; end;
if RaiseExceptionIfNotFound then
begin
raise EMVCRESTClientException.CreateFmt('Cookie "%s" not found', [aName]);
end;
end; end;
function TMVCRESTResponse.Cookies: TCookies; function TMVCRESTResponse.Cookies: TCookies;

View File

@ -232,16 +232,12 @@ begin
inherited Destroy; inherited Destroy;
end; end;
// class procedure TMVCSessionFactory.DestroyInstance;
// begin
// if Assigned(cInstance) then
// cInstance.Free;
// end;
class function TMVCSessionFactory.GetInstance: TMVCSessionFactory; class function TMVCSessionFactory.GetInstance: TMVCSessionFactory;
begin begin
if not Assigned(cInstance) then if not Assigned(cInstance) then
begin
cInstance := TMVCSessionFactory.Create; cInstance := TMVCSessionFactory.Create;
end;
Result := cInstance; Result := cInstance;
end; end;

View File

@ -12,7 +12,6 @@ uses
DUnitX.TestFramework, DUnitX.TestFramework,
{$IFDEF CONSOLE_TESTRUNNER} {$IFDEF CONSOLE_TESTRUNNER}
DUnitX.Loggers.Console, DUnitX.Loggers.Console,
DUnitX.Loggers.XML.NUnit,
{$ENDIF } {$ENDIF }
{$IFDEF TESTINSIGHT} {$IFDEF TESTINSIGHT}
TestInsight.DUnitX, TestInsight.DUnitX,
@ -73,7 +72,9 @@ uses
EntitiesProcessors in 'EntitiesProcessors.pas', EntitiesProcessors in 'EntitiesProcessors.pas',
MVCFramework.Nullables in '..\..\..\sources\MVCFramework.Nullables.pas', MVCFramework.Nullables in '..\..\..\sources\MVCFramework.Nullables.pas',
IntfObjectPoolTestU in 'IntfObjectPoolTestU.pas', IntfObjectPoolTestU in 'IntfObjectPoolTestU.pas',
ObjectPoolTestU in 'ObjectPoolTestU.pas'; ObjectPoolTestU in 'ObjectPoolTestU.pas',
MVCFramework.DotEnv.Parser in '..\..\..\sources\MVCFramework.DotEnv.Parser.pas',
MVCFramework.DotEnv in '..\..\..\sources\MVCFramework.DotEnv.pas';
{$R *.RES} {$R *.RES}

View File

@ -183,8 +183,7 @@
<PropertyGroup Condition="'$(Cfg_2_Win64)'!=''"> <PropertyGroup Condition="'$(Cfg_2_Win64)'!=''">
<VerInfo_Locale>1033</VerInfo_Locale> <VerInfo_Locale>1033</VerInfo_Locale>
<VerInfo_Keys>CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=</VerInfo_Keys> <VerInfo_Keys>CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=</VerInfo_Keys>
<Manifest_File>(None)</Manifest_File> <DCC_DebugDCUs>false</DCC_DebugDCUs>
<AppDPIAwarenessMode>none</AppDPIAwarenessMode>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<DelphiCompile Include="$(MainSource)"> <DelphiCompile Include="$(MainSource)">
@ -259,6 +258,8 @@
<DCCReference Include="..\..\..\sources\MVCFramework.Nullables.pas"/> <DCCReference Include="..\..\..\sources\MVCFramework.Nullables.pas"/>
<DCCReference Include="IntfObjectPoolTestU.pas"/> <DCCReference Include="IntfObjectPoolTestU.pas"/>
<DCCReference Include="ObjectPoolTestU.pas"/> <DCCReference Include="ObjectPoolTestU.pas"/>
<DCCReference Include="..\..\..\sources\MVCFramework.DotEnv.Parser.pas"/>
<DCCReference Include="..\..\..\sources\MVCFramework.DotEnv.pas"/>
<BuildConfiguration Include="Base"> <BuildConfiguration Include="Base">
<Key>Base</Key> <Key>Base</Key>
</BuildConfiguration> </BuildConfiguration>
@ -342,6 +343,8 @@
<Source Name="MainSource">DMVCFrameworkTests.dpr</Source> <Source Name="MainSource">DMVCFrameworkTests.dpr</Source>
</Source> </Source>
<Excluded_Packages> <Excluded_Packages>
<Excluded_Packages Name="$(BDSBIN)\bcboffice2k280.bpl">Embarcadero C++Builder Office 2000 Servers Package</Excluded_Packages>
<Excluded_Packages Name="$(BDSBIN)\bcbofficexp280.bpl">Embarcadero C++Builder Office XP Servers Package</Excluded_Packages>
<Excluded_Packages Name="$(BDSBIN)\dcloffice2k280.bpl">Microsoft Office 2000 Sample Automation Server Wrapper Components</Excluded_Packages> <Excluded_Packages Name="$(BDSBIN)\dcloffice2k280.bpl">Microsoft Office 2000 Sample Automation Server Wrapper Components</Excluded_Packages>
<Excluded_Packages Name="$(BDSBIN)\dclofficexp280.bpl">Microsoft Office XP Sample Automation Server Wrapper Components</Excluded_Packages> <Excluded_Packages Name="$(BDSBIN)\dclofficexp280.bpl">Microsoft Office XP Sample Automation Server Wrapper Components</Excluded_Packages>
</Excluded_Packages> </Excluded_Packages>

View File

@ -253,6 +253,34 @@ type
procedure TestPutGet_Check_No_AV; procedure TestPutGet_Check_No_AV;
end; end;
[TestFixture]
TTestDotEnv = class(TObject)
public
[Test]
procedure TestWithoutProfiles;
[Test]
procedure TestWithDevProfile;
[Test]
procedure TestWithDevAndTestProfile;
end;
[TestFixture]
TTestDotEnvParser = class(TObject)
public
[Test]
procedure TestKeyValue;
[Test]
procedure TestKeyValueWithQuotedValues;
[Test]
procedure TestValueWithMultiline;
[Test]
procedure TestVarPlaceHolders;
[Test]
procedure TestInLineComments;
end;
implementation implementation
{$WARN SYMBOL_DEPRECATED OFF} {$WARN SYMBOL_DEPRECATED OFF}
@ -275,7 +303,8 @@ uses
{$ENDIF} {$ENDIF}
TestServerControllerU, System.Classes, TestServerControllerU, System.Classes,
MVCFramework.DuckTyping, System.IOUtils, MVCFramework.SystemJSONUtils, MVCFramework.DuckTyping, System.IOUtils, MVCFramework.SystemJSONUtils,
IdGlobal, System.TypInfo, System.Types, Winapi.Windows; IdGlobal, System.TypInfo, System.Types, Winapi.Windows, MVCFramework.DotEnv,
MVCFramework.DotEnv.Parser;
var var
JWT_SECRET_KEY_TEST: string = 'myk3y'; JWT_SECRET_KEY_TEST: string = 'myk3y';
@ -2101,6 +2130,189 @@ begin
end; end;
end; end;
{ TTestDotEnv }
function Are2FilesEqual(const File1, File2: TFileName): Boolean;
var
ms1, ms2: TMemoryStream;
begin
Result := False;
ms1 := TMemoryStream.Create;
try
ms1.LoadFromFile(File1);
ms2 := TMemoryStream.Create;
try
ms2.LoadFromFile(File2);
if ms1.Size = ms2.Size then
begin
Result := CompareMem(ms1.Memory, ms2.memory, ms1.Size);
end;
finally
ms2.Free;
end;
finally
ms1.Free;
end
end;
procedure TTestDotEnv.TestWithDevAndTestProfile;
var
lDotEnv: IMVCDotEnv;
begin
lDotEnv := NewDotEnv.UseProfile('dev').UseProfile('test').Build('..\dotEnv');
lDotEnv.SaveToFile('..\dotEnv\dotEnvDump-profile-dev-and-test.test.txt');
Assert.IsTrue(Are2FilesEqual('..\dotEnv\dotEnvDump-profile-dev-and-test.correct.txt','..\dotEnv\dotEnvDump-profile-dev-and-test.test.txt'), 'Files are different');
end;
procedure TTestDotEnv.TestWithDevProfile;
var
lDotEnv: IMVCDotEnv;
begin
lDotEnv := NewDotEnv.UseProfile('dev').Build('..\dotEnv');
lDotEnv.SaveToFile('..\dotEnv\dotEnvDump-profile-dev.test.txt');
Assert.IsTrue(Are2FilesEqual('..\dotEnv\dotEnvDump-profile-dev.correct.txt','..\dotEnv\dotEnvDump-profile-dev.test.txt'), 'Files are different');
end;
procedure TTestDotEnv.TestWithoutProfiles;
var
lDotEnv: IMVCDotEnv;
begin
lDotEnv := NewDotEnv.Build('..\dotEnv');
lDotEnv.SaveToFile('..\dotEnv\dotEnvDump-noprofile.test.txt');
Assert.IsTrue(Are2FilesEqual('..\dotEnv\dotEnvDump-noprofile.correct.txt','..\dotEnv\dotEnvDump-noprofile.test.txt'), 'Files are different');
end;
{ TTestDotEnvParser }
procedure TTestDotEnvParser.TestInLineComments;
const
DOTENVCODE =
'#comment1' + sLineBreak +
'#comment2' + sLineBreak +
'key1= "value1" #inline comment' + sLineBreak +
';comment3' + sLineBreak +
'key2 = ''value2'' #inline comment' + sLineBreak +
';comment' + sLineBreak +
'key3 = value3 #inline comment' + sLineBreak +
'key4 = " value4 " #inline comment' + sLineBreak +
';commentX';
begin
var lParser := TMVCDotEnvParser.Create;
try
var lDict := TMVCDotEnvDictionary.Create();
try
lParser.Parse(lDict, DOTENVCODE);
Assert.AreEqual('value1', lDict['key1']);
Assert.AreEqual('value2', lDict['key2']);
Assert.AreEqual('value3', lDict['key3']);
Assert.AreEqual(' value4 ', lDict['key4']);
finally
lDict.Free;
end;
finally
lParser.Free;
end;
end;
procedure TTestDotEnvParser.TestKeyValue;
const
DOTENVCODE = 'key1=value1' + sLineBreak + 'key2 = value2 with another value' + sLineBreak;
begin
var lParser := TMVCDotEnvParser.Create;
try
var lDict := TMVCDotEnvDictionary.Create();
try
lParser.Parse(lDict, DOTENVCODE);
Assert.AreEqual('value1', lDict['key1']);
Assert.AreEqual('value2 with another value', lDict['key2']);
finally
lDict.Free;
end;
finally
lParser.Free;
end;
end;
procedure TTestDotEnvParser.TestKeyValueWithQuotedValues;
const
DOTENVCODE =
'key1= "value1"' + sLineBreak +
'key2 = ''value2''' + sLineBreak +
'key3 = "uno''due''"' + sLineBreak +
'key4 = ''uno"due"''' + sLineBreak;
begin
var lParser := TMVCDotEnvParser.Create;
try
var lDict := TMVCDotEnvDictionary.Create();
try
lParser.Parse(lDict, DOTENVCODE);
Assert.AreEqual('value1', lDict['key1']);
Assert.AreEqual('value2', lDict['key2']);
Assert.AreEqual('uno''due''', lDict['key3']);
Assert.AreEqual('uno"due"', lDict['key4']);
finally
lDict.Free;
end;
finally
lParser.Free;
end;
end;
procedure TTestDotEnvParser.TestValueWithMultiline;
const
DOTENVCODE =
'key1= "value1' + sLineBreak +
'value2' + sLineBreak +
'value3" # comment' + sLineBreak +
'key2 = value2' + sLineBreak;
begin
var lParser := TMVCDotEnvParser.Create;
try
var lDict := TMVCDotEnvDictionary.Create();
try
lParser.Parse(lDict, DOTENVCODE);
Assert.AreEqual('value1' + slinebreak + 'value2' + sLineBreak + 'value3', lDict['key1']);
Assert.AreEqual('value2', lDict['key2']);
finally
lDict.Free;
end;
finally
lParser.Free;
end;
end;
procedure TTestDotEnvParser.TestVarPlaceHolders;
const
DOTENVCODE =
'#comment1' + sLineBreak +
'#comment2' + sLineBreak +
'key1= "value1"' + sLineBreak +
';comment3' + sLineBreak +
'key2 = ''value2''' + sLineBreak +
';comment' + sLineBreak +
'key3 = |${key1}|${key2}|' + sLineBreak +
'key4 = value4' + sLineBreak +
';commentX';
begin
var lParser := TMVCDotEnvParser.Create;
try
var lDict := TMVCDotEnvDictionary.Create();
try
lParser.Parse(lDict, DOTENVCODE);
Assert.AreEqual('value1', lDict['key1']);
Assert.AreEqual('value2', lDict['key2']);
Assert.AreEqual('|${key1}|${key2}|', lDict['key3']);
Assert.AreEqual('value4', lDict['key4']);
finally
lDict.Free;
end;
finally
lParser.Free;
end;
end;
initialization initialization
TDUnitX.RegisterTestFixture(TTestRouting); TDUnitX.RegisterTestFixture(TTestRouting);
@ -2110,6 +2322,8 @@ TDUnitX.RegisterTestFixture(TTestMultiMap);
TDUnitX.RegisterTestFixture(TTestNameCase); TDUnitX.RegisterTestFixture(TTestNameCase);
TDUnitX.RegisterTestFixture(TTestCryptUtils); TDUnitX.RegisterTestFixture(TTestCryptUtils);
TDUnitX.RegisterTestFixture(TTestLRUCache); TDUnitX.RegisterTestFixture(TTestLRUCache);
TDUnitX.RegisterTestFixture(TTestDotEnv);
TDUnitX.RegisterTestFixture(TTestDotEnvParser);
finalization finalization

View File

@ -348,6 +348,9 @@ type
// objects tests // objects tests
[Test] [Test]
procedure TestRequestWithObjectParameters; procedure TestRequestWithObjectParameters;
// exception tests
[Test]
procedure TestRequestWithException;
// hooks tests // hooks tests
[Test] [Test]
procedure TestHooks; procedure TestHooks;
@ -683,7 +686,7 @@ procedure TServerTest.TestControllerWithExceptionInCreate(const URLSegment: stri
var var
res: IMVCRESTResponse; res: IMVCRESTResponse;
begin begin
res := RESTClient.Get(URLSegment); res := RESTClient.Accept(TMVCMediaType.APPLICATION_JSON).Get(URLSegment);
Assert.areEqual(HTTP_STATUS.InternalServerError, res.StatusCode); Assert.areEqual(HTTP_STATUS.InternalServerError, res.StatusCode);
// Assert.Contains(res.ContentType, 'text/plain', true, 'Is not a text/plain in case of error'); // Assert.Contains(res.ContentType, 'text/plain', true, 'Is not a text/plain in case of error');
Assert.Contains(res.ContentType, 'application/json', true, Assert.Contains(res.ContentType, 'application/json', true,
@ -2490,13 +2493,21 @@ var
c1: IMVCRESTClient; c1: IMVCRESTClient;
res: IMVCRESTResponse; res: IMVCRESTResponse;
S: string; S: string;
lCookie: TCookie;
begin begin
c1 := TMVCRESTClient.New.BaseURL(TEST_SERVER_ADDRESS, 9999); c1 := TMVCRESTClient
c1.Accept(TMVCMediaType.APPLICATION_JSON); .New
.BaseURL(TEST_SERVER_ADDRESS, 9999)
.Accept(TMVCMediaType.APPLICATION_JSON);
res := c1.Post('/session/daniele teti'); // imposto un valore in sessione res := c1.Post('/session/daniele teti'); // imposto un valore in sessione
S := res.HeaderValue('Set-Cookie'); Assert.IsTrue(res.Cookies.Count > 0);
Assert.IsFalse(S.Contains('Expires'), 'Session cookie contains "expires" attribute'); lCookie := res.CookieByName('dtsessionid', True);
res := c1.Get('/session'); // rileggo il valore dalla sessione Assert.AreEqual('dtsessionid', lCookie.Name);
// Assert.IsFalse(S.Contains('Expires'), 'Session cookie contains "expires" attribute');
res := c1
.AddCookie('dtsessionid', lCookie.Value)
.Get('/session'); // rileggo il valore dalla sessione
S := res.Content;
Assert.areEqual('daniele teti', res.Content); Assert.areEqual('daniele teti', res.Content);
c1.Accept(TMVCMediaType.TEXT_PLAIN); c1.Accept(TMVCMediaType.TEXT_PLAIN);
res := c1.Get('/session'); res := c1.Get('/session');
@ -3077,6 +3088,22 @@ begin
Assert.Contains(lRPCResp.Error.ErrMessage, 'cannot find parameter', true); Assert.Contains(lRPCResp.Error.ErrMessage, 'cannot find parameter', true);
end; end;
procedure TJSONRPCServerTest.TestRequestWithException;
var
lReq: IJSONRPCRequest;
lResp: IJSONRPCResponse;
lPersSrc: TPerson;
begin
lReq := TJSONRPCRequest.Create;
lReq.Method := 'DoError';
lPersSrc := TPerson.GetNew('Daniele','Teti', EncodeDate(1979,12,1), True);
lReq.Params.AddByName('MyObj', lPersSrc);
lReq.RequestID := 1;
lResp := FExecutor2.ExecuteRequest(lReq);
Assert.IsTrue(lResp.IsError);
Assert.AreEqual('BOOOM!! (TTestJSONRPCClass.DoError)', lResp.Error.ErrMessage);
end;
procedure TJSONRPCServerTest.TestRequestWithNamedParams_I_I_I_ret_O; procedure TJSONRPCServerTest.TestRequestWithNamedParams_I_I_I_ret_O;
var var
lReq: IJSONRPCRequest; lReq: IJSONRPCRequest;

View File

@ -0,0 +1,14 @@
key1=value1
key2= value2
key3 = value3 #incline comment
key4 = value4
;comment1
key2.1= "value2.1"
key2.2= 'value2.2'
key2.3 = 'value2.3'
key2.4 = '${key2.1}|${key2.2}|${key2.3}|${key7}'
#comment2
key5=${key6}
key6=${key3}
key7=${key6}

View File

@ -0,0 +1,5 @@
key1=devvalue1
key2= devvalue2
key3 = devvalue3 #incline comment
key7=${key2.1}
mode=dev

View File

@ -0,0 +1,2 @@
key1=testvalue1
mode=test

View File

@ -0,0 +1,11 @@
key1=value1
key2=value2
key2.1=value2.1
key2.2=value2.2
key2.3=value2.3
key2.4=value2.1|value2.2|value2.3|value3
key3=value3
key4=value4
key5=value3
key6=value3
key7=value3

View File

@ -0,0 +1,12 @@
key1=testvalue1
key2=devvalue2
key2.1=value2.1
key2.2=value2.2
key2.3=value2.3
key2.4=value2.1|value2.2|value2.3|value2.1
key3=devvalue3
key4=value4
key5=devvalue3
key6=devvalue3
key7=value2.1
mode=test

View File

@ -0,0 +1,12 @@
key1=devvalue1
key2=devvalue2
key2.1=value2.1
key2.2=value2.2
key2.3=value2.3
key2.4=value2.1|value2.2|value2.3|value2.1
key3=devvalue3
key4=value4
key5=devvalue3
key6=devvalue3
key7=value2.1
mode=dev

View File

@ -2,7 +2,7 @@
// //
// Delphi MVC Framework // Delphi MVC Framework
// //
// Copyright (c) 2010-2017 Daniele Teti and the DMVCFramework Team // Copyright (c) 2010-2023 Daniele Teti and the DMVCFramework Team
// //
// https://github.com/danieleteti/delphimvcframework // https://github.com/danieleteti/delphimvcframework
// //

View File

@ -2,7 +2,7 @@
// //
// Delphi MVC Framework // Delphi MVC Framework
// //
// Copyright (c) 2010-2017 Daniele Teti and the DMVCFramework Team // Copyright (c) 2010-2023 Daniele Teti and the DMVCFramework Team
// //
// https://github.com/danieleteti/delphimvcframework // https://github.com/danieleteti/delphimvcframework
// //

View File

@ -119,6 +119,7 @@
<Icon_MainIcon>TestServer_Icon.ico</Icon_MainIcon> <Icon_MainIcon>TestServer_Icon.ico</Icon_MainIcon>
<Manifest_File>(None)</Manifest_File> <Manifest_File>(None)</Manifest_File>
<AppDPIAwarenessMode>none</AppDPIAwarenessMode> <AppDPIAwarenessMode>none</AppDPIAwarenessMode>
<DCC_DebugDCUs>true</DCC_DebugDCUs>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<DelphiCompile Include="$(MainSource)"> <DelphiCompile Include="$(MainSource)">
@ -159,6 +160,8 @@
<Source Name="MainSource">TestServer.dpr</Source> <Source Name="MainSource">TestServer.dpr</Source>
</Source> </Source>
<Excluded_Packages> <Excluded_Packages>
<Excluded_Packages Name="$(BDSBIN)\bcboffice2k280.bpl">Embarcadero C++Builder Office 2000 Servers Package</Excluded_Packages>
<Excluded_Packages Name="$(BDSBIN)\bcbofficexp280.bpl">Embarcadero C++Builder Office XP Servers Package</Excluded_Packages>
<Excluded_Packages Name="$(BDSBIN)\dcloffice2k280.bpl">Microsoft Office 2000 Sample Automation Server Wrapper Components</Excluded_Packages> <Excluded_Packages Name="$(BDSBIN)\dcloffice2k280.bpl">Microsoft Office 2000 Sample Automation Server Wrapper Components</Excluded_Packages>
<Excluded_Packages Name="$(BDSBIN)\dclofficexp280.bpl">Microsoft Office XP Sample Automation Server Wrapper Components</Excluded_Packages> <Excluded_Packages Name="$(BDSBIN)\dclofficexp280.bpl">Microsoft Office XP Sample Automation Server Wrapper Components</Excluded_Packages>
</Excluded_Packages> </Excluded_Packages>

View File

@ -2,7 +2,7 @@
// //
// Delphi MVC Framework // Delphi MVC Framework
// //
// Copyright (c) 2010-2017 Daniele Teti and the DMVCFramework Team // Copyright (c) 2010-2023 Daniele Teti and the DMVCFramework Team
// //
// https://github.com/danieleteti/delphimvcframework // https://github.com/danieleteti/delphimvcframework
// //

View File

@ -1,3 +1,27 @@
// ***************************************************************************
//
// Delphi MVC Framework
//
// Copyright (c) 2010-2023 Daniele Teti and the DMVCFramework Team
//
// https://github.com/danieleteti/delphimvcframework
//
// ***************************************************************************
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// *************************************************************************** }
unit TestServerControllerJSONRPCU; unit TestServerControllerJSONRPCU;
interface interface
@ -44,6 +68,10 @@ type
[MVCInheritable] [MVCInheritable]
function AddTimeToDateTime(aDateTime: TDateTime; aTime: TTime): TDateTime; function AddTimeToDateTime(aDateTime: TDateTime; aTime: TTime): TDateTime;
//exceptions
[MVCInheritable]
function DoError(MyObj: TPerson): TPerson;
//objects support //objects support
[MVCInheritable] [MVCInheritable]
function HandlingObjects(MyObj: TPerson): TPerson; function HandlingObjects(MyObj: TPerson): TPerson;
@ -204,6 +232,11 @@ begin
Result := aDateTime + aTime; Result := aDateTime + aTime;
end; end;
function TTestJSONRPCClass.DoError(MyObj: TPerson): TPerson;
begin
raise Exception.Create('BOOOM!! (TTestJSONRPCClass.DoError)');
end;
function TTestJSONRPCClass.EchoArrayOfRecords( function TTestJSONRPCClass.EchoArrayOfRecords(
const ComplexRecordArray: TComplexRecordArray): TComplexRecordArray; const ComplexRecordArray: TComplexRecordArray): TComplexRecordArray;
begin begin

View File

@ -2,7 +2,7 @@
// //
// Delphi MVC Framework // Delphi MVC Framework
// //
// Copyright (c) 2010-2017 Daniele Teti and the DMVCFramework Team // Copyright (c) 2010-2023 Daniele Teti and the DMVCFramework Team
// //
// https://github.com/danieleteti/delphimvcframework // https://github.com/danieleteti/delphimvcframework
// //

View File

@ -759,7 +759,7 @@ procedure TTestServerController.SessionGet;
var var
s: string; s: string;
begin begin
ContentType := Context.Request.Accept; ContentType := TMVCMediaType.TEXT_PLAIN;
s := Session['value']; s := Session['value'];
Render(s); Render(s);
end; end;