Skip to content
This repository was archived by the owner on Aug 13, 2024. It is now read-only.

6 - Implement swagger node middleware as the base API structure#23

Open
n0rbs wants to merge 5 commits intoIBM-Blockchain-Starter-Kit:masterfrom
n0rbs:6-swagger-middleware
Open

6 - Implement swagger node middleware as the base API structure#23
n0rbs wants to merge 5 commits intoIBM-Blockchain-Starter-Kit:masterfrom
n0rbs:6-swagger-middleware

Conversation

@n0rbs
Copy link

@n0rbs n0rbs commented Mar 7, 2019

Summary

  • Use swagger node middleware as the base API structure. Swagger definition of routes, operation and security are tightly integrated in the swagger.yaml file.
  • Separation of controller and model concerns

n0rbs added 4 commits March 7, 2019 15:02
Signed-off-by: Norbert Aluba Dela Pena <norbert@sg.ibm.com>
Signed-off-by: Norbert Aluba Dela Pena <norbert@sg.ibm.com>
Signed-off-by: Norbert Aluba Dela Pena <norbert@sg.ibm.com>
…ger file

Signed-off-by: Norbert Aluba Dela Pena <norbert@sg.ibm.com>
@n0rbs n0rbs force-pushed the 6-swagger-middleware branch from dda7c00 to d66235a Compare March 7, 2019 07:02
@n0rbs n0rbs requested a review from eymorale March 7, 2019 15:32
@n0rbs
Copy link
Author

n0rbs commented Mar 7, 2019

@eymorale you can check how the middleware works and please let me know what you think. I haven't added the authentication libs as I just want to showcase the base template

@eymorale
Copy link
Collaborator

eymorale commented Mar 7, 2019

@n0rbs I'm not very familiar with swagger-node-runner. How would you mount middleware functions to a specific path(s) with this implementation?

@n0rbs
Copy link
Author

n0rbs commented Mar 8, 2019

@eymorale For now, we can just wrap the original function with the middleware we want to use.

const { jwtMiddleware } = require('./path/to/middleware.jwt');


/**
 * @swagger
 * My SwaggerJSDoc config I coupled with swagger-tools
 *
 **/
const criticalAction = (request, response, next) => jwtMiddleware(request, response, ( error ) => {
    if (error) return void next( error );
    // my ordinary express code
});

module.exports = {
    criticalAction,
};

or

module.exports.createPuppy = [
  (req, res, next) => {
    res.myOwnVar = 'this is a middleware.';
    next();
  },
  (req, res, next) => {
    res.end('success');
  }
]

Source: apigee-127/swagger-tools#291

@eymorale
Copy link
Collaborator

eymorale commented Mar 8, 2019

@n0rbs sorry for the delay. I've pulled down your changes and I'm in the middle of testing it out. I got it working mostly out of the box with a few tweaks that I'll add as inline comments in the code changes for you to address.

Specifically I want to thoroughly test the bit about mounting route level middleware functions ^. We are working on adding a middleware function for some fabric plumbing code (plus any other middleware requirements someone might have). But if all that checks out, I think this code is good.

I also think it would be helpful to add a section in the README about the swagger-node-runner and how that works regarding registering your paths to the application and doing the middleware to route them to their controllers, etc as some developers (including myself) might be unfamiliar with it and how to use it.

@@ -36,7 +46,6 @@ paths:
examples:
application/json: { "success": "true", "message": "Server is up!", "status": "UP" }

Copy link
Collaborator

Choose a reason for hiding this comment

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

Realized we don't have the ping route in the swagger so the route wasn't registered:

/ping:
    x-swagger-router-controller: ping
    get:
      operationId: pingMe
      tags:
        - Ping
      description: Ping blockchain network
      responses:
        200:
          description: Ping response
          schema:
            $ref: "#/definitions/ResponseBody"

/**
* pingMe
*/
controller.pingMe = (req, res) => {
Copy link
Collaborator

Choose a reason for hiding this comment

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

this should be controller.pingMe = async (req, res) => {

*/
controller.pingMe = (req, res) => {
try {
const result = Ping.pingCC();
Copy link
Collaborator

Choose a reason for hiding this comment

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

This should be const result = await Ping.pingCC();

@n0rbs
Copy link
Author

n0rbs commented Mar 9, 2019

@eymorale please take your time reviewing. Since we will be collaborating and you will be writing the code, you need to fully grasp how it works. Let's further discuss and make sure you're comfortable with the framework.

Yes, I didn't add the /ping route. The middleware will complain that the route is not defined in the swagger file.

re README. I have written instructions on how to structure and implement the routes in swagger and a typical implementation in the controller. The instruction is in the GHE repo, api-template. Let me post them here for reference and I'll commit another update for the README, containing the instructions.

Swagger definition

# Routes to call a chaincode function either via invoke or query method

  # ...api/v1/blockchains/invoke/bulk_charge_fees
  /blockchains/invoke/{fname}:
    # Folder name, blockchains, under the ./api/v1/ directory (Required)
    x-swagger-router-controller: blockchains
    post:
      ...
      # The matching function name, bcInvoke, exposed by the controller
      operationId: bcInvoke
      ...
      # Required token, chaincode function, and JSON attributes
      parameters:
        - $ref: '#/components/parameters/X-org-api-token'
        - $ref: '#/components/parameters/Fname'
      requestBody:
        $ref: '#/components/requestBodies/Body'

  # api/v1/blockchains/query/get_charging_fee_by_account
  /blockchains/query/{fname}:
    x-swagger-router-controller: blockchains
    post:
      ...
      operationId: bcQuery
      ...

Controller

controller.bcInvoke = (req, res) => {
  try {
    // Extract the parameters from the req.swagger object
    const { value: fname } = req.swagger.params.fname;
    const { value: attrs } = req.swagger.params.body;
    ...
    // Impelementation of basic logic here
    ...
    return res.json(response); // 201 - res.status(201), default is 200
  } catch (error) {
    return res.status(500).json({
      message: `Failed to perform operation ${fn}`,
    });
  }
};

// Exposed methods for `operationId` in swagger file - bcInvoke, bcQuery, anotherNameOfBcInvoke
module.exports = {
    bcInvoke,
    bcQuery,
    anotherNameOfBcInvoke: bcInvoke,
}

…ementation

Signed-off-by: Norbert Aluba Dela Pena <norbert@sg.ibm.com>
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants