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

Move config into a local singleton #7

Merged
merged 5 commits into from
Feb 22, 2017
Merged

Move config into a local singleton #7

merged 5 commits into from
Feb 22, 2017

Conversation

tswicegood
Copy link
Contributor

Why are we doing this?

I'm trying to extract all of the shared state into something that can be accessed across modules. Right now the this.config inside Messenger is the majority of the shared state. In talking this through with @stripethree, we realized there might be an issue with non-namespaced configuration as well. This introduces launch-vehicle-fbm. as the prefix for all configuration variables.

Did you document your work?

Haven't yet – there currently isn't any configuration in the README.md so nothing's changed.

How can someone test these changes?

  1. npm i
  2. npm t
  3. No test failures and three new tests under the config module

What possible risks or adverse effects are there?

None, assuming the test coverage has caught edge cases for configuration changes. A few tests failed with the change, but that was caused by the constructor changing.

Copy link
Contributor

@stripethree stripethree left a comment

Choose a reason for hiding this comment

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

In our IRL conversation we talked about having an application level configuration that we recommend developers use. Is that something we are interested in articulating in the README? That also seems like it would be useful to have a simple, supporting getter function as well:

getAppConfig() {
  return config.has("application") ? config.get("application") : {};
}

I don't view this as required, and it could also be a follow-up to this PR; to me this serves to solidify the delineation between SDK config and application config.

@@ -360,15 +358,15 @@ class Messenger extends EventEmitter {
const signature = req.headers['x-hub-signature'];

if (!signature) {
throw new Error(`Couldn't validate the signature with app secret: ${this.config.get('messenger.appSecret')}`);
throw new Error(`Couldn't validate the signature with app secret: ${config.get('messenger.appSecret')}`);
Copy link
Contributor

Choose a reason for hiding this comment

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

Something nags at me about us having the appSecret in the error message here, but that's also existing and was not added in this PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Agreed. Feels like this might be a good place for custom Error subclass that provides the additional context outside of the message property. Not sure how / if Error subclasses works as expected in JavaScript.

@pgoldrbx
Copy link

+1 for namespace

Q: would this change mean that downstream users of the SDK wouldn't be able to override or extend the app config?

src/config.js Outdated
key = `${prefix}${key}`;
}
return config.get(key);
};
Copy link
Contributor

@stripethree stripethree Feb 17, 2017

Choose a reason for hiding this comment

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

Might also want to warp has, as that could be used in conversationLogger. I think it'd look like this:

exports.has = function (key, prefix = 'launch-vehicle-fbm') {
  if (prefix) {
    key = `${prefix}${key}`;
  }
  return config.has(key);
};

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@stripethree agreed – let's add that in when we've got the use-case though.

src/config.js Outdated

exports.config = config;

exports.get = function (key, prefix = 'launch-vehicle-fbm.') {
Copy link
Contributor

@stripethree stripethree Feb 17, 2017

Choose a reason for hiding this comment

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

NITPICK: In the mindset of a possible contributor, I would think the separator would be handled by this wrapper function, not as part of the prefix argument. The exception would be if someone else were to swap an alternate package in place of node-config, but I think that would take more changes across the SDK.

config.get('modiface', 'apiUrl')

if (prefix) {
  key = `${prefix}.${key}`;
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Extracted this into getNamespaceKey so it can be tested independently and can be reused in a has function down the road.

@stripethree
Copy link
Contributor

stripethree commented Feb 17, 2017

@pgoldrbx - Extend, yes. We talked about having an application namespace at the same level that we recommend for use. That's what this comment is referring to. Override... not without changing the SDK to modify our specific prefix.

Extending will be a given because there are things built in that are optional, like Dashbot, that we would probably prefer the user to specify within the SDK namespace. I think we should follow this PR up with moving the conversationLogger to use the config object instead of pulling directly from the env.

@tswicegood
Copy link
Contributor Author

@stripethree sorry – I totally took something different away. I was thinking that we would namespace all of our configuration then let the end-user do whatever they like so long as they realize that everything in launch-vehicle-fbm was owned by us.

@stripethree
Copy link
Contributor

@tswicegood That's fine too! I sometimes end up favoring or wanting more structure than is really needed (and may not have articulated that well in our convo). I have at least one personal project I want to use with this once we open it up and might experiment with some config things there.

@crccheck
Copy link
Contributor

I was actually thinking we should get rid of the dependency on config and have everything become an option in the constructor. I never got around to it when it was sharing with the app, but the only real reason why it's not like that now is because it's a really fat object to pass in, but very typical.

@tswicegood
Copy link
Contributor Author

@crccheck if it's cool with you, I'd like to merge this and we can tackle possibly removing configuration all together. I think there's some value in that for sure, but that seems tangential to what what I need in #6 (a way to break this out of the state of Messenger).

@crccheck
Copy link
Contributor

crccheck commented Feb 17, 2017

What about this...

Original:

this.config.get('messenger.appSecret')

This PR:

const config = require('./config')
config.get('messenger.appSecret')

My proposal:

const config = require('config')
config.get('launch-vehicle-fbm.messenger.appSecret')

You get the same config singleton, you get the namespacing, you get the standard usage of the config package. But you just got these long strings you have to deal with. You could do ES6 template strings (${PREFIX}messenger.appSecret) but I think since it doesn't really save on line length, we say use find/replace, it exists.


In other words: I'm for using config the way you're supposed to use it. I'm very opposed to writing wrappers.

@tswicegood
Copy link
Contributor Author

@crccheck what's the goal with your proposal? That does save adding an extra module, but increases the duplication of a code by 16x for the current configuration usage. The additional characters that have to be duplicated end up within 5 bytes the whole file size of src/config though, so the savings goes negative as with next config value added.

@stripethree
Copy link
Contributor

stripethree commented Feb 17, 2017

Why not just assume the namespace is there in the first place?

   const config = require('config').get('launch-vehicle-fbm');
   config.get('messenger.appSecret')

The namespace doesn't really need to be explicit in this codebase beyond that, does it?

Edit: ran a local test and this worked as expected

const config = require('config').get('launch-vehicle-fbm');
console.log(Object.keys(config));
console.log(config.get('facebook.appId'));
[ 'facebook', 'messenger' ]
************6968

@tswicegood
Copy link
Contributor Author

I like that @stripethree. Didn't realize that node-config returned a config object when returning part of the nested structure. It does make testing a little more difficult as the configuration is now at the module level. Given that this is needed in a couple of places (not currently, but will in #6) I still think abstracting this into ./src/config.js is the right answer, but it's drastically simpler.

Copy link
Contributor

@crccheck crccheck left a comment

Choose a reason for hiding this comment

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

Like! There's only one structural bit I'm not a fan of, and it's such a trivial change to go from require('./config') to require('config').get('launch-vehicle-fbm') that it's not worth fussing over.

like:

  1. namespace config
  2. the config grabbing looks more like how others use the package
  3. the constructor now only takes in one options object as people would expect

dislike:

  1. people are going to wonder why the config layer of abstraction exists

describe('config', () => {
it('should be an instance of node-config with launch-vehicle-fbm', () => {
// sanity check
expect(nodeConfig.get('launch-vehicle-fbm')).to.not.be.empty;
Copy link
Contributor

Choose a reason for hiding this comment

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

The other tests use assert instead of chai. I think assert is easier to read and write, but I like chai's error handling. And since tests rarely fail but are read all the time, I stick to assert.

// sanity check
expect(nodeConfig.get('launch-vehicle-fbm')).to.not.be.empty;

// actual test
Copy link
Contributor

Choose a reason for hiding this comment

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

i don't think we need this comment

@stripethree
Copy link
Contributor

@tswicegood any outstanding things before squash and merge?

@stripethree stripethree merged commit 9908134 into master Feb 22, 2017
@crccheck crccheck deleted the refactor/config branch February 22, 2017 16:00
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.

4 participants