Ant Design Pro V5 OpenAPI

Posted by mona02 on Tue, 08 Mar 2022 00:26:21 +0100

preface

After Ant Design Pro is upgraded to v5, an openapi plug-in is introduced. What is this used for?

In the past, when writing service files, we used to customize the interface name according to swagger, write our own comments, and write our own interface call methods. If typescript (hereinafter referred to as Ts) is used, we also need to define the field type one field by one. When the back-end interface is not written, we also need to write our own mock data for self-tuning.

Now, Pro v5 comes out with an OpenAPI. We only need a few simple configurations to automatically generate a service file according to swagger. This file contains almost all your requirements. The manual ones mentioned above have become automatic washing machines (they don't need to dry their own clothes) and automatically distinguish directories (according to swagger directories), The name of the connection port has been defined for you (it's the gospel of naming difficult households).

Install plug-ins

When we use umi to build the framework, we choose the Pro v5 version to have its own openapi plug-in. If you use the unofficial version of v5 (the unofficial version you upgraded to v5 is probably not understood by this rookie), you can install this plug-in through the following command:

yarn add @umijs/plugin-openapi

perhaps

npm i @umijs/plugin-openapi–save

Recall the directory structure of the umi framework

Next, let's take a look at the specific operation.

First, config TS configuration

import { join } from 'path';
openAPI: {
    requestLibPath: "import { request } from 'umi'",
    // Online version
    // schemaPath: "https://gw.alipayobjects.com/os/antfincdn/M%24jrzTTYJN/oneapi.json",
    // Local version
    schemaPath: join(__dirname, 'oneapi.json'),
    mock: false,
  }

• requestLibPath: the way to introduce request
Generally, we use the request provided by umi, but sometimes we need to change the request configuration, such as creating a new request file under utils: requestLibPath: "import request from '@ utils/request'",

• schemaPath: the json address of the generated service file
a. Online version: i.e. swagger address, which needs json format
b. Local json file: generally, a new json file is created in the config file

• mock: set to true to automatically generate false data
First take a look at the format given by schemaPath on the official website: https://gw.alipayobjects.com/os/antfincdn/M%24jrzTTYJN/oneapi.json

{
  "openapi": "3.0.1",
  "info": {
    "title": "Ant Design Pro",
    "version": "1.0.0"
  },
  "servers": [
    {
      "url": "http://localhost:8000/"
    },
    {
      "url": "https://localhost:8000/"
    }
  ],
  "paths": {
    "/api/currentUser": {
      "get": {
        "tags": ["api"],
        "description": "Get current user",
        "operationId": "currentUser",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CurrentUser"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      },
      "x-swagger-router-controller": "api"
    },
    "/api/login/captcha": {
      "post": {
        "description": "Send verification code",
        "operationId": "getFakeCaptcha",
        "tags": ["login"],
        "parameters": [
          {
            "name": "phone",
            "in": "query",
            "description": "cell-phone number",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FakeCaptcha"
                }
              }
            }
          }
        }
      }
    },
    "/api/login/outLogin": {
      "post": {
        "description": "Login interface",
        "operationId": "outLogin",
        "tags": ["login"],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      },
      "x-swagger-router-controller": "api"
    },
    "/api/login/account": {
      "post": {
        "tags": ["login"],
        "description": "Login interface",
        "operationId": "login",
        "requestBody": {
          "description": "Login system",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LoginParams"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LoginResult"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        },
        "x-codegen-request-body-name": "body"
      },
      "x-swagger-router-controller": "api"
    },
    "/api/notices": {
      "summary": "getNotices",
      "description": "NoticeIconItem",
      "get": {
        "tags": ["api"],
        "operationId": "getNotices",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NoticeIconList"
                }
              }
            }
          }
        }
      }
    },
    "/api/rule": {
      "get": {
        "tags": ["rule"],
        "description": "Get rule list",
        "operationId": "rule",
        "parameters": [
          {
            "name": "current",
            "in": "query",
            "description": "Current page number",
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "pageSize",
            "in": "query",
            "description": "Page capacity",
            "schema": {
              "type": "number"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RuleList"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": ["rule"],
        "description": "New rule",
        "operationId": "addRule",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RuleListItem"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      },
      "put": {
        "tags": ["rule"],
        "description": "New rule",
        "operationId": "updateRule",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RuleListItem"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": ["rule"],
        "description": "Delete rule",
        "operationId": "removeRule",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "description": "Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      },
      "x-swagger-router-controller": "api"
    },
    "/swagger": {
      "x-swagger-pipe": "swagger_raw"
    }
  },
  "components": {
    "schemas": {
      "CurrentUser": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "avatar": {
            "type": "string"
          },
          "userid": {
            "type": "string"
          },
          "email": {
            "type": "string"
          },
          "signature": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "group": {
            "type": "string"
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "key": {
                  "type": "string"
                },
                "label": {
                  "type": "string"
                }
              }
            }
          },
          "notifyCount": {
            "type": "integer",
            "format": "int32"
          },
          "unreadCount": {
            "type": "integer",
            "format": "int32"
          },
          "country": {
            "type": "string"
          },
          "access": {
            "type": "string"
          },
          "geographic": {
            "type": "object",
            "properties": {
              "province": {
                "type": "object",
                "properties": {
                  "label": {
                    "type": "string"
                  },
                  "key": {
                    "type": "string"
                  }
                }
              },
              "city": {
                "type": "object",
                "properties": {
                  "label": {
                    "type": "string"
                  },
                  "key": {
                    "type": "string"
                  }
                }
              }
            }
          },
          "address": {
            "type": "string"
          },
          "phone": {
            "type": "string"
          }
        }
      },
      "LoginResult": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string"
          },
          "type": {
            "type": "string"
          },
          "currentAuthority": {
            "type": "string"
          }
        }
      },
      "PageParams": {
        "type": "object",
        "properties": {
          "current": {
            "type": "number"
          },
          "pageSize": {
            "type": "number"
          }
        }
      },
      "RuleListItem": {
        "type": "object",
        "properties": {
          "key": {
            "type": "integer",
            "format": "int32"
          },
          "disabled": {
            "type": "boolean"
          },
          "href": {
            "type": "string"
          },
          "avatar": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "owner": {
            "type": "string"
          },
          "desc": {
            "type": "string"
          },
          "callNo": {
            "type": "integer",
            "format": "int32"
          },
          "status": {
            "type": "integer",
            "format": "int32"
          },
          "updatedAt": {
            "type": "string",
            "format": "datetime"
          },
          "createdAt": {
            "type": "string",
            "format": "datetime"
          },
          "progress": {
            "type": "integer",
            "format": "int32"
          }
        }
      },
      "RuleList": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/RuleListItem"
            }
          },
          "total": {
            "type": "integer",
            "description": "Total contents of the list",
            "format": "int32"
          },
          "success": {
            "type": "boolean"
          }
        }
      },
      "FakeCaptcha": {
        "type": "object",
        "properties": {
          "code": {
            "type": "integer",
            "format": "int32"
          },
          "status": {
            "type": "string"
          }
        }
      },
      "LoginParams": {
        "type": "object",
        "properties": {
          "username": {
            "type": "string"
          },
          "password": {
            "type": "string"
          },
          "autoLogin": {
            "type": "boolean"
          },
          "type": {
            "type": "string"
          }
        }
      },
      "ErrorResponse": {
        "required": ["errorCode"],
        "type": "object",
        "properties": {
          "errorCode": {
            "type": "string",
            "description": "Error code of business agreement"
          },
          "errorMessage": {
            "type": "string",
            "description": "Business error message"
          },
          "success": {
            "type": "boolean",
            "description": "Is the business request successful"
          }
        }
      },
      "NoticeIconList": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/NoticeIconItem"
            }
          },
          "total": {
            "type": "integer",
            "description": "Total contents of the list",
            "format": "int32"
          },
          "success": {
            "type": "boolean"
          }
        }
      },
      "NoticeIconItemType": {
        "title": "NoticeIconItemType",
        "description": "Enumeration of read unread lists",
        "type": "string",
        "properties": {},
        "enum": ["notification", "message", "event"]
      },
      "NoticeIconItem": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "extra": {
            "type": "string",
            "format": "any"
          },
          "key": { "type": "string" },
          "read": {
            "type": "boolean"
          },
          "avatar": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "datetime": {
            "type": "string",
            "format": "date"
          },
          "description": {
            "type": "string"
          },
          "type": {
            "extensions": {
              "x-is-enum": true
            },
            "$ref": "#/components/schemas/NoticeIconItemType"
          }
        }
      }
    }
  }
}

After taking a look at the swagger I use now, it is somewhat different from the example on the official website. However, when building swagger, we can get the files in the format we need according to the OpenApi standard format

After some research, although it is different from the official website, it can also be used

Generally, we use the swagger address: http://localhost:8080/swagger-ui.html
We can't use this address. It's different from the format required by schemaPath
You'd better do it yourself and have plenty of food and clothing

  1. (online version) manually modify swagger address, / swagger UI HTML changed to / V2 / API docs
    The page is like this
  2. (local file)
    • open the previous step http://localhost:8080/v2/api -The path of docs, and then Ctrl+A, Ctrl+C or F12 open the mode page, select Network, view the / V2 / API docs interface, and then Response, Ctrl+A, Ctrl+C
    • Ctrl+V paste into config / new json file

Secondly, package JSON configuration

(the umi framework Pro v5 version comes with this command, so you don't need to configure it anymore)

"openapi": "umi openapi",

Finally, a command is done

npm run openapi

effect

Directory structure of generated files (under services file)

The name of the folder generated by Ant Design Pro # automatically (you can modify it manually)
│ ├── index.ts # interface public import file
│ ├── api.ts # interface definition file (the file name is based on the name defined in the swagger directory)
│ ├── typings.d.ts # Ts type file

(sometimes the directory on swagger is in Chinese, so the generated service file is also in Chinese, which can be modified through communication with the backend or by yourself)
It seems that it's still a little troublesome. There are still many places that need to be modified by yourself. However, compared with handwriting, Ts type is also very troublesome. Such a comparison is much simpler.

index.ts

// @ts-ignore
/* eslint-disable */
// API update time:
// API unique identifier:
import * as api from './api';
import * as login from './login';
export default {
  api,
  login,
};

api.ts

// Configuration of requestLibPath
import { request } from 'umi';

/** Get rule list GET /api/rule */
export async function rule(params: API.PageParams, options?: { [key: string]: any }) {
  return request<API.RuleList>('/api/rule', {
    method: 'GET',
    params: {
      ...params,
    },
    ...(options || {}),
  });
}

typings.d.ts

This file will automatically generate the Chinese name, dictionary value, remarks and ts type of the field corresponding to the field. One bad point is that the ts type definitions are put in the same file, which will not give you a directory. If you want to open them, you can only manually

declare namespace API {
  type RuleListItem = {
    key?: number;
    disabled?: boolean;
    href?: string;
    avatar?: string;
    name?: string;
    owner?: string;
    desc?: string;
    callNo?: number;
    status?: number;
    updatedAt?: string;
    createdAt?: string;
    progress?: number;
  };

  type RuleList = {
    data?: RuleListItem[];
    /** Total contents of the list */
    total?: number;
    success?: boolean;
  };
}

mock

import { Request, Response } from 'express';

export default {
  'GET /api/rule': (req: Request, res: Response) => {
    res.status(200).send({
      data: [
        {
          key: 86,
          disabled: false,
          href: 'https://ant.design',
          avatar: 'https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg',
          name: 'Luo Xiulan',
          owner: 'Garcia',
          desc: 'It's hard for star to eliminate and build the wind, but it's hard to wait for this day.',
          callNo: 96,
          status: 89,
          updatedAt: 'PpVmJ50',
          createdAt: 'FbRG',
          progress: 100,
        },
      ],
      total: 98,
      success: false,
    });
  },
};

Over!

Topics: React