Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

initial development #3

Closed
wants to merge 10 commits into from
Closed

initial development #3

wants to merge 10 commits into from

Conversation

sqweelygig
Copy link
Contributor

* created an abstract class for polling an api
@sqweelygig sqweelygig requested a review from Page- January 25, 2017 12:35
@sqweelygig sqweelygig self-assigned this Jan 25, 2017
super
@interval = 1000
@lastReport = null
@clearToPoll = true
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does clearToPoll control? The name isn't obvious to me and I'm wondering if there's a better alternative

processDone: (error, response, report, done) =>
@clearToPoll = true
@report report
if done?
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be done?(error, response) which will specifically check if done is a function rather than just nullish

* made method that deals with top level methodology
* relies on implementing classes to perform a couple of methods
@sqweelygig
Copy link
Contributor Author

closes #2
closes #4
(only done on this PR because the code I've just done is dependant on previous code)

package.json Outdated
"description": "A base adapter for Hubot",
"main": "./src/base-adapter.coffee",
"name": "hubot-abstract-api-adapter",
"version": "0.0.2",
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a fairly minor point in context, but just for good practice: this is certainly a large enough change to bump to warrant a minor bump.

class AbstractAPIAdapter extends Adapter
constructor: ->
super
@interval = 1000
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could be a constant (assuming external clients aren't supposed to change this)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inherited classes and situation may dictate changes to this. eg if a server is refusing due to usage limits or high load we may choose to increase our poll interval.

Copy link

@pimterry pimterry Feb 7, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, if this is intended I'd make this more explicit - e.g. a constructor argument that defaults to 1000.

@interval = 1000
@lastReport = null
@allowedToPoll = true
throw new TypeError('Abstract class')
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What? How are subclasses supposed to be able to instantiate this? http://stackoverflow.com/a/30560792/68051 has some good suggested approaches that'll do what you're looking for.

super
@interval = 1000
@lastReport = null
@allowedToPoll = true
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could make the usage of this clearer by flipping the boolean, and renaming it to pollInProgress.

if response?.statusCode in [429, 503]
@report { error: error, html: response?.statusCode, url: options.url }
# Retry if the error code indicates temporary outage
@getUntil options, filter, each, done
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You've flipped the order of filter/each here.

if not error and headers.statusCode is 200
parseResponse(JSON.parse(body), callback)
else
callback(error ? headers, null)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Calling with error makes sense, but headers is a weird fallback. It's be good behaviour for this to be an actual Error instance. Something like: new Error("Received #{headers.statusCode} response: #{body}") would be better.

# Format taken from https://github.com/github/hubot/blob/master/docs/scripting.md#making-http-calls
requestObject.post(JSON.stringify(requestObject.payload)) (error, headers, body) ->
if not error and headers.statusCode is 200
parseResponse(JSON.parse(body), callback)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd wrap the JSON.parse in a try/catch, and then callback with an error complaining about the body if it fails. We're not checking the headers here to check we're getting JSON atm (which isn't 'perfect', but parsing optimistically is fine for this), and eventually we'll definitely hit an issue somewhere here somebody doesn't give us JSON back (some HTML error page or something), which currently will silently kill this method - it'll never call the callback at all.

* @param {object} ids to use. ids = {user, flow, thread?}
* @param {function} function to receive (error, new ids object). ids = {thread, comment?}
###
postUsing: (text, ids, callback) =>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason we're not using promises here, and for done in getUntil/processDone? It'd make this code and code using this tidier and more reliable (done right, you should get automatic error -> promise error propagation, rather than having to ensure we're guaranteed to call the callback in every case)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does my not knowing promises yet count as a reason?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It counts as an extra good reason to do it, since they're a core part of lot of the rest of the codebase, and this will be a very convenient place to learn them :D. Jake Archibald has a good intro: https://developers.google.com/web/fundamentals/getting-started/primers/promises.

To be clearer on the end goal: it'd be nice if postUsing returned a promise, and didn't take callback, and if getUntil and processDone both returned promises, and didn't take done. Ping me if you've got any questions or if you need a hand, happy to help.

requestObject = @robot.http(requestDetails.url)
requestObject.header('Accept', 'application/json')
for key, value of requestObject.headers
requestObject.header(key, value)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

...what are these two lines doing? This seems very strange. Is this supposed to be looping over requestDetails instead?

return
if @extractNext responseObject
options.url = @extractNext responseObject
@getUntil options, filter, each, done
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Flipped filter/each here too.

This does suggest we haven't really tested this code... Can we have some automated tests for this somewhere to catch these issues? Here if that's easy, or an integration test in an example extending class somewhere else would be great.

@sqweelygig
Copy link
Contributor Author

It's true, this PR is relatively untested. It originated from a time before I'd started implementing alpha branches, so at the time couldn't test until implemented, and couldn't develop from this foundation until accepted. Since then I'm trialling a slightly different branch convention that should allow me to conduct dependant development while a PR is in progress.

@sqweelygig
Copy link
Contributor Author

closes #4

@lurch
Copy link

lurch commented Feb 9, 2017

Do I need to ignore this as there's no mention of marshmallows? ;-)

package.json Outdated
"hubot-mock-adapter": "^1.0.0",
"hubot-slack": "^4.3.1",
"resin-lint": "^1.4.0"
"hubot": "^2.19.0",
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this in devDependencies rather than dependencies?

###*
* Given a set of ids make best effort to publish the text and pass on the published ids
* @param {string} text to publish
* @param {object} ids to use. ids = {user, flow, thread?}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do the mentions of flow and thread mean that this is specific to Flowdock, and maybe as not as "abstract" as you intended?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Every one of our chat systems uses a different terminology to mean the same thing. It's muddling me up so much that I've got a chart of the equivalences in my exercise book.
This said, the most common comms tool in resin is Flowdock, so where I've a choice I've used Flowdock's nouns.

* @param {string} text to publish
* @param {object} ids to use. ids = {user, flow, thread?}
###
postUsing: (text, ids) -> new Promise (resolve, reject) =>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know nothing about writing hubot scripts, but should there also be a getUsing?

Copy link
Contributor Author

@sqweelygig sqweelygig Feb 9, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nah. This is actually nothing to do with hubot's scripts and is a utility function for without context posting. Each adapter has a way of getting without context.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Should work(!)
4 participants