Class, constructor and inherance.

Rikki Cattermole via Digitalmars-d-learn digitalmars-d-learn at puremagic.com
Tue Oct 13 19:22:57 PDT 2015


On 13/10/15 5:17 PM, holo wrote:
> On Tuesday, 13 October 2015 at 02:03:46 UTC, Rikki Cattermole wrote:
>> On 13/10/15 5:56 AM, holo wrote:
>>> @Rikki:
>>>
>>>> If you didn't need to make it easily changeable I would say not even
>>>> bother with OOP at all.
>>>
>>> Basically that what i had was enough for me and on top of that i could
>>> build my app. It need to just periodically check for new instances and
>>> if they are started or stopped and count "up and running time" for
>>> billing purpose. But like i said i want to learn programming in D and
>>> basically OOP too so i want to make it "proper way".
>>>
>>> BTW: i think right now i understand what tuple is, but still don't know
>>> for what to duplicate struct functionalities :). Those Templates still
>>> don't understand but i hope that will came naturally with time and
>>> practice. eg.. they are very similar to normal functions but you can
>>> call it with types not only attributes.. strange ;)
>>>
>>> I red yours advises and try to create according to it my own first
>>> class.
>>>
>>> I moved time functions and variables to method "go" they need to be
>>> current as possible when im sending request, if wont authorization could
>>> not pass.. so i think they shouldn't be in constructor.
>>>
>>> I moved some other variables too, and created interface.
>>>
>>>  From all that things came out such monster which is working and doing
>>> its job :)
>>>
>>> module sigawsv4;
>>>
>>> import std.stdio, std.process;
>>> import std.digest.sha, std.digest.hmac;
>>> import std.string;
>>> import std.conv;
>>> import std.datetime;
>>> import std.net.curl;
>>>
>>> interface credential
>>> {
>>>      int go();
>>> }
>>>
>>> class sigv4 : credential
>>> {
>>>      //could be changed to take some structure as parameter instead of
>>> such ammount of attributes
>>>
>>>      this(string methodStr = "GET", string serviceStr = "ec2", string
>>> hostStr = "ec2.amazonaws.com", string regionStr = "us-east-1", string
>>> endpointStr = "https://ec2.amazonaws.com", string payloadStr = "",
>>> string parmStr = "Action=DescribeInstances&Version=2013-10-15")
>>>      {
>>>
>>>          this.method = methodStr;
>>>          this.service = serviceStr;
>>>          this.host = hostStr;
>>>          this.region = regionStr;
>>>          this.endpoint = endpointStr;
>>>          this.payload = payloadStr;
>>>          this.request_parameters = parmStr;
>>>
>>>          this.accessKey = environment.get("AWS_ACCESS_KEY");
>>>          this.secretKey = environment.get("AWS_SECRET_KEY");
>>>      }
>>>
>>>      public:
>>>          string method;
>>>          string service;
>>>          string host;
>>>          string region;
>>>          string endpoint;
>>>          string payload;
>>>          string request_parameters;
>>>
>>>
>>>          int go()
>>>          {
>>>              //time need to be set when we are sending request not
>>> before
>>>              auto currentClock = Clock.currTime(UTC());
>>>              auto currentDate = cast(Date)currentClock;
>>>              auto curDateStr = currentDate.toISOString;
>>>              auto currentTime = cast(TimeOfDay)currentClock;
>>>              auto curTimeStr = currentTime.toISOString;
>>>              auto xamztime = curDateStr ~ "T" ~ curTimeStr ~ "Z";
>>>
>>>              canonicalURI = "/";
>>>              canonicalQueryString = request_parameters;
>>>              canonicalHeadersString =  "host:" ~ this.host ~ "\n" ~
>>> "x-amz-date:" ~ xamztime ~ "\n";
>>>              signedHeaders = "host;x-amz-date";
>>>
>>>              auto canonicalRequest = getCanonicalRequest(canonicalURI,
>>> canonicalQueryString, canonicalHeadersString, signedHeaders);
>>>
>>>              string credentialScope = curDateStr ~ "/" ~ region ~ "/" ~
>>> service ~ "/" ~ "aws4_request";
>>>
>>>              string stringToSign = algorithm ~ "\n" ~ xamztime ~ "\n" ~
>>> credentialScope ~ "\n" ~ sha256Of(canonicalRequest).toHexString.toLower;
>>>
>>>              auto signingKey = getSignatureKey(secretKey, curDateStr,
>>> region, service);
>>>
>>>              string signature = hmac!SHA256(stringToSign.representation,
>>> signingKey).toHexString.toLower;
>>>
>>>              string authorizationHeader = algorithm ~ " " ~
>>> "Credential=" ~ accessKey ~ "/" ~ credentialScope ~ ", " ~
>>> "SignedHeaders=" ~ signedHeaders ~ ", " ~ "Signature=" ~ signature;
>>>
>>>
>>>              auto client = HTTP(endpoint ~ "?" ~ canonicalQueryString);
>>>              client.method = HTTP.Method.get;
>>>              client.addRequestHeader("x-amz-date", xamztime);
>>>              client.addRequestHeader("Authorization",
>>> authorizationHeader);
>>>              auto content = client.perform();
>>>
>>>              return content;
>>>          }
>>>
>>>      private:
>>>          const algorithm = "AWS4-HMAC-SHA256";
>>>
>>>          string accessKey;
>>>          string secretKey;
>>>
>>>          string currentClock;
>>>          string currentDate;
>>>          string curDateStr;
>>>          string currentTime;
>>>          string curTimeStr;
>>>          string xamztime;
>>>
>>>          string canonicalURI;
>>>          string canonicalQueryString;
>>>                 string canonicalHeadersString;
>>>                 string signedHeaders;
>>>
>>>
>>>
>>>          alias sign = hmac!SHA256;
>>>
>>>          auto getSignatureKey(string key, string dateStamp, string
>>> regionName, string serviceName)
>>>          {
>>>              auto kString = ("AWS4" ~ key).representation;
>>>              auto kDate = sign(dateStamp.representation, kString);
>>>              auto kRegion = sign(regionName.representation, kDate);
>>>              auto kService = sign(serviceName.representation, kRegion);
>>>              auto kSigning = sign("aws4_request".representation,
>>> kService);
>>>
>>>              return kSigning;
>>>          }
>>>
>>>
>>>          auto getCanonicalRequest(string canonicalURI, string
>>> canonicalQueryString, string canonicalHeadersString, string
>>> signedHeaders)
>>>          {
>>>              string payloadHash = sha256Of(payload).toHexString.toLower;
>>>              string canonicalRequest = method ~ "\n" ~ canonicalURI ~
>>> "\n" ~ canonicalQueryString ~ "\n" ~ canonicalHeadersString ~ "\n" ~
>>> signedHeaders ~ "\n" ~ payloadHash;
>>>              return canonicalRequest;
>>>          }
>>> }
>>>
>>> void main()
>>> {
>>>      sigv4 sig = new sigv4();
>>>      writeln(sig.go());
>>> }
>>>
>>> I want to ask you for  advises what i could do with that class to make
>>> it looks more "pro"/elegant/build in "proper way". Probably there are
>>> lot of mistakes which all beginners are doing.
>>>
>>> eg.: Did i use interface correctly?
>>
>> You are reasonably close:
>> credential sig = new sigv4();
>
> Why are you creating sigv4 object with type credential? How in your
> opinion should look interface for such class?
>
>>
>> Although go is not really doing what I expect it to do.
>> To me go should be dedicated to performing a request given what ever
>> you need for just that request. The class sigv4 is your global state
>> aka what doesn't change per request.
>> To me what go returns is whatever is the common denominator for what
>> you need from it is.
>
> Not sure if i correctly understood that. Do you propose to take out eg
> signing process from go function and let it only to make requests?
> Signature is depending on kind of request and accurate time when request
> is send. That is why i all that things put to it.

The implementation such as sigv4 can be configured with as much detail 
as possible extra that you need.
The method go can for instance do what ever it needs to, to for fill 
it's goal.

>>
>> From what I can see, you probably want go to take the payload as an
>> argument.
>> That way you can reuse an instance of sigv4. Which is the ultimate goal.
>
> In plan i wanted to change variables per request that why i left that
> public variables and created that arguments for constructor - to have
> possibility to change that  values durring creation of object.
>
> On example, im expecting it will be behaving like that:
>
> sigv4 obj = new sigv4();
> content =  obj.go();    //will get instances list
> obj.request_parameters = "Action=DescribeRegions&Version=2013-10-15"
> content2 = obj.go();    //will get list  of regions
> destroy(obj);
>
> so that go will get me back what i need per request not per one object
> live, or it wont work like that?

It indeed should not.
Also remember go is currently returning an integer. Not data from the 
request.

> go method returns xml form (need to find out how force AWS to return
> JSON cos i saw xml module in dlang is deprecated) with structures
> depends on query - in default query that will be list of  instances with
> all  parameters. That xml file i want to parse in higher
> classes/functions (different class for different request).

If you need to use std.xml use it. We don't appear to be replacing it 
any time soon.

So here is the thing. You are tieing your usage of the API to SigV4 
standard. This is bad bad bad. Any updates or changes to it, will cause 
problems. If you want to tie yourself to said standard, then you don't 
need OOP.

Another thing, you may want to consider to use an interface as the 
return type of go. That way your implementation of it can have extra 
fields/methods which if you know that it is sigv4 you can cast to, to 
get access to, should you need it.

Also classes start with a capital letter, like Credential or SigV4. And 
D prefers camal casing aka requestParameters for variables/functions.


More information about the Digitalmars-d-learn mailing list