Skip to content

Conversation

@hacksparrow
Copy link
Contributor

Add local API Explorer

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

@hacksparrow hacksparrow self-assigned this Nov 1, 2018
@hacksparrow hacksparrow changed the title feat: local API Explorer [WIP] feat: local API Explorer Nov 1, 2018
Add local API Explorer
/**
* A map of name/class pairs for binding static assets
*/
export type staticAssetsMap = {
Copy link
Member

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',
Copy link
Member

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';
Copy link
Member

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);
Copy link
Member

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);
  }
}

/**
* An array of directory to serve static assets
*/
staticAssets?: staticAssetsSettings[];
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 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.

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 generalize it to allow components to contribute middleware based routes?

Copy link
Contributor Author

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.

Copy link
Member

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.

/**
* Static assets settings
*/
export type staticAssetsSettings = {
Copy link
Member

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
@raymondfeng
Copy link
Contributor

@hacksparrow Are you proposing it as a replacement to #1664?

options?: object;
}

export interface Component extends CoreComponent {
Copy link
Contributor

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.

Copy link
Member

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.

Copy link
Contributor

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?

Copy link
Contributor Author

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 to StaticComponent.

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.

Copy link
Member

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) {
Copy link
Contributor

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?

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 app is extending from Application, then there is no REST server configured by default and the rest-specific exports are silently ignored.

Copy link
Contributor

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.

Copy link
Contributor Author

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?

Copy link
Contributor

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.

Copy link
Contributor Author

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.

Copy link
Member

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.

Copy link
Member

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);
Copy link
Contributor

Choose a reason for hiding this comment

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

Remove console.log

@dhmlau dhmlau mentioned this pull request Nov 1, 2018
3 tasks
@hacksparrow
Copy link
Contributor Author

Are you proposing it as a replacement to #1664?

@raymondfeng yes, a different approach.

review 2
@raymondfeng
Copy link
Contributor

@raymondfeng yes, a different approach.

Does it assume that API explorer UI is purely based on static assets without any server-side templating/rendering?

options?: object;
}

export interface StaticComponent {
Copy link
Contributor

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.

Copy link
Contributor Author

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.

@hacksparrow
Copy link
Contributor Author

Does it assume that API explorer UI is purely based on static assets without any server-side templating/rendering?

Yes.

@bajtos
Copy link
Member

bajtos commented Nov 2, 2018

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 staticAssets array). I am proposing to abandon that approach for now and focus on how to implement self-hosted API explorer using the tools that LB provides right now.

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

@hacksparrow
Copy link
Contributor Author

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.

@raymondfeng
Copy link
Contributor

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.

@hacksparrow
Copy link
Contributor Author

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.

@hacksparrow
Copy link
Contributor Author

Closing this in favor of #1976.

@hacksparrow hacksparrow closed this Nov 5, 2018
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