Can someone help me understand how to make a REST POST request in HPCC? I've read through the documentation but I couldn't find any examples. Any help would be appreciated.
Here is an example of an HTTP POST that sends JSON content. It was a working example, but unfortunately the example service it calls no longer seems to be available.
The fact that is being provided to send, and the format specified is JSON it is what causes it to do a POST with JSON content.
Notice the format of the call matches closely with how SOAPCALL works but in this case will send and receive content as JSON. There is a JIRA open to get HTTPCALL POST added to the documentation, but in the meantime you can use the SOAPCALL documentation as a guideline for what additional options might be available. Most of the options you can add to a SOAPCALL can also be used with an HTTPCALL POST.
Also note that the "service name" is passed in as ''. Filling in the service name automatically adds another layer of JSON around the record that creates a JSON object named after that parameter. That's not usually what you want.
sendContent := RECORD
string name {XPATH('name')} := 'bob';
string salary {XPATH('salary')} := '22';
string age {XPATH('age')} := '105';
END;
receiveContent := RECORD
string name {XPATH('name')};
string salary {XPATH('salary')};
string age {XPATH('age')};
integer4 id {XPATH('id')};
END;
receiveRec := RECORD
string status {XPATH('status')};
receiveContent content {XPATH('data')};
END;
OUTPUT(HTTPCALL('https://dummy.restapiexample.com/api/v1/create', '', sendContent, DATASET(receiveRec), JSON, LOG));
HTTPCALL is a function that calls a REST service. Here is an example from the Language Reference Manual:
worldBankSource := RECORD
STRING name {XPATH('name')}
END;
OutRec1 := RECORD
DATASET(worldBankSource) Fred{XPATH('/source')};
END;
raw := HTTPCALL('http://api.worldbank.org/sources', 'GET', 'text/xml', OutRec1, );
OUTPUT(raw);
////Using HTTPHEADER to pass Authorization info
raw2 := HTTPCALL('http://api.worldbank.org/sources', 'GET', 'text/xml',
OutRec1, HTTPHEADER('Authorization','Basic
dXNlcm5hbWU6cGFzc3dvcmQ='),HTTPHEADER('MyLiteral','FOO'));
OUTPUT(raw2);
Hope this helps!
Bob
Related
I need get property name as string from object class. It is possible in Delphi?
I need property transfer as argument of method and get property name as string. I don't want use property name as argument because the compiler does not catch error when name of property is changed in class.
type
TMyClass = class
private
fField: some_type;
public
property Field:some_type read fField;
end;
function GetPropertyName(arg: ??):string
begin
Result := arg.PropertyName; // here I need get property name form transfer type
end;
var
obj: TMyClass;
name: string;
begin
name := GetPropertyName(obj.Field);
end;
To clarify, as discussed in comments, I'm looking for a direct equivalent to the C# nameof function.
From the comments you make it clear that you are looking for a Delphi equivalent to the C# nameof function.
No such equivalent exists in Delphi, and the language does not have facilities for you to create it yourself. Instead you will need to name the method as a string literal in code.
Recently at my company we tried to use DUnitX with all it's blessings to test classes we wrote. Since those classes reflect entities in database all fields have to accept null values as well as specific type (e. g. Integer or string).
Since spring4d already have those we tried to use them:
INaleznosc = interface
['{D5D6C901-3DB9-4EC2-8070-EB0BEDBC7B06}']
function DajPodstawaVAT(): TNullableCurrency;
property PodstawaVAT: TNullableCurrency read DajPodstawaVAT;
end;
TNaleznosc = class(TInterfacedObject, INaleznosc)
strict private
FId: TNullableInt64;
FPodstawaVAT: Currency;
function TNaleznosc.DajPodstawaVAT(): TNullableCurrency;
published
property PodstawaVAT: TNullableCurrency read DajPodstawaVAT;
end;
INaleznoscFunkcje = interface
['{509288AB-110A-4A52-BE93-3723E5725F4B}']
function DajPodstawaVAT(pID: TNullableInt64): TNullableCurrency;
end;
function TNaleznosc.DajPodstawaVAT(): TNullableCurrency;
begin
FPodstawaVAT := FFunkcje.DajPodstawaVAT(FId);
end;
procedure TTestNaleznosc.PodstawaVATGetterNieWywolujefunkcji();
var
funkcjeNaleznosc: TMock<INaleznoscFunkcje>;
klasa: INaleznosc;
id: TNullableInteger;
begin
//initialize tested elements
funkcjeNaleznosc := TMock<INaleznoscFunkcje>.Create();
id := 15;
klasa := TNaleznosc.Create(funkcjeNaleznosc, id, zmienne);
//setup expected behaviour from mock
funkcjeNaleznosc.Setup.WillReturn(2).When.DajPodstawaVAT(id);
funkcjeNaleznosc.Setup.Expect.Once.When.DajPodstawaVAT(id);
//this triggers getter
klasa.PodstawaVAT;
end;
When this code is executed we get AV exception First chance exception at $00000000. Exception class $C0000005 with message 'access violation at 0x00000000: access of address 0x00000000'. Process Tests.exe (6556).
Eventually we narrowed this issue down to Move procedure in System.Rtti unit, TValueDataImpl.ExtractRawDataNoCopy function:
when Length(FData) is less or equal to 8 it works fine
when Length(FData) is between 9 and 32 at line 5905 of System unit (FISTP QWORD PTR [EDX+8] {Save Second 8}) whole call stack disappears beside two lines (we are not sure whether it's relevant or not, but it doesn't look like good sign) and after getting to topmost function (according to call stack) we get error.
Call stack before "saving second 8"
Call stack after "saving second 8"
Is it our fault or is it some issue with system/spring/dunitx units? How can we use nullable types and tests at the same time?
I am not sure if Delphi Mocks has a generic type parameter on its WillReturn method but if so then pass TNullableCurrency there - otherwise the compiler will infer the type from the parameter 2 you are passing and obviously internally it fails to put that into the TNullableCurrency it should return.
If it does not and only allows TValue then you need to pass one that contains a TNullableCurrency and not 2 which it would by using its implicit operator like so: TValue.From<TNullableCurrency>(2)
Furthermore I am not sure if they did fix the code in the SameValue routine in Delphi Mocks when the value to be compared is a record (as TNullableCurrency is)
Edit: no, they did not - see https://github.com/VSoftTechnologies/Delphi-Mocks/issues/39
You might want to consider giving Spring4D mocks a try which should be able to handle nullables.
How can I lookup DNS records with Indy in Delphi? For example, SRV records, SPF records, TEXT records, etc.
I know we can use nslookup directly from Windows, but I want to do this with Indy, or any other Delphi component.
I tried searching Google, and I found something like this:
function ReverseDNSLookup(IPAddress: String; DNSServer: String =
SDefaultDNS; Timeout: Integer = 30; Retries: Integer = 3) : string;
var
AIdDNSResolver: TIdDNSResolver;
RetryCount: Integer;
begin
Result := '';
IPAddress := ReverseIP(IPAddress);
AIdDNSResolver := TIdDNSResolver.Create(nil);
try
AIdDNSResolver.QueryResult.Clear;
AIdDNSResolver.WaitingTime := Timeout;
AIdDNSResolver.QueryType := [qtPTR];
AIdDNSResolver.Host := DNSServer;
RetryCount := Retries;
repeat
try
dec(RetryCount);
AIdDNSResolver.Resolve(IPAddress);
Break;
except
on e: Exception do
begin
if RetryCount <= 0 then
begin
// if SameText(e.Message, RSCodeQueryName) then
// Result := FALSE
// else
raise Exception.Create(e.Message);
Break;
end;
end;
end;
until false;
if AIdDNSResolver.QueryResult.Count > 0 then
Result := AIdDNSResolver.QueryResult.DomainName;
finally
FreeAndNil(AIdDNSResolver);
end;
end;
But all it is for is looking up IP addresses. I want SRV and TEXT records, and maybe SPF records.
TIdDNSResolver is what you are looking for. The example you show is only using a small subset of what TIdDNSResolver supports. You simply need to set the TIdDNSResolver.QueryType property to specify the type(s) of record(s) you want to query, and then loop through the TIdDNSResolver.QueryResult collection to access the individual records. TIdDNSResolver supports SRV and TXT records, for example:
var
DNS: TIdDNSResolver;
I: Integer;
Record: TResultRecord;
Txt: TTextRecord;
Srv: TSRVRecord;
begin
DNS := TIdDNSResolver.Create(nil);
try
DNS.WaitingTime := Timeout;
DNS.QueryType := [qtTXT, qtService];
DNS.Host := 'some.dns.server';
DNS.Resolve('some.hostname');
for I := 0 to DNS.QueryResult.Count -1 do
begin
Record := DNS.QueryResult[I];
case Record.RecType of
begin
qtTXT: begin
Txt := TTextRecord(Record);
// use Txt.Text as needed...
end;
qtService: begin
Srv := TSRVRecord(Record);
// use Srv.OriginalName, Srv.Service, Srv.Protocol, etc as needed...
end;
else
// something else...
end;
end;
finally
DNS.Free;
end;
end;
TIdDNSResolver does not support the SPF record type (code 99) that was defined in RFC 4408 in 2006:
This document defines a new DNS RR of type SPF, code 99. The format of this type is identical to the TXT RR [RFC1035]. For either type, the character content of the record is encoded as [US-ASCII].
It is recognized that the current practice (using a TXT record) is not optimal, but it is necessary because there are a number of DNS server and resolver implementations in common use that cannot handle the new RR type. The two-record-type scheme provides a forward path to the better solution of using an RR type reserved for this purpose.
That record type was later obsoleted by RFC 7208 in 2014:
SPF records MUST be published as a DNS TXT (type 16) Resource Record (RR) [RFC1035] only. The character content of the record is encoded as [US-ASCII]. Use of alternative DNS RR types was supported in SPF's experimental phase but has been discontinued.
In 2003, when SPF was first being developed, the requirements for assignment of a new DNS RR type were considerably more stringent than they are now. Additionally, support for easy deployment of new DNS RR types was not widely deployed in DNS servers and provisioning systems. As a result, developers of SPF found it easier and more practical to use the TXT RR type for SPF records.
In its review of [RFC4408], the SPFbis working group concluded that its dual RR type transition model was fundamentally flawed since it contained no common RR type that implementers were required to serve and required to check. Many alternatives were considered to resolve this issue, but ultimately the working group concluded that significant migration to the SPF RR type in the foreseeable future was very unlikely and that the best solution for resolving this interoperability issue was to drop support for the SPF RR type from SPF version 1. See Appendix A of [RFC6686] for further information.
The circumstances surrounding SPF's initial deployment a decade ago are unique. If a future update to SPF were developed that did not reuse existing SPF records, it could use the SPF RR type. SPF's use of the TXT RR type for structured data should in no way be taken as precedent for future protocol designers. Further discussion of design considerations when using new DNS RR types can be found in [RFC5507].
Following on from my question Inno Setup disable component selection when a specific component is selected, I think there may be a way to get this to work without the problem of checked states set in code being permanent (though use of the Checked property) by instead using:
function CheckItem(const Index: Integer; const AOperation: TCheckItemOperation): Boolean;
in TNewCheckListBox, however I am having trouble getting the syntax correct. I am trying:
CheckItem(CompIndexSync, coUncheck) := Checked[CompIndexClient];
where the CompIndexes are constants assigned to the indexes for the component values. I get an Identifier Expected error at compile. Can someone advise how to correctly use this function and what I am doing wrong?
The CheckItem member of the TNewCheckListBox class is a method of type function which updates the checked state by the AOperation operation and returns True if any changes were made to the state of the item at Index, or any of its children. Here is its prototype (source):
function TNewCheckListBox.CheckItem(const Index: Integer;
const AOperation: TCheckItemOperation): Boolean;
The problem was that you were trying to assign a value to the function result. That's not what you can do in Pascal language in general.
What you want to do with the item(s) is passed by the AOperation parameter. In pseudocode e.g.:
var
CheckList: TNewCheckListBox;
Operation: TCheckItemOperation;
begin
...
if ShouldCheck then
Operation := coCheck
else
Operation := coUncheck;
if CheckList.CheckItem(ItemIndex, Operation) then
MsgBox('An item has changed its state.', mbInformation, MB_OK);
end;
I want to modify xml file in Inno Setup - but installer crashes. I tried different things, and as result got sample code with problem
procedure testXml();
var
xmlDocLocal, nodeLocal: Variant;
begin
try
xmlDocLocal := CreateOleObject('MSXML2.DOMDocument');
xmlDocLocal.async := False;
xmlDocLocal.resolveExternals := False;
xmlDocLocal.loadXML('<root></root>');
nodeLocal := xmlDocLocal.CreateElement('element1');
xmlDocLocal.documentElement.appendChild(nodeLocal);
except
end;
end;
During second call, installer crashes on the appendChild method. What am I doing wrong ?
Three ideas: first, we're using InnoSetup, but for us the OleObject needs to be created with another string ending with the specific version 6.0:
try
XMLDoc := CreateOleObject('MSXML2.DOMDocument.6.0');
except
RaiseException('Please install MSXML first.'#13#13'(Error ''' + GetExceptionMessage + ''' occurred)');
end;
Second idea: try adding an xml header to the XML string you have in your code. Like this:
xmlDocLocal.loadXML('<?xml version="1.0" encoding="UTF-8" ?><root></root>');
Third idea: try checking for errors (as I already showed in the first snippet). That might give you a pretty good idea what goes wrong. This is how we do it (and it works):
XMLDoc.load(XMLFileName);
if XMLDoc.parseError.errorCode <> 0 then
XMLDoc.load(XMLFileName2);
if XMLDoc.parseError.errorCode <> 0 then
RaiseException('Error on line ' + IntToStr(XMLDoc.parseError.line) + ', position ' + IntToStr(XMLDoc.parseError.linepos) + ': ' + XMLDoc.parseError.reason);
Hope this helps you. Hard to solve an unknown issue ;-)
Though this is an old issue, I would like to bring it up once more. I am using InnoSetup 6 and have spent two days working on this until I found this stackoverflow issue. For me it seems that the problem is still there. My installer keeps crashing with an Access Violation and I boilt it down to a very similar example like the one above. It makes no difference if I use createElement or createNode.
xmlDocument := CreateOleObject('MSXML2.DOMDocument.6.0');
xmlDocument.async := false;
xmlDocument.resolveExternals := false;
xmlDocument.loadXML('<broker><web bind="http://localhost:8161"></web></broker>');
//xmlDocument.setProperty('SelectionLanguage', 'XPath');
// Select the <web> node
node := xmlDocument.selectSingleNode('/broker/web');
// save attribute value into variable
bind := node.getAttribute('bind');
// remove legacy attribute
node.removeAttribute('bind');
// add new <binding> element as first child of <web>
//newNode := xmlDocument.createNode(1, 'binding', '');
newNode := xmlDocument.createElement('binding');
newNode.setAttribute('uri', bind);
Log(Format('### Appending %s as first child of %s', [newNode.xml, node.xml]));
node.appendChild(newNode);
Log(Format('### Inserted %s as first child of %s', [newNode.xml, node.xml]));
xmlDocument.Save(bootConfig);
All I see when running the code above is this:
The difference with createElement and createNode is, that createElement creates the exception message above and createNode simply kills the installer silently.
The last I see in the logs is this line:
2022-04-25 13:44:47.597 ### Appending <binding uri="http://localhost:8161"/> as first child of <web></web>
2022-04-25 13:44:47.597 CurStepChanged raised an exception.
2022-04-25 13:44:47.597 Exception message:
2022-04-25 13:44:47.597 Message box (OK):
Runtime error (at 211:2827):
Access violation at address 03CC8380. Execution of address 03CC8380.
Has this been addressed in some way? I cannot see from Russel Jordan's site that there has been any bugfix for this.