review
In the last section, we talked about the entire lifecycle of use case execution. As I came to think of it later, it would have taken a lot of time to write if it had to be fully compatible with code generation from the start. In order to give you a good experience, you still have to eat one bite at a time, not all at once.
So let's finish the code free mode first
Design a few core tables roughly
In order to facilitate the management of our use cases in the future, we will definitely need to do some classification and permission control, so we will first design several core tables:
-
The project table
Use cases are separated in each project, similar to the concept of directory. Different use cases are located in different projects, which facilitates the classification of use cases.
-
Project Role table
Each project needs to have its own members. For members, their responsibilities are different because of their different identities. We give several simple identities, such as:
Group leader and group members, in which group leader can change the relevant permissions of group members.
-
Test case table
It stores the information of each use case and is the smallest execution unit of the test platform.
The project table models/project. Py
from app.models import db
from datetime import datetime
class Project(db.Model) :
id = db.Column(db.INT, primary_key=True)
name = db.Column(db.String(16), unique=True, index=True)
owner = db.Column(db.INT)
created_at = db.Column(db.DATETIME, nullable=False)
updated_at = db.Column(db.DATETIME, nullable=False)
deleted_at = db.Column(db.DATETIME)
create_user = db.Column(db.INT, nullable=True)
update_user = db.Column(db.INT, nullable=True)
private = db.Column(db.BOOLEAN, default=False)
def __init__(self, name, owner, create_user, private=False) :
self.name = name
self.owner = owner
self.private = private
self.created_at = datetime.now()
self.updated_at = datetime.now()
self.create_user = create_user
self.update_user = create_user
self.deleted_at = None
Copy the code
In addition to creation/update time/user, there are also the following fields:
Owner (group leader) Name (project name) private
Project role table Models/project_role-py
from app.models import db
from datetime import datetime
class ProjectRole(db.Model) :
id = db.Column(db.INT, primary_key=True)
project_id = db.Column(db.INT, index=True)
project_role = db.Column(db.INT, index=True)
created_at = db.Column(db.DATETIME, nullable=False)
updated_at = db.Column(db.DATETIME, nullable=False)
deleted_at = db.Column(db.DATETIME)
create_user = db.Column(db.INT, nullable=True)
update_user = db.Column(db.INT, nullable=True)
def __init__(self, project_id, project_role, create_user) :
self.project_id = project_id
self.project_role = project_role
self.created_at = datetime.now()
self.updated_at = datetime.now()
self.create_user = create_user
self.update_user = create_user
self.deleted_at = None
Copy the code
Similar to the previous form, project_id is the project ID and project_Role is the project role.
We stipulate that:
1: group leader 2: group member
Test case table Models /test_case.py
from app.models import db
from datetime import datetime
class TestCase(db.Model) :
id = db.Column(db.INT, primary_key=True)
name = db.Column(db.String(32), unique=True, index=True)
request_type = db.Column(db.INT, default=1, comment=Request type 1: HTTP 2: GRPC 3: Dubbo)
url = db.Column(db.TEXT, nullable=False, comment="The request url")
request_method = db.Column(db.String(12), nullable=True, comment="Request mode, null if non-HTTP")
request_header = db.Column(db.TEXT, comment="Request header, nullable")
project_id = db.Column(db.INT, comment="Project")
tag = db.Column(db.String(64), comment="Use Case label")
status = db.Column(db.INT, comment="Use case status: 1: pending 2: temporarily closed 3: Normal operation")
expected = db.Column(db.TEXT, comment="Expected results, support for EL expressions", nullable=False)
created_at = db.Column(db.DATETIME, nullable=False)
updated_at = db.Column(db.DATETIME, nullable=False)
deleted_at = db.Column(db.DATETIME)
create_user = db.Column(db.INT, nullable=False)
update_user = db.Column(db.INT, nullable=False)
def __init__(self, name, request_type, url, project_id, tag, status, expected, create_user, request_header=None,
request_method=None) :self.name = name self.request_type = request_type self.url = url self.project_id = project_id self.tag = tag self.status = status self.expected = expected self.create_user = create_user self.update_user = create_user self.request_header = request_header self.request_method = request_method self.created_at = datetime.now() self.updated_at = datetime.now()Copy the code
Basically, the content is in the comment, you can just watch, and there is no more explanation. Certainly there are some features that are not considered, after the order can be added to solve the field.
Write a decorator that checks user permissions
Create_user = update_user; create_user = update_user; create_user = update_user;
Remember the method you wrote earlier to resolve tokens? Let’s write a decorator that will automatically determine whether a user’s token is valid and get user information.
If the permission is not within the specified range, the normal member cannot invoke this interface. If the permission is not within the specified range, the normal member cannot invoke this interface.
Complete code:
from functools import wraps
from flask import request, jsonify
from app import pity
from app.middleware.Jwt import UserToken
FORBIDDEN = "Sorry, you don't have enough clearance."
class SingletonDecorator:
def __init__(self, cls) :
self.cls = cls
self.instance = None
def __call__(self, *args, **kwargs) :
if self.instance is None:
self.instance = self.cls(*args, **kwargs)
return self.instance
def permission(role=pity.config.get("GUEST")) :
def login_required(func) :
@wraps(func)
def wrapper(*args, **kwargs) :
try:
headers = request.headers
token = headers.get('token')
if token is None:
return jsonify(dict(code=401, msg="User information authentication failed, please check"))
user_info = UserToken.parse_token(token)
Write user information to kwargs
kwargs["user_info"] = user_info
except Exception as e:
return jsonify(dict(code=401, msg=str(e)))
# check whether the user has sufficient permissions, if not, return directly, do not continue
if user_info.get("role".0) < role:
return jsonify(dict(code=400, msg=FORBIDDEN))
return func(*args, **kwargs)
return wrapper
return login_required
Copy the code
kwargs["user_info"] = user_info
Focus on this sentence.
Add permissions to the HTTP/Request interface we wrote earlier
Because our interface temporarily supports login users, so the default permission is ok, that is, user role is 0(ordinary user)!
The important thing to note here is that our http_request must have a user_info parameter, otherwise we will get an error because we may need user information later.
Revamp the front-end code
Since our front-end will not bring user token when requesting the interface, we need to transform the front-end request method:
- Send HTTP requests with token data
- Verification returns code, if it is 401, the user will be automatically logged out, indicating that the user has not logged in!
- Create a SRC/utils/auth. Js
import { message } from 'antd';
export default {
headers: (json = true) = > {
const token = localStorage.getItem('pityToken');
const headers = { token };
if (json) {
headers['Content-Type'] = 'application/json';
}
return headers;
},
response: (res, info = false) = > {
if (res.code === 0) {
if (info) {
message.info(res.msg);
}
return true;
}
if (res.code === 401) {
// Indicates that the user is not authenticated
message.info(res.msg);
localStorage.setItem('pityToken'.null);
localStorage.setItem('pityUser'.null);
window.location.href = '/user/login';
}
message.error(res.msg);
return false; }};Copy the code
The method to obtain the token and form headers is written here, and the code for token expiration and insufficient permission is also written for different processing.
- Modify the SRC/services/request. Js
- Modify request result:
-
To begin testing
We found that our error message was not fully displayed because we needed to not validate the request in case of failure, so we also needed to adjust the back-end interface:
Check whether the value of status is True. If not, return the 110 status code
Let’s test it with a dynamic diagram:
Then test the permissions
From the application of the developer tool, you can see:
Then we adjust the permissions so that only the administrator can request the interface
Here’s a GIF:
After finishing work, recently qingming can be slightly updated a little more 😳
Good night to my twelve (including myself) viewers!
AD time
Preview address: http://47.112.32.195/
Back end code address: github.com/wuranxu/pit…
Front-end code address: github.com/wuranxu/pit…
Welcome to pay attention to the public number test development pit, there are groups together to discuss related issues ah! If the group two-dimensional code expired, you can add my personal wechat: Wuranxu I pull you into the group ~