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: enable swagger-ui token #267

Closed
wants to merge 4 commits into from
Closed

Conversation

jannyHou
Copy link
Contributor

@jannyHou jannyHou commented Sep 9, 2019

The spike PR for loopbackio/loopback-next#2027
Details are explained in the readme file.

@hacksparrow
Copy link
Contributor

With the limitations and the next plans explained in the docs, this is looking good. Excellent job @jannyHou 👍

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.

Great work, @jannyHou. I especially like the screenshot you are adding to README 👍

I'd like to discuss the implementation details though, please see my second comment below.

README.md Outdated
`swagger-ui` module is built with authorization component, which will show up by
adding the security schema and operation security spec in the OpenAPI spec.

You can check the swagger
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
You can check the swagger
You can check the OpenAPI

packages/shopping/src/index.ts Outdated Show resolved Hide resolved
@jannyHou jannyHou force-pushed the spike/swagger-ui-token branch from e106cbe to ee75b64 Compare September 12, 2019 16:29
...SECURITY_SCHEMA_SPEC,
},
security: SECURITY_SPEC,
}),
Copy link
Member

Choose a reason for hiding this comment

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

I believe it should be fine to call this.api() just with Security parts, because the code gathering spec from controllers & routes will merge that new spec with the object supplied to this.api().

Have you tried to call this.api() from the constructor?

export class ShoppingApplication extends BootMixin(
  ServiceMixin(RepositoryMixin(RestApplication)),
) {
  constructor(options?: ApplicationConfig) {
    super(options);

    this.api({
      components: {
          ...SECURITY_SCHEMA_SPEC,
      },
      security: SECURITY_SPEC,
  });

  // etc.
}

In my opinion, the security spec does not depend on the app being started, therefore it should be available even before app.start() is called. Thoughts?

Copy link
Member

Choose a reason for hiding this comment

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

Should we perhaps add a story to improve app.api() to intelligently merge the new spec with any spec provided by previous calls of app.api()?

Copy link
Contributor Author

@jannyHou jannyHou Sep 13, 2019

Choose a reason for hiding this comment

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

@bajtos Now app.api() takes in an entire spec in type OpenAPISpec, not Partial<OpenAPISpec>, that's why it's not possible to merge a partial spec into the generated server's spec.

Do we want to change its behaviour to take in a partial OpenAPI spec? If so I can create a story for it :), see comment loopbackio/loopback-next#2027 (comment)

Copy link
Member

Choose a reason for hiding this comment

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

Well, what are the required fields? Isn't it just a paths object that can be empty?

this.api({
  components: {
    ...SECURITY_SCHEMA_SPEC,
  },
  security: SECURITY_SPEC,
  paths: {
    // will be filled by LoopBack from controller metadata
  }
});

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 Not really, the problem is the openapi and info fields, they are required and especially the info object, you cannot provide an empty value like '' or {}.

See my last commit, to call this.api(), I have to provide valid value for openapi and info, but the rest server doesn't overwrite them later.

The good spec filled by rest server:
Screen Shot 2019-09-16 at 11 04 53 AM

The one generated by calling this.api() with "empty" openapi and info fields:

Screen Shot 2019-09-16 at 11 08 58 AM

We can improve this by defining and checking the empty value of openapi and info, like

// in rest server
if (spec.openapi == '') {// generate openapi field }
if (spec.info.title == '' && spec.info.version == '') {// generate info field}

Thought? If this is the right direction for improvement I can create an issue (or just submit a PR)

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, thank you for explaining the details!

I think that ideally, I'd like:

  • app.api() require fields like info to force the user to provide these required fields. The current implementation is good for that 👍
  • Have a way for extensions to contribute additional OpenAPI metadata. This could be a new method in Application or RestServer, but we can also use binding-based approach where the app collects OpenAPI metadata from bindings with a given tag (or other metadata-based specification). I think this is out of scope of your current work (both on the spike and on enabling auth in swagger-ui), so let's create a spike issue to research different options.

As for this spike PR, I think the current proposal (as shown in the committed code) is good enough and no further changes are necessary 👍

/cc @raymondfeng

Copy link
Contributor

Choose a reason for hiding this comment

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

We're already exploring the idea to create an extension point to enhance the generated OpenAPI spec so that the application or other extensions can add code to transform the spec.

@@ -83,6 +95,7 @@ export class UserController {
}

@get('/users/{userId}', {
security: SECURITY_SPEC_OPERATION,
Copy link
Member

Choose a reason for hiding this comment

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

IIUC, this is configuring basicAuth schema. Is that intentional? I thought that we use bearerAuth schema everywhere. Or is this just for the spike?

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 Oh this is just a demo, for spike only, I just want to verify that a operation level security policy(which is different than the global bearerAuth) works.

I thought that we use bearerAuth schema everywhere

Exactly 💯

Copy link

@agnes512 agnes512 left a comment

Choose a reason for hiding this comment

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

I don't know much about the implement part. But the README writeup is pretty straightforward~! Just a few comments.

README.md Outdated
![set-token](/imgs/set-token.png)

Paste the token you just copied in the field, then click "Authorize". The token
will be be hidden:

Choose a reason for hiding this comment

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

-> will be hidden

README.md Outdated
}
```

The "Authorize" dialog will show up your new entry as:
Copy link

@agnes512 agnes512 Sep 13, 2019

Choose a reason for hiding this comment

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

your new entry -> the new added entry better?

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 with a small comment to consider.

@@ -47,6 +48,13 @@ export class ShoppingApplication extends BootMixin(
) {
constructor(options?: ApplicationConfig) {
super(options);
this.api({
openapi: '3.0.0',
info: {title: '', version: ''},
Copy link
Member

Choose a reason for hiding this comment

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

Can you fill title and version with something more meaningful please? Personally, I'd use metadata from package.json.

import * as package from '../package.json';
// ...

info: {title: package.name, version: package.version}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

updated 👌

@raymondfeng
Copy link
Contributor

@jannyHou Please fix the code-lint issue.

@jannyHou jannyHou force-pushed the spike/swagger-ui-token branch from 434a7d2 to 9e0bd5f Compare September 16, 2019 18:04
@jannyHou
Copy link
Contributor Author

Follow up stories see loopbackio/loopback-next#2027 (comment)

this.api({
openapi: '3.0.0',
info: {title: pkg.name, version: pkg.version},
paths: {},
Copy link
Contributor Author

Choose a reason for hiding this comment

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

servers: [{url: '/'}]

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@jannyHou
Copy link
Contributor Author

Closed as the tutorial is added :)

@jannyHou jannyHou closed this Oct 22, 2019
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.

5 participants