-
Notifications
You must be signed in to change notification settings - Fork 1.1k
[WIP] feat: local API Explorer #1957
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
Conversation
Add local API Explorer
cdacd6b to
ff10a9c
Compare
packages/core/src/component.ts
Outdated
| /** | ||
| * A map of name/class pairs for binding static assets | ||
| */ | ||
| export type staticAssetsMap = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Uff, then name and comment seems very misleading to me, because there is no map involved AFAICT.
Also please use interface instead of type.
| staticAssets = [ | ||
| { | ||
| path: '/explorer1', | ||
| rootDir: './public', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How is this relative path going to be resolved into an absolute one? IMO, it should be the responsibility of the component to resolve the path. E.g. rootDir: path.resolve(__dirname, '../../public')
| import {format} from 'util'; | ||
| import {RestBindings} from './keys'; | ||
| import {RestComponent} from './rest.component'; | ||
| import {ExplorerComponent} from './explorer.component'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't bundle the explorer component inside @loopback/rest. The coupling between the API explorer (e.g. a specific swagger-ui version used) and the runtime (@loopback/rest) is the thing we want to avoid by moving explorer into a new component.
The API Explorer should be added to applications as part of our project scaffolding provided by lb4 app. Nice to have: lb4 app can ask the user whether they want to add API Explorer to their project or not.
| mountComponent(this, explorerInstance); | ||
| console.log(explorerInstance.staticAssets); | ||
| const explorer = explorerInstance.staticAssets![0]; | ||
| this.static(explorer.path, explorer.rootDir, explorer.options); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This block needs to be split into two parts:
RestApplication class should override component method provided by Application, iterate over component.staticAssets and call this.static to register them.
The scaffolded project-specific application class should register the component in the constructor, e.g.
class TodoApplication extends RepositoryMixin(BootMixin(RestApplication)) {
constructor () {
// ...
this.component(ExplorerComponent);
}
}
packages/core/src/component.ts
Outdated
| /** | ||
| * An array of directory to serve static assets | ||
| */ | ||
| staticAssets?: staticAssetsSettings[]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think these REST-specific extensions should be defined in a new interface inside @loopback/rest, because applications using different transports (e.g GraphQL or gRPC) cannot support static assets.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we generalize it to allow components to contribute middleware based routes?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds like a separate feature to me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1 to leave middleware-based routes out of scope.
Personally, I would like to avoid middleware-based routes for as long as we can. LB4 already supports handler-based routes that play nice with OpenAPI, I would prefer extensions to use that approach.
packages/core/src/component.ts
Outdated
| /** | ||
| * Static assets settings | ||
| */ | ||
| export type staticAssetsSettings = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
export interface StaticAssetsSettings (use interface, not type; interface names start with a capital letter)
I would also pick a slightly different name, e.g. staticAssetsDefinition or staticAssetsConfig.
review 1
|
@hacksparrow Are you proposing it as a replacement to #1664? |
packages/rest/src/types.ts
Outdated
| options?: object; | ||
| } | ||
|
|
||
| export interface Component extends CoreComponent { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's probably better to name it as ComponentWithStaticAssets.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We my want to add additional exports besides static assets later in the future. I think RestComponent would be a better name?
I also think the interface should not be inheriting from CoreComponent to allow composition of multiple transport-specific interfaces, e.g. Component & RestComponent & GrapQLComponent.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't we have a class named as RestComponent?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't we have a class named as RestComponent
@raymondfeng Yes, we have that already. For now I have renamed it toStaticComponent.
I also think the interface should not be inheriting from CoreComponent ...
@bajtos in that case we'll have to do componentCtor: Constructor<Component | StaticComponent>, etc. Is that OK?
I think this should be an independent interface from RestComponent, StaticComponent does not need any of the properties defined in RestComponent. Clubbing them together is not efficient.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't we have a class named as RestComponent?
We actually do! It's a component adding REST servers to an app.
The intention here was to describe a component that's contributing REST-specific artifacts like static assets.
What a confusion 🙈
| this.component(RestComponent); | ||
| } | ||
|
|
||
| component(componentCtor: Constructor<Component>, name?: string) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if an application does not extend from RestApplication but mount the RestComponent?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the app is extending from Application, then there is no REST server configured by default and the rest-specific exports are silently ignored.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What I'm trying to say is that RestComponent should be responsible for mounting such routes. In this case, other components contribute routes for the RestComponent.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@raymondfeng can you elaborate on the above statement, please?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A developer can do the the following:
const app = new Application();
app.component(RestComponent);
app.component(ExplorerComponent);With the current implementation, no /explorer route will be mounted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, that explains it.
@bajtos that would require us to leaveRestApplication.component alone and let Application.component take care of mounting components. However, now we'll have to have a list of supported components (Component | RestComponent etc.) in the parameters, IF we don't want RestComponent to extend Component.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const app = new Application();
app.component(RestComponent);
app.component(ExplorerComponent);Good catch! I did not realize we have RestComponent.
I need to think this through, will post a follow-up comment later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As I commented at the bottom, I think this is a wider problem that we should address outside of this pull request, I opened a new issue for that - see #1967
| // Assuming components can be synchronously instantiated | ||
| const instance = this.getSync<Component>(componentKey); | ||
| mountComponent(this, instance); | ||
| console.log(instance.staticAssets); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove console.log
@raymondfeng yes, a different approach. |
review 2
Does it assume that API explorer UI is purely based on static assets without any server-side templating/rendering? |
| options?: object; | ||
| } | ||
|
|
||
| export interface StaticComponent { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The name StaticComponent does not make sense to me. It's really a component that contributes static assets. I propose to consider #1924 so that we can give more flexibility to component providers. For example, the ExplorerComponent can expose a binding for its static assets so that the RestComponent can discover that and mount it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed StaticComponent isn't the best name. We can decide on the appropriate name later, for now I chose something to get moving.
Yes. |
|
Based on the thread #1957 (comment), it looks like I underestimated the complexity of allowing components to contribute static assets in an indirect way (by exporting an In an older chat with @hacksparrow, we were discussing the following approach: class ExplorerComponent {
constructor(
@inject(CoreBindings.APPLICATION_INSTANCE) app: Application
) {
app.static(/*...*/);
}
}I opened a new issue to discuss the best way how to allow extensions to contribute static assets: #1967 |
|
Although we did not achieve what we wanted to with this approach at this moment, at least we discovered an important missing feature - contributing static assets from components. I will be opening a new PR for local API Explorer with the approach @bajtos described above. |
|
I think that would be just simplified version of #1664 based on the assumption that ExplorerComponent only requires static assets without server-side rendering/templating. |
Yes. |
|
Closing this in favor of #1976. |
Add local API Explorer
Checklist
npm testpasses on your machinepackages/cliwere updatedexamples/*were updated