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

chore: clean up extension generator #6158

Merged
merged 1 commit into from
Aug 31, 2020
Merged

chore: clean up extension generator #6158

merged 1 commit into from
Aug 31, 2020

Conversation

hacksparrow
Copy link
Contributor

Addresses #5336.

Checklist

  • DCO (Developer Certificate of Origin) signed in all commits
  • 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 👈

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.

Thank you for starting the work in cleaning up the template for extensions. I find your implementation as very repetitive and easy to break if we don't remember to update all places repeating the same string manipulation logic. Please move the string manipulation to the generator.

This of the generator as a controller and the template as a view in MVC pattern. Views should be dump and deal only with rendering - converting data produced by the controller to the output format (HTML in MVC, TypeScript code in our case). Data manipulation and business logic belongs to the controller.

@@ -1,9 +1,13 @@
import {Component, ProviderMap} from '@loopback/core';
import {<%= project.name.charAt(0).toUpperCase() + project.name.slice(1) %>Bindings} from './keys'
Copy link
Member

Choose a reason for hiding this comment

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

We should not perform case manipulation in the template. Please move this code to the generator and make sure we use the same logic for all places (templates) that need to acces {ProjectName}Bindings.

Suggested change
import {<%= project.name.charAt(0).toUpperCase() + project.name.slice(1) %>Bindings} from './keys'
import {<%= project.bindingsNamespace %>} from './keys'

See how we lb4 app is building appClassWithMixins string for inspiration:

https://github.com/strongloop/loopback-next/blob/4fbe6afb19f689b3a27aa4992377e3993ab62973/packages/cli/generators/app/index.js#L118-L134

@@ -1,9 +1,13 @@
import {Component, ProviderMap} from '@loopback/core';
import {<%= project.name.charAt(0).toUpperCase() + project.name.slice(1) %>Bindings} from './keys'
import {<%= project.name.toUpperCase() %>_OPTIONS} from './types';
Copy link
Member

Choose a reason for hiding this comment

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

Similarly here.

Suggested change
import {<%= project.name.toUpperCase() %>_OPTIONS} from './types';
import {<%= project.optionsConstant %>} from './types';

@hacksparrow
Copy link
Contributor Author

Thanks @bajtos I have applied your feedback.

@hacksparrow hacksparrow force-pushed the cleanup-extension branch 2 times, most recently from 6c8bc9b to 4fd36e3 Compare August 26, 2020 16:22
@hacksparrow hacksparrow marked this pull request as ready for review August 26, 2020 16:26
@raymondfeng
Copy link
Contributor

@hacksparrow It would be nice to document the extension project layout as #6151 does for applications.

More often than not, the component may want to offer different value providers
depending on the configuration. For example, a component providing an email API
may offer different transports (stub, SMTP, and so on).
Components can be configured by an app by calling `this.configure()` in its
Copy link
Contributor Author

Choose a reason for hiding this comment

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

CHANGE NOTE

Let's keep examples as simple as possible and avoid using concepts and terms which can be overwhelming for beginners.

@@ -0,0 +1,7 @@
export interface <%= project.optionsInterface %> {
Copy link
Contributor

Choose a reason for hiding this comment

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

Let's add some comments here to explain the purpose of <Extension>Options and describe specific properties should be added into the empty body.

@inject(CoreBindings.APPLICATION_INSTANCE)
private application: Application,
@config()
options: <%= project.optionsInterface %> = <%= project.defaultOptions %>,
Copy link
Contributor

Choose a reason for hiding this comment

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

private options?


@bind({tags: {[ContextTags.KEY]: <%= project.bindingsNamespace %>.COMPONENT}})
Copy link
Contributor

Choose a reason for hiding this comment

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

Let's add some comments for the component.

@@ -190,7 +190,23 @@ An application-level component usually contributes:
### Learn from existing ones

- [loopback4-example-log-extension](https://github.com/strongloop/loopback-next/tree/master/examples/log-extension)
- [@loopback/apiconnect](https://github.com/strongloop/loopback-next/tree/master/extensions/apiconnect)
Copy link
Member

Choose a reason for hiding this comment

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

If we are going to have the list of extensions we created in Component.md, it might be better to reference the list in here. It might be to keep both list updated as we're adding more extensions. :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Following the acceptance criteria. Would like to hear from @bajtos.

Copy link
Member

Choose a reason for hiding this comment

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

Please use your own judgment. The goal is to have a list of all extensions, to make it easier for our users to find them. I don't mind that much where the list is.

I agree with @dhmlau that we should have only one place where we are listing all components. Keeping multiple lists in sync is too much work and if we don't have automated checks in place, then we will almost certainly to forget to update all places sometime in the future.

Here is a list of components officially created and maintained by the LoopBack
team.

- [@loopback/apiconnect](https://github.com/strongloop/loopback-next/tree/master/extensions/apiconnect) - An extension for IBM API Connect
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
- [@loopback/apiconnect](https://github.com/strongloop/loopback-next/tree/master/extensions/apiconnect) - An extension for IBM API Connect
- [@loopback/apiconnect](https://github.com/strongloop/loopback-next/tree/master/extensions/apiconnect) - An extension for integrating with [IBM API Connect](https://www.ibm.com/cloud/api-connect)


- [@loopback/apiconnect](https://github.com/strongloop/loopback-next/tree/master/extensions/apiconnect) - An extension for IBM API Connect
- [@loopback/authentication](https://github.com/strongloop/loopback-next/tree/master/packages/authentication) - A LoopBack component for authentication support
- [@loopback/authentication-jwt](https://github.com/strongloop/loopback-next/tree/master/extensions/authentication-jwt) - Extension for the prototype of JWT
Copy link
Member

Choose a reason for hiding this comment

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

maybe simply Extension for JWT authentication?

@@ -83,6 +83,37 @@ const app = new RestApplication();
app.component(AuthenticationComponent);
```

## Official components

Here is a list of components officially created and maintained by the LoopBack
Copy link
Member

Choose a reason for hiding this comment

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

I see the list below contains the components from the core (e.g. @loopback/rest) and extensions (e.g. @loopback/apiconnect). Perhaps it's better to separate the 2 categories?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@bajtos what do you say?

Copy link
Member

Choose a reason for hiding this comment

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

I don't mind either way.

@hacksparrow
Copy link
Contributor Author

hacksparrow commented Aug 27, 2020

It would be nice to document the extension project layout as #6151 does for applications.

@raymondfeng let's do that in a follow up task, there are two more nice-to-haves in the acceptance criteria, I will club them together.

@hacksparrow hacksparrow force-pushed the cleanup-extension branch 2 times, most recently from ecf7037 to 7df1e9d Compare August 27, 2020 12:25
this.projectInfo.name.slice(1);
this.projectInfo.optionsInterface = `${titleCase}Options`;
this.projectInfo.bindingsNamespace = `${titleCase}Bindings`;
this.projectInfo.defaultOptions = `DEFAULT_${this.projectInfo.name.toUpperCase()}_OPTIONS`;
Copy link
Member

Choose a reason for hiding this comment

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

Please make sure we are handling multi-word names correctly and consistently with other places that are building PascalCase and camelCase identifiers.

A test case to consider: projectInfo.name is todo-demo. I expect the following strings to be used:

  • options interface: TodoDemoOptions
  • bindings namespace: TodoDemoBindings
  • default options: DEFAULT_TODO_DEMO_OPTIONS

Please check how is lb4 app generator dealing with multi-word app names and apply the same solution for extensions. If the app generators behaves differently from what I described above, then please follow what the app generator is doing.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

lb4 app is not doing the upperase-underscore thing (eg: DEFAULT_TODO_DEMO_OPTIONS), generating it this way for extension:

      const uppercaseUnderscore = this.projectInfo.name
        .toUpperCase()
        .replace(/\W/g, '_');
      this.projectInfo.defaultOptions = `DEFAULT_${uppercaseUnderscore}_OPTIONS`;

Rest, updated to be like lb4 app.


## Installation

Describe the installation steps.
Copy link
Member

Choose a reason for hiding this comment

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

I think the generator can pre-fill this section, the typical instructions is to npm install the package, right?


## Basic Use

Describe the usage.
Copy link
Member

Choose a reason for hiding this comment

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

Can the generator put the default example we are using in our components? Something along the following lines (replace MyComponent with the real name):

this.configure(MyComponent.COMPONENT).to({
 // put the configuration options here
});
this.component(MyComponent);


## License

Specify the license.
Copy link
Member

Choose a reason for hiding this comment

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

I suspect that many people won't bother to edit this section and push it to github & npmjs as-is. I think it's better to leave out the License section completely, rather than have content that's meaningless.

config,
ContextTags,
CoreBindings,
createBindingFromClass,
Copy link
Contributor

Choose a reason for hiding this comment

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

The import is not used. Please remove or leave it behind a comment.

as shown below.

```ts
...
Copy link
Contributor

Choose a reason for hiding this comment

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

Use // ... instead?

Copy link
Contributor

Choose a reason for hiding this comment

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

Do we want to include import statements here?

// Put the configuration options here
});
this.component(<%= project.componentName %>);
...
Copy link
Contributor

Choose a reason for hiding this comment

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

Ditto.


```ts
...
this.configure(<%= project.componentName %>.COMPONENT).to({
Copy link
Contributor

Choose a reason for hiding this comment

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

This should be <%= project.bindingsNamespace %>.COMPONENT.

Copy link
Contributor

Choose a reason for hiding this comment

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

Should we have something like:

const options: <%= project.optionsInterface %> = <%= project.defaultOptions %>;
this.configure(<%= project.bindingsNamespace %>.COMPONENT).to(options);

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, please address @raymondfeng comments.

I am not sure if the algorithm for building defaultOptions name will produce values consistent with other names (especially in app generator) in all edge cases. This should be easy to fix later, so I guess it's ok to wait until there is a bug report (if there is any problem ever).

{% include code-caption.html content="application.ts" %}

```ts
...
Copy link
Member

Choose a reason for hiding this comment

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

Let's keep code snippets a valid TypeScript please.

Suggested change
...
// ...

Per Raymond's suggestions above. The same comment applies to all other places where you are using ....

Copy link
Member

@dhmlau dhmlau left a comment

Choose a reason for hiding this comment

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

LGTM from the documentation perspective. I had one suggestion to include the community extensions as well.

Resource pooling service for LoopBack 4
- [@loopback/typeorm](https://github.com/strongloop/loopback-next/tree/master/extensions/typeorm) -
Adds support for TypeORM in LoopBack

Copy link
Member

Choose a reason for hiding this comment

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

do we want to add the link for community extensions as well? https://loopback.io/doc/en/lb4/Community-extensions.html

@hacksparrow hacksparrow force-pushed the cleanup-extension branch 2 times, most recently from 0e0ac32 to 6c80af3 Compare August 31, 2020 14:33
Copy link
Contributor

@raymondfeng raymondfeng left a comment

Choose a reason for hiding this comment

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

Please squash all commits into one before landing the PR.

@hacksparrow
Copy link
Contributor Author

Sure, thanks.

Clean up the extension generator.

Signed-off-by: Yaapa Hage <[email protected]>
@hacksparrow hacksparrow merged commit a4d9b15 into master Aug 31, 2020
@hacksparrow hacksparrow deleted the cleanup-extension branch August 31, 2020 15:59
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