Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 282ae6fa28 | |||
| 7cc7705b29 | |||
| 8e820eb2f3 | |||
| e576805e84 | |||
| b260f184f8 |
15
README.md
15
README.md
@@ -113,6 +113,21 @@ Once the first none-empty line is unrecognized (no command found), the parser wi
|
|||||||
: Sets authentication for the HTTP proxy.
|
: Sets authentication for the HTTP proxy.
|
||||||
Same syntax as `BasicAuth`.
|
Same syntax as `BasicAuth`.
|
||||||
|
|
||||||
|
`Generate <variablename> <generator>`
|
||||||
|
: Sets a variable with a generated value.
|
||||||
|
Possible generators:
|
||||||
|
* `UUID`
|
||||||
|
* `unixtime` (a unix timestamp)
|
||||||
|
* `localtime` (a formatted time)
|
||||||
|
* `isodatetime` (Date and Time according to ISO8601)
|
||||||
|
* `isodate` (Date according to ISO8601)
|
||||||
|
* `isotime` (Time according to ISO8601)
|
||||||
|
|
||||||
|
`Expect <expectation type>=<expecation>`
|
||||||
|
: Checks expectations after the request.
|
||||||
|
* `Status=<statuscode>`: checks, if the status code matches
|
||||||
|
If an expectation fails, the application exists with code 5.
|
||||||
|
|
||||||
`Call <URL>`
|
`Call <URL>`
|
||||||
: This prepares the actual call by providing the URL to be called.
|
: This prepares the actual call by providing the URL to be called.
|
||||||
Variables in the form of `@<variablename>` are replaced accordingly.
|
Variables in the form of `@<variablename>` are replaced accordingly.
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ type
|
|||||||
FFormFields: TStrings;
|
FFormFields: TStrings;
|
||||||
FFilters: TFilterList;
|
FFilters: TFilterList;
|
||||||
FBeautify: Boolean;
|
FBeautify: Boolean;
|
||||||
|
FExpectations: TStringList;
|
||||||
FURL: String;
|
FURL: String;
|
||||||
FMethod: String;
|
FMethod: String;
|
||||||
//Main
|
//Main
|
||||||
@@ -69,11 +70,14 @@ type
|
|||||||
procedure CmdFormField(AData: String);
|
procedure CmdFormField(AData: String);
|
||||||
procedure CmdProxy(AData: String);
|
procedure CmdProxy(AData: String);
|
||||||
procedure CmdProxyAuth(AData: String);
|
procedure CmdProxyAuth(AData: String);
|
||||||
|
procedure CmdGenerate(AData: String);
|
||||||
|
procedure CmdExpect(AData: String);
|
||||||
procedure ProcessCall(AURL: String);
|
procedure ProcessCall(AURL: String);
|
||||||
//Helper
|
//Helper
|
||||||
procedure WriteHelp;
|
procedure WriteHelp;
|
||||||
procedure ListTemplates;
|
procedure ListTemplates;
|
||||||
procedure WriteContent;
|
procedure WriteContent;
|
||||||
|
procedure CheckExpectations;
|
||||||
public
|
public
|
||||||
constructor Create; overload;
|
constructor Create; overload;
|
||||||
destructor Destroy; override;
|
destructor Destroy; override;
|
||||||
@@ -83,7 +87,7 @@ implementation
|
|||||||
|
|
||||||
uses
|
uses
|
||||||
strutils, crt, UCRTHelper, fpjson, DOM, XMLRead, XMLWrite, vinfo,
|
strutils, crt, UCRTHelper, fpjson, DOM, XMLRead, XMLWrite, vinfo,
|
||||||
IdCompressorZLib, IdHeaderList, IdGlobalProtocols;
|
IdCompressorZLib, IdHeaderList, IdGlobalProtocols, xmlxsdparser;
|
||||||
|
|
||||||
type
|
type
|
||||||
TContentType = (ctOther, ctJSON, ctXML);
|
TContentType = (ctOther, ctJSON, ctXML);
|
||||||
@@ -165,8 +169,8 @@ begin
|
|||||||
|
|
||||||
if FileExists(ParamStr(ParamCount)) then
|
if FileExists(ParamStr(ParamCount)) then
|
||||||
begin
|
begin
|
||||||
AssignFile(data, ParamStr(1));
|
AssignFile(data, ParamStr(ParamCount));
|
||||||
FTemplateName := ExtractFileName(ParamStr(1));
|
FTemplateName := ExtractFileName(ParamStr(ParamCount));
|
||||||
if AnsiEndsStr('.rest', FTemplateName) then
|
if AnsiEndsStr('.rest', FTemplateName) then
|
||||||
FTemplateName := Copy(FTemplateName, 1, Length(FTemplateName) - 5);
|
FTemplateName := Copy(FTemplateName, 1, Length(FTemplateName) - 5);
|
||||||
end else
|
end else
|
||||||
@@ -267,6 +271,16 @@ begin
|
|||||||
Result := True;
|
Result := True;
|
||||||
CmdProxy(Copy(ALine, 10, Length(ALine)));
|
CmdProxy(Copy(ALine, 10, Length(ALine)));
|
||||||
end else
|
end else
|
||||||
|
if AnsiStartsStr('Generate ', ALine) then
|
||||||
|
begin
|
||||||
|
Result := True;
|
||||||
|
CmdGenerate(Copy(ALine, 10, Length(ALine)));
|
||||||
|
end else
|
||||||
|
if AnsiStartsStr('Expect ', ALine) then
|
||||||
|
begin
|
||||||
|
Result := True;
|
||||||
|
CmdExpect(Copy(ALine, 8, Length(ALine)));
|
||||||
|
end else
|
||||||
if AnsiStartsStr('Call ', ALine) then
|
if AnsiStartsStr('Call ', ALine) then
|
||||||
begin
|
begin
|
||||||
Result := True;
|
Result := True;
|
||||||
@@ -289,6 +303,13 @@ begin
|
|||||||
|
|
||||||
AName := Copy(AName, 2, Length(AName));
|
AName := Copy(AName, 2, Length(AName));
|
||||||
|
|
||||||
|
//Check if the variable has been set on the commandline; we do not
|
||||||
|
//have to ask the user then.
|
||||||
|
if HasOption('var:' + AName) then
|
||||||
|
begin
|
||||||
|
value := GetOptionValue('var:' + AName);
|
||||||
|
end else
|
||||||
|
begin
|
||||||
if mode = pmNormal then
|
if mode = pmNormal then
|
||||||
begin
|
begin
|
||||||
default := FSessionIni.ReadString(FTemplateName, AName, '');
|
default := FSessionIni.ReadString(FTemplateName, AName, '');
|
||||||
@@ -300,6 +321,7 @@ begin
|
|||||||
|
|
||||||
if value = '' then
|
if value = '' then
|
||||||
Halt(3); //Cancelled
|
Halt(3); //Cancelled
|
||||||
|
end;
|
||||||
|
|
||||||
FParser.Fields.Add(AName, value);
|
FParser.Fields.Add(AName, value);
|
||||||
|
|
||||||
@@ -331,6 +353,10 @@ var
|
|||||||
separator: Char;
|
separator: Char;
|
||||||
i: Integer;
|
i: Integer;
|
||||||
begin
|
begin
|
||||||
|
FParser.Content := AData;
|
||||||
|
FParser.Replace;
|
||||||
|
AData := FParser.Content;
|
||||||
|
|
||||||
separator := AData[1];
|
separator := AData[1];
|
||||||
i := 2;
|
i := 2;
|
||||||
while (i < Length(AData)) and (AData[i] <> separator) do
|
while (i < Length(AData)) and (AData[i] <> separator) do
|
||||||
@@ -379,19 +405,60 @@ begin
|
|||||||
FHttp.ProxyParams.ProxyPassword := Copy(AData, i + 1, Length(AData));
|
FHttp.ProxyParams.ProxyPassword := Copy(AData, i + 1, Length(AData));
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure TRestemplateApplication.CmdGenerate(AData: String);
|
||||||
|
var
|
||||||
|
i: Integer;
|
||||||
|
varName, generator: String;
|
||||||
|
guid: TGuid;
|
||||||
|
begin
|
||||||
|
i := 1;
|
||||||
|
while (i < Length(AData)) and (AData[i] <> ' ') do
|
||||||
|
Inc(i);
|
||||||
|
|
||||||
|
varName := Copy(AData, 1, i - 1);
|
||||||
|
generator := LowerCase(Copy(AData, i + 1, Length(AData)));
|
||||||
|
|
||||||
|
case generator of
|
||||||
|
'uuid':
|
||||||
|
begin
|
||||||
|
CreateGUID(guid);
|
||||||
|
FParser.Fields.Add(varName, Copy(GUIDToString(guid), 2, 36));
|
||||||
|
end;
|
||||||
|
'unixtime': FParser.Fields.Add(varName, IntToStr(DateTimeToUnix(Now)));
|
||||||
|
'localtime': FParser.Fields.Add(varName, DateTimeToStr(Now));
|
||||||
|
'isodatetime': FParser.Fields.Add(varName, xsdFormatDateTime(Now, nil));
|
||||||
|
'isodate': FParser.Fields.Add(varName, xsdFormatDate(Now));
|
||||||
|
'isotime': FParser.Fields.Add(varName, xsdFormatTime(Now));
|
||||||
|
else
|
||||||
|
raise Exception.Create('Unknown generator: ' + generator);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TRestemplateApplication.CmdExpect(AData: String);
|
||||||
|
begin
|
||||||
|
FExpectations.Add(AData);
|
||||||
|
end;
|
||||||
|
|
||||||
procedure TRestemplateApplication.ProcessCall(AURL: String);
|
procedure TRestemplateApplication.ProcessCall(AURL: String);
|
||||||
var
|
var
|
||||||
s: String;
|
s, httpMethod: String;
|
||||||
request, response: TStream;
|
request, response: TStream;
|
||||||
jsonParser: TJSONParser;
|
jsonParser: TJSONParser;
|
||||||
jsonData: TJSONData;
|
jsonData: TJSONData;
|
||||||
contentType: TContentType;
|
contentType: TContentType;
|
||||||
xmlDoc: TXMLDocument;
|
xmlDoc: TXMLDocument;
|
||||||
begin
|
begin
|
||||||
|
httpMethod := Trim(FMethod);
|
||||||
|
if httpMethod = '' then
|
||||||
|
if (FContent.Count > 0) or (FFormFields.Count > 0) then
|
||||||
|
httpMethod := 'POST'
|
||||||
|
else
|
||||||
|
httpMethod := 'GET';
|
||||||
|
|
||||||
FParser.Content := AURL;
|
FParser.Content := AURL;
|
||||||
FParser.Replace;
|
FParser.Replace;
|
||||||
AURL := FParser.Content;
|
AURL := FParser.Content;
|
||||||
writeln('Calling ', AURL);
|
writeln(httpMethod, ' ', AURL);
|
||||||
|
|
||||||
response := TMemoryStream.Create;
|
response := TMemoryStream.Create;
|
||||||
request := nil;
|
request := nil;
|
||||||
@@ -407,18 +474,24 @@ begin
|
|||||||
FContent.SaveToStream(request);
|
FContent.SaveToStream(request);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
if HasOption('showrequest') then
|
||||||
|
begin
|
||||||
|
//TODO Handle URL-Encoded FormFields here!
|
||||||
|
writeln;
|
||||||
|
writeln('Request:');
|
||||||
|
for s in FContent do
|
||||||
|
writeln(' ', s);
|
||||||
|
end;
|
||||||
|
|
||||||
try
|
try
|
||||||
FRequest.Prepare;
|
FRequest.Prepare;
|
||||||
FHttp.Request := FRequest;
|
FHttp.Request := FRequest;
|
||||||
FHttp.HTTPOptions := FHttp.HTTPOptions + [hoNoProtocolErrorException];
|
FHttp.HTTPOptions := FHttp.HTTPOptions + [hoNoProtocolErrorException];
|
||||||
|
|
||||||
// Workaround for CustomHeaders not being assigned :-/
|
if SameText('POST', httpMethod) and (FFormFields.Count > 0) then
|
||||||
FHttp.Request.CustomHeaders.AddStrings(FRequest.CustomHeaders);
|
|
||||||
|
|
||||||
if SameText('POST', FMethod) and (FFormFields.Count > 0) then
|
|
||||||
FHttp.Post(AURL, FFormFields, response)
|
FHttp.Post(AURL, FFormFields, response)
|
||||||
else
|
else
|
||||||
FHttp.Perform(FMethod, AURL, request, response);
|
FHttp.Perform(httpMethod, AURL, request, response);
|
||||||
except
|
except
|
||||||
on E: Exception do
|
on E: Exception do
|
||||||
begin
|
begin
|
||||||
@@ -468,6 +541,18 @@ begin
|
|||||||
|
|
||||||
response.Free;
|
response.Free;
|
||||||
request.Free;
|
request.Free;
|
||||||
|
|
||||||
|
try
|
||||||
|
CheckExpectations;
|
||||||
|
except
|
||||||
|
on e: Exception do
|
||||||
|
begin
|
||||||
|
writeln;
|
||||||
|
writeln('Expections failed:');
|
||||||
|
writeln(e.Message);
|
||||||
|
ExitCode := 5;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TRestemplateApplication.WriteHelp;
|
procedure TRestemplateApplication.WriteHelp;
|
||||||
@@ -485,6 +570,8 @@ begin
|
|||||||
Writeln('Options:');
|
Writeln('Options:');
|
||||||
Writeln(' -l --list');
|
Writeln(' -l --list');
|
||||||
Writeln(' Print a list of known templates.');
|
Writeln(' Print a list of known templates.');
|
||||||
|
Writeln(' --var:<variableName>=<value>');
|
||||||
|
Writeln(' Sets the variable <variableName> to <value>.');
|
||||||
Writeln(' -h --help');
|
Writeln(' -h --help');
|
||||||
Writeln(' Print this help screen.');
|
Writeln(' Print this help screen.');
|
||||||
end;
|
end;
|
||||||
@@ -562,6 +649,26 @@ begin
|
|||||||
highlights.Free;
|
highlights.Free;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure TRestemplateApplication.CheckExpectations;
|
||||||
|
var
|
||||||
|
i: Integer;
|
||||||
|
expectType, expectation: String;
|
||||||
|
begin
|
||||||
|
for i := 0 to FExpectations.Count - 1 do
|
||||||
|
begin
|
||||||
|
expectType := LowerCase(FExpectations.Names[i]);
|
||||||
|
expectation := FExpectations.ValueFromIndex[i];
|
||||||
|
|
||||||
|
case expectType of
|
||||||
|
'status':
|
||||||
|
if FHttp.ResponseCode <> StrToInt(expectation) then
|
||||||
|
raise Exception.Create(Format('Status code unexpected! %d <> %s', [FHttp.ResponseCode, expectation]));
|
||||||
|
else
|
||||||
|
raise Exception.Create('Invalid expectation: ' + FExpectations[i]);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
constructor TRestemplateApplication.Create;
|
constructor TRestemplateApplication.Create;
|
||||||
begin
|
begin
|
||||||
inherited Create(nil);
|
inherited Create(nil);
|
||||||
@@ -578,6 +685,7 @@ begin
|
|||||||
FRequest.RawHeaders.AddValue('User-Agent', 'Mozilla/4.0 (compatible; restemplate ' + VersionInfo.GetProductVersionString + ')');
|
FRequest.RawHeaders.AddValue('User-Agent', 'Mozilla/4.0 (compatible; restemplate ' + VersionInfo.GetProductVersionString + ')');
|
||||||
FFilters := TFilterList.Create;
|
FFilters := TFilterList.Create;
|
||||||
FParser := TJTemplateParser.Create;
|
FParser := TJTemplateParser.Create;
|
||||||
|
FExpectations := TStringList.Create;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
destructor TRestemplateApplication.Destroy;
|
destructor TRestemplateApplication.Destroy;
|
||||||
@@ -589,6 +697,7 @@ begin
|
|||||||
//TODO Owned? FRequest.Free;
|
//TODO Owned? FRequest.Free;
|
||||||
FFilters.Free;
|
FFilters.Free;
|
||||||
FParser.Free;
|
FParser.Free;
|
||||||
|
FExpectations.Free;
|
||||||
inherited Destroy;
|
inherited Destroy;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|||||||
@@ -336,6 +336,7 @@ begin
|
|||||||
begin
|
begin
|
||||||
LDest := TIdEntityHeaderInfo(Destination);
|
LDest := TIdEntityHeaderInfo(Destination);
|
||||||
LDest.FRawHeaders.Assign(FRawHeaders);
|
LDest.FRawHeaders.Assign(FRawHeaders);
|
||||||
|
LDest.FCustomHeaders.Assign(FCustomHeaders);
|
||||||
LDest.FCacheControl := FCacheControl;
|
LDest.FCacheControl := FCacheControl;
|
||||||
LDest.FCharSet := FCharSet;
|
LDest.FCharSet := FCharSet;
|
||||||
LDest.FContentDisposition := FContentDisposition;
|
LDest.FContentDisposition := FContentDisposition;
|
||||||
@@ -966,9 +967,9 @@ begin
|
|||||||
// TODO: omitted intentionally?
|
// TODO: omitted intentionally?
|
||||||
// LDest.FHost := FHost;
|
// LDest.FHost := FHost;
|
||||||
// LDest.FProxyConnection := FProxyConnection;
|
// LDest.FProxyConnection := FProxyConnection;
|
||||||
end else begin
|
|
||||||
inherited AssignTo(Destination);
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
inherited AssignTo(Destination);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TIdRequestHeaderInfo.Clear;
|
procedure TIdRequestHeaderInfo.Clear;
|
||||||
|
|||||||
@@ -19,7 +19,8 @@
|
|||||||
<VersionInfo>
|
<VersionInfo>
|
||||||
<UseVersionInfo Value="True"/>
|
<UseVersionInfo Value="True"/>
|
||||||
<MinorVersionNr Value="6"/>
|
<MinorVersionNr Value="6"/>
|
||||||
<StringTable LegalCopyright="Andreas Schneider" ProductName="restemplate" ProductVersion="0.6.0"/>
|
<RevisionNr Value="2"/>
|
||||||
|
<StringTable LegalCopyright="Andreas Schneider" ProductName="restemplate" ProductVersion="0.6.2"/>
|
||||||
</VersionInfo>
|
</VersionInfo>
|
||||||
<MacroValues Count="1">
|
<MacroValues Count="1">
|
||||||
<Macro1 Name="LCLWidgetType" Value="nogui"/>
|
<Macro1 Name="LCLWidgetType" Value="nogui"/>
|
||||||
|
|||||||
Reference in New Issue
Block a user