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

feat(cli): add lb4 interceptor to generate interceptors and a booter to load interceptors #2907

Merged
merged 4 commits into from
Jun 6, 2019

Conversation

raymondfeng
Copy link
Contributor

@raymondfeng raymondfeng commented May 15, 2019

  1. Add lb4 interceptor command to generate interceptors.
  2. Add interceptor booter to load interceptors from src/interceptors.

Checklist

👉 Read and sign the CLA (Contributor License Agreement) 👈

  • 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

👉 Check out how to submit a PR 👈

@raymondfeng raymondfeng requested a review from bajtos as a code owner May 15, 2019 23:27
@raymondfeng raymondfeng changed the title feat(cli): add lb4 interceptor command to generate global interceptors feat(cli): add lb4 interceptor to generate global interceptors and a booter to load global interceptors May 15, 2019
@raymondfeng raymondfeng force-pushed the cli-interceptor branch 4 times, most recently from 16bdb94 to e74c2f1 Compare May 21, 2019 20:25
@raymondfeng raymondfeng force-pushed the cli-interceptor branch 3 times, most recently from 6280b9b to da707f3 Compare May 23, 2019 17:01
Copy link
Member

@bajtos bajtos left a comment

Choose a reason for hiding this comment

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

Nice addition.

I see two problematic parts:

  • I consider anonymous functions in Providers as a bad practice, let's use named class methods instead.
  • I'd like us to consider non-global interceptors too and find a design that can accommodate both global and non-global interceptors. Ideally, I'd like the CLI prompt to ask the developer whether they want to register the interceptor-being-created as a global one.

docs/site/Global-interceptor-generator.md Outdated Show resolved Hide resolved
packages/boot/src/booters/interceptor.booter.ts Outdated Show resolved Hide resolved
name: 'group',
// capitalization
message: utils.toClassName(this.artifactInfo.type) + ' group:',
default: '',
Copy link
Member

Choose a reason for hiding this comment

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

How can the person running lb4 interceptor find out what are the valid group names?

What happens when they enter a group name that's not recognized by the app?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Based on our design, unknown group is sorted by alphabetical order before the known groups.

Copy link
Member

Choose a reason for hiding this comment

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

Based on our design, unknown group is sorted by alphabetical order before the known groups.

That answered my second question 👍

The first one is still not answered though:

How can the person running lb4 interceptor find out what are the valid group names?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Since the valid group names are configured by a binding, I'm not sure how we can check. Maybe we can improve the prompt to explain so.

Copy link
Member

Choose a reason for hiding this comment

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

Maybe we can improve the prompt to explain so.

Sounds good.

Ideally, I'd like the prompt to tell me:

  1. What are the usual (default) group names
  2. Where can I find the actual list of group names as recognized by my application

binding.tag(ContextTags.GLOBAL_INTERCEPTOR);
binding
.tag(ContextTags.GLOBAL_INTERCEPTOR)
.tag({[ContextTags.NAMESPACE]: GLOBAL_INTERCEPTOR_NAMESPACE});
Copy link
Member

Choose a reason for hiding this comment

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

I am confused. Why do we need both GLOBAL_INTERCEPTOR tag and GLOBAL_INTERCEPTOR_NAMESPACE? I mean why is it not enough to use the tag only?

Is it a good idea to use the same namespace for all interceptors, regardless of which component/extension is contributing them? Wouldn't it better to use namespaces similarly to how Java treat packages, i.e. interceptors contributed by @loopback/authorization package should use @loopback/authorization namespace.

Let's discuss.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Only GLOBAL_INTERCEPTOR is required. GLOBAL_INTERCEPTOR_NAMESPACE is to make the binding key more readable.

Copy link
Member

Choose a reason for hiding this comment

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

Please add a code comment to capture this information.

Also, what is your opinion about using the same namespace for all interceptors vs. using a namespace based on the component/extension contributing the interceptor?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Personally, I prefer to use globalInterceptors.*.

@raymondfeng raymondfeng force-pushed the cli-interceptor branch 3 times, most recently from 8cf4fb0 to 8ec48b8 Compare May 24, 2019 17:43
@raymondfeng
Copy link
Contributor Author

I consider anonymous functions in Providers as a bad practice, let's use named class methods instead.
I'd like us to consider non-global interceptors too and find a design that can accommodate both global and non-global interceptors. Ideally, I'd like the CLI prompt to ask the developer whether they want to register the interceptor-being-created as a global one.

Both are addressed.

@raymondfeng raymondfeng requested a review from bajtos May 24, 2019 18:04
@raymondfeng raymondfeng changed the title feat(cli): add lb4 interceptor to generate global interceptors and a booter to load global interceptors feat(cli): add lb4 interceptor to generate interceptors and a booter to load interceptors May 24, 2019
@raymondfeng raymondfeng force-pushed the cli-interceptor branch 4 times, most recently from 7bb2c8a to 5fb119b Compare May 31, 2019 21:17
@dhmlau dhmlau added this to the June 2019 milestone milestone May 31, 2019
Copy link
Member

@bajtos bajtos left a comment

Choose a reason for hiding this comment

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

Looks mostly good at high level, let's do one more iteration to improve details and UX.

docs/site/Interceptor-generator.md Outdated Show resolved Hide resolved
command. If provided, the tool will use that as the default when it prompts for
the name.

`--global` - Optional flag to indicate a global interceptor (default to `true`)
Copy link
Member

Choose a reason for hiding this comment

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

If the flag is true by default, what's the syntax for turning it off? Is it --no-global, --global=false or something else? Please explain that in the docs.

docs/site/Interceptor-generator.md Outdated Show resolved Hide resolved
docs/site/Interceptor-generator.md Show resolved Hide resolved
*/

value() {
const interceptor: Interceptor = async (invocationCtx, next) => {
Copy link
Member

Choose a reason for hiding this comment

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

Please rework the interceptor into a named method, i.e. TestInterceptor.prototype.intercept.

Suggested change
const interceptor: Interceptor = async (invocationCtx, next) => {
return this.intercept.bind(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.

Can you remind me why you are against using an arrow function inside value()? Is it about the stack trace? In my test, the stack trace shows interceptor (name of the const) as the function name, which is good to me.

Copy link
Member

Choose a reason for hiding this comment

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

Yes, it's mostly about the stack trace. IMO, TestInterceptor.intercept is most helpful.

interceptor is not entirely bad, but I see few problems:

  • It's not clear which interceptor is involved, one has to look to the filename to find that. (This works only when there is a single interceptor exported by that source file.)

  • It's not clear why we are storing the interceptor in a local variable. Personally, I would simplify the code as follows, with unintended consequence on stack traces:

    value() {
      return async (invocationCtx, next) => {
        // ...
      };
    }

More subjectively, this is also about Single Responsibility Principle - the value function should be concerned with obtaining the interceptor function only, the actual interception should be implemented elsewhere.

docs/site/Interceptor-generator.md Outdated Show resolved Hide resolved
@raymondfeng raymondfeng force-pushed the cli-interceptor branch 2 times, most recently from bb1c657 to 1b7fe3f Compare June 4, 2019 15:28
@raymondfeng raymondfeng requested a review from bajtos June 4, 2019 15:33
Copy link
Member

@bajtos bajtos left a comment

Choose a reason for hiding this comment

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

LGTM.

Please get at least one more approval from @strongloop/loopback-maintainers before landing.

if (group) binding.tag({[ContextTags.GLOBAL_INTERCEPTOR_GROUP]: group});
};
}

/**
* `@globalInterceptor` decorator to mark a class a global interceptor
Copy link
Member

Choose a reason for hiding this comment

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

Missing "as"? English is not my mother tongue, I may be wrong.

Suggested change
* `@globalInterceptor` decorator to mark a class a global interceptor
* `@globalInterceptor` decorator to mark a class as a global interceptor

@bajtos bajtos requested a review from a team June 6, 2019 08:54
@bajtos bajtos added the feature label Jun 6, 2019
@bajtos
Copy link
Member

bajtos commented Jun 6, 2019

@raymondfeng I am not sure if this comment has been addressed yet: #2907 (comment)

Maybe we can improve the prompt to explain so.

Sounds good.

Ideally, I'd like the prompt to tell me:

  1. What are the usual (default) group names
  2. Where can I find the actual list of group names as recognized by my application

@raymondfeng
Copy link
Contributor Author

@bajtos Did you notice that we now have the following prompt to address #2907 (comment)?

? Interceptor name: test
? Is it a global interceptor? Yes
? Global interceptors are sorted by the order of an array of group names bound t
o ContextBindings.GLOBAL_INTERCEPTOR_ORDERED_GROUPS. See https://loopback.io/doc
/en/lb4/Interceptors.html#order-of-invocation-for-interceptors.
Group name for the global interceptor: ('') 

@hacksparrow hacksparrow self-requested a review June 6, 2019 14:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants