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

RFC: helpful error message for deferred bindings [WIP] #2109

Closed
wants to merge 1 commit into from

Conversation

bajtos
Copy link
Member

@bajtos bajtos commented Dec 3, 2018

When using our authentication package, many users try to inject the current user directly via @inject(). Because this injection is resolved before the sequence is executed, and the current-user value is set only inside the sequence, such attempt fails with an error that does not provide any guide on how to fix it.

In this pull request, I am introducing a new kind of bound value: deferred. It is serving as a placeholder for bindings that will be set later via @inject.setter. When users try to access such binding too early, we can provide a helpful error message pointing them to @inject.getter.

Such improvement is relying on the good will of extension authors to create a deferred binding for a value they will set later using @inject.setter(). I think that's too much to ask and we should provide a way to enforce this behavior.

The second part if this pull request is modification of @inject.setter to report a warning when the binding key is now know (was not registered as deferred). Originally, I want to throw new Error, but that would break backwards compatibility (which I don't want to right now).

TODO

  • tests
  • optional injection - add a test, decide on the expected behavior
  • remove warnings triggered by existing code (e.g. tests)
  • documentation

@strongloop/loopback-next what do you think? If you have encountered Error: The key authentication.currentUser was not bound to any value., would you find it useful if the error message told you to use @inject.getter()?

Related issues:

Checklist

  • npm test passes on your machine
  • New tests added or existing tests modified to cover all changes
  • Code conforms with the style guide
  • API Documentation in code was updated
  • Documentation in /docs/site was updated
  • Affected artifact templates in packages/cli were updated
  • Affected example projects in examples/* were updated

When using our authentication package, many users try to inject the
current user directly via `@inject()`. Because this injection is
resolved before the sequence is executed, and the current-user value
is set only inside the sequence, such attempt fails with an error
that does not provide any guide on how to fix it.

In this commit, I am adding a new kind of bound value: deferred. It
is serving as a placeholder for bindings that will be set later
via `@inject.setter`. When users try to access such binding too early,
we can provide a helpful error message pointing them to
`@inject.getter`.

TODO:
 - unit tests
 - optional injection
 - fix failing tests
 - documentation
 - a feature flag to preserve backwards compatibility
@bajtos bajtos requested review from raymondfeng and a team December 3, 2018 10:45
@bajtos bajtos self-assigned this Dec 3, 2018
@bajtos bajtos added developer-experience Issues affecting ease of use and overall experience of LB users feature IoC/Context @loopback/context: Dependency Injection, Inversion of Control labels Dec 3, 2018
@clayrisser
Copy link
Contributor

Ah yes I ran into this problem and figured it out after banging my head on the table.

@raymondfeng
Copy link
Contributor

@bajtos I don't think we have to introduce a new API. It can be achieved using apply() from https://github.com/strongloop/loopback-next/blob/lifecycle/packages/context/src/binding.ts#L437.

export const toBeBound = binding => {
  binding.toDynamicValue(() => Promise.reject(
        new Error(
          `There was no value provided for "${
            binding.key
          }" yet. Consider using \`@inject.getter()\`.`,
        ),
      );
}

ctx.bind(AuthenticationBindings.CURRENT_USER).apply(toBeBound);

Or maybe it can be even more simpler by defaulting Binding._getValue() to reject until the value is configured with to, toClass, etc. This way, we can simply do:

ctx.bind(AuthenticationBindings.CURRENT_USER); // without further calls, it will be resolved to a rejected promise with meaning error information.

@bajtos
Copy link
Member Author

bajtos commented Dec 4, 2018

@raymondfeng I prefer the code to be easy to write & understand.

When I read ctx.bind(AuthenticationBindings.CURRENT_USER), it feels incomplete - we are defining a binding but not saying anything about the value. Also, we may want to distinguish between the case when the developer forgot to provide the bound value at binding time (no setter will be called later) and the case where the bound value is intentionally not provided at binding time and a setter will provide it later.

The option ctx.bind(AuthenticationBindings.CURRENT_USER).apply(toBeBound); seems unnecessary long and also a bit cryptic to me.

I don't think we have to introduce a new API.

IMO: Bindings with a value set later are an integral part of Context, similarly to @inject.getter and @inject.setter. The API for defining deferred bindings early in the application setup is just a missing piece needed to improve user experience.

@bajtos
Copy link
Member Author

bajtos commented Dec 4, 2018

On the second thought, I am no longer convinced this is such a good idea. It turns out that @loopback/authentication works in such way that the current user can be injected directly via @inject.

To make this feature useful, I think we would have to offer a more complex advice, e.g. check that the sequence is calling authenticate action, etc.

I am going to close this pull request for now, we can revisit it later if we learn about other scenarios when users encounter Error: The key XYZ was not bound to any value.

@bajtos bajtos closed this Dec 4, 2018
@bajtos bajtos deleted the feat/better-error-message-for-late-bindings branch December 4, 2018 12:29
@bajtos bajtos restored the feat/better-error-message-for-late-bindings branch December 4, 2018 12:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
developer-experience Issues affecting ease of use and overall experience of LB users feature IoC/Context @loopback/context: Dependency Injection, Inversion of Control
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants