const slow: Promise<string> = sleepPlease(500).then(() => "yay finally");I imported promisify from "util". setTimeout is built in, but its arguments are in the wrong order to naturally pass to promisify.

Tuesday, July 18, 2017

TL;DR: when using an object as a dictionary in TypeScript/ES6, iterate through it using `Object.keys()`.

Coming from statically typed languages, I keep looking for a Map or Dict type in TypeScript (or JavaScript). People use objects for this, though. Objects have key-value pairs in them, and you can add them and delete them and declare them statically and it looks nice, especially in literals.

Flip through the enumerable keys defined on an object:

This one makes the most sense to me; it's what I expect from a dictionary. It flips through the values in an array of the keys defined on that object directly, no prototypal inheritance considered. This is how JSON.stringify() prints your object.

Flip through the enumerable keys defined on that object and its prototype chain:

This is the easiest one to write. It flips through the keys defined on the object and its prototype chain. If you're using an ordinary object for this, and no one has done anything bizarre like add an enumerable property to Object, it's fine. tslint hates it though; it bugs me about "for(...in...) statements must be filtered with an if statement." tslint is like "OMG, you do not know what is on that thing's prototype chain it could be ANYTHING"

I find it backwards that for(...in...) flips through property names of an object, while for(...of...) flips through values in an array. This confuses me daily in TypeScript. If you accidentally use for(...of...) instead of for(...in...) on an object, then you'll see 0 iterations of your loop. Very sneaky. If you accidentally use for(...in...) on an array, you get the indices instead of the values, also confusing. TypeScript and tslint don't warn about either error, as far as I can tell. :-(

Flip through the enumerable and non-enumerable keys defined on that object:

This one flips through only keys on the object, not its prototype chain, and also gives you the names of non-enumerable properties. You probably don't want those.

What are non-enumerable properties?

Conceptually, they're properties that don't make sense to flip through, that we don't want JSON.stringify() to look at. They're hidden from for(...in...) iteration and from Object.keys(). You can still access them on the object. For instance, constructors of TypeScript classes are non-enumerable properties. Methods on built-in types like Array and object are non-enumerable. They stay out of the way.

When would we want to flip through them, like in Object.getOwnPropertyNames()?
I don't know, maybe for debugging.

Why make a non-enumerable property?
I hit a use case for this today: serializing an instance of a class with recursive fields. JSON.stringify() can't print recursive structures.

Side quest: Making recursive objects printable

In TypeScript, every field in a class instance (including any inherited field) is an enumerable property, returned from Object.keys() and printed by JSON.stringify(). See this TreeNode class that tracks its children, and its children track it:

Here's a tricky way to say "Hey JavaScript, don't print that _parent field". Explicitly override its enumerable-ness in the constructor.

classTreeNode {

private _parent: TreeNode;

publicchildren: TreeNode[] = [];

publicconstructor(publicreadonlyvalue: string) {

Object.defineProperty(this, "_parent", { enumerable:false });

}

...

}

We can get tricky with TypeScript class fields. After all, they get tricky with us.

Properties on a class instance

In TypeScript, class instance fields show up in Object.keys() and accessor-based properties (like with getters and setters) do not. They're properties on the prototype, which is the class itself. So if you want to see accessor properties, use for(...in...) on class instances. That gets the enumerable properties from the prototype chain. Watch out: these include methods.

Why iterate through the properties on a class? I don't know, maybe for debugging again. If you do it, I suggest skipping methods. This makes tslint happy because its an if statement:

Recommendations

If you have a class instance, access its properties with dot notation, like treeNode.children. That way TypeScript can help you avoid mistakes. If you have a dictionary object, access its properties with index notation, like dictionary["impute"] (and turn off the angry tslint rule). Class instances have specific types; dictionary objects are type object. Access the contents of a dictionary using Object.keys().

Friday, July 14, 2017

My objective is to run some services on Pivotal Web Services (PWS; hosted instance of Pivotal Cloud Foundry), and have them respond to requests to `https://survey.atomist.com` at various paths. Currently these services run on AWS, along with services that respond at other subdomains of atomist.com.

TL;DR: this is easy enough for HTTP requests and prohibitively difficult for real HTTPS, for only one subdomain.

This posts describes some tricky bits in this process, and the bits that leave me stuck.

Prerequisites: I have PWS set up and a few apps deployed. Meanwhile all our existing infrastructure runs on AWS.

First: multiple apps responding at satellite-of-love.cfapps.io

The instructions tell me how to point my own domain at a single app in PWS, but I want multiple apps to serve paths from my domain. The caller should not know or care which service is responding to its request for a resource.

To do this, I set up a route in cloud foundry, with a hostname (which seems to be PCF's name for the third-from-the-right segment of the domain name, anyone know why?) that doesn't correspond to any one app.

`cf create-route jessitroncfapps.io --hostname satellite-of-love`

Here, jessitron is my space in PWS. cfapps.io is PWS's domain, this gets requests into Cloud Foundry for routing. satellite-of-love is a domain name that I like, it matches my github org.

That path is going to 404, but I have called dibs on satellite-of-love.cfapps.io. It'll route to my jessitron space and no one else's.

Now I can make routes for each endpoint and tell it which app serves it. For the /vote endpoint on Kitty Survey, I have an app running called london, so I hook that up:

`cf map-route londonsatellite-of-love.cfapps.io --path /vote`

Now I can hit https://satellite-of-love.cfapps.io/vote and my london app receives a request at path /vote. This is good for testing.

This part totally works with HTTPS. If you don't mind changing your clients to point to this URL, stop here.

This is DNS setup. We happen to use AWS Route53 for this. I go into the AWS console to set up a CNAME record for survey.atomist.com -> satellite-of-love.cfapps.io. There was one tricky bit to this in Route53: I clicked on the existing survey.atomist.com record (if it didn't exist I would click Create Record Set), and tried to enter my target BUT NO
It was all "The record set could not be saved because:- Alias Target contains an invalid value."

Here's the trick: choose Alias: No.

With a regular CNAME (the Alias ones are an internal-to-AWS thing), I can route to an external domain from Route53.

Next, over in Cloud Foundry land, I can tell it about this domain.

`cf create-domain atomistsurvey.atomist.com`

Here, atomist is my PWS org. Then I tell it to send requests to my space please:

`cf create-route jessitronsurvey.atomist.com`

And then I create routes for each of the endpoints, but with this new domain. (I'm pretty sure this is necessary.)

`cf map-route londonsurvey.atomist.com --path /vote`

I'll need to make these two routes (or at least the last one) for every endpoint I add to my service. Soon I'll add this to my "add REST endpoint" automation in Rug.

Third: security certificates and https

There are two ways to get an HTTPS:// endpoint on PWS. They recommend using CloudFlare, which can be free. There are two problems with that.

CloudFlare -> Cloud Foundry

The first is, to route anything at atomist.com through CloudFlare, I have to route atomist.com through CloudFlare. I have to change the routing for my entire company. :-(

Even if I did reroute our whole domain through CloudFlare, the second problem appears: I can get the appearance of security but not actual end-to-end SSL. The easy option to choose is "Flexible", meaning users get SSL from browser<->CloudFlare and it looks secure to them, but behind the scenes it's HTTP between CloudFlare and my app. This seems unprofessional to me, letting everyone's requests happen without SSL behind the scenes while telling them it's secure.

The other option to choose is "Full SSL," but then I need SSL on Cloud Foundry anyway, so ...

SSL in Cloud Foundry

There's a Pivotal SSL service available in the PWS marketplace for SSL termination. For $20/month (they don't mention that in the documentation), it'll let you upload one certificate.

Currently, we use AWS Certificate Manager, which provides free certificates that only work on AWS.

Can I get that for survey.atomist.com separately, while leaving the rest of atomist.com alone? I'm going to try that, from some other source -- but not today.

Therefore, because our security certificates are tied to AWS

and because I decline to change the routing of our entire domain in order to experiment with this subdomain,

I give up. My toy website doesn't need HTTPS anyway.

The moral is: if you want to experiment with moving part of your infrastructure off of AWS (designated by a subdomain), be prepared to change how requests are routed to the root domain.

I want to start recording the benefits I get out of personal and local (team-level) automation.

When I have a local version of a maven library installed, say 0.19.0-SNAPSHOT, but I want to go back to using the last released version, say 0.18.0, then I need to delete the library from my local maven repo. I've learned the hard way that it is not enough to delete the directory in the jar; I have to delete another file too. So a while back, I scripted that.

This tiny program deletes a version of the `rug` library, since that's usually the one I'm messing with.