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

Add some error messages for the developer when .service.js is malformed #1894

Merged
merged 6 commits into from
Oct 28, 2018
Merged

Add some error messages for the developer when .service.js is malformed #1894

merged 6 commits into from
Oct 28, 2018

Conversation

paulmelnikow
Copy link
Member

When loading .service.js files which don’t contain services, such as when the export is forgotten, print helpful error messages. This will only occur during development; the unit tests will catch these problems well before code reaches the server.

When loading `.service.js` files which don’t contain services, such as when the export is forgotten, print helpful error messages. This will only occur during development; the unit tests will catch these problems well before code reaches the server.
@paulmelnikow paulmelnikow added the developer-experience Dev tooling, test framework, and CI label Aug 10, 2018
@shields-ci
Copy link

Messages
📖

✨ Thanks for your contribution to Shields, @paulmelnikow!

Generated by 🚫 dangerJS

Copy link
Member

@chris48s chris48s left a comment

Choose a reason for hiding this comment

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

Spotted a couple of issues here. Also I can see why it is fiddly to test this, but I think this would ideally benefit from having some unit tests so we can be more confident the code is working as desired.

serviceClasses.push(service)
servicePaths.forEach(path => {
const module = require(path)
if (!module) {
Copy link
Member

@chris48s chris48s Aug 11, 2018

Choose a reason for hiding this comment

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

I don't think this is working as you expect. A module that exports nothing gives module the value {} here but in that situation this path is not taken because {} is truthy in javascript. Also if we explicitly module.exports = [], [] is also truthy, but we would want to follow this path in that situation too.

Copy link
Member Author

Choose a reason for hiding this comment

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

Ahh, yes, good point.

} else {
for (const serviceClass in service) {
serviceClasses.push(service[serviceClass])
for (const key in module) {
Copy link
Member

Choose a reason for hiding this comment

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

This assumes that if we've got something that isn't instanceof BaseService we've got an iterable, but if I declare a service file that starts

module.exports = class MyService extends SomethingThatDoesntExtendBaseService...

we'll get to here and try to iterate MyService, which doesn't make much sense.

Copy link
Member Author

Choose a reason for hiding this comment

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

That's true, though is not extending BaseService a use pattern we need to support?

Copy link
Member

Choose a reason for hiding this comment

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

No - we don't want to support it. We want to throw an error. That's the point of testing against (serviceClass.prototype instanceof BaseService) - right?

but because MyService isn't iterable we never enter the loop so nothing ever gets tested against the condition (serviceClass.prototype instanceof BaseService) in this case.

Copy link
Member Author

Choose a reason for hiding this comment

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

Ah, I gotcha.

What if we accumulate the valid services found in each file, and throw an error if none are found?

Copy link
Member

Choose a reason for hiding this comment

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

You'd still want to continue checking as you go, or also throw if

num valid services != num total services

to also cover the condition where >=1 services extend BaseService and >=1 don't, but yeah.. if we get to the end of that loop and have 0 valid services that's definitely an error.

Copy link
Member Author

Choose a reason for hiding this comment

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

We're right now supporting both arrays and objects, and if we keep doing that, it seems simpler to bail if we ever find a non-service, and check that each file has contained at least one before moving on to the next one.

Alternatively we could drop support for objects and require a single service or an array. Then we could use count, validate in map, and probably simplify this.

Copy link
Member

Choose a reason for hiding this comment

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

In the relatively small subset of services we've refactored so far, we've already found valid uses for multiple export styles:

object:

module.exports = {
APMDownloads,
APMVersion,
APMLicense,
}

array:

module.exports = ['week', 'month', 'year', 'total'].map(DownloadsForInterval)

class:

module.exports = class Clojars extends BaseJsonService {

All of those are sensible module export styles and I think we should continue to support all of them so we keep things relatively flexible for future contributors and allow ourselves and contributors to write idiomatic code. Even if that complicates the service loading code a bit, I think that's worthwhile.

We can detect a module which exports a single class from one exporting an Array or Object using instanceof Function:

> class FooBar {
... }

> console.log(FooBar instanceof Function);
true

> console.log({} instanceof Function);
false

> console.log([] instanceof Function);
false

and special-case it.

@paulmelnikow
Copy link
Member Author

This doesn't feel like a high priority for an automated test, though I see where you're coming from. If it seems important to you, I'll do it. Or if you feel like tackling the tests, go for it!

@paulmelnikow
Copy link
Member Author

@chris48s What do you think about merging this? The existing files should prevent regression on the existing use cases, and we could manually test if this code ever gets touched again. It's kind of a pain to test and I feel like the time could be better spent elsewhere.

(This is having just spent a couple hours writing tests for #2021 because I want to get that merged…)

@chris48s
Copy link
Member

I don't mind merging it without tests if its a complete pain, but either way we should fix those two cases noted in the review where we're not throwing on malformed exports.

That said, having had another look I do have an idea for how to write tests for this. Let me have a quick go at it.. I'm happy to pick it up and push some commits to this branch (if I can write to it) or open a new PR to take it over the line..

@paulmelnikow
Copy link
Member Author

Awesome! You should be able to write to the branch.

- allow us to inject servicePaths for testing
- throw a custom exception
- add test suite and fixtures
- cover off missing cases
@chris48s
Copy link
Member

The new commit I've pushed covers every case I can think of. Does that seem reasonable?

Copy link
Member Author

@paulmelnikow paulmelnikow left a comment

Choose a reason for hiding this comment

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

💯

@paulmelnikow
Copy link
Member Author

Looks awesome! I can’t 👍 this since it’s my PR. Would you like to merge?

@chris48s chris48s merged commit e66d92f into badges:master Oct 28, 2018
@shields-deployment
Copy link

This pull request was merged to master branch. This change is now waiting for deployment, which will usually happen within a few days. Stay tuned by joining our #ops channel on Discord!

After deployment, changes are copied to gh-pages branch:

@paulmelnikow paulmelnikow deleted the service-loader-erroring branch October 28, 2018 16:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
developer-experience Dev tooling, test framework, and CI
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants