6 Commits
v0.6 ... master

Author SHA1 Message Date
4742af20fe Fixed list in README 2015-10-22 11:01:58 +02:00
282ae6fa28 Added template support to BasicAuth command 2015-10-18 21:44:25 +02:00
7cc7705b29 Added status expectation 2015-10-08 11:59:41 +02:00
8e820eb2f3 * Set default method (GET or POST)
* Allow setting variables on the commandline
2015-10-08 11:33:07 +02:00
e576805e84 * Added generator
* Added request output
2015-10-07 16:22:44 +02:00
b260f184f8 Fixed Indy to correctly assign request headers 2015-10-06 22:01:27 +02:00
4 changed files with 147 additions and 20 deletions

View File

@@ -113,6 +113,22 @@ 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.

View File

@@ -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,17 +303,25 @@ begin
AName := Copy(AName, 2, Length(AName)); AName := Copy(AName, 2, Length(AName));
if mode = pmNormal then //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 begin
default := FSessionIni.ReadString(FTemplateName, AName, ''); value := GetOptionValue('var:' + AName);
value := Prompt(AName, default);
end else end else
begin begin
value := Prompt(AName, mode); if mode = pmNormal then
end; begin
default := FSessionIni.ReadString(FTemplateName, AName, '');
value := Prompt(AName, default);
end else
begin
value := Prompt(AName, mode);
end;
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;

View File

@@ -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;

View File

@@ -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"/>