diff --git a/services/community/api/auth/token.go b/services/community/api/auth/token.go
index ffa453c6..c20f2e77 100644
--- a/services/community/api/auth/token.go
+++ b/services/community/api/auth/token.go
@@ -75,11 +75,11 @@ func ExtractTokenID(r *http.Request, db *gorm.DB) (uint32, error) {
tokenValid := resp.StatusCode == 200
token, _, err := new(jwt.Parser).ParseUnverified(tokenString, jwt.MapClaims{})
- claims, ok := token.Claims.(jwt.MapClaims)
if err != nil {
log.Println(err)
return 0, err
}
+ claims, ok := token.Claims.(jwt.MapClaims)
if ok && tokenValid {
name := claims["sub"]
diff --git a/services/community/api/controllers/post_controller.go b/services/community/api/controllers/post_controller.go
index 3538ac26..6077fcb0 100644
--- a/services/community/api/controllers/post_controller.go
+++ b/services/community/api/controllers/post_controller.go
@@ -71,15 +71,17 @@ func (s *Server) GetPostByID(w http.ResponseWriter, r *http.Request) {
}
+
+
//GetPost Vulnerabilities
func (s *Server) GetPost(w http.ResponseWriter, r *http.Request) {
//post := models.Post{}
limit_param := r.URL.Query().Get("limit")
- limit := 30
+ var limit int64 = 30
err := error(nil)
if limit_param != "" {
// Parse limit_param and set to limit
- limit, err = strconv.Atoi(limit_param)
+ limit, err = strconv.ParseInt(limit_param, 10, 64)
if err != nil {
limit = 30
}
@@ -88,10 +90,10 @@ func (s *Server) GetPost(w http.ResponseWriter, r *http.Request) {
limit = 50
}
- offset := 0
+ var offset int64 = 0
offset_param := r.URL.Query().Get("offset")
if offset_param != "" {
- offset, err = strconv.Atoi(offset_param)
+ offset, err = strconv.ParseInt(offset_param, 10, 64)
if err != nil {
offset = 0
}
diff --git a/services/community/api/models/coupon.go b/services/community/api/models/coupon.go
index 171772b7..7cb0b58c 100644
--- a/services/community/api/models/coupon.go
+++ b/services/community/api/models/coupon.go
@@ -46,20 +46,20 @@ func (c *Coupon) Prepare() {
func (c *Coupon) Validate() error {
if c.CouponCode == "" {
- return errors.New("Required Coupon Code")
+ return errors.New("required coupon code")
}
if c.Amount == "" {
- return errors.New("Required Coupon Amount")
+ return errors.New("required coupon amount")
}
return nil
}
-//SaveCoupon save coupon database.
+//SaveCoupon save coupon in database.
func SaveCoupon(client *mongo.Client, coupon Coupon) (Coupon, error) {
// Get a handle for your collection
- collection := client.Database("crapi").Collection("coupon")
+ collection := client.Database("crapi").Collection("coupons")
// Insert a single document
insertResult, err := collection.InsertOne(context.TODO(), coupon)
@@ -67,7 +67,6 @@ func SaveCoupon(client *mongo.Client, coupon Coupon) (Coupon, error) {
log.Println(err)
}
log.Println("Inserted a single document: ", insertResult.InsertedID)
-
return coupon, err
}
diff --git a/services/community/api/models/post.go b/services/community/api/models/post.go
index 71f9fabd..6a5f80ae 100644
--- a/services/community/api/models/post.go
+++ b/services/community/api/models/post.go
@@ -19,7 +19,6 @@ import (
"errors"
"html"
"log"
- "reflect"
"strings"
"time"
@@ -51,17 +50,24 @@ func (post *Post) Prepare() {
post.CreatedAt = time.Now()
}
+type PostsResponse struct {
+ Posts []Post `json:"posts"`
+ NextOffset *int64 `json:"next_offset"`
+ PrevOffset *int64 `json:"previous_offset"`
+ Total int `json:"total"`
+}
+
//Validate data of post
func (post *Post) Validate() error {
if post.Title == "" {
- return errors.New("Required Title")
+ return errors.New("required title")
}
if post.Content == "" {
- return errors.New("Required Content")
+ return errors.New("tequired content")
}
if post.AuthorID < 1 {
- return errors.New("Required Author")
+ return errors.New("required author")
}
return nil
}
@@ -97,42 +103,57 @@ func GetPostByID(client *mongo.Client, ID string) (Post, error) {
collection := client.Database("crapi").Collection("post")
filter := bson.D{{Key: "id", Value: ID}}
err := collection.FindOne(context.TODO(), filter).Decode(&post)
+ if err != nil {
+ log.Println(err)
+ }
return post, err
}
//FindAllPost return all recent post
-func FindAllPost(client *mongo.Client, offset int, limit int) ([]interface{}, error) {
- post := []Post{}
-
+func FindAllPost(client *mongo.Client, offset int64, limit int64) (PostsResponse, error) {
+ postList := []Post{}
+ var postsResponse PostsResponse = PostsResponse{}
options := options.Find()
options.SetSort(bson.D{{Key: "_id", Value: -1}})
- options.SetLimit(int64(limit))
- options.SetSkip(int64(offset * limit))
+ options.SetLimit(limit)
+ options.SetSkip(offset)
+ ctx := context.Background()
collection := client.Database("crapi").Collection("post")
- cur, err := collection.Find(context.Background(), bson.D{}, options)
+ cur, err := collection.Find(ctx, bson.D{}, options)
if err != nil {
- log.Println(err)
+ log.Println("Error in finding posts: ", err)
+ return postsResponse, err
}
- log.Println(cur)
- objectType := reflect.TypeOf(post).Elem()
- var list = make([]interface{}, 0)
- defer cur.Close(context.Background())
- for cur.Next(context.Background()) {
- result := reflect.New(objectType).Interface()
- err := cur.Decode(result)
-
+ for cur.Next(ctx) {
+ var elem Post
+ err := cur.Decode(&elem)
if err != nil {
- log.Println(err)
- return nil, err
+ log.Println("Error in decoding posts: ", err)
+ return postsResponse, err
}
+ postList = append(postList, elem)
+ }
- list = append(list, result)
+ postsResponse.Posts = postList
+ // get posts count for pagination
+ count, err1 := collection.CountDocuments(context.Background(), bson.D{})
+ if err1 != nil {
+ log.Println("Error in counting posts: ", err1)
+ return postsResponse, err1
}
- if err := cur.Err(); err != nil {
- return nil, err
+ if offset - limit >= 0 {
+ tempOffset := offset - limit
+ postsResponse.PrevOffset = &tempOffset
}
-
- return list, err
+ if offset + limit < count {
+ tempOffset := offset + limit
+ postsResponse.NextOffset = &tempOffset
+ }
+ postsResponse.Total = len(postList)
+ if err = cur.Err(); err != nil {
+ log.Println("Error in cursor: ", err)
+ }
+ return postsResponse, err
}
diff --git a/services/community/api/models/user.go b/services/community/api/models/user.go
index 1c6e44f8..f95d54d0 100644
--- a/services/community/api/models/user.go
+++ b/services/community/api/models/user.go
@@ -62,36 +62,36 @@ func (u *Author) Validate(action string) error {
switch strings.ToLower(action) {
case "update":
if u.Nickname == "" {
- return errors.New("Required Nickname")
+ return errors.New("required nickname")
}
if u.Email == "" {
- return errors.New("Required Email")
+ return errors.New("required email")
}
if err := checkmail.ValidateFormat(u.Email); err != nil {
- return errors.New("Invalid Email")
+ return errors.New("invalid email")
}
return nil
case "login":
if u.Nickname == "" {
- return errors.New("Required Nickname")
+ return errors.New("required nickname")
}
if u.Email == "" {
- return errors.New("Required Email")
+ return errors.New("required email")
}
if err := checkmail.ValidateFormat(u.Email); err != nil {
- return errors.New("Invalid Email")
+ return errors.New("invalid email")
}
return nil
default:
if u.Nickname == "" {
- return errors.New("Required Nickname")
+ return errors.New("required nickname")
}
if u.Email == "" {
- return errors.New("Required Email")
+ return errors.New("required email")
}
if err := checkmail.ValidateFormat(u.Email); err != nil {
- return errors.New("Invalid Email")
+ return errors.New("invalid email")
}
return nil
}
diff --git a/services/web/package.json b/services/web/package.json
index 1f3e7fee..6c9ca87a 100644
--- a/services/web/package.json
+++ b/services/web/package.json
@@ -2,6 +2,7 @@
"name": "crapi-web",
"version": "0.1.0",
"private": true,
+ "proxy": "http://localhost:8889",
"dependencies": {
"@ant-design/icons": "^4.8.1",
"@testing-library/jest-dom": "^4.2.4",
@@ -28,7 +29,8 @@
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject",
- "lint": "prettier --check src/**/*.{js,jsx}"
+ "lint": "prettier --check src/**/*.{js,jsx}",
+ "lint:fix": "prettier --write src/**/*.{js,jsx}"
},
"eslintConfig": {
"extends": "react-app"
diff --git a/services/web/src/actions/communityActions.js b/services/web/src/actions/communityActions.js
index 117f9f21..e788831f 100644
--- a/services/web/src/actions/communityActions.js
+++ b/services/web/src/actions/communityActions.js
@@ -15,11 +15,12 @@
import actionTypes from "../constants/actionTypes";
-export const getPostsAction = ({ accessToken, callback }) => {
+export const getPostsAction = ({ accessToken, callback, ...data }) => {
return {
type: actionTypes.GET_POSTS,
accessToken,
callback,
+ ...data,
};
};
diff --git a/services/web/src/actions/shopActions.js b/services/web/src/actions/shopActions.js
index 328a49b2..15d5f958 100644
--- a/services/web/src/actions/shopActions.js
+++ b/services/web/src/actions/shopActions.js
@@ -15,11 +15,12 @@
import actionTypes from "../constants/actionTypes";
-export const getProductsAction = ({ callback, accessToken }) => {
+export const getProductsAction = ({ callback, accessToken, ...data }) => {
return {
type: actionTypes.GET_PRODUCTS,
accessToken,
callback,
+ ...data,
};
};
@@ -32,11 +33,12 @@ export const buyProductAction = ({ callback, accessToken, ...data }) => {
};
};
-export const getOrdersAction = ({ callback, accessToken }) => {
+export const getOrdersAction = ({ callback, accessToken, ...data }) => {
return {
type: actionTypes.GET_ORDERS,
accessToken,
callback,
+ ...data,
};
};
diff --git a/services/web/src/actions/userActions.js b/services/web/src/actions/userActions.js
index 7af5b239..53b72535 100644
--- a/services/web/src/actions/userActions.js
+++ b/services/web/src/actions/userActions.js
@@ -108,11 +108,12 @@ export const resetPasswordAction = ({
};
};
-export const getServicesAction = ({ callback, accessToken }) => {
+export const getServicesAction = ({ callback, accessToken, ...data }) => {
return {
type: actionTypes.GET_SERVICES,
accessToken,
callback,
+ ...data,
};
};
diff --git a/services/web/src/actions/vehicleActions.js b/services/web/src/actions/vehicleActions.js
index e5e13508..dd532408 100644
--- a/services/web/src/actions/vehicleActions.js
+++ b/services/web/src/actions/vehicleActions.js
@@ -24,20 +24,27 @@ export const verifyVehicleAction = ({ callback, accessToken, ...data }) => {
};
};
-export const getMechanicsAction = ({ callback, accessToken }) => {
+export const getMechanicsAction = ({ callback, accessToken, ...data }) => {
return {
type: actionTypes.GET_MECHANICS,
accessToken,
callback,
+ ...data,
};
};
-export const getVehiclesAction = ({ callback, accessToken, email }) => {
+export const getVehiclesAction = ({
+ callback,
+ accessToken,
+ email,
+ ...data
+}) => {
return {
type: actionTypes.GET_VEHICLES,
accessToken,
callback,
email,
+ ...data,
};
};
diff --git a/services/web/src/components/forum/forum.js b/services/web/src/components/forum/forum.js
index 3bc0a931..da2d2775 100644
--- a/services/web/src/components/forum/forum.js
+++ b/services/web/src/components/forum/forum.js
@@ -37,12 +37,13 @@ const { Meta } = Card;
const { Paragraph } = Typography;
const Forum = (props) => {
- const { posts } = props;
+ const { posts, prevOffset, nextOffset } = props;
const renderAvatar = (url) => (
);
-
+ console.log("Prev offset", prevOffset);
+ console.log("Next offset", nextOffset);
return (
{
))}
+
+
+
+
);
@@ -98,10 +119,15 @@ const Forum = (props) => {
Forum.propTypes = {
history: PropTypes.object,
posts: PropTypes.array,
+ prevOffset: PropTypes.number,
+ nextOffset: PropTypes.number,
+ handleOffsetChange: PropTypes.func,
};
-const mapStateToProps = ({ communityReducer: { posts } }) => {
- return { posts };
+const mapStateToProps = ({
+ communityReducer: { posts, prevOffset, nextOffset },
+}) => {
+ return { posts, prevOffset, nextOffset };
};
export default connect(mapStateToProps)(Forum);
diff --git a/services/web/src/components/pastOrders/pastOrders.js b/services/web/src/components/pastOrders/pastOrders.js
index 4237b7a5..60ab3666 100644
--- a/services/web/src/components/pastOrders/pastOrders.js
+++ b/services/web/src/components/pastOrders/pastOrders.js
@@ -85,6 +85,28 @@ const PastOrders = (props) => {
))}
+
+
+
+
);
@@ -94,10 +116,15 @@ PastOrders.propTypes = {
history: PropTypes.object,
pastOrders: PropTypes.array,
returnOrder: PropTypes.func,
+ prevOffset: PropTypes.number,
+ nextOffset: PropTypes.number,
+ handleOffsetChange: PropTypes.func,
};
-const mapStateToProps = ({ shopReducer: { pastOrders } }) => {
- return { pastOrders };
+const mapStateToProps = ({
+ shopReducer: { pastOrders, prevOffset, nextOffset },
+}) => {
+ return { pastOrders, prevOffset, nextOffset };
};
export default connect(mapStateToProps)(PastOrders);
diff --git a/services/web/src/components/shop/shop.js b/services/web/src/components/shop/shop.js
index 5d613802..88be7bc5 100644
--- a/services/web/src/components/shop/shop.js
+++ b/services/web/src/components/shop/shop.js
@@ -68,6 +68,7 @@ const ProductDescription = (product, onBuyProduct) => (
const Shop = (props) => {
const {
+ accessToken,
products,
availableCredit,
history,
@@ -76,6 +77,9 @@ const Shop = (props) => {
hasErrored,
errorMessage,
onFinish,
+ prevOffset,
+ nextOffset,
+ onOffsetChange,
} = props;
return (
@@ -123,10 +127,32 @@ const Shop = (props) => {
))}
+
+
+
+
setIsCouponFormOpen(false)}
>
@@ -156,6 +182,7 @@ const Shop = (props) => {
};
Shop.propTypes = {
+ accessToken: PropTypes.string,
history: PropTypes.object,
products: PropTypes.array,
availableCredit: PropTypes.number,
@@ -165,10 +192,21 @@ Shop.propTypes = {
hasErrored: PropTypes.bool,
errorMessage: PropTypes.string,
onFinish: PropTypes.func,
+ prevOffset: PropTypes.number,
+ nextOffset: PropTypes.number,
+ onOffsetChange: PropTypes.func,
};
-const mapStateToProps = ({ shopReducer: { availableCredit, products } }) => {
- return { availableCredit, products };
+const mapStateToProps = ({
+ shopReducer: {
+ accessToken,
+ availableCredit,
+ products,
+ prevOffset,
+ nextOffset,
+ },
+}) => {
+ return { accessToken, availableCredit, products, prevOffset, nextOffset };
};
export default connect(mapStateToProps)(Shop);
diff --git a/services/web/src/containers/forum/forum.js b/services/web/src/containers/forum/forum.js
index 63a49404..17c7e599 100644
--- a/services/web/src/containers/forum/forum.js
+++ b/services/web/src/containers/forum/forum.js
@@ -37,7 +37,19 @@ const ForumContainer = (props) => {
getPosts({ callback, accessToken });
}, [accessToken, getPosts]);
- return ;
+ const onOffsetChange = (offset) => {
+ const callback = (res, data) => {
+ if (res !== responseTypes.SUCCESS) {
+ Modal.error({
+ title: FAILURE_MESSAGE,
+ content: data,
+ });
+ }
+ };
+ getPosts({ callback, accessToken, offset });
+ };
+
+ return ;
};
const mapStateToProps = ({ userReducer: { accessToken } }) => {
@@ -52,6 +64,9 @@ ForumContainer.propTypes = {
accessToken: PropTypes.string,
getPosts: PropTypes.func,
history: PropTypes.object,
+ prevOffset: PropTypes.string,
+ nextOffset: PropTypes.string,
+ handleOffsetChange: PropTypes.func,
};
export default connect(mapStateToProps, mapDispatchToProps)(ForumContainer);
diff --git a/services/web/src/containers/pastOrders/pastOrders.js b/services/web/src/containers/pastOrders/pastOrders.js
index 25fafef8..3ee22c62 100644
--- a/services/web/src/containers/pastOrders/pastOrders.js
+++ b/services/web/src/containers/pastOrders/pastOrders.js
@@ -37,6 +37,18 @@ const PastOrdersContainer = (props) => {
getOrders({ callback, accessToken });
}, [accessToken, getOrders]);
+ const handleOffsetChange = (offset) => {
+ const callback = (res, data) => {
+ if (res !== responseTypes.SUCCESS) {
+ Modal.error({
+ title: FAILURE_MESSAGE,
+ content: data,
+ });
+ }
+ };
+ getOrders({ callback, accessToken, offset });
+ };
+
const handleReturnOrder = (orderId) => {
const callback = (res, data) => {
if (res === responseTypes.SUCCESS) {
@@ -62,7 +74,13 @@ const PastOrdersContainer = (props) => {
returnOrder({ callback, accessToken, orderId });
};
- return ;
+ return (
+
+ );
};
const mapStateToProps = ({ userReducer: { accessToken } }) => {
@@ -79,6 +97,9 @@ PastOrdersContainer.propTypes = {
getOrders: PropTypes.func,
returnOrder: PropTypes.func,
history: PropTypes.object,
+ prevOffset: PropTypes.number,
+ nextOffset: PropTypes.number,
+ handleOffsetChange: PropTypes.func,
};
export default connect(
diff --git a/services/web/src/containers/shop/shop.js b/services/web/src/containers/shop/shop.js
index 124478f5..9ff576ef 100644
--- a/services/web/src/containers/shop/shop.js
+++ b/services/web/src/containers/shop/shop.js
@@ -64,6 +64,18 @@ const ShopContainer = (props) => {
buyProduct({ callback, accessToken, productId: product.id });
};
+ const handleOffsetChange = (offset) => {
+ const callback = (res, data) => {
+ if (res !== responseTypes.SUCCESS) {
+ Modal.error({
+ title: FAILURE_MESSAGE,
+ content: data,
+ });
+ }
+ };
+ getProducts({ callback, accessToken, offset });
+ };
+
const handleFormFinish = (values) => {
const callback = (res, data) => {
if (res === responseTypes.SUCCESS) {
@@ -93,12 +105,16 @@ const ShopContainer = (props) => {
hasErrored={hasErrored}
errorMessage={errorMessage}
onFinish={handleFormFinish}
+ onOffsetChange={handleOffsetChange}
+ {...props}
/>
);
};
-const mapStateToProps = ({ userReducer: { accessToken } }) => {
- return { accessToken };
+const mapStateToProps = ({
+ userReducer: { accessToken, prevOffset, nextOffset },
+}) => {
+ return { accessToken, prevOffset, nextOffset };
};
const mapDispatchToProps = {
@@ -113,6 +129,9 @@ ShopContainer.propTypes = {
buyProduct: PropTypes.func,
applyCoupon: PropTypes.func,
history: PropTypes.object,
+ nextOffset: PropTypes.number,
+ prevOffset: PropTypes.number,
+ onOffsetChange: PropTypes.func,
};
export default connect(mapStateToProps, mapDispatchToProps)(ShopContainer);
diff --git a/services/web/src/reducers/communityReducer.js b/services/web/src/reducers/communityReducer.js
index 427fceb1..0b3b478a 100644
--- a/services/web/src/reducers/communityReducer.js
+++ b/services/web/src/reducers/communityReducer.js
@@ -17,6 +17,8 @@ import actionTypes from "../constants/actionTypes";
const initialData = {
posts: [],
+ prevOffset: null,
+ nextOffset: null,
};
const communityReducer = (state = initialData, action) => {
@@ -24,7 +26,9 @@ const communityReducer = (state = initialData, action) => {
case actionTypes.FETCHED_POSTS:
return {
...state,
- posts: action.payload,
+ posts: action.payload.posts,
+ prevOffset: action.payload.previous_offset,
+ nextOffset: action.payload.next_offset,
};
case actionTypes.FETCHED_POST:
return {
diff --git a/services/web/src/reducers/shopReducer.js b/services/web/src/reducers/shopReducer.js
index b0eb745e..4c53d0fc 100644
--- a/services/web/src/reducers/shopReducer.js
+++ b/services/web/src/reducers/shopReducer.js
@@ -19,6 +19,8 @@ const initialData = {
availableCredit: 0,
products: [],
pastOrders: [],
+ prevOffset: null,
+ nextOffset: null,
};
const profileReducer = (state = initialData, action) => {
@@ -32,11 +34,15 @@ const profileReducer = (state = initialData, action) => {
return {
...state,
products: action.payload.products,
+ prevOffset: action.payload.prevOffset,
+ nextOffset: action.payload.nextOffset,
};
case actionTypes.FETCHED_ORDERS:
return {
...state,
pastOrders: action.payload.orders,
+ prevOffset: action.payload.prevOffset,
+ nextOffset: action.payload.nextOffset,
};
case actionTypes.FETCHED_ORDER:
return {
diff --git a/services/web/src/reducers/vehicleReducer.js b/services/web/src/reducers/vehicleReducer.js
index 025f06fb..3c7f9936 100644
--- a/services/web/src/reducers/vehicleReducer.js
+++ b/services/web/src/reducers/vehicleReducer.js
@@ -31,6 +31,8 @@ const vehicleReducer = (state = initialData, action) => {
return {
...state,
mechanics: action.payload,
+ prevOffset: action.payload.prevOffset,
+ nextOffset: action.payload.nextOffset,
};
case actionTypes.REFRESHED_LOCATION:
return {
diff --git a/services/web/src/sagas/communitySaga.js b/services/web/src/sagas/communitySaga.js
index c836c9e9..9436eb07 100644
--- a/services/web/src/sagas/communitySaga.js
+++ b/services/web/src/sagas/communitySaga.js
@@ -37,8 +37,15 @@ export function* getPosts(param) {
let recievedResponse = {};
try {
yield put({ type: actionTypes.FETCHING_DATA });
-
- const getUrl = APIService.COMMUNITY_SERVICE + requestURLS.GET_POSTS;
+ let offset = 0;
+ if (param.offset) {
+ offset = param.offset;
+ }
+ const getUrl =
+ APIService.COMMUNITY_SERVICE +
+ requestURLS.GET_POSTS +
+ "?limit=30&offset=" +
+ offset;
const headers = {
"Content-Type": "application/json",
Authorization: `Bearer ${accessToken}`,
diff --git a/services/web/src/sagas/shopSaga.js b/services/web/src/sagas/shopSaga.js
index d9370548..1f6e8b1b 100644
--- a/services/web/src/sagas/shopSaga.js
+++ b/services/web/src/sagas/shopSaga.js
@@ -35,11 +35,15 @@ import {
* callback : callback method
*/
export function* getProducts(param) {
- const { accessToken, callback } = param;
+ const { callback, accessToken } = param;
let recievedResponse = {};
+ let offset = param.offset ? param.offset : 0;
try {
yield put({ type: actionTypes.FETCHING_DATA });
- const getUrl = APIService.WORKSHOP_SERVICE + requestURLS.GET_PRODUCTS;
+ const getUrl =
+ APIService.WORKSHOP_SERVICE +
+ requestURLS.GET_PRODUCTS +
+ `?limit=30&offset=${offset}`;
const headers = {
"Content-Type": "application/json",
Authorization: `Bearer ${accessToken}`,
@@ -60,13 +64,18 @@ export function* getProducts(param) {
});
yield put({
type: actionTypes.FETCHED_PRODUCTS,
- payload: { products: ResponseJson.products },
+ payload: {
+ products: ResponseJson.products,
+ prevOffset: ResponseJson.previous_offset,
+ nextOffset: ResponseJson.next_offset,
+ },
});
callback(responseTypes.SUCCESS, ResponseJson);
} else {
callback(responseTypes.FAILURE, ResponseJson.message);
}
} catch (e) {
+ console.log(e);
yield put({ type: actionTypes.FETCHED_DATA, payload: recievedResponse });
callback(responseTypes.FAILURE, NO_PRODUCTS);
}
@@ -74,7 +83,7 @@ export function* getProducts(param) {
/**
* buy a product
- * @param { accessToken, callback, product_id} param
+ * @param { callback, accessToken, product_id} param
* accessToken: access token of the user
* callback : callback method
* product_id: id of the product which is to be bought
@@ -125,7 +134,11 @@ export function* getOrders(param) {
let recievedResponse = {};
try {
yield put({ type: actionTypes.FETCHING_DATA });
- const getUrl = APIService.WORKSHOP_SERVICE + requestURLS.GET_ORDERS;
+ let offset = param.offset ? param.offset : 0;
+ const getUrl =
+ APIService.WORKSHOP_SERVICE +
+ requestURLS.GET_ORDERS +
+ `?limit=30&offset=${offset}`;
const headers = {
"Content-Type": "application/json",
Authorization: `Bearer ${accessToken}`,
@@ -142,7 +155,11 @@ export function* getOrders(param) {
if (recievedResponse.ok) {
yield put({
type: actionTypes.FETCHED_ORDERS,
- payload: { orders: ResponseJson.orders },
+ payload: {
+ orders: ResponseJson.orders,
+ prevOffset: ResponseJson.previous_offset,
+ nextOffset: ResponseJson.next_offset,
+ },
});
callback(responseTypes.SUCCESS, ResponseJson);
} else {
diff --git a/services/workshop/__init__.py b/services/workshop/__init__.py
index 713a92b0..e9b106a0 100644
--- a/services/workshop/__init__.py
+++ b/services/workshop/__init__.py
@@ -10,4 +10,3 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-
diff --git a/services/workshop/core/management/commands/seed_database.py b/services/workshop/core/management/commands/seed_database.py
index 29517ea5..213c93e3 100644
--- a/services/workshop/core/management/commands/seed_database.py
+++ b/services/workshop/core/management/commands/seed_database.py
@@ -32,51 +32,46 @@
def create_products():
from crapi.shop.models import Product
+
product_details_all = [
- {
- 'name': 'Seat',
- 'price': 10,
- 'image_url': 'images/seat.svg'
- },
- {
- 'name': 'Wheel',
- 'price': 10,
- 'image_url': 'images/wheel.svg'
- }
+ {"name": "Seat", "price": 10, "image_url": "images/seat.svg"},
+ {"name": "Wheel", "price": 10, "image_url": "images/wheel.svg"},
]
for product_details in product_details_all:
- if Product.objects.filter(name=product_details['name']).exists():
- logger.info("Product already exists. Skipping: "+ product_details['name'])
+ if Product.objects.filter(name=product_details["name"]).exists():
+ logger.info("Product already exists. Skipping: " + product_details["name"])
continue
product = Product.objects.create(
- name=product_details['name'],
- price=float(product_details['price']),
- image_url=product_details['image_url']
+ name=product_details["name"],
+ price=float(product_details["price"]),
+ image_url=product_details["image_url"],
)
product.save()
- logger.info("Created Product: "+str(product.__dict__))
+ logger.info("Created Product: " + str(product.__dict__))
+
def create_mechanics():
from crapi.user.models import User, UserDetails
from crapi.mechanic.models import Mechanic
+
mechanic_details_all = [
{
- 'name': 'Jhon',
- 'email': 'jhon@example.com',
- 'number': '',
- 'password': 'Admin1@#',
- 'mechanic_code': 'TRAC_JHN'
+ "name": "Jhon",
+ "email": "jhon@example.com",
+ "number": "",
+ "password": "Admin1@#",
+ "mechanic_code": "TRAC_JHN",
},
{
- 'name': 'James',
- 'email': 'james@example.com',
- 'number': '',
- 'password': 'Admin1@#',
- 'mechanic_code': 'TRAC_JME'
+ "name": "James",
+ "email": "james@example.com",
+ "number": "",
+ "password": "Admin1@#",
+ "mechanic_code": "TRAC_JME",
},
]
for mechanic_details in mechanic_details_all:
- uset = User.objects.filter(email=mechanic_details['email'])
+ uset = User.objects.filter(email=mechanic_details["email"])
if not uset.exists():
try:
cursor = connection.cursor()
@@ -84,34 +79,36 @@ def create_mechanics():
result = cursor.fetchone()
user_id = result[0]
except Exception as e:
- logger.error("Failed to fetch user_login_id_seq"+str(e))
+ logger.error("Failed to fetch user_login_id_seq" + str(e))
user_id = 1
user = User.objects.create(
id=user_id,
- email=mechanic_details['email'],
- number=mechanic_details['number'],
+ email=mechanic_details["email"],
+ number=mechanic_details["number"],
password=bcrypt.hashpw(
- mechanic_details['password'].encode('utf-8'),
- bcrypt.gensalt()
+ mechanic_details["password"].encode("utf-8"), bcrypt.gensalt()
).decode(),
role=User.ROLE_CHOICES.MECH,
- created_on=timezone.now()
+ created_on=timezone.now(),
)
user.save()
- logger.info("Created User: "+str(user.__dict__))
+ logger.info("Created User: " + str(user.__dict__))
else:
user = uset.first()
- if Mechanic.objects.filter(mechanic_code=mechanic_details['mechanic_code']):
- logger.info("Mechanic already exists. Skipping: "
- + mechanic_details['mechanic_code']
- + " " + mechanic_details['name'] + " "
- + mechanic_details['email'])
+ if Mechanic.objects.filter(mechanic_code=mechanic_details["mechanic_code"]):
+ logger.info(
+ "Mechanic already exists. Skipping: "
+ + mechanic_details["mechanic_code"]
+ + " "
+ + mechanic_details["name"]
+ + " "
+ + mechanic_details["email"]
+ )
continue
mechanic = Mechanic.objects.create(
- mechanic_code=mechanic_details['mechanic_code'],
- user=user
+ mechanic_code=mechanic_details["mechanic_code"], user=user
)
mechanic.save()
try:
@@ -120,17 +117,18 @@ def create_mechanics():
result = cursor.fetchone()
user_details_id = result[0]
except Exception as e:
- logger.error("Failed to fetch user_details_id_seq"+str(e))
+ logger.error("Failed to fetch user_details_id_seq" + str(e))
user_details_id = 1
userdetails = UserDetails.objects.create(
id=user_details_id,
available_credit=0,
- name=mechanic_details['name'],
- status='ACTIVE',
- user=user
+ name=mechanic_details["name"],
+ status="ACTIVE",
+ user=user,
)
userdetails.save()
+
def create_reports():
import random
import sys
@@ -138,8 +136,9 @@ def create_reports():
from crapi.user.models import User, UserDetails, Vehicle
from crapi.mechanic.models import Mechanic, ServiceRequest
from django.utils import timezone
+
count = ServiceRequest.objects.all().count()
- if (count >= 5):
+ if count >= 5:
return
logger.info("Creating Reports")
mechanics = Mechanic.objects.all()
@@ -156,7 +155,8 @@ def create_reports():
service_request = ServiceRequest.objects.create(
vehicle=vehicle,
mechanic=mechanic,
- problem_details=textwrap.dedent("""\
+ problem_details=textwrap.dedent(
+ """\
My car {} - {} is having issues.
Can you give me a call on my mobile {},
Or send me an email at {}
@@ -167,28 +167,35 @@ def create_reports():
vehicle_model.model,
user.number,
user.email,
- user_detail.name)
+ user_detail.name,
+ )
),
status=status,
- created_on=timezone.now()
+ created_on=timezone.now(),
)
service_request.save()
- logger.info("Created Service Request for User %s: %s", user.email, service_request.__dict__)
+ logger.info(
+ "Created Service Request for User %s: %s",
+ user.email,
+ service_request.__dict__,
+ )
except Exception as e:
print(sys.exc_info()[0])
- logger.error("Failed to create report: "+str(e))
+ logger.error("Failed to create report: " + str(e))
+
def create_orders():
import uuid
from crapi.user.models import User, UserDetails
from crapi.shop.models import Product
from crapi.shop.models import Order
+
if Order.objects.all().count() >= 1:
return
- users = User.objects.all().order_by('id')
+ users = User.objects.all().order_by("id")
users_seed = users[:5]
for user in users_seed:
- product = Product.objects.filter(name='Seat').first()
+ product = Product.objects.filter(name="Seat").first()
order = Order.objects.create(
user=user,
product=product,
@@ -201,7 +208,7 @@ def create_orders():
class Command(BaseCommand):
- help = 'Seed the database with initial data.'
+ help = "Seed the database with initial data."
def handle(self, *args, **kwargs):
"""
@@ -212,16 +219,16 @@ def handle(self, *args, **kwargs):
try:
create_products()
except Exception as e:
- logger.error("Cannot Pre Populate Products: "+str(e))
+ logger.error("Cannot Pre Populate Products: " + str(e))
try:
create_mechanics()
except Exception as e:
- logger.error("Cannot Pre Populate Mechanics: "+str(e))
+ logger.error("Cannot Pre Populate Mechanics: " + str(e))
try:
create_reports()
except Exception as e:
- logger.error("Cannot Pre Populate Reports: "+str(e))
+ logger.error("Cannot Pre Populate Reports: " + str(e))
try:
create_orders()
except Exception as e:
- logger.error("Cannot Pre Populate Orders: "+str(e))
+ logger.error("Cannot Pre Populate Orders: " + str(e))
diff --git a/services/workshop/crapi/__init__.py b/services/workshop/crapi/__init__.py
index 713a92b0..e9b106a0 100644
--- a/services/workshop/crapi/__init__.py
+++ b/services/workshop/crapi/__init__.py
@@ -10,4 +10,3 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-
diff --git a/services/workshop/crapi/apps.py b/services/workshop/crapi/apps.py
index 59b3913f..d84efc85 100644
--- a/services/workshop/crapi/apps.py
+++ b/services/workshop/crapi/apps.py
@@ -30,51 +30,46 @@
def create_products():
from crapi.shop.models import Product
+
product_details_all = [
- {
- 'name': 'Seat',
- 'price': 10,
- 'image_url': 'images/seat.svg'
- },
- {
- 'name': 'Wheel',
- 'price': 10,
- 'image_url': 'images/wheel.svg'
- }
+ {"name": "Seat", "price": 10, "image_url": "images/seat.svg"},
+ {"name": "Wheel", "price": 10, "image_url": "images/wheel.svg"},
]
for product_details in product_details_all:
- if Product.objects.filter(name=product_details['name']).exists():
- logger.info("Product already exists. Skipping: "+ product_details['name'])
+ if Product.objects.filter(name=product_details["name"]).exists():
+ logger.info("Product already exists. Skipping: " + product_details["name"])
continue
product = Product.objects.create(
- name=product_details['name'],
- price=float(product_details['price']),
- image_url=product_details['image_url']
+ name=product_details["name"],
+ price=float(product_details["price"]),
+ image_url=product_details["image_url"],
)
product.save()
- logger.info("Created Product: "+str(product.__dict__))
+ logger.info("Created Product: " + str(product.__dict__))
+
def create_mechanics():
from crapi.user.models import User, UserDetails
from crapi.mechanic.models import Mechanic
+
mechanic_details_all = [
{
- 'name': 'Jhon',
- 'email': 'jhon@example.com',
- 'number': '',
- 'password': 'Admin1@#',
- 'mechanic_code': 'TRAC_JHN'
+ "name": "Jhon",
+ "email": "jhon@example.com",
+ "number": "",
+ "password": "Admin1@#",
+ "mechanic_code": "TRAC_JHN",
},
{
- 'name': 'James',
- 'email': 'james@example.com',
- 'number': '',
- 'password': 'Admin1@#',
- 'mechanic_code': 'TRAC_JME'
+ "name": "James",
+ "email": "james@example.com",
+ "number": "",
+ "password": "Admin1@#",
+ "mechanic_code": "TRAC_JME",
},
]
for mechanic_details in mechanic_details_all:
- uset = User.objects.filter(email=mechanic_details['email'])
+ uset = User.objects.filter(email=mechanic_details["email"])
if not uset.exists():
try:
cursor = connection.cursor()
@@ -82,34 +77,36 @@ def create_mechanics():
result = cursor.fetchone()
user_id = result[0]
except Exception as e:
- logger.error("Failed to fetch user_login_id_seq"+str(e))
+ logger.error("Failed to fetch user_login_id_seq" + str(e))
user_id = 1
user = User.objects.create(
id=user_id,
- email=mechanic_details['email'],
- number=mechanic_details['number'],
+ email=mechanic_details["email"],
+ number=mechanic_details["number"],
password=bcrypt.hashpw(
- mechanic_details['password'].encode('utf-8'),
- bcrypt.gensalt()
+ mechanic_details["password"].encode("utf-8"), bcrypt.gensalt()
).decode(),
role=User.ROLE_CHOICES.MECH,
- created_on=timezone.now()
+ created_on=timezone.now(),
)
user.save()
- logger.info("Created User: "+str(user.__dict__))
+ logger.info("Created User: " + str(user.__dict__))
else:
user = uset.first()
- if Mechanic.objects.filter(mechanic_code=mechanic_details['mechanic_code']):
- logger.info("Mechanic already exists. Skipping: "
- + mechanic_details['mechanic_code']
- + " " + mechanic_details['name'] + " "
- + mechanic_details['email'])
+ if Mechanic.objects.filter(mechanic_code=mechanic_details["mechanic_code"]):
+ logger.info(
+ "Mechanic already exists. Skipping: "
+ + mechanic_details["mechanic_code"]
+ + " "
+ + mechanic_details["name"]
+ + " "
+ + mechanic_details["email"]
+ )
continue
mechanic = Mechanic.objects.create(
- mechanic_code=mechanic_details['mechanic_code'],
- user=user
+ mechanic_code=mechanic_details["mechanic_code"], user=user
)
mechanic.save()
try:
@@ -118,17 +115,18 @@ def create_mechanics():
result = cursor.fetchone()
user_details_id = result[0]
except Exception as e:
- logger.error("Failed to fetch user_details_id_seq"+str(e))
+ logger.error("Failed to fetch user_details_id_seq" + str(e))
user_details_id = 1
userdetails = UserDetails.objects.create(
id=user_details_id,
available_credit=0,
- name=mechanic_details['name'],
- status='ACTIVE',
- user=user
+ name=mechanic_details["name"],
+ status="ACTIVE",
+ user=user,
)
userdetails.save()
+
def create_reports():
import random
import sys
@@ -136,8 +134,9 @@ def create_reports():
from crapi.user.models import User, UserDetails, Vehicle
from crapi.mechanic.models import Mechanic, ServiceRequest
from django.utils import timezone
+
count = ServiceRequest.objects.all().count()
- if (count >= 5):
+ if count >= 5:
return
logger.info("Creating Reports")
mechanics = Mechanic.objects.all()
@@ -154,7 +153,8 @@ def create_reports():
service_request = ServiceRequest.objects.create(
vehicle=vehicle,
mechanic=mechanic,
- problem_details=textwrap.dedent("""\
+ problem_details=textwrap.dedent(
+ """\
My car {} - {} is having issues.
Can you give me a call on my mobile {},
Or send me an email at {}
@@ -165,27 +165,34 @@ def create_reports():
vehicle_model.model,
user.number,
user.email,
- user_detail.name)
+ user_detail.name,
+ )
),
status=status,
- created_on=timezone.now()
+ created_on=timezone.now(),
)
service_request.save()
- logger.info("Created Service Request for User %s: %s", user.email, service_request.__dict__)
+ logger.info(
+ "Created Service Request for User %s: %s",
+ user.email,
+ service_request.__dict__,
+ )
except Exception as e:
print(sys.exc_info()[0])
- logger.error("Failed to create report: "+str(e))
+ logger.error("Failed to create report: " + str(e))
+
def create_orders():
import uuid
from crapi.user.models import User, UserDetails
from crapi.shop.models import Product
from crapi.shop.models import Order
+
if Order.objects.all().count() >= 1:
return
- users = User.objects.filter(role=User.ROLE_CHOICES.PREDEFINED).order_by('id')
+ users = User.objects.filter(role=User.ROLE_CHOICES.PREDEFINED).order_by("id")
for user in users:
- product = Product.objects.filter(name='Seat').first()
+ product = Product.objects.filter(name="Seat").first()
order = Order.objects.create(
user=user,
product=product,
@@ -197,14 +204,12 @@ def create_orders():
logger.info("Created Order for User %s: %s", user.email, order.__dict__)
-
-
-
class CRAPIConfig(AppConfig):
"""
Stores all meta data of crapi application
"""
- name = 'crapi'
+
+ name = "crapi"
def ready(self):
"""
@@ -219,16 +224,16 @@ def ready(self):
try:
create_products()
except Exception as e:
- logger.error("Cannot Pre Populate Products: "+str(e))
+ logger.error("Cannot Pre Populate Products: " + str(e))
try:
create_mechanics()
except Exception as e:
- logger.error("Cannot Pre Populate Mechanics: "+str(e))
+ logger.error("Cannot Pre Populate Mechanics: " + str(e))
try:
create_reports()
except Exception as e:
- logger.error("Cannot Pre Populate Reports: "+str(e))
+ logger.error("Cannot Pre Populate Reports: " + str(e))
try:
create_orders()
except Exception as e:
- logger.error("Cannot Pre Populate Orders: "+str(e))
+ logger.error("Cannot Pre Populate Orders: " + str(e))
diff --git a/services/workshop/crapi/mechanic/__init__.py b/services/workshop/crapi/mechanic/__init__.py
index bfd4bb9d..e9b106a0 100644
--- a/services/workshop/crapi/mechanic/__init__.py
+++ b/services/workshop/crapi/mechanic/__init__.py
@@ -10,5 +10,3 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-
-
diff --git a/services/workshop/crapi/mechanic/models.py b/services/workshop/crapi/mechanic/models.py
index 09b7bf26..79cd9323 100644
--- a/services/workshop/crapi/mechanic/models.py
+++ b/services/workshop/crapi/mechanic/models.py
@@ -24,17 +24,19 @@
from django_db_cascade.fields import ForeignKey, OneToOneField
from django_db_cascade.deletions import DB_CASCADE
+
class Mechanic(models.Model):
"""
Mechanic Model
represents a mechanic for the application
"""
+
id = models.AutoField(primary_key=True)
mechanic_code = models.CharField(max_length=100, null=False, unique=True)
user = ForeignKey(User, DB_CASCADE)
class Meta:
- db_table = 'mechanic'
+ db_table = "mechanic"
def __str__(self):
return f""
@@ -45,6 +47,7 @@ class ServiceRequest(models.Model):
Service Request Model
represents a service request in the application
"""
+
id = models.AutoField(primary_key=True)
mechanic = ForeignKey(Mechanic, DB_CASCADE)
vehicle = ForeignKey(Vehicle, DB_CASCADE)
@@ -53,13 +56,14 @@ class ServiceRequest(models.Model):
updated_on = models.DateTimeField(null=True)
STATUS_CHOICES = Choices(
- ('PEN', "pending", "Pending"),
- ('FIN', "finished", "Finished")
+ ("PEN", "pending", "Pending"), ("FIN", "finished", "Finished")
+ )
+ status = models.CharField(
+ max_length=10, choices=STATUS_CHOICES, default=STATUS_CHOICES.PEN
)
- status = models.CharField(max_length=10, choices=STATUS_CHOICES, default=STATUS_CHOICES.PEN)
class Meta:
- db_table = 'service_request'
+ db_table = "service_request"
def __str__(self):
- return f''
+ return f""
diff --git a/services/workshop/crapi/mechanic/serializers.py b/services/workshop/crapi/mechanic/serializers.py
index 7c666914..af845c03 100644
--- a/services/workshop/crapi/mechanic/serializers.py
+++ b/services/workshop/crapi/mechanic/serializers.py
@@ -25,20 +25,23 @@ class MechanicSerializer(serializers.ModelSerializer):
"""
Serializer for Mechanic model
"""
+
user = UserSerializer()
class Meta:
"""
Meta class for MechanicSerializer
"""
+
model = Mechanic
- fields = ('id', 'mechanic_code', 'user')
+ fields = ("id", "mechanic_code", "user")
class ServiceRequestSerializer(serializers.ModelSerializer):
"""
Serializer for Mechanic model
"""
+
mechanic = MechanicSerializer()
vehicle = VehicleSerializer()
created_on = serializers.DateTimeField(format="%d %B, %Y, %H:%M:%S")
@@ -47,16 +50,25 @@ class Meta:
"""
Meta class for ServiceRequestSerializer
"""
+
model = ServiceRequest
- fields = ('id', 'mechanic', 'vehicle', 'problem_details', 'status', 'created_on')
+ fields = (
+ "id",
+ "mechanic",
+ "vehicle",
+ "problem_details",
+ "status",
+ "created_on",
+ )
class ReceiveReportSerializer(serializers.Serializer):
"""
Serializer for Receive Report API
"""
+
mechanic_code = serializers.CharField()
- problem_details= serializers.CharField()
+ problem_details = serializers.CharField()
vin = serializers.CharField()
owner_id = serializers.CharField(required=False)
@@ -65,6 +77,7 @@ class SignUpSerializer(serializers.Serializer):
"""
Serializer for Sign up
"""
+
name = serializers.CharField()
email = serializers.EmailField()
number = serializers.CharField()
diff --git a/services/workshop/crapi/mechanic/tests.py b/services/workshop/crapi/mechanic/tests.py
index 1c22a92b..7e33142c 100644
--- a/services/workshop/crapi/mechanic/tests.py
+++ b/services/workshop/crapi/mechanic/tests.py
@@ -16,7 +16,7 @@
from unittest.mock import patch
from utils.mock_methods import get_sample_mechanic_data, mock_jwt_auth_required
-patch('utils.jwt.jwt_auth_required', mock_jwt_auth_required).start()
+patch("utils.jwt.jwt_auth_required", mock_jwt_auth_required).start()
from django.test import TestCase, Client
from utils import messages
@@ -46,15 +46,19 @@ def test_duplicate_email_signup(self):
should get an error response saying email already registered
:return: None
"""
- res = self.client.post('/workshop/api/mechanic/signup',
- self.mechanic,
- content_type="application/json")
+ res = self.client.post(
+ "/workshop/api/mechanic/signup",
+ self.mechanic,
+ content_type="application/json",
+ )
self.assertEqual(res.status_code, 200)
- res = self.client.post('/workshop/api/mechanic/signup',
- self.mechanic,
- content_type="application/json")
+ res = self.client.post(
+ "/workshop/api/mechanic/signup",
+ self.mechanic,
+ content_type="application/json",
+ )
self.assertNotEqual(res.status_code, 200)
- self.assertEqual(res.json()['message'], messages.EMAIL_ALREADY_EXISTS)
+ self.assertEqual(res.json()["message"], messages.EMAIL_ALREADY_EXISTS)
def test_duplicate_mechanic_code(self):
"""
@@ -64,18 +68,21 @@ def test_duplicate_mechanic_code(self):
should get an error response saying mechanic_code already exists
:return: None
"""
- res = self.client.post('/workshop/api/mechanic/signup',
- self.mechanic,
- content_type="application/json")
+ res = self.client.post(
+ "/workshop/api/mechanic/signup",
+ self.mechanic,
+ content_type="application/json",
+ )
self.assertEqual(res.status_code, 200)
- self.mechanic['email'] = 'abcd@example.com'
- res = self.client.post('/workshop/api/mechanic/signup',
- self.mechanic,
- content_type="application/json")
+ self.mechanic["email"] = "abcd@example.com"
+ res = self.client.post(
+ "/workshop/api/mechanic/signup",
+ self.mechanic,
+ content_type="application/json",
+ )
self.assertNotEqual(res.status_code, 200)
- self.assertEqual(res.json()['message'],
- messages.MEC_CODE_ALREADY_EXISTS)
+ self.assertEqual(res.json()["message"], messages.MEC_CODE_ALREADY_EXISTS)
def test_no_duplicate(self):
"""
@@ -85,20 +92,22 @@ def test_no_duplicate(self):
should get a valid response(200) on second signup also
:return:
"""
- res = self.client.post('/workshop/api/mechanic/signup',
- self.mechanic,
- content_type="application/json")
+ res = self.client.post(
+ "/workshop/api/mechanic/signup",
+ self.mechanic,
+ content_type="application/json",
+ )
self.assertEqual(res.status_code, 200)
- self.mechanic['email'] = 'abcd@example.com'
- self.mechanic['mechanic_code'] = 'TRAC_MEC_4'
- res = self.client.post('/workshop/api/mechanic/signup',
- self.mechanic,
- content_type="application/json")
+ self.mechanic["email"] = "abcd@example.com"
+ self.mechanic["mechanic_code"] = "TRAC_MEC_4"
+ res = self.client.post(
+ "/workshop/api/mechanic/signup",
+ self.mechanic,
+ content_type="application/json",
+ )
self.assertEqual(res.status_code, 200)
- self.assertIn(
- messages.MEC_CREATED.split(':')[0],
- res.json()['message'])
+ self.assertIn(messages.MEC_CREATED.split(":")[0], res.json()["message"])
def test_jwt_token(self):
"""
@@ -110,18 +119,18 @@ def test_jwt_token(self):
should get a valid response(200) of the api
:return: None
"""
- self.client.post('/workshop/api/mechanic/signup',
- self.mechanic,
- content_type="application/json")
+ self.client.post(
+ "/workshop/api/mechanic/signup",
+ self.mechanic,
+ content_type="application/json",
+ )
- res = self.client.get('/workshop/api/mechanic/')
+ res = self.client.get("/workshop/api/mechanic/")
self.assertNotEqual(res.status_code, 200)
- self.assertEqual(res.json()['message'], messages.JWT_REQUIRED)
+ self.assertEqual(res.json()["message"], messages.JWT_REQUIRED)
- auth_headers = {
- 'HTTP_AUTHORIZATION': 'Bearer ' + self.mechanic['email']
- }
- res = self.client.get('/workshop/api/mechanic/', **auth_headers)
+ auth_headers = {"HTTP_AUTHORIZATION": "Bearer " + self.mechanic["email"]}
+ res = self.client.get("/workshop/api/mechanic/", **auth_headers)
self.assertEqual(res.status_code, 200)
def test_invalid_jwt_token(self):
@@ -132,13 +141,13 @@ def test_invalid_jwt_token(self):
should get an error response saying token invalid
:return: None
"""
- res = self.client.get('/workshop/api/mechanic/')
+ res = self.client.get("/workshop/api/mechanic/")
self.assertNotEqual(res.status_code, 200)
- auth_headers = {'HTTP_AUTHORIZATION': 'Bearer invalid.token'}
- res = self.client.get('/workshop/api/mechanic/', **auth_headers)
+ auth_headers = {"HTTP_AUTHORIZATION": "Bearer invalid.token"}
+ res = self.client.get("/workshop/api/mechanic/", **auth_headers)
self.assertNotEqual(res.status_code, 200)
- self.assertEqual(res.json()['message'], messages.INVALID_TOKEN)
+ self.assertEqual(res.json()["message"], messages.INVALID_TOKEN)
def test_bad_request(self):
"""
@@ -146,8 +155,10 @@ def test_bad_request(self):
should get a bad request response
:return: None
"""
- del [self.mechanic['password']]
- res = self.client.post('/workshop/api/mechanic/signup',
- self.mechanic,
- content_type="application/json")
+ del [self.mechanic["password"]]
+ res = self.client.post(
+ "/workshop/api/mechanic/signup",
+ self.mechanic,
+ content_type="application/json",
+ )
self.assertEqual(res.status_code, 400)
diff --git a/services/workshop/crapi/mechanic/urls.py b/services/workshop/crapi/mechanic/urls.py
index f9071f66..361cb49a 100644
--- a/services/workshop/crapi/mechanic/urls.py
+++ b/services/workshop/crapi/mechanic/urls.py
@@ -21,9 +21,13 @@
import crapi.mechanic.views as mechanic_views
urlpatterns = [
- re_path(r'signup$', mechanic_views.SignUpView.as_view()),
- re_path(r'receive_report$', mechanic_views.ReceiveReportView.as_view()),
- re_path(r'mechanic_report$', mechanic_views.GetReportView.as_view(), name="get-mechanic-report"),
- re_path(r'service_requests$', mechanic_views.ServiceRequestsView.as_view()),
- re_path(r'$', mechanic_views.MechanicView.as_view()),
+ re_path(r"signup$", mechanic_views.SignUpView.as_view()),
+ re_path(r"receive_report$", mechanic_views.ReceiveReportView.as_view()),
+ re_path(
+ r"mechanic_report$",
+ mechanic_views.GetReportView.as_view(),
+ name="get-mechanic-report",
+ ),
+ re_path(r"service_requests$", mechanic_views.ServiceRequestsView.as_view()),
+ re_path(r"$", mechanic_views.MechanicView.as_view()),
]
diff --git a/services/workshop/crapi/mechanic/views.py b/services/workshop/crapi/mechanic/views.py
index 782df179..4e0d7bf4 100644
--- a/services/workshop/crapi/mechanic/views.py
+++ b/services/workshop/crapi/mechanic/views.py
@@ -29,12 +29,20 @@
from crapi.user.models import User, Vehicle, UserDetails
from utils.logging import log_error
from .models import Mechanic, ServiceRequest
-from .serializers import MechanicSerializer, ServiceRequestSerializer, ReceiveReportSerializer, SignUpSerializer
+from .serializers import (
+ MechanicSerializer,
+ ServiceRequestSerializer,
+ ReceiveReportSerializer,
+ SignUpSerializer,
+)
+from rest_framework.pagination import LimitOffsetPagination
+
class SignUpView(APIView):
"""
Used to add a new mechanic
"""
+
@csrf_exempt
def post(self, request):
"""
@@ -48,52 +56,69 @@ def post(self, request):
"""
serializer = SignUpSerializer(data=request.data)
if not serializer.is_valid():
- log_error(request.path, request.data, status.HTTP_400_BAD_REQUEST, serializer.errors)
+ log_error(
+ request.path,
+ request.data,
+ status.HTTP_400_BAD_REQUEST,
+ serializer.errors,
+ )
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
mechanic_details = serializer.data
- if User.objects.filter(email=mechanic_details['email']).exists():
- return Response({'message': messages.EMAIL_ALREADY_EXISTS}, status=status.HTTP_400_BAD_REQUEST)
- if Mechanic.objects.filter(mechanic_code=mechanic_details['mechanic_code']).exists():
- return Response({'message': messages.MEC_CODE_ALREADY_EXISTS}, status=status.HTTP_400_BAD_REQUEST)
+ if User.objects.filter(email=mechanic_details["email"]).exists():
+ return Response(
+ {"message": messages.EMAIL_ALREADY_EXISTS},
+ status=status.HTTP_400_BAD_REQUEST,
+ )
+ if Mechanic.objects.filter(
+ mechanic_code=mechanic_details["mechanic_code"]
+ ).exists():
+ return Response(
+ {"message": messages.MEC_CODE_ALREADY_EXISTS},
+ status=status.HTTP_400_BAD_REQUEST,
+ )
try:
- user_id = User.objects.aggregate(models.Max('id'))['id__max'] + 1
+ user_id = User.objects.aggregate(models.Max("id"))["id__max"] + 1
except TypeError:
user_id = 1
user = User.objects.create(
id=user_id,
- email=mechanic_details['email'],
- number=mechanic_details['number'],
+ email=mechanic_details["email"],
+ number=mechanic_details["number"],
password=bcrypt.hashpw(
- mechanic_details['password'].encode('utf-8'),
- bcrypt.gensalt()
+ mechanic_details["password"].encode("utf-8"), bcrypt.gensalt()
).decode(),
role=User.ROLE_CHOICES.MECH,
- created_on=timezone.now()
+ created_on=timezone.now(),
)
Mechanic.objects.create(
- mechanic_code=mechanic_details['mechanic_code'],
- user=user
+ mechanic_code=mechanic_details["mechanic_code"], user=user
)
try:
- user_details_id = UserDetails.objects.aggregate(models.Max('id'))['id__max'] + 1
+ user_details_id = (
+ UserDetails.objects.aggregate(models.Max("id"))["id__max"] + 1
+ )
except TypeError:
user_details_id = 1
UserDetails.objects.create(
id=user_details_id,
available_credit=0,
- name=mechanic_details['name'],
- status='ACTIVE',
- user=user
+ name=mechanic_details["name"],
+ status="ACTIVE",
+ user=user,
+ )
+ return Response(
+ {"message": messages.MEC_CREATED.format(user.email)},
+ status=status.HTTP_200_OK,
)
- return Response({'message': messages.MEC_CREATED.format(user.email)}, status=status.HTTP_200_OK)
-class MechanicView(APIView):
+class MechanicView(APIView, LimitOffsetPagination):
"""
Mechanic view to fetch all the mechanics
"""
+
@jwt_auth_required
def get(self, request, user=None):
"""
@@ -106,10 +131,24 @@ def get(self, request, user=None):
mechanics list and 200 status if no error
message and corresponding status if error
"""
- mechanics = Mechanic.objects.all()
- serializer = MechanicSerializer(mechanics, many=True)
+ mechanics = Mechanic.objects.all().order_by("id")
+ paginated = self.paginate_queryset(mechanics, request)
+ if paginated is None:
+ return Response(
+ {"message": messages.NO_OBJECT_FOUND},
+ status=status.HTTP_400_BAD_REQUEST,
+ )
+ serializer = MechanicSerializer(paginated, many=True)
response_data = dict(
- mechanics=serializer.data
+ mechanics=serializer.data,
+ previous_offset=(
+ self.offset - self.limit if self.offset - self.limit >= 0 else None
+ ),
+ next_offset=(
+ self.offset + self.limit
+ if self.offset + self.limit < self.count
+ else None
+ ),
)
return Response(response_data, status=status.HTTP_200_OK)
@@ -118,6 +157,7 @@ class ReceiveReportView(APIView):
"""
View to receive report from contact mechanic feature
"""
+
def get(self, request):
"""
receive_report endpoint for mechanic
@@ -130,31 +170,38 @@ def get(self, request):
"""
serializer = ReceiveReportSerializer(data=request.GET)
if not serializer.is_valid():
- log_error(request.path, request.data, status.HTTP_400_BAD_REQUEST, serializer.errors)
+ log_error(
+ request.path,
+ request.data,
+ status.HTTP_400_BAD_REQUEST,
+ serializer.errors,
+ )
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
report_details = serializer.data
- mechanic = Mechanic.objects.get(mechanic_code=report_details['mechanic_code'])
- vehicle = Vehicle.objects.get(vin=report_details['vin'])
+ mechanic = Mechanic.objects.get(mechanic_code=report_details["mechanic_code"])
+ vehicle = Vehicle.objects.get(vin=report_details["vin"])
service_request = ServiceRequest.objects.create(
vehicle=vehicle,
mechanic=mechanic,
- problem_details=report_details['problem_details'],
- created_on=timezone.now()
+ problem_details=report_details["problem_details"],
+ created_on=timezone.now(),
)
service_request.save()
- report_link = "{}?report_id={}".format(reverse("get-mechanic-report"), service_request.id)
+ report_link = "{}?report_id={}".format(
+ reverse("get-mechanic-report"), service_request.id
+ )
report_link = request.build_absolute_uri(report_link)
- return Response({
- 'id': service_request.id,
- 'sent': True,
- 'report_link': report_link
- }, status=status.HTTP_200_OK)
+ return Response(
+ {"id": service_request.id, "sent": True, "report_link": report_link},
+ status=status.HTTP_200_OK,
+ )
class GetReportView(APIView):
"""
View to get only particular service request
"""
+
@jwt_auth_required
def get(self, request, user=None):
"""
@@ -165,33 +212,38 @@ def get(self, request, user=None):
service request object and 200 status if no error
message and corresponding status if error
"""
- report_id = request.GET['report_id']
+ report_id = request.GET["report_id"]
if not report_id:
return Response(
- {'message': messages.REPORT_ID_MISSING},
- status=status.HTTP_400_BAD_REQUEST
+ {"message": messages.REPORT_ID_MISSING},
+ status=status.HTTP_400_BAD_REQUEST,
)
if not report_id.isnumeric():
return Response(
- {'message': messages.INVALID_REPORT_ID},
- status=status.HTTP_400_BAD_REQUEST
+ {"message": messages.INVALID_REPORT_ID},
+ status=status.HTTP_400_BAD_REQUEST,
)
service_request = ServiceRequest.objects.filter(id=report_id).first()
if not service_request:
return Response(
- {'message': messages.REPORT_DOES_NOT_EXIST},
- status=status.HTTP_400_BAD_REQUEST
+ {"message": messages.REPORT_DOES_NOT_EXIST},
+ status=status.HTTP_400_BAD_REQUEST,
)
serializer = ServiceRequestSerializer(service_request)
response_data = dict(serializer.data)
return Response(response_data, status=status.HTTP_200_OK)
-class ServiceRequestsView(APIView):
+class ServiceRequestsView(APIView, LimitOffsetPagination):
"""
View to return all the service requests
"""
+
+ def __init__(self):
+ super(ServiceRequestsView, self).__init__()
+ self.default_limit = settings.DEFAULT_LIMIT
+
@jwt_auth_required
def get(self, request, user=None):
"""
@@ -204,24 +256,27 @@ def get(self, request, user=None):
list of service request object and 200 status if no error
message and corresponding status if error
"""
- limit = request.GET.get('limit', str(settings.DEFAULT_LIMIT))
- offset = request.GET.get('offset', str(settings.DEFAULT_OFFSET))
- if not limit.isdigit() or not offset.isdigit():
+
+ service_requests = ServiceRequest.objects.filter(mechanic__user=user).order_by(
+ "id"
+ )
+ paginated = self.paginate_queryset(service_requests, request)
+ if paginated is None:
return Response(
- {'message': messages.INVALID_LIMIT_OR_OFFSET},
- status=status.HTTP_400_BAD_REQUEST
+ {"message": messages.NO_OBJECT_FOUND},
+ status=status.HTTP_400_BAD_REQUEST,
)
- limit = int(limit)
- offset = int(offset)
- if limit > settings.MAX_LIMIT:
- limit = 100
- if limit < 0:
- limit = settings.DEFAULT_LIMIT
- if offset < 0:
- offset = settings.DEFAULT_OFFSET
- service_requests = ServiceRequest.objects.filter(mechanic__user=user).order_by('id')[offset:offset+limit]
serializer = ServiceRequestSerializer(service_requests, many=True)
response_data = dict(
- service_requests=serializer.data
+ service_requests=serializer.data,
+ next_offset=(
+ self.offset + self.limit
+ if self.offset + self.limit < self.count
+ else None
+ ),
+ previous_offset=(
+ self.offset - self.limit if self.offset - self.limit >= 0 else None
+ ),
+ count=self.get_count(paginated),
)
return Response(response_data, status=status.HTTP_200_OK)
diff --git a/services/workshop/crapi/merchant/__init__.py b/services/workshop/crapi/merchant/__init__.py
index bfd4bb9d..e9b106a0 100644
--- a/services/workshop/crapi/merchant/__init__.py
+++ b/services/workshop/crapi/merchant/__init__.py
@@ -10,5 +10,3 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-
-
diff --git a/services/workshop/crapi/merchant/serializers.py b/services/workshop/crapi/merchant/serializers.py
index 09074715..9bcbea1a 100644
--- a/services/workshop/crapi/merchant/serializers.py
+++ b/services/workshop/crapi/merchant/serializers.py
@@ -22,6 +22,7 @@ class ContactMechanicSerializer(serializers.Serializer):
"""
Serializer for Contact Mechanic model.
"""
+
mechanic_api = serializers.CharField()
repeat_request_if_failed = serializers.BooleanField(required=False)
number_of_repeats = serializers.IntegerField(required=False)
diff --git a/services/workshop/crapi/merchant/tests.py b/services/workshop/crapi/merchant/tests.py
index 7ba8f65e..0b73ca4f 100644
--- a/services/workshop/crapi/merchant/tests.py
+++ b/services/workshop/crapi/merchant/tests.py
@@ -14,9 +14,13 @@
contains all the test cases related to merchant
"""
from unittest.mock import patch
-from utils.mock_methods import get_sample_mechanic_data, mock_jwt_auth_required, get_sample_user_data
+from utils.mock_methods import (
+ get_sample_mechanic_data,
+ mock_jwt_auth_required,
+ get_sample_user_data,
+)
-patch('utils.jwt.jwt_auth_required', mock_jwt_auth_required).start()
+patch("utils.jwt.jwt_auth_required", mock_jwt_auth_required).start()
import bcrypt
from django.test import TestCase, Client
@@ -47,49 +51,53 @@ def setUp(self):
"""
self.client = Client()
self.mechanic = get_sample_mechanic_data()
- self.client.post('/workshop/api/mechanic/signup',
- self.mechanic,
- content_type="application/json")
+ self.client.post(
+ "/workshop/api/mechanic/signup",
+ self.mechanic,
+ content_type="application/json",
+ )
user_data = get_sample_user_data()
self.user = User.objects.create(
id=2,
- email=user_data['email'],
- number=user_data['number'],
- password=bcrypt.hashpw(user_data['password'].encode('utf-8'),
- bcrypt.gensalt()).decode(),
+ email=user_data["email"],
+ number=user_data["number"],
+ password=bcrypt.hashpw(
+ user_data["password"].encode("utf-8"), bcrypt.gensalt()
+ ).decode(),
role=User.ROLE_CHOICES.USER,
- created_on=timezone.now())
- self.user_auth_headers = {
- 'HTTP_AUTHORIZATION': 'Bearer ' + user_data['email']
- }
+ created_on=timezone.now(),
+ )
+ self.user_auth_headers = {"HTTP_AUTHORIZATION": "Bearer " + user_data["email"]}
self.mechanic_auth_headers = {
- 'HTTP_AUTHORIZATION': 'Bearer ' + self.mechanic['email']
+ "HTTP_AUTHORIZATION": "Bearer " + self.mechanic["email"]
}
- self.vehicle_company = VehicleCompany.objects.create(
- name='RandomCompany')
+ self.vehicle_company = VehicleCompany.objects.create(name="RandomCompany")
self.vehicle_model = VehicleModel.objects.create(
- fuel_type='1',
- model='NewModel',
- vehicle_img='Image',
- vehiclecompany=self.vehicle_company)
-
- self.vehicle = Vehicle.objects.create(pincode='1234',
- vin='9NFXO86WBWA082766',
- year='2020',
- status='ACTIVE',
- owner=self.user,
- vehicle_model=self.vehicle_model)
+ fuel_type="1",
+ model="NewModel",
+ vehicle_img="Image",
+ vehiclecompany=self.vehicle_company,
+ )
+
+ self.vehicle = Vehicle.objects.create(
+ pincode="1234",
+ vin="9NFXO86WBWA082766",
+ year="2020",
+ status="ACTIVE",
+ owner=self.user,
+ vehicle_model=self.vehicle_model,
+ )
self.contact_mechanic_request_body = {
- 'mechanic_api': 'https://www.google.com',
- 'repeat_request_if_failed': True,
- 'number_of_repeats': 5,
- 'mechanic_code': self.mechanic['mechanic_code'],
- 'vin': self.vehicle.vin,
- 'problem_details': 'My Car is not working',
+ "mechanic_api": "https://www.google.com",
+ "repeat_request_if_failed": True,
+ "number_of_repeats": 5,
+ "mechanic_code": self.mechanic["mechanic_code"],
+ "vin": self.vehicle.vin,
+ "problem_details": "My Car is not working",
}
def test_max_retries_exceeded(self):
@@ -98,14 +106,15 @@ def test_max_retries_exceeded(self):
should get an error message
:return: None
"""
- self.contact_mechanic_request_body['number_of_repeats'] = 110
- res = self.client.post('/workshop/api/merchant/contact_mechanic',
- self.contact_mechanic_request_body,
- **self.user_auth_headers,
- content_type="application/json")
+ self.contact_mechanic_request_body["number_of_repeats"] = 110
+ res = self.client.post(
+ "/workshop/api/merchant/contact_mechanic",
+ self.contact_mechanic_request_body,
+ **self.user_auth_headers,
+ content_type="application/json"
+ )
self.assertNotEqual(res.status_code, 200)
- self.assertEqual(res.json()['message'],
- messages.NO_OF_REPEATS_EXCEEDED)
+ self.assertEqual(res.json()["message"], messages.NO_OF_REPEATS_EXCEEDED)
def test_wrong_mechanic_api(self):
"""
@@ -113,12 +122,15 @@ def test_wrong_mechanic_api(self):
should get an error message
:return: None
"""
- self.contact_mechanic_request_body['mechanic_api'] = \
- 'https://jsonplaceholder.typicode.com/post'
- res = self.client.post('/workshop/api/merchant/contact_mechanic',
- self.contact_mechanic_request_body,
- **self.user_auth_headers,
- content_type="application/json")
+ self.contact_mechanic_request_body["mechanic_api"] = (
+ "https://jsonplaceholder.typicode.com/post"
+ )
+ res = self.client.post(
+ "/workshop/api/merchant/contact_mechanic",
+ self.contact_mechanic_request_body,
+ **self.user_auth_headers,
+ content_type="application/json"
+ )
self.assertNotEqual(res.status_code, 200)
def test_contact_mechanic(self):
@@ -127,13 +139,14 @@ def test_contact_mechanic(self):
should get a valid response from mechanic_api
:return: None
"""
- res = self.client.post('/workshop/api/merchant/contact_mechanic',
- self.contact_mechanic_request_body,
- **self.user_auth_headers,
- content_type="application/json")
+ res = self.client.post(
+ "/workshop/api/merchant/contact_mechanic",
+ self.contact_mechanic_request_body,
+ **self.user_auth_headers,
+ content_type="application/json"
+ )
self.assertEqual(res.status_code, 200)
- self.assertIn('Google',
- res.json()['response_from_mechanic_api'])
+ self.assertIn("Google", res.json()["response_from_mechanic_api"])
def test_repeat_missing_request(self):
"""
@@ -141,11 +154,13 @@ def test_repeat_missing_request(self):
should get a bad request response
:return: None
"""
- del self.contact_mechanic_request_body['repeat_request_if_failed']
- res = self.client.post('/workshop/api/merchant/contact_mechanic',
- self.contact_mechanic_request_body,
- **self.user_auth_headers,
- content_type="application/json")
+ del self.contact_mechanic_request_body["repeat_request_if_failed"]
+ res = self.client.post(
+ "/workshop/api/merchant/contact_mechanic",
+ self.contact_mechanic_request_body,
+ **self.user_auth_headers,
+ content_type="application/json"
+ )
self.assertEqual(res.status_code, 200)
def test_receive_report_and_get_report(self):
@@ -161,28 +176,40 @@ def test_receive_report_and_get_report(self):
should get the same report
:return: None
"""
- res = self.client.get('/workshop/api/mechanic/receive_report',
- self.contact_mechanic_request_body,
- **self.user_auth_headers,
- content_type="application/json")
+ res = self.client.get(
+ "/workshop/api/mechanic/receive_report",
+ self.contact_mechanic_request_body,
+ **self.user_auth_headers,
+ content_type="application/json"
+ )
self.assertEqual(res.status_code, 200)
- self.assertTrue(res.json()['sent'])
- self.assertIn('report_link', res.json())
-
- report_res = self.client.get(res.json()['report_link'],
- **self.user_auth_headers,
- content_type="application/json")
+ self.assertTrue(res.json()["sent"])
+ self.assertIn("report_link", res.json())
+
+ report_res = self.client.get(
+ res.json()["report_link"],
+ **self.user_auth_headers,
+ content_type="application/json"
+ )
self.assertEqual(report_res.status_code, 200)
- self.assertEqual(report_res.json()['problem_details'],
- self.contact_mechanic_request_body['problem_details'])
- self.assertEqual(report_res.json()['mechanic']['mechanic_code'],
- self.contact_mechanic_request_body['mechanic_code'])
- self.assertEqual(report_res.json()['vehicle']['vin'],
- self.contact_mechanic_request_body['vin'])
+ self.assertEqual(
+ report_res.json()["problem_details"],
+ self.contact_mechanic_request_body["problem_details"],
+ )
+ self.assertEqual(
+ report_res.json()["mechanic"]["mechanic_code"],
+ self.contact_mechanic_request_body["mechanic_code"],
+ )
+ self.assertEqual(
+ report_res.json()["vehicle"]["vin"],
+ self.contact_mechanic_request_body["vin"],
+ )
service_requests = self.client.get(
- '/workshop/api/mechanic/service_requests',
+ "/workshop/api/mechanic/service_requests",
**self.mechanic_auth_headers,
- content_type="application/json")
- self.assertEqual(service_requests.json()['service_requests'][0],
- report_res.json())
+ content_type="application/json"
+ )
+ self.assertEqual(
+ service_requests.json()["service_requests"][0], report_res.json()
+ )
diff --git a/services/workshop/crapi/merchant/urls.py b/services/workshop/crapi/merchant/urls.py
index e69d37d3..963a37f2 100644
--- a/services/workshop/crapi/merchant/urls.py
+++ b/services/workshop/crapi/merchant/urls.py
@@ -21,5 +21,5 @@
import crapi.merchant.views as merchant_views
urlpatterns = [
- re_path(r'contact_mechanic$', merchant_views.ContactMechanicView.as_view()),
+ re_path(r"contact_mechanic$", merchant_views.ContactMechanicView.as_view()),
]
diff --git a/services/workshop/crapi/merchant/views.py b/services/workshop/crapi/merchant/views.py
index 06f516c5..d2dcf51b 100644
--- a/services/workshop/crapi/merchant/views.py
+++ b/services/workshop/crapi/merchant/views.py
@@ -33,6 +33,7 @@ class ContactMechanicView(APIView):
"""
View for contact mechanic feature
"""
+
@jwt_auth_required
def post(self, request, user=None):
"""
@@ -49,32 +50,37 @@ def post(self, request, user=None):
request_data = request.data
serializer = ContactMechanicSerializer(data=request_data)
if not serializer.is_valid():
- log_error(request.path, request.data, status.HTTP_400_BAD_REQUEST, serializer.errors)
+ log_error(
+ request.path,
+ request.data,
+ status.HTTP_400_BAD_REQUEST,
+ serializer.errors,
+ )
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
- repeat_request_if_failed = request_data.get('repeat_request_if_failed', False)
- number_of_repeats = request_data.get('number_of_repeats', 1)
+ repeat_request_if_failed = request_data.get("repeat_request_if_failed", False)
+ number_of_repeats = request_data.get("number_of_repeats", 1)
if repeat_request_if_failed and number_of_repeats < 1:
return Response(
- {'message': messages.MIN_NO_OF_REPEATS_FAILED},
- status=status.HTTP_503_SERVICE_UNAVAILABLE
+ {"message": messages.MIN_NO_OF_REPEATS_FAILED},
+ status=status.HTTP_503_SERVICE_UNAVAILABLE,
)
elif repeat_request_if_failed and number_of_repeats > 100:
return Response(
- {'message': messages.NO_OF_REPEATS_EXCEEDED},
- status=status.HTTP_503_SERVICE_UNAVAILABLE
+ {"message": messages.NO_OF_REPEATS_EXCEEDED},
+ status=status.HTTP_503_SERVICE_UNAVAILABLE,
)
repeat_count = 0
while True:
- request_url=request_data['mechanic_api']
+ request_url = request_data["mechanic_api"]
logger.info(f"Repeat count: {repeat_count}, mechanic_api: {request_url}")
try:
mechanic_response = requests.get(
request_url,
params=request_data,
- headers={'Authorization': request.META.get('HTTP_AUTHORIZATION')},
- verify=False
+ headers={"Authorization": request.META.get("HTTP_AUTHORIZATION")},
+ verify=False,
)
if mechanic_response.status_code == status.HTTP_200_OK:
logger.info(f"Got a valid response at repeat count: {repeat_count}")
@@ -86,17 +92,17 @@ def post(self, request, user=None):
repeat_count += 1
except (MissingSchema, InvalidURL) as e:
log_error(request.path, request.data, status.HTTP_400_BAD_REQUEST, e)
- return Response({'message': str(e)}, status=status.HTTP_400_BAD_REQUEST)
+ return Response({"message": str(e)}, status=status.HTTP_400_BAD_REQUEST)
except requests.exceptions.ConnectionError as e:
if not repeat_request_if_failed:
return Response(
- {'message': messages.COULD_NOT_CONNECT},
- status=status.HTTP_400_BAD_REQUEST
+ {"message": messages.COULD_NOT_CONNECT},
+ status=status.HTTP_400_BAD_REQUEST,
)
if repeat_count == number_of_repeats:
return Response(
- {'message': messages.COULD_NOT_CONNECT},
- status=status.HTTP_400_BAD_REQUEST
+ {"message": messages.COULD_NOT_CONNECT},
+ status=status.HTTP_400_BAD_REQUEST,
)
repeat_count += 1
continue
@@ -105,7 +111,10 @@ def post(self, request, user=None):
mechanic_response = mechanic_response.json()
except ValueError:
mechanic_response = mechanic_response.text
- return Response({
- 'response_from_mechanic_api': mechanic_response,
- 'status': mechanic_response_status
- }, status=mechanic_response_status)
+ return Response(
+ {
+ "response_from_mechanic_api": mechanic_response,
+ "status": mechanic_response_status,
+ },
+ status=mechanic_response_status,
+ )
diff --git a/services/workshop/crapi/migrations/0001_initial.py b/services/workshop/crapi/migrations/0001_initial.py
index b0cbd678..a9882af9 100644
--- a/services/workshop/crapi/migrations/0001_initial.py
+++ b/services/workshop/crapi/migrations/0001_initial.py
@@ -19,158 +19,264 @@
import django.db.models.deletion
import django_db_cascade
+
class Migration(migrations.Migration):
initial = True
- dependencies = [
- ]
+ dependencies = []
operations = [
migrations.CreateModel(
- name='User',
+ name="User",
fields=[
- ('id', models.AutoField(primary_key=True, serialize=False)),
- ('created_on', models.DateTimeField()),
- ('email', models.CharField(max_length=255, unique=True)),
- ('jwt_token', models.CharField(max_length=500, null=True, unique=True)),
- ('number', models.CharField(max_length=255, null=True)),
- ('password', models.CharField(max_length=255)),
- ('role', models.IntegerField(choices=[(1, 'User'), (2, 'Mechanic'), (3, 'Admin')], default=1)),
+ ("id", models.AutoField(primary_key=True, serialize=False)),
+ ("created_on", models.DateTimeField()),
+ ("email", models.CharField(max_length=255, unique=True)),
+ ("jwt_token", models.CharField(max_length=500, null=True, unique=True)),
+ ("number", models.CharField(max_length=255, null=True)),
+ ("password", models.CharField(max_length=255)),
+ (
+ "role",
+ models.IntegerField(
+ choices=[(1, "User"), (2, "Mechanic"), (3, "Admin")], default=1
+ ),
+ ),
],
- options={
- 'db_table': 'user_login',
- 'managed': settings.IS_TESTING
- },
+ options={"db_table": "user_login", "managed": settings.IS_TESTING},
),
migrations.CreateModel(
- name='VehicleCompany',
+ name="VehicleCompany",
fields=[
- ('id', models.AutoField(primary_key=True, serialize=False)),
- ('name', models.CharField(max_length=255)),
+ ("id", models.AutoField(primary_key=True, serialize=False)),
+ ("name", models.CharField(max_length=255)),
],
- options={
- 'db_table': 'vehicle_company',
- 'managed': settings.IS_TESTING
- },
+ options={"db_table": "vehicle_company", "managed": settings.IS_TESTING},
),
migrations.CreateModel(
- name='VehicleModel',
+ name="VehicleModel",
fields=[
- ('id', models.AutoField(primary_key=True, serialize=False)),
- ('fuel_type', models.BigIntegerField()),
- ('model', models.CharField(max_length=255)),
- ('vehicle_img', models.CharField(max_length=255, null=True)),
- ('vehiclecompany', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='crapi.VehicleCompany'))
+ ("id", models.AutoField(primary_key=True, serialize=False)),
+ ("fuel_type", models.BigIntegerField()),
+ ("model", models.CharField(max_length=255)),
+ ("vehicle_img", models.CharField(max_length=255, null=True)),
+ (
+ "vehiclecompany",
+ models.ForeignKey(
+ on_delete=django.db.models.deletion.CASCADE,
+ to="crapi.VehicleCompany",
+ ),
+ ),
],
- options={
- 'db_table': 'vehicle_model',
- 'managed': settings.IS_TESTING
- },
+ options={"db_table": "vehicle_model", "managed": settings.IS_TESTING},
),
migrations.CreateModel(
- name='Vehicle',
+ name="Vehicle",
fields=[
- ('id', models.AutoField(primary_key=True, serialize=False)),
- ('pincode', models.CharField(max_length=255, null=True)),
- ('vin', models.CharField(max_length=255)),
- ('year', models.BigIntegerField(null=True)),
- ('vehicle_model', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='crapi.VehicleModel')),
- ('status', models.CharField(max_length=255)),
- ('location_id', models.BigIntegerField(null=True)),
- ('owner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='crapi.User')),
+ ("id", models.AutoField(primary_key=True, serialize=False)),
+ ("pincode", models.CharField(max_length=255, null=True)),
+ ("vin", models.CharField(max_length=255)),
+ ("year", models.BigIntegerField(null=True)),
+ (
+ "vehicle_model",
+ models.ForeignKey(
+ on_delete=django.db.models.deletion.CASCADE,
+ to="crapi.VehicleModel",
+ ),
+ ),
+ ("status", models.CharField(max_length=255)),
+ ("location_id", models.BigIntegerField(null=True)),
+ (
+ "owner",
+ models.ForeignKey(
+ on_delete=django.db.models.deletion.CASCADE, to="crapi.User"
+ ),
+ ),
],
- options={
- 'db_table': 'vehicle_details',
- 'managed': settings.IS_TESTING
- },
+ options={"db_table": "vehicle_details", "managed": settings.IS_TESTING},
),
migrations.CreateModel(
- name='UserDetails',
+ name="UserDetails",
fields=[
- ('id', models.AutoField(primary_key=True, serialize=False)),
- ('available_credit', models.FloatField()),
- ('name', models.CharField(max_length=255, null=True)),
- ('status', models.CharField(max_length=255, null=True)),
- ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='crapi.User')),
+ ("id", models.AutoField(primary_key=True, serialize=False)),
+ ("available_credit", models.FloatField()),
+ ("name", models.CharField(max_length=255, null=True)),
+ ("status", models.CharField(max_length=255, null=True)),
+ (
+ "user",
+ models.ForeignKey(
+ on_delete=django.db.models.deletion.CASCADE, to="crapi.User"
+ ),
+ ),
],
- options={
- 'db_table': 'user_details',
- 'managed': settings.IS_TESTING
- },
+ options={"db_table": "user_details", "managed": settings.IS_TESTING},
),
migrations.CreateModel(
- name='Mechanic',
+ name="Mechanic",
fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('mechanic_code', models.CharField(max_length=100, unique=True)),
- ('user', django_db_cascade.fields.ForeignKey(on_delete=django_db_cascade.deletions.DB_CASCADE, to='crapi.User')),
+ (
+ "id",
+ models.AutoField(
+ auto_created=True,
+ primary_key=True,
+ serialize=False,
+ verbose_name="ID",
+ ),
+ ),
+ ("mechanic_code", models.CharField(max_length=100, unique=True)),
+ (
+ "user",
+ django_db_cascade.fields.ForeignKey(
+ on_delete=django_db_cascade.deletions.DB_CASCADE,
+ to="crapi.User",
+ ),
+ ),
],
options={
- 'db_table': 'mechanic',
+ "db_table": "mechanic",
},
),
migrations.CreateModel(
- name='Product',
+ name="Product",
fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('name', models.CharField(max_length=255)),
- ('price', models.DecimalField(decimal_places=2, max_digits=20)),
- ('image_url', models.CharField(max_length=255)),
+ (
+ "id",
+ models.AutoField(
+ auto_created=True,
+ primary_key=True,
+ serialize=False,
+ verbose_name="ID",
+ ),
+ ),
+ ("name", models.CharField(max_length=255)),
+ ("price", models.DecimalField(decimal_places=2, max_digits=20)),
+ ("image_url", models.CharField(max_length=255)),
],
options={
- 'db_table': 'product',
+ "db_table": "product",
},
),
migrations.CreateModel(
- name='ServiceRequest',
+ name="ServiceRequest",
fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('problem_details', models.CharField(blank=True, max_length=500)),
- ('created_on', models.DateTimeField()),
- ('updated_on', models.DateTimeField(null=True)),
- ('status', models.CharField(choices=[('Pending', 'Pending'), ('Finished', 'Finished')], default='Pending', max_length=10)),
- ('mechanic', django_db_cascade.fields.ForeignKey(on_delete=django_db_cascade.deletions.DB_CASCADE, to='crapi.Mechanic')),
- ('vehicle', django_db_cascade.fields.ForeignKey(on_delete=django_db_cascade.deletions.DB_CASCADE, to='crapi.Vehicle')),
+ (
+ "id",
+ models.AutoField(
+ auto_created=True,
+ primary_key=True,
+ serialize=False,
+ verbose_name="ID",
+ ),
+ ),
+ ("problem_details", models.CharField(blank=True, max_length=500)),
+ ("created_on", models.DateTimeField()),
+ ("updated_on", models.DateTimeField(null=True)),
+ (
+ "status",
+ models.CharField(
+ choices=[("pending", "Pending"), ("finished", "Finished")],
+ default="pending",
+ max_length=10,
+ ),
+ ),
+ (
+ "mechanic",
+ django_db_cascade.fields.ForeignKey(
+ on_delete=django_db_cascade.deletions.DB_CASCADE,
+ to="crapi.Mechanic",
+ ),
+ ),
+ (
+ "vehicle",
+ django_db_cascade.fields.ForeignKey(
+ on_delete=django_db_cascade.deletions.DB_CASCADE,
+ to="crapi.Vehicle",
+ ),
+ ),
],
options={
- 'db_table': 'service_request',
+ "db_table": "service_request",
},
),
migrations.CreateModel(
- name='Order',
+ name="Order",
fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('quantity', models.IntegerField(default=1)),
- ('created_on', models.DateTimeField()),
- ('status', models.CharField(choices=[('delivered', 'delivered'), ('return pending', 'return pending'), ('returned', 'returned')], default='delivered', max_length=20)),
- ('product', django_db_cascade.fields.ForeignKey(on_delete=django_db_cascade.deletions.DB_CASCADE, to='crapi.Product')),
- ('user', django_db_cascade.fields.ForeignKey(on_delete=django_db_cascade.deletions.DB_CASCADE, to='crapi.User')),
+ (
+ "id",
+ models.AutoField(
+ auto_created=True,
+ primary_key=True,
+ serialize=False,
+ verbose_name="ID",
+ ),
+ ),
+ ("quantity", models.IntegerField(default=1)),
+ ("created_on", models.DateTimeField()),
+ (
+ "status",
+ models.CharField(
+ choices=[
+ ("delivered", "delivered"),
+ ("return pending", "return pending"),
+ ("returned", "returned"),
+ ],
+ default="delivered",
+ max_length=20,
+ ),
+ ),
+ (
+ "product",
+ django_db_cascade.fields.ForeignKey(
+ on_delete=django_db_cascade.deletions.DB_CASCADE,
+ to="crapi.Product",
+ ),
+ ),
+ (
+ "user",
+ django_db_cascade.fields.ForeignKey(
+ on_delete=django_db_cascade.deletions.DB_CASCADE,
+ to="crapi.User",
+ ),
+ ),
],
options={
- 'db_table': 'order',
+ "db_table": "order",
},
),
migrations.CreateModel(
- name='AppliedCoupon',
+ name="AppliedCoupon",
fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('coupon_code', models.CharField(max_length=255)),
- ('user', django_db_cascade.fields.ForeignKey(on_delete=django_db_cascade.deletions.DB_CASCADE, to='crapi.User')),
+ (
+ "id",
+ models.AutoField(
+ auto_created=True,
+ primary_key=True,
+ serialize=False,
+ verbose_name="ID",
+ ),
+ ),
+ ("coupon_code", models.CharField(max_length=255)),
+ (
+ "user",
+ django_db_cascade.fields.ForeignKey(
+ on_delete=django_db_cascade.deletions.DB_CASCADE,
+ to="crapi.User",
+ ),
+ ),
],
options={
- 'db_table': 'applied_coupon',
+ "db_table": "applied_coupon",
},
),
migrations.CreateModel(
- name='Coupon',
+ name="Coupon",
fields=[
- ('coupon_code', models.CharField(max_length=255, primary_key=True, serialize=False)),
- ('amount', models.CharField(max_length=255)),
+ (
+ "coupon_code",
+ models.CharField(max_length=255, primary_key=True, serialize=False),
+ ),
+ ("amount", models.CharField(max_length=255)),
],
- options={
- 'db_table': 'coupons',
- 'managed': settings.IS_TESTING
- },
+ options={"db_table": "coupons", "managed": settings.IS_TESTING},
),
]
diff --git a/services/workshop/crapi/migrations/0002_order_transaction_id.py b/services/workshop/crapi/migrations/0002_order_transaction_id.py
index 5d2394bc..ae727107 100644
--- a/services/workshop/crapi/migrations/0002_order_transaction_id.py
+++ b/services/workshop/crapi/migrations/0002_order_transaction_id.py
@@ -7,13 +7,13 @@
class Migration(migrations.Migration):
dependencies = [
- ('crapi', '0001_initial'),
+ ("crapi", "0001_initial"),
]
operations = [
migrations.AddField(
- model_name='order',
- name='transaction_id',
+ model_name="order",
+ name="transaction_id",
field=models.CharField(default=uuid.uuid4, max_length=255),
),
]
diff --git a/services/workshop/crapi/migrations/0003_alter_appliedcoupon_id_alter_mechanic_id_and_more.py b/services/workshop/crapi/migrations/0003_alter_appliedcoupon_id_alter_mechanic_id_and_more.py
index 2062d1b1..baec155e 100644
--- a/services/workshop/crapi/migrations/0003_alter_appliedcoupon_id_alter_mechanic_id_and_more.py
+++ b/services/workshop/crapi/migrations/0003_alter_appliedcoupon_id_alter_mechanic_id_and_more.py
@@ -6,33 +6,33 @@
class Migration(migrations.Migration):
dependencies = [
- ('crapi', '0002_order_transaction_id'),
+ ("crapi", "0002_order_transaction_id"),
]
operations = [
migrations.AlterField(
- model_name='appliedcoupon',
- name='id',
+ model_name="appliedcoupon",
+ name="id",
field=models.AutoField(primary_key=True, serialize=False),
),
migrations.AlterField(
- model_name='mechanic',
- name='id',
+ model_name="mechanic",
+ name="id",
field=models.AutoField(primary_key=True, serialize=False),
),
migrations.AlterField(
- model_name='order',
- name='id',
+ model_name="order",
+ name="id",
field=models.AutoField(primary_key=True, serialize=False),
),
migrations.AlterField(
- model_name='product',
- name='id',
+ model_name="product",
+ name="id",
field=models.AutoField(primary_key=True, serialize=False),
),
migrations.AlterField(
- model_name='servicerequest',
- name='id',
+ model_name="servicerequest",
+ name="id",
field=models.AutoField(primary_key=True, serialize=False),
),
]
diff --git a/services/workshop/crapi/migrations/__init__.py b/services/workshop/crapi/migrations/__init__.py
index bfd4bb9d..e9b106a0 100644
--- a/services/workshop/crapi/migrations/__init__.py
+++ b/services/workshop/crapi/migrations/__init__.py
@@ -10,5 +10,3 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-
-
diff --git a/services/workshop/crapi/models.py b/services/workshop/crapi/models.py
index bfd4bb9d..e9b106a0 100644
--- a/services/workshop/crapi/models.py
+++ b/services/workshop/crapi/models.py
@@ -10,5 +10,3 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-
-
diff --git a/services/workshop/crapi/shop/__init__.py b/services/workshop/crapi/shop/__init__.py
index bfd4bb9d..e9b106a0 100644
--- a/services/workshop/crapi/shop/__init__.py
+++ b/services/workshop/crapi/shop/__init__.py
@@ -10,5 +10,3 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-
-
diff --git a/services/workshop/crapi/shop/models.py b/services/workshop/crapi/shop/models.py
index 043ea4d4..9003fdb5 100644
--- a/services/workshop/crapi/shop/models.py
+++ b/services/workshop/crapi/shop/models.py
@@ -26,18 +26,20 @@
from django_db_cascade.deletions import DB_CASCADE
from django.db.models import DO_NOTHING, SET_NULL
+
class Product(models.Model):
"""
Product Model
represents a product in the application
"""
+
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=255)
price = models.DecimalField(max_digits=20, decimal_places=2)
image_url = models.CharField(max_length=255)
class Meta:
- db_table = 'product'
+ db_table = "product"
def __str__(self):
return f"{self.name} - {self.price}"
@@ -48,6 +50,7 @@ class Order(models.Model):
Order Model
represents an order in the application
"""
+
id = models.AutoField(primary_key=True)
user = ForeignKey(User, DB_CASCADE)
product = ForeignKey(Product, DB_CASCADE)
@@ -58,42 +61,48 @@ class Order(models.Model):
STATUS_CHOICES = Choices(
("DELIVERED", "delivered", "delivered"),
("RETURN_PENDING", "return pending", "return pending"),
- ("RETURNED", "returned", "returned")
+ ("RETURNED", "returned", "returned"),
+ )
+ status = models.CharField(
+ max_length=20, choices=STATUS_CHOICES, default=STATUS_CHOICES.DELIVERED
)
- status = models.CharField(max_length=20, choices=STATUS_CHOICES, default=STATUS_CHOICES.DELIVERED)
class Meta:
- db_table = 'order'
+ db_table = "order"
def __str__(self):
return f"{self.user.email} - {self.product.name} "
+
class Coupon(models.Model):
"""
AppliedCoupon Model
represents a mapping between coupon_code and user
"""
+
coupon_code = models.CharField(max_length=255, primary_key=True)
amount = models.CharField(max_length=255)
class Meta:
- db_table = 'coupons'
+ db_table = "coupons"
managed = settings.IS_TESTING
def __str__(self):
return f"{self.coupon_code} - {self.amount}"
+
class AppliedCoupon(models.Model):
"""
AppliedCoupon Model
represents a mapping between coupon_code and user
"""
+
id = models.AutoField(primary_key=True)
user = ForeignKey(User, DB_CASCADE)
coupon_code = models.CharField(max_length=255)
class Meta:
- db_table = 'applied_coupon'
+ db_table = "applied_coupon"
def __str__(self):
return f"{self.user.email} - {self.coupon_code} "
diff --git a/services/workshop/crapi/shop/serializers.py b/services/workshop/crapi/shop/serializers.py
index 74ef6f35..b5422f82 100644
--- a/services/workshop/crapi/shop/serializers.py
+++ b/services/workshop/crapi/shop/serializers.py
@@ -30,14 +30,16 @@ class Meta:
"""
Meta class for ProductSerializer
"""
+
model = Product
- fields = ('id', 'name', 'price', 'image_url')
+ fields = ("id", "name", "price", "image_url")
class OrderSerializer(serializers.ModelSerializer):
"""
Serializer for Order model
"""
+
user = UserSerializer()
product = ProductSerializer()
@@ -45,20 +47,32 @@ class Meta:
"""
Meta class for OrderSerializer
"""
+
model = Order
- fields = ('id', 'user', 'product', 'quantity', 'status', 'transaction_id', 'created_on')
+ fields = (
+ "id",
+ "user",
+ "product",
+ "quantity",
+ "status",
+ "transaction_id",
+ "created_on",
+ )
class CouponSerializer(serializers.Serializer):
"""
Serializer for Coupon model
"""
+
coupon_code = serializers.CharField()
amount = serializers.IntegerField()
+
class Meta:
"""
Meta class for CouponSerializer
"""
+
model = Coupon
@@ -66,5 +80,6 @@ class ProductQuantitySerializer(serializers.Serializer):
"""
Serializer for Product order API
"""
+
product_id = serializers.IntegerField()
quantity = serializers.IntegerField()
diff --git a/services/workshop/crapi/shop/tests.py b/services/workshop/crapi/shop/tests.py
index 312a8883..e3e912b5 100644
--- a/services/workshop/crapi/shop/tests.py
+++ b/services/workshop/crapi/shop/tests.py
@@ -16,7 +16,7 @@
from unittest.mock import patch
from utils.mock_methods import get_sample_user_data, mock_jwt_auth_required
-patch('utils.jwt.jwt_auth_required', mock_jwt_auth_required).start()
+patch("utils.jwt.jwt_auth_required", mock_jwt_auth_required).start()
import logging
import bcrypt
@@ -27,7 +27,7 @@
from crapi.user.models import User, UserDetails
from crapi.shop.models import Coupon
-logger = logging.getLogger('ProductTest')
+logger = logging.getLogger("ProductTest")
class ProductTestCase(TestCase):
@@ -41,7 +41,7 @@ class ProductTestCase(TestCase):
order_id: Order Identifier of the prder created
"""
- databases = '__all__'
+ databases = "__all__"
def setUp(self):
"""
@@ -51,29 +51,33 @@ def setUp(self):
"""
self.client = Client()
- self.coupon = Coupon.objects.using('mongodb').create(
- coupon_code='TRAC075', amount=75)
+ self.coupon = Coupon.objects.using("mongodb").create(
+ coupon_code="TRAC075", amount=75
+ )
- self.coupon = Coupon.objects.using('mongodb').create(
- coupon_code='TRAC100', amount=100)
+ self.coupon = Coupon.objects.using("mongodb").create(
+ coupon_code="TRAC100", amount=100
+ )
user_data = get_sample_user_data()
self.user = User.objects.create(
- email=user_data['email'],
- number=user_data['number'],
- password=bcrypt.hashpw(user_data['password'].encode('utf-8'),
- bcrypt.gensalt()).decode(),
+ email=user_data["email"],
+ number=user_data["number"],
+ password=bcrypt.hashpw(
+ user_data["password"].encode("utf-8"), bcrypt.gensalt()
+ ).decode(),
role=User.ROLE_CHOICES.USER,
- created_on=timezone.now())
+ created_on=timezone.now(),
+ )
- self.user_details = UserDetails.objects.create(available_credit=100,
- name=user_data['name'],
- status='ACTIVE',
- user=self.user)
+ self.user_details = UserDetails.objects.create(
+ available_credit=100,
+ name=user_data["name"],
+ status="ACTIVE",
+ user=self.user,
+ )
- self.auth_headers = {
- 'HTTP_AUTHORIZATION': 'Bearer ' + user_data['email']
- }
+ self.auth_headers = {"HTTP_AUTHORIZATION": "Bearer " + user_data["email"]}
def add_product(self):
"""
@@ -82,16 +86,14 @@ def add_product(self):
:return: None
"""
product_details = {
- 'name':
- 'test_Seat',
- 'price':
- 10,
- 'image_url':
- 'https://4.imimg.com/data4/NI/WE/MY-19393581/ciaz-car-seat-cover-500x500.jpg',
+ "name": "test_Seat",
+ "price": 10,
+ "image_url": "https://4.imimg.com/data4/NI/WE/MY-19393581/ciaz-car-seat-cover-500x500.jpg",
}
- res = self.client.post('/workshop/api/shop/products', product_details,
- **self.auth_headers)
- self.product_id = json.loads(res.content)['id']
+ res = self.client.post(
+ "/workshop/api/shop/products", product_details, **self.auth_headers
+ )
+ self.product_id = json.loads(res.content)["id"]
self.assertEqual(res.status_code, 200)
def apply_coupon(self):
@@ -100,14 +102,16 @@ def apply_coupon(self):
should get a valid response saying coupon applied
:return: None
"""
- coupon_details = {'coupon_code': 'TRAC075', 'amount': 75}
- res = self.client.post('/workshop/api/shop/apply_coupon',
- data=coupon_details,
- content_type='application/json',
- **self.auth_headers)
+ coupon_details = {"coupon_code": "TRAC075", "amount": 75}
+ res = self.client.post(
+ "/workshop/api/shop/apply_coupon",
+ data=coupon_details,
+ content_type="application/json",
+ **self.auth_headers
+ )
logger.info(res.json())
self.assertEqual(res.status_code, 200)
- self.assertEqual(res.json()['message'], messages.COUPON_APPLIED)
+ self.assertEqual(res.json()["message"], messages.COUPON_APPLIED)
def test_apply_coupon_twice(self):
"""
@@ -115,20 +119,25 @@ def test_apply_coupon_twice(self):
should get a error response saying coupon already applied
:return: None
"""
- coupon_details = {'coupon_code': 'TRAC100', 'amount': 100}
- res = self.client.post('/workshop/api/shop/apply_coupon',
- data=coupon_details,
- content_type='application/json',
- **self.auth_headers)
- res = self.client.post('/workshop/api/shop/apply_coupon',
- data=coupon_details,
- content_type='application/json',
- **self.auth_headers)
+ coupon_details = {"coupon_code": "TRAC100", "amount": 100}
+ res = self.client.post(
+ "/workshop/api/shop/apply_coupon",
+ data=coupon_details,
+ content_type="application/json",
+ **self.auth_headers
+ )
+ res = self.client.post(
+ "/workshop/api/shop/apply_coupon",
+ data=coupon_details,
+ content_type="application/json",
+ **self.auth_headers
+ )
logger.info(res.json())
self.assertEqual(res.status_code, 400)
self.assertEqual(
- res.json()['message'], coupon_details['coupon_code'] + ' ' +
- messages.COUPON_ALREADY_APPLIED)
+ res.json()["message"],
+ coupon_details["coupon_code"] + " " + messages.COUPON_ALREADY_APPLIED,
+ )
def test_invalid_coupon(self):
"""
@@ -136,14 +145,16 @@ def test_invalid_coupon(self):
should get a valid response saying coupon is invalid
:return: None
"""
- coupon_details = {'coupon_code': 'TRAC105', 'amount': 75}
- res = self.client.post('/workshop/api/shop/apply_coupon',
- data=coupon_details,
- content_type='application/json',
- **self.auth_headers)
+ coupon_details = {"coupon_code": "TRAC105", "amount": 75}
+ res = self.client.post(
+ "/workshop/api/shop/apply_coupon",
+ data=coupon_details,
+ content_type="application/json",
+ **self.auth_headers
+ )
logger.info(res.json())
self.assertEqual(res.status_code, 400)
- self.assertEqual(res.json()['message'], messages.COUPON_NOT_FOUND)
+ self.assertEqual(res.json()["message"], messages.COUPON_NOT_FOUND)
def test_sql_injection(self):
"""
@@ -152,20 +163,22 @@ def test_sql_injection(self):
:return: None
"""
coupon_details = {
- 'coupon_code':
- "'; SELECT number FROM user_login WHERE email='" + self.user.email,
- 'amount':
- 75
+ "coupon_code": "'; SELECT number FROM user_login WHERE email='"
+ + self.user.email,
+ "amount": 75,
}
- res = self.client.post('/workshop/api/shop/apply_coupon',
- data=coupon_details,
- content_type='application/json',
- **self.auth_headers)
+ res = self.client.post(
+ "/workshop/api/shop/apply_coupon",
+ data=coupon_details,
+ content_type="application/json",
+ **self.auth_headers
+ )
logger.info(res.json())
self.assertEqual(res.status_code, 400)
self.assertEqual(
- res.json()['message'],
- self.user.number + ' ' + messages.COUPON_ALREADY_APPLIED)
+ res.json()["message"],
+ self.user.number + " " + messages.COUPON_ALREADY_APPLIED,
+ )
def create_order(self):
"""
@@ -174,11 +187,13 @@ def create_order(self):
:return: order details
"""
order_details = {"product_id": str(self.product_id), "quantity": 1}
- res = self.client.post('/workshop/api/shop/orders',
- order_details,
- content_type='application/json',
- **self.auth_headers)
- self.order_id = json.loads(res.content)['id']
+ res = self.client.post(
+ "/workshop/api/shop/orders",
+ order_details,
+ content_type="application/json",
+ **self.auth_headers
+ )
+ self.order_id = json.loads(res.content)["id"]
self.assertEqual(res.status_code, 200)
def test_unauthenticated_get_order(self):
@@ -190,6 +205,5 @@ def test_unauthenticated_get_order(self):
self.add_product()
self.apply_coupon()
self.create_order()
- res = self.client.get('/workshop/api/shop/orders/' +
- str(self.order_id))
+ res = self.client.get("/workshop/api/shop/orders/" + str(self.order_id))
self.assertEqual(res.status_code, 200)
diff --git a/services/workshop/crapi/shop/urls.py b/services/workshop/crapi/shop/urls.py
index e84cb327..dc74121c 100644
--- a/services/workshop/crapi/shop/urls.py
+++ b/services/workshop/crapi/shop/urls.py
@@ -22,11 +22,15 @@
urlpatterns = [
# Do not change the order of URLs
- re_path(r'products$', shop_views.ProductView.as_view()),
- re_path(r'orders/all$', shop_views.OrderDetailsView.as_view()),
- re_path(r'orders/return_order$', shop_views.ReturnOrder.as_view()),
- re_path(r'orders/(?P\d+)$', shop_views.OrderControlView.as_view()),
- re_path(r'orders$', shop_views.OrderControlView.as_view()),
- re_path(r'apply_coupon$', shop_views.ApplyCouponView.as_view()),
- re_path(r'return_qr_code$', shop_views.ReturnQRCodeView.as_view(), name="shop-return-qr-code"),
+ re_path(r"products$", shop_views.ProductView.as_view()),
+ re_path(r"orders/all$", shop_views.OrderDetailsView.as_view()),
+ re_path(r"orders/return_order$", shop_views.ReturnOrder.as_view()),
+ re_path(r"orders/(?P\d+)$", shop_views.OrderControlView.as_view()),
+ re_path(r"orders$", shop_views.OrderControlView.as_view()),
+ re_path(r"apply_coupon$", shop_views.ApplyCouponView.as_view()),
+ re_path(
+ r"return_qr_code$",
+ shop_views.ReturnQRCodeView.as_view(),
+ name="shop-return-qr-code",
+ ),
]
diff --git a/services/workshop/crapi/shop/views.py b/services/workshop/crapi/shop/views.py
index ec79bee3..a7965cea 100644
--- a/services/workshop/crapi/shop/views.py
+++ b/services/workshop/crapi/shop/views.py
@@ -27,8 +27,12 @@
from rest_framework.response import Response
from rest_framework.views import APIView
from utils.helper import basic_auth
-
-from crapi.shop.serializers import OrderSerializer, ProductSerializer, CouponSerializer, ProductQuantitySerializer
+from crapi.shop.serializers import (
+ OrderSerializer,
+ ProductSerializer,
+ CouponSerializer,
+ ProductQuantitySerializer,
+)
from crapi.user.serializers import UserSerializer
from utils.jwt import jwt_auth_required
from utils import messages
@@ -36,11 +40,14 @@
from crapi.user.models import UserDetails
from utils.logging import log_error
from django.core.exceptions import ObjectDoesNotExist
+from rest_framework.pagination import LimitOffsetPagination
+
-class ProductView(APIView):
+class ProductView(APIView, LimitOffsetPagination):
"""
Product Controller View
"""
+
@jwt_auth_required
def get(self, request, user):
"""
@@ -54,11 +61,21 @@ def get(self, request, user):
message and corresponding status if error
"""
user_details = UserDetails.objects.get(user=user)
- products = Product.objects.all()
- serializer = ProductSerializer(products, many=True)
+ products = Product.objects.all().order_by("-id")
+ paginated = self.paginate_queryset(products, request, view=self)
+ serializer = ProductSerializer(paginated, many=True)
response_data = dict(
products=serializer.data,
credit=user_details.available_credit,
+ next_offset=(
+ self.offset + self.limit
+ if self.offset + self.limit < self.count
+ else None
+ ),
+ previous_offset=(
+ self.offset - self.limit if self.offset - self.limit >= 0 else None
+ ),
+ count=self.get_count(paginated),
)
return Response(response_data, status=status.HTTP_200_OK)
@@ -88,6 +105,7 @@ class OrderControlView(APIView):
"""
Order Controller View
"""
+
def get(self, request, order_id=None, user=None):
"""
order view for fetching a particular order
@@ -109,35 +127,37 @@ def get(self, request, order_id=None, user=None):
try:
user_dict = UserSerializer(user).data
user_details = UserDetails.objects.get(user=user)
- user_dict['name'] = user_details.name
+ user_dict["name"] = user_details.name
gateway_endpoint = settings.API_GATEWAY_URL + "/v1/payment"
- gateway_credential = basic_auth(settings.API_GATEWAY_USERNAME, settings.API_GATEWAY_PASSWORD)
+ gateway_credential = basic_auth(
+ settings.API_GATEWAY_USERNAME, settings.API_GATEWAY_PASSWORD
+ )
logging.debug(gateway_endpoint)
- data = {
- }
- data['user'] = user_dict
- data['order'] = order_serializer.data
- data['amount'] = float(order.product.price) * int(order.quantity)
+ data = {}
+ data["user"] = user_dict
+ data["order"] = order_serializer.data
+ data["amount"] = float(order.product.price) * int(order.quantity)
payment_response = requests.post(
gateway_endpoint,
headers={
"Authorization": gateway_credential,
- "Content-Type": "application/json"
- },
+ "Content-Type": "application/json",
+ },
json=data,
- verify=False
+ verify=False,
)
if payment_response.status_code == 200:
payment = payment_response.json()
else:
- logging.error("Payment response error, {}: {}".format(payment_response.status_code, payment_response.content))
+ logging.error(
+ "Payment response error, {}: {}".format(
+ payment_response.status_code, payment_response.content
+ )
+ )
logging.debug("payment response: {}".format(payment))
except Exception as e:
logging.error(e, exc_info=True)
- response_data = dict(
- order=order_serializer.data,
- payment=payment
- )
+ response_data = dict(order=order_serializer.data, payment=payment)
return Response(response_data, status=status.HTTP_200_OK)
@jwt_auth_required
@@ -159,29 +179,37 @@ def post(self, request, order_id=None, user=None):
request_data = request.data
serializer = ProductQuantitySerializer(data=request_data)
if not serializer.is_valid():
- log_error(request.path, request.data, status.HTTP_400_BAD_REQUEST, serializer.errors)
+ log_error(
+ request.path,
+ request.data,
+ status.HTTP_400_BAD_REQUEST,
+ serializer.errors,
+ )
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
- product = Product.objects.get(id=request_data['product_id'])
+ product = Product.objects.get(id=request_data["product_id"])
user_details = UserDetails.objects.get(user=user)
if user_details.available_credit < product.price:
return Response(
- {'message': messages.INSUFFICIENT_BALANCE},
- status=status.HTTP_400_BAD_REQUEST
+ {"message": messages.INSUFFICIENT_BALANCE},
+ status=status.HTTP_400_BAD_REQUEST,
)
- user_details.available_credit -= float(product.price * request_data['quantity'])
+ user_details.available_credit -= float(product.price * request_data["quantity"])
order = Order.objects.create(
user=user,
product=product,
- quantity=request_data['quantity'],
+ quantity=request_data["quantity"],
created_on=timezone.now(),
transaction_id=uuid.uuid4(),
)
user_details.save()
- return Response({
- 'id': order.id,
- 'message': messages.ORDER_CREATED,
- 'credit': user_details.available_credit,
- }, status=status.HTTP_200_OK)
+ return Response(
+ {
+ "id": order.id,
+ "message": messages.ORDER_CREATED,
+ "credit": user_details.available_credit,
+ },
+ status=status.HTTP_200_OK,
+ )
@jwt_auth_required
def put(self, request, order_id=None, user=None):
@@ -202,29 +230,32 @@ def put(self, request, order_id=None, user=None):
request_data = request.data
order = Order.objects.get(id=order_id)
if user != order.user:
- return Response({'message': messages.RESTRICTED}, status=status.HTTP_403_FORBIDDEN)
- if 'quantity' in request_data:
- order.quantity = request_data['quantity']
- if 'status' in request_data and not Order.STATUS_CHOICES.has_value(request_data['status']):
return Response(
- {'message': messages.INVALID_STATUS},
- status=status.HTTP_400_BAD_REQUEST
+ {"message": messages.RESTRICTED}, status=status.HTTP_403_FORBIDDEN
+ )
+ if "quantity" in request_data:
+ order.quantity = request_data["quantity"]
+ if "status" in request_data and not Order.STATUS_CHOICES.has_value(
+ request_data["status"]
+ ):
+ return Response(
+ {"message": messages.INVALID_STATUS}, status=status.HTTP_400_BAD_REQUEST
)
user_details = UserDetails.objects.get(user=order.user)
- if 'status' in request_data and request_data['status'] != order.status:
- order.status = request_data['status']
- if request_data['status'] == Order.STATUS_CHOICES.RETURNED.value:
- user_details.available_credit += float(order.quantity * order.product.price)
+ if "status" in request_data and request_data["status"] != order.status:
+ order.status = request_data["status"]
+ if request_data["status"] == Order.STATUS_CHOICES.RETURNED.value:
+ user_details.available_credit += float(
+ order.quantity * order.product.price
+ )
user_details.save()
order.save()
serializer = OrderSerializer(order)
- response_data = dict(
- orders=serializer.data
- )
+ response_data = dict(orders=serializer.data)
return Response(response_data, status=status.HTTP_200_OK)
-class OrderDetailsView(APIView):
+class OrderDetailsView(APIView, LimitOffsetPagination):
"""
Get the details of the orders.
"""
@@ -241,25 +272,20 @@ def get(self, request, user=None):
list of order object and 200 status if no error
message and corresponding status if error
"""
- limit = request.GET.get('limit', str(settings.DEFAULT_LIMIT))
- offset = request.GET.get('offset', str(settings.DEFAULT_OFFSET))
- if not limit.isdigit() or not offset.isdigit():
- return Response(
- {'message': messages.INVALID_LIMIT_OR_OFFSET},
- status=status.HTTP_400_BAD_REQUEST
- )
- limit = int(limit)
- offset = int(offset)
- if limit > settings.MAX_LIMIT:
- limit = 100
- if limit < 0:
- limit = settings.DEFAULT_LIMIT
- if offset < 0:
- offset = settings.DEFAULT_OFFSET
- orders = Order.objects.filter(user=user).order_by('-id')[offset:offset+limit]
- serializer = OrderSerializer(orders, many=True)
+ orders = Order.objects.filter(user=user).order_by("-id")
+ paginated = self.paginate_queryset(orders, request, view=self)
+ serializer = OrderSerializer(paginated, many=True)
response_data = dict(
- orders=serializer.data
+ orders=serializer.data,
+ next_offset=(
+ self.offset + self.limit
+ if self.offset + self.limit < self.count
+ else None
+ ),
+ previous_offset=(
+ self.offset - self.limit if self.offset - self.limit >= 0 else None
+ ),
+ count=self.get_count(paginated),
)
return Response(response_data, status=status.HTTP_200_OK)
@@ -268,6 +294,7 @@ class ReturnOrder(APIView):
"""
Return Order View
"""
+
@jwt_auth_required
def post(self, request, user=None):
"""
@@ -280,35 +307,41 @@ def post(self, request, user=None):
message and 200 status if no error
message and corresponding status if error
"""
- order = Order.objects.get(id=request.GET['order_id'])
+ order = Order.objects.get(id=request.GET["order_id"])
if user != order.user:
- return Response({'message': messages.RESTRICTED}, status=status.HTTP_403_FORBIDDEN)
+ return Response(
+ {"message": messages.RESTRICTED}, status=status.HTTP_403_FORBIDDEN
+ )
if order.status == Order.STATUS_CHOICES.RETURNED.value:
return Response(
- {'message': messages.ORDER_ALREADY_RETURNED},
- status=status.HTTP_400_BAD_REQUEST
+ {"message": messages.ORDER_ALREADY_RETURNED},
+ status=status.HTTP_400_BAD_REQUEST,
)
elif order.status == Order.STATUS_CHOICES.RETURN_PENDING.value:
return Response(
- {'message': messages.ORDER_RETURNED_PENDING},
- status=status.HTTP_400_BAD_REQUEST
+ {"message": messages.ORDER_RETURNED_PENDING},
+ status=status.HTTP_400_BAD_REQUEST,
)
qr_code_url = request.build_absolute_uri(reverse("shop-return-qr-code"))
order.status = Order.STATUS_CHOICES.RETURN_PENDING.value
order.save()
serializer = OrderSerializer(order)
- return Response({
- 'message': messages.ORDER_RETURNING,
- 'qr_code_url': qr_code_url,
- 'order': serializer.data
- }, status=status.HTTP_200_OK)
+ return Response(
+ {
+ "message": messages.ORDER_RETURNING,
+ "qr_code_url": qr_code_url,
+ "order": serializer.data,
+ },
+ status=status.HTTP_200_OK,
+ )
class ReturnQRCodeView(APIView):
"""
QR code image view
"""
+
def get(self, request):
"""
returns a qr code image
@@ -316,7 +349,7 @@ def get(self, request):
method allowed: GET
:return: FileResponse
"""
- img = open('utils/return-qr-code.png', 'rb')
+ img = open("utils/return-qr-code.png", "rb")
return FileResponse(img)
@@ -324,6 +357,7 @@ class ApplyCouponView(APIView):
"""
Apply Coupon View to increase the available credit
"""
+
@jwt_auth_required
def post(self, request, user=None):
"""
@@ -347,44 +381,49 @@ def post(self, request, user=None):
row = None
with connection.cursor() as cursor:
try:
- cursor.execute("SELECT coupon_code from applied_coupon WHERE user_id = "\
- + str(user.id)\
- + " AND coupon_code = '"\
- + coupon_request_body['coupon_code']\
- + "'")
+ cursor.execute(
+ "SELECT coupon_code from applied_coupon WHERE user_id = "
+ + str(user.id)
+ + " AND coupon_code = '"
+ + coupon_request_body["coupon_code"]
+ + "'"
+ )
row = cursor.fetchall()
except Exception as e:
log_error(request.path, request.data, 500, e)
return Response(
- {'message': e},
- status=status.HTTP_500_INTERNAL_SERVER_ERROR
+ {"message": e}, status=status.HTTP_500_INTERNAL_SERVER_ERROR
)
if row and row != None:
return Response(
{
- 'message': row[0][0] + " " + messages.COUPON_ALREADY_APPLIED,
+ "message": row[0][0] + " " + messages.COUPON_ALREADY_APPLIED,
},
- status=status.HTTP_400_BAD_REQUEST
+ status=status.HTTP_400_BAD_REQUEST,
)
try:
- coupon = Coupon.objects.using('mongodb').get(coupon_code=coupon_request_body['coupon_code'])
+ coupon = Coupon.objects.using("mongodb").get(
+ coupon_code=coupon_request_body["coupon_code"]
+ )
except ObjectDoesNotExist as e:
log_error(request.path, request.data, 400, e)
return Response(
- {'message': messages.COUPON_NOT_FOUND},
- status=status.HTTP_400_BAD_REQUEST
+ {"message": messages.COUPON_NOT_FOUND},
+ status=status.HTTP_400_BAD_REQUEST,
)
AppliedCoupon.objects.create(
- user=user,
- coupon_code=coupon_request_body['coupon_code']
+ user=user, coupon_code=coupon_request_body["coupon_code"]
)
user_details = UserDetails.objects.get(user=user)
- user_details.available_credit += coupon_request_body['amount']
+ user_details.available_credit += coupon_request_body["amount"]
user_details.save()
- return Response({
- 'credit': user_details.available_credit,
- 'message': messages.COUPON_APPLIED
- }, status=status.HTTP_200_OK)
+ return Response(
+ {
+ "credit": user_details.available_credit,
+ "message": messages.COUPON_APPLIED,
+ },
+ status=status.HTTP_200_OK,
+ )
diff --git a/services/workshop/crapi/urls.py b/services/workshop/crapi/urls.py
index 5ffa7cf6..8793324c 100644
--- a/services/workshop/crapi/urls.py
+++ b/services/workshop/crapi/urls.py
@@ -19,8 +19,8 @@
from django.urls import path, include
urlpatterns = [
- path('api/mechanic/', include('crapi.mechanic.urls')),
- path('api/merchant/', include('crapi.merchant.urls')),
- path('api/shop/', include('crapi.shop.urls')),
- path('api/management/', include('crapi.user.urls')),
+ path("api/mechanic/", include("crapi.mechanic.urls")),
+ path("api/merchant/", include("crapi.merchant.urls")),
+ path("api/shop/", include("crapi.shop.urls")),
+ path("api/management/", include("crapi.user.urls")),
]
diff --git a/services/workshop/crapi/user/__init__.py b/services/workshop/crapi/user/__init__.py
index 713a92b0..e9b106a0 100644
--- a/services/workshop/crapi/user/__init__.py
+++ b/services/workshop/crapi/user/__init__.py
@@ -10,4 +10,3 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-
diff --git a/services/workshop/crapi/user/models.py b/services/workshop/crapi/user/models.py
index c248f4c2..5417e0f2 100644
--- a/services/workshop/crapi/user/models.py
+++ b/services/workshop/crapi/user/models.py
@@ -22,6 +22,7 @@
from collections import OrderedDict
from extended_choices import Choices
+
class User(models.Model):
"""
User Model
@@ -36,16 +37,16 @@ class User(models.Model):
password = models.CharField(max_length=255)
ROLE_CHOICES = Choices(
- ('PREDEFINED', 0, 'Predefined'),
- ('USER', 1, 'User'),
- ('MECH', 2, 'Mechanic'),
- ('ADMIN', 3, 'Admin'),
- dict_class = OrderedDict
+ ("PREDEFINED", 0, "Predefined"),
+ ("USER", 1, "User"),
+ ("MECH", 2, "Mechanic"),
+ ("ADMIN", 3, "Admin"),
+ dict_class=OrderedDict,
)
role = models.IntegerField(choices=ROLE_CHOICES, default=ROLE_CHOICES.USER)
class Meta:
- db_table = 'user_login'
+ db_table = "user_login"
managed = settings.IS_TESTING
def __str__(self):
@@ -57,6 +58,7 @@ class UserDetails(models.Model):
UserDetails Model
stores additional details for the user
"""
+
id = models.AutoField(primary_key=True)
available_credit = models.FloatField()
name = models.CharField(max_length=255, null=True)
@@ -64,7 +66,7 @@ class UserDetails(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
class Meta:
- db_table = 'user_details'
+ db_table = "user_details"
managed = settings.IS_TESTING
def __str__(self):
@@ -76,11 +78,12 @@ class VehicleCompany(models.Model):
UserDetails Model
stores additional details for the user
"""
+
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=255)
class Meta:
- db_table = 'vehicle_company'
+ db_table = "vehicle_company"
managed = settings.IS_TESTING
def __str__(self):
@@ -92,6 +95,7 @@ class VehicleModel(models.Model):
UserDetails Model
stores additional details for the user
"""
+
id = models.AutoField(primary_key=True)
fuel_type = models.BigIntegerField()
model = models.CharField(max_length=255)
@@ -99,7 +103,7 @@ class VehicleModel(models.Model):
vehiclecompany = models.ForeignKey(VehicleCompany, on_delete=models.CASCADE)
class Meta:
- db_table = 'vehicle_model'
+ db_table = "vehicle_model"
managed = settings.IS_TESTING
def __str__(self):
@@ -111,6 +115,7 @@ class Vehicle(models.Model):
Vehicle Model
represents a vehicle in the application
"""
+
id = models.AutoField(primary_key=True)
pincode = models.CharField(max_length=255, null=True)
vin = models.CharField(max_length=255)
@@ -121,7 +126,7 @@ class Vehicle(models.Model):
location_id = models.BigIntegerField(null=True)
class Meta:
- db_table = 'vehicle_details'
+ db_table = "vehicle_details"
managed = settings.IS_TESTING
def __str__(self):
diff --git a/services/workshop/crapi/user/sapps.py b/services/workshop/crapi/user/sapps.py
index 5387d8e0..a700398c 100644
--- a/services/workshop/crapi/user/sapps.py
+++ b/services/workshop/crapi/user/sapps.py
@@ -22,4 +22,5 @@ class UserConfig(AppConfig):
"""
Stores all meta data of user application
"""
- name = 'user'
+
+ name = "user"
diff --git a/services/workshop/crapi/user/serializers.py b/services/workshop/crapi/user/serializers.py
index cc9a309d..f018a8ff 100644
--- a/services/workshop/crapi/user/serializers.py
+++ b/services/workshop/crapi/user/serializers.py
@@ -29,33 +29,38 @@ class Meta:
"""
Meta class for UserSerializer
"""
+
model = User
- fields = ('email', 'number')
+ fields = ("email", "number")
class UserDetailsSerializer(serializers.ModelSerializer):
"""
Serializer for User Details model
"""
+
user = UserSerializer()
class Meta:
"""
Meta class for UserSerializer
"""
+
model = UserDetails
- fields = ('user', 'available_credit')
+ fields = ("user", "available_credit")
class VehicleSerializer(serializers.ModelSerializer):
"""
Serializer for Vehicle model
"""
+
owner = UserSerializer()
class Meta:
"""
Meta class for MechanicSerializer
"""
+
model = Vehicle
- fields = ('id', 'vin', 'owner')
+ fields = ("id", "vin", "owner")
diff --git a/services/workshop/crapi/user/tests.py b/services/workshop/crapi/user/tests.py
index 657f4fbd..fa25ab30 100644
--- a/services/workshop/crapi/user/tests.py
+++ b/services/workshop/crapi/user/tests.py
@@ -16,9 +16,14 @@
from unittest.mock import patch
from django.db import connection
-from utils.mock_methods import get_sample_admin_user, get_sample_user_data, get_sample_users, mock_jwt_auth_required
+from utils.mock_methods import (
+ get_sample_admin_user,
+ get_sample_user_data,
+ get_sample_users,
+ mock_jwt_auth_required,
+)
-patch('utils.jwt.jwt_auth_required', mock_jwt_auth_required).start()
+patch("utils.jwt.jwt_auth_required", mock_jwt_auth_required).start()
import logging
import bcrypt
@@ -29,9 +34,10 @@
from crapi_site import settings
from crapi.user.models import User, UserDetails
-logger = logging.getLogger('UserTest')
+logger = logging.getLogger("UserTest")
MAX_USER_COUNT = 40
+
class UserDetailsTestCase(TestCase):
"""
contains all the test cases related to UserDetails
@@ -41,35 +47,34 @@ class UserDetailsTestCase(TestCase):
auth_headers: Auth headers for dummy user
"""
- databases = '__all__'
+ databases = "__all__"
setup_done = False
def setUp(self):
self.client = Client()
user_data = get_sample_admin_user()
- uset = User.objects.filter(email=user_data['email'])
+ uset = User.objects.filter(email=user_data["email"])
if not uset.exists():
user = User.objects.create(
- email=user_data['email'],
- number=user_data['number'],
- password=bcrypt.hashpw(user_data['password'].encode('utf-8'),
- bcrypt.gensalt()).decode(),
- role=user_data['role'],
- created_on=timezone.now())
- user_detail = UserDetails.objects.create(available_credit=100,
- name=user_data['name'],
- status='ACTIVE',
- user=user)
+ email=user_data["email"],
+ number=user_data["number"],
+ password=bcrypt.hashpw(
+ user_data["password"].encode("utf-8"), bcrypt.gensalt()
+ ).decode(),
+ role=user_data["role"],
+ created_on=timezone.now(),
+ )
+ user_detail = UserDetails.objects.create(
+ available_credit=100, name=user_data["name"], status="ACTIVE", user=user
+ )
user.save()
user_detail.save()
- self.auth_headers = {
- 'HTTP_AUTHORIZATION': 'Bearer ' + user_data['email']
- }
+ self.auth_headers = {"HTTP_AUTHORIZATION": "Bearer " + user_data["email"]}
def setup_database(self):
self.users_data = get_sample_users(MAX_USER_COUNT)
for user_data in self.users_data:
- uset = User.objects.filter(email=user_data['email'])
+ uset = User.objects.filter(email=user_data["email"])
if not uset.exists():
try:
cursor = connection.cursor()
@@ -77,24 +82,27 @@ def setup_database(self):
result = cursor.fetchone()
user_id = result[0]
except Exception as e:
- logger.error("Failed to fetch user_login_id_seq"+str(e))
+ logger.error("Failed to fetch user_login_id_seq" + str(e))
user_id = 1
user_i = User.objects.create(
- id = user_id,
- email=user_data['email'],
- number=user_data['number'],
- password=bcrypt.hashpw(user_data['password'].encode('utf-8'),
- bcrypt.gensalt()).decode(),
- role=user_data['role'],
- created_on=timezone.now())
- user_details_i = UserDetails.objects.create(available_credit=100,
- name=user_data['name'],
- status='ACTIVE',
- user=user_i)
+ id=user_id,
+ email=user_data["email"],
+ number=user_data["number"],
+ password=bcrypt.hashpw(
+ user_data["password"].encode("utf-8"), bcrypt.gensalt()
+ ).decode(),
+ role=user_data["role"],
+ created_on=timezone.now(),
+ )
+ user_details_i = UserDetails.objects.create(
+ available_credit=100,
+ name=user_data["name"],
+ status="ACTIVE",
+ user=user_i,
+ )
user_i.save()
user_details_i.save()
- logger.info("Created user with id: "+str(user_id))
-
+ logger.info("Created user with id: " + str(user_id))
def test_get_api_management_users_all(self):
"""
@@ -102,27 +110,38 @@ def test_get_api_management_users_all(self):
:return: None
"""
self.setup_database()
- response = self.client.get('/workshop/api/management/users/all', **self.auth_headers)
+ response = self.client.get(
+ "/workshop/api/management/users/all", **self.auth_headers
+ )
self.assertEqual(response.status_code, 200)
response_data = json.loads(response.content)
- self.assertEqual(len(response_data['users']), settings.DEFAULT_LIMIT)
- response = self.client.get('/workshop/api/management/users/all?limit=10&offset=0', **self.auth_headers)
+ all_users_length = len(response_data["users"])
+ self.assertEqual(
+ len(response_data["users"]), min(all_users_length, settings.MAX_LIMIT)
+ )
+ response = self.client.get(
+ "/workshop/api/management/users/all?limit=10&offset=0", **self.auth_headers
+ )
self.assertEqual(response.status_code, 200)
response_data = json.loads(response.content)
- self.assertEqual(len(response_data['users']), 10)
- response2 = self.client.get('/workshop/api/management/users/all?limit=10&offset=10', **self.auth_headers)
+ self.assertEqual(len(response_data["users"]), 10)
+ response2 = self.client.get(
+ "/workshop/api/management/users/all?limit=10&offset=10", **self.auth_headers
+ )
self.assertEqual(response2.status_code, 200)
response_data2 = json.loads(response2.content)
- self.assertNotEquals(response_data['users'], response_data2['users'])
-
+ self.assertNotEquals(response_data["users"], response_data2["users"])
+ response = self.client.get(
+ "/workshop/api/management/users/all?limit=a&offset=-1", **self.auth_headers
+ )
+ self.assertEqual(response.status_code, 200)
+ response_data = json.loads(response.content)
+ self.assertEqual(len(response_data["users"]), all_users_length)
def test_bad_get_api_management_users_all(self):
"""
tests the get user details api
:return: None
"""
- response = self.client.get('/workshop/api/management/users/all')
+ response = self.client.get("/workshop/api/management/users/all")
self.assertEqual(response.status_code, 401)
- response = self.client.get('/workshop/api/management/users/all?limit=a&offset=-1', **self.auth_headers)
- self.assertEqual(response.status_code, 400)
-
diff --git a/services/workshop/crapi/user/urls.py b/services/workshop/crapi/user/urls.py
index 5ba07973..82db32b0 100644
--- a/services/workshop/crapi/user/urls.py
+++ b/services/workshop/crapi/user/urls.py
@@ -22,5 +22,5 @@
urlpatterns = [
# Do not change the order of URLs
- re_path(r'users/all$', user_views.AdminUserView.as_view()),
-]
\ No newline at end of file
+ re_path(r"users/all$", user_views.AdminUserView.as_view()),
+]
diff --git a/services/workshop/crapi/user/views.py b/services/workshop/crapi/user/views.py
index a3c99c52..04c479ca 100644
--- a/services/workshop/crapi/user/views.py
+++ b/services/workshop/crapi/user/views.py
@@ -27,10 +27,12 @@
from utils.jwt import jwt_auth_required
from utils import messages
from utils.logging import log_error
+from rest_framework.pagination import LimitOffsetPagination
logger = logging.getLogger()
-class AdminUserView(APIView):
+
+class AdminUserView(APIView, LimitOffsetPagination):
"""
View for admin user to fetch user details
"""
@@ -47,31 +49,24 @@ def get(self, request, user=None):
user details and 200 status if no error
message and corresponding status if error
"""
- limit = request.GET.get('limit', str(settings.DEFAULT_LIMIT))
- offset = request.GET.get('offset', str(settings.DEFAULT_OFFSET))
- if not limit.isdigit() or not offset.isdigit():
- return Response(
- {'message': messages.INVALID_LIMIT_OR_OFFSET},
- status=status.HTTP_400_BAD_REQUEST
- )
- limit = int(limit)
- offset = int(offset)
- if limit > settings.MAX_LIMIT:
- limit = 100
- if limit < 0:
- limit = settings.DEFAULT_LIMIT
- if offset < 0:
- offset = settings.DEFAULT_OFFSET
# Sort by id
- userdetails = UserDetails.objects.all().order_by('id')[offset:offset+limit]
+ userdetails = UserDetails.objects.all().order_by("id")
if not userdetails:
return Response(
- {'message': messages.NO_USER_DETAILS},
- status=status.HTTP_404_NOT_FOUND
+ {"message": messages.NO_USER_DETAILS}, status=status.HTTP_404_NOT_FOUND
)
- serializer = UserDetailsSerializer(userdetails, many=True)
- response_data = {
- "users": serializer.data
- }
+ paginated = self.paginate_queryset(userdetails, request)
+ serializer = UserDetailsSerializer(paginated, many=True)
+ response_data = dict(
+ users=serializer.data,
+ next_offset=(
+ self.offset + self.limit
+ if self.offset + self.limit < self.count
+ else None
+ ),
+ previous_offset=(
+ self.offset - self.limit if self.offset - self.limit >= 0 else None
+ ),
+ count=self.get_count(paginated),
+ )
return Response(response_data, status=status.HTTP_200_OK)
-
diff --git a/services/workshop/crapi_site/__init__.py b/services/workshop/crapi_site/__init__.py
index bfd4bb9d..e9b106a0 100644
--- a/services/workshop/crapi_site/__init__.py
+++ b/services/workshop/crapi_site/__init__.py
@@ -10,5 +10,3 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-
-
diff --git a/services/workshop/crapi_site/settings.py b/services/workshop/crapi_site/settings.py
index d81d19fe..3693971f 100644
--- a/services/workshop/crapi_site/settings.py
+++ b/services/workshop/crapi_site/settings.py
@@ -32,11 +32,12 @@
DEFAULT_OFFSET = 0
MAX_LIMIT = 100
+
def get_env_value(env_variable):
try:
return os.environ[env_variable]
except KeyError:
- error_msg = f'Set the {env_variable} environment variable'
+ error_msg = f"Set the {env_variable} environment variable"
raise ImproperlyConfigured(error_msg)
@@ -47,123 +48,119 @@ def get_env_value(env_variable):
# See https://docs.djangoproject.com/en/2.2/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
-SECRET_KEY = get_env_value('SECRET_KEY')
+SECRET_KEY = get_env_value("SECRET_KEY")
# Enable it if Testing
-IS_TESTING = os.environ.get('IS_TESTING', False)
+IS_TESTING = os.environ.get("IS_TESTING", False)
# SECURITY WARNING: don't run with debug turned on in production!
-DEBUG = os.environ.get('DEBUG', False)
+DEBUG = os.environ.get("DEBUG", False)
-ALLOWED_HOSTS = ['*']
+ALLOWED_HOSTS = ["*"]
-API_GATEWAY_URL = get_env_value('API_GATEWAY_URL')
+API_GATEWAY_URL = get_env_value("API_GATEWAY_URL")
API_GATEWAY_USERNAME = "vendorcrapi"
API_GATEWAY_PASSWORD = "Pa$$4Vendor_1"
# Application definition
INSTALLED_APPS = [
- 'django.contrib.admin',
- 'django.contrib.auth',
- 'django.contrib.contenttypes',
- 'django.contrib.sessions',
- 'django.contrib.messages',
- 'django.contrib.staticfiles',
- 'corsheaders',
- 'health_check',
- 'health_check.db',
- 'core',
- 'crapi',
+ "django.contrib.admin",
+ "django.contrib.auth",
+ "django.contrib.contenttypes",
+ "django.contrib.sessions",
+ "django.contrib.messages",
+ "django.contrib.staticfiles",
+ "corsheaders",
+ "health_check",
+ "health_check.db",
+ "core",
+ "crapi",
# 'crapi.apps.CRAPIConfig',
# 'user.apps.UserConfig',
"django_extensions",
]
MIDDLEWARE = [
- 'django.middleware.security.SecurityMiddleware',
- 'django.contrib.sessions.middleware.SessionMiddleware',
- 'django.middleware.common.CommonMiddleware',
- 'django.middleware.csrf.CsrfViewMiddleware',
- 'django.contrib.auth.middleware.AuthenticationMiddleware',
- 'django.contrib.messages.middleware.MessageMiddleware',
- 'django.middleware.clickjacking.XFrameOptionsMiddleware',
- 'corsheaders.middleware.CorsMiddleware',
- 'django.middleware.common.BrokenLinkEmailsMiddleware',
- 'django.middleware.common.CommonMiddleware',
+ "django.middleware.security.SecurityMiddleware",
+ "django.contrib.sessions.middleware.SessionMiddleware",
+ "django.middleware.common.CommonMiddleware",
+ "django.middleware.csrf.CsrfViewMiddleware",
+ "django.contrib.auth.middleware.AuthenticationMiddleware",
+ "django.contrib.messages.middleware.MessageMiddleware",
+ "django.middleware.clickjacking.XFrameOptionsMiddleware",
+ "corsheaders.middleware.CorsMiddleware",
+ "django.middleware.common.BrokenLinkEmailsMiddleware",
+ "django.middleware.common.CommonMiddleware",
]
CORS_ORIGIN_ALLOW_ALL = True
-ROOT_URLCONF = 'crapi_site.urls'
+ROOT_URLCONF = "crapi_site.urls"
-TEST_RUNNER = 'xmlrunner.extra.djangotestrunner.XMLTestRunner'
+TEST_RUNNER = "xmlrunner.extra.djangotestrunner.XMLTestRunner"
-TEST_OUTPUT_DIR = os.path.join(BASE_DIR, 'test-reports')
+TEST_OUTPUT_DIR = os.path.join(BASE_DIR, "test-reports")
TEMPLATES = [
{
- 'BACKEND': 'django.template.backends.django.DjangoTemplates',
- 'DIRS': [],
- 'APP_DIRS': True,
- 'OPTIONS': {
- 'context_processors': [
- 'django.template.context_processors.debug',
- 'django.template.context_processors.request',
- 'django.contrib.auth.context_processors.auth',
- 'django.contrib.messages.context_processors.messages',
+ "BACKEND": "django.template.backends.django.DjangoTemplates",
+ "DIRS": [],
+ "APP_DIRS": True,
+ "OPTIONS": {
+ "context_processors": [
+ "django.template.context_processors.debug",
+ "django.template.context_processors.request",
+ "django.contrib.auth.context_processors.auth",
+ "django.contrib.messages.context_processors.messages",
],
},
},
]
-WSGI_APPLICATION = 'crapi_site.wsgi.application'
+WSGI_APPLICATION = "crapi_site.wsgi.application"
REST_FRAMEWORK = {
- 'DEFAULT_RENDERER_CLASSES': [
- 'rest_framework.renderers.JSONRenderer',
+ "DEFAULT_RENDERER_CLASSES": [
+ "rest_framework.renderers.JSONRenderer",
],
- 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
- 'PAGE_SIZE': 100,
- 'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',),
- 'UNAUTHENTICATED_USER': None, # Needed once you disable django.contrib.auth
+ "DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.LimitOffsetPagination",
+ "PAGE_SIZE": MAX_LIMIT,
+ "DEFAULT_FILTER_BACKENDS": ("django_filters.rest_framework.DjangoFilterBackend",),
+ "UNAUTHENTICATED_USER": None, # Needed once you disable django.contrib.auth
}
LOGGING = {
- 'version': 1,
- 'disable_existing_loggers': False,
- 'formatters': {
- 'standard': {
- 'format': '%(asctime)s %(name)-12s [%(levelname)s]: %(message)s'
- },
- 'console': {
- 'format': '%(name)-12s %(levelname)-8s %(message)s'
- },
+ "version": 1,
+ "disable_existing_loggers": False,
+ "formatters": {
+ "standard": {"format": "%(asctime)s %(name)-12s [%(levelname)s]: %(message)s"},
+ "console": {"format": "%(name)-12s %(levelname)-8s %(message)s"},
},
- 'handlers': {
- 'file': {
- 'level': 'DEBUG',
- 'class': 'logging.FileHandler',
- 'filename': BASE_DIR + '/debug.log',
- 'formatter': 'standard',
+ "handlers": {
+ "file": {
+ "level": "DEBUG",
+ "class": "logging.FileHandler",
+ "filename": BASE_DIR + "/debug.log",
+ "formatter": "standard",
},
- 'console': {
- 'level': 'INFO',
- 'class': 'logging.StreamHandler',
- 'formatter': 'console',
+ "console": {
+ "level": "INFO",
+ "class": "logging.StreamHandler",
+ "formatter": "console",
},
},
- 'root': {
- 'handlers': ['file', 'console'],
- 'level': 'DEBUG',
+ "root": {
+ "handlers": ["file", "console"],
+ "level": "DEBUG",
},
- 'django.request': {
- 'handlers': ['file'],
- 'level': 'DEBUG',
+ "django.request": {
+ "handlers": ["file"],
+ "level": "DEBUG",
},
- 'djongo': {
- 'level': 'DEBUG',
- 'handlers': ['console'],
- 'propogate': True,
+ "djongo": {
+ "level": "DEBUG",
+ "handlers": ["console"],
+ "propogate": True,
},
}
@@ -171,34 +168,31 @@ def get_env_value(env_variable):
# https://docs.djangoproject.com/en/2.2/ref/settings/#databases
DATABASES = {
- 'default': {
- 'ENGINE': 'django.db.backends.postgresql',
- 'NAME': get_env_value('DB_NAME'),
- 'USER': get_env_value('DB_USER'),
- 'PASSWORD': get_env_value('DB_PASSWORD'),
- 'HOST': get_env_value('DB_HOST'),
- 'PORT': get_env_value('DB_PORT'),
- 'TEST': {
- 'NAME': 'test_crapi',
- 'USER': get_env_value('DB_USER'),
+ "default": {
+ "ENGINE": "django.db.backends.postgresql",
+ "NAME": get_env_value("DB_NAME"),
+ "USER": get_env_value("DB_USER"),
+ "PASSWORD": get_env_value("DB_PASSWORD"),
+ "HOST": get_env_value("DB_HOST"),
+ "PORT": get_env_value("DB_PORT"),
+ "TEST": {
+ "NAME": "test_crapi",
+ "USER": get_env_value("DB_USER"),
},
- 'CONN_MAX_AGE': 0,
+ "CONN_MAX_AGE": 0,
},
- 'mongodb': {
- 'ENGINE': 'djongo',
- 'NAME': get_env_value('MONGO_DB_NAME'),
- 'CLIENT' : {
- 'host': get_env_value('MONGO_DB_HOST'),
- 'port': int(get_env_value('MONGO_DB_PORT')),
- 'username': get_env_value('MONGO_DB_USER'),
- 'password': get_env_value('MONGO_DB_PASSWORD'),
- 'authSource': 'admin'
+ "mongodb": {
+ "ENGINE": "djongo",
+ "NAME": get_env_value("MONGO_DB_NAME"),
+ "CLIENT": {
+ "host": get_env_value("MONGO_DB_HOST"),
+ "port": int(get_env_value("MONGO_DB_PORT")),
+ "username": get_env_value("MONGO_DB_USER"),
+ "password": get_env_value("MONGO_DB_PASSWORD"),
+ "authSource": "admin",
},
- 'TEST': {
- 'NAME': 'test_crapi_mongo',
- 'USER': get_env_value('MONGO_DB_USER')
- }
- }
+ "TEST": {"NAME": "test_crapi_mongo", "USER": get_env_value("MONGO_DB_USER")},
+ },
}
# Password validation
@@ -222,9 +216,9 @@ def get_env_value(env_variable):
# Internationalization
# https://docs.djangoproject.com/en/2.2/topics/i18n/
-LANGUAGE_CODE = 'en-us'
+LANGUAGE_CODE = "en-us"
-TIME_ZONE = 'UTC'
+TIME_ZONE = "UTC"
USE_I18N = True
@@ -235,13 +229,19 @@ def get_env_value(env_variable):
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.2/howto/static-files/
-STATIC_URL = '/static/'
-
-IDENTITY_VERIFY = 'http://{}/identity/api/auth/verify'.format(get_env_value('IDENTITY_SERVICE'))
-IDENTITY_LOGIN = 'http://{}/identity/api/auth/login'.format(get_env_value('IDENTITY_SERVICE'))
-TLS_ENABLED = os.environ.get('TLS_ENABLED')
-if TLS_ENABLED and (TLS_ENABLED.lower() in ['true', '1', 'yes']):
- IDENTITY_VERIFY = 'https://{}/identity/api/auth/verify'.format(get_env_value('IDENTITY_SERVICE'))
- IDENTITY_LOGIN = 'https://{}/identity/api/auth/login'.format(get_env_value('IDENTITY_SERVICE'))
-
-
+STATIC_URL = "/static/"
+
+IDENTITY_VERIFY = "http://{}/identity/api/auth/verify".format(
+ get_env_value("IDENTITY_SERVICE")
+)
+IDENTITY_LOGIN = "http://{}/identity/api/auth/login".format(
+ get_env_value("IDENTITY_SERVICE")
+)
+TLS_ENABLED = os.environ.get("TLS_ENABLED")
+if TLS_ENABLED and (TLS_ENABLED.lower() in ["true", "1", "yes"]):
+ IDENTITY_VERIFY = "https://{}/identity/api/auth/verify".format(
+ get_env_value("IDENTITY_SERVICE")
+ )
+ IDENTITY_LOGIN = "https://{}/identity/api/auth/login".format(
+ get_env_value("IDENTITY_SERVICE")
+ )
diff --git a/services/workshop/crapi_site/urls.py b/services/workshop/crapi_site/urls.py
index 86df2c00..7cf13eda 100644
--- a/services/workshop/crapi_site/urls.py
+++ b/services/workshop/crapi_site/urls.py
@@ -32,7 +32,7 @@
from django.urls import path, include
urlpatterns = [
- path('workshop/admin/', admin.site.urls),
- path('workshop/health_check/', include('health_check.urls')),
- path('workshop/', include('crapi.urls')),
+ path("workshop/admin/", admin.site.urls),
+ path("workshop/health_check/", include("health_check.urls")),
+ path("workshop/", include("crapi.urls")),
]
diff --git a/services/workshop/crapi_site/wsgi.py b/services/workshop/crapi_site/wsgi.py
index 0433bba8..a4df462d 100644
--- a/services/workshop/crapi_site/wsgi.py
+++ b/services/workshop/crapi_site/wsgi.py
@@ -25,6 +25,6 @@
from django.core.wsgi import get_wsgi_application
-os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'crapi_site.settings')
+os.environ.setdefault("DJANGO_SETTINGS_MODULE", "crapi_site.settings")
application = get_wsgi_application()
diff --git a/services/workshop/manage.py b/services/workshop/manage.py
index 221e0e90..9c8780fb 100755
--- a/services/workshop/manage.py
+++ b/services/workshop/manage.py
@@ -19,7 +19,7 @@
def main():
- os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'crapi_site.settings')
+ os.environ.setdefault("DJANGO_SETTINGS_MODULE", "crapi_site.settings")
try:
from django.core.management import execute_from_command_line
except ImportError as exc:
@@ -31,5 +31,5 @@ def main():
execute_from_command_line(sys.argv)
-if __name__ == '__main__':
+if __name__ == "__main__":
main()
diff --git a/services/workshop/requirements.txt b/services/workshop/requirements.txt
index d8b2d67b..6c71f547 100644
--- a/services/workshop/requirements.txt
+++ b/services/workshop/requirements.txt
@@ -20,4 +20,5 @@ Werkzeug==2.0.3
Faker==22.1.0
gunicorn==21.2.0
coverage==7.4.1
-unittest-xml-reporting==3.2.0
\ No newline at end of file
+unittest-xml-reporting==3.2.0
+black==24.4.2
diff --git a/services/workshop/setup.py b/services/workshop/setup.py
index 316fcc88..0de6f87f 100644
--- a/services/workshop/setup.py
+++ b/services/workshop/setup.py
@@ -15,11 +15,12 @@
from setuptools import setup, find_packages
-with open('requirements.txt') as f:
- requirements = f.readlines()
+with open("requirements.txt") as f:
+ requirements = f.readlines()
-setup(name='crapi_site',
- version='1.0',
- packages=find_packages(),
- install_requires=requirements,
- )
+setup(
+ name="crapi_site",
+ version="1.0",
+ packages=find_packages(),
+ install_requires=requirements,
+)
diff --git a/services/workshop/utils/__init__.py b/services/workshop/utils/__init__.py
index bfd4bb9d..e9b106a0 100644
--- a/services/workshop/utils/__init__.py
+++ b/services/workshop/utils/__init__.py
@@ -10,5 +10,3 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-
-
diff --git a/services/workshop/utils/helper.py b/services/workshop/utils/helper.py
index fff21e7c..0561ea1e 100644
--- a/services/workshop/utils/helper.py
+++ b/services/workshop/utils/helper.py
@@ -2,5 +2,5 @@
def basic_auth(username, password=""):
- token = b64encode(f"{username}:{password}".encode('utf-8')).decode("ascii")
- return f'Basic {token}'
\ No newline at end of file
+ token = b64encode(f"{username}:{password}".encode("utf-8")).decode("ascii")
+ return f"Basic {token}"
diff --git a/services/workshop/utils/jwt.py b/services/workshop/utils/jwt.py
index bdb4fbda..a0d9f1a3 100644
--- a/services/workshop/utils/jwt.py
+++ b/services/workshop/utils/jwt.py
@@ -28,6 +28,7 @@
logger = logging.getLogger()
+
def jwt_auth_required(func):
"""
decorator for authorizing http requests
@@ -41,36 +42,49 @@ def jwt_auth_required(func):
def new_func(*args, **kwargs):
try:
request = args[1]
- if 'HTTP_AUTHORIZATION' in request.META \
- and request.META.get('HTTP_AUTHORIZATION')[0:7] == 'Bearer ':
- token = request.META.get('HTTP_AUTHORIZATION')[7:]
- tokenJson = {'token': token}
+ if (
+ "HTTP_AUTHORIZATION" in request.META
+ and request.META.get("HTTP_AUTHORIZATION")[0:7] == "Bearer "
+ ):
+ token = request.META.get("HTTP_AUTHORIZATION")[7:]
+ tokenJson = {"token": token}
identity_url = settings.IDENTITY_VERIFY
logger.debug(f"Identity url: {identity_url}, tokenJson: {tokenJson}")
token_verify_response = requests.post(
- identity_url, json=tokenJson, verify=False)
- logger.debug(f"Identity url: {identity_url}, token_verify_response: {token_verify_response}")
+ identity_url, json=tokenJson, verify=False
+ )
+ logger.debug(
+ f"Identity url: {identity_url}, token_verify_response: {token_verify_response}"
+ )
response_status_code = token_verify_response.status_code
if response_status_code == status.HTTP_200_OK:
decoded = jwt.decode(token, options={"verify_signature": False})
- username = decoded['sub']
+ username = decoded["sub"]
user = User.objects.get(email=username)
# Add user object to the view function if authorized
- kwargs['user'] = user
+ kwargs["user"] = user
return func(*args, **kwargs)
logger.debug("JWT token verification failed")
- return Response({'message': messages.INVALID_TOKEN},
- status=status.HTTP_401_UNAUTHORIZED,
- content_type="application/json")
+ return Response(
+ {"message": messages.INVALID_TOKEN},
+ status=status.HTTP_401_UNAUTHORIZED,
+ content_type="application/json",
+ )
logger.debug("JWT token not found in request header")
- return Response({'message': messages.JWT_REQUIRED},
- status=status.HTTP_401_UNAUTHORIZED,
- content_type="application/json")
+ return Response(
+ {"message": messages.JWT_REQUIRED},
+ status=status.HTTP_401_UNAUTHORIZED,
+ content_type="application/json",
+ )
except (jwt.exceptions.DecodeError, User.DoesNotExist) as e:
- logger.debug(f"JWT token verification failed with exception: {e}", exc_info=True)
- return Response({'message': messages.INVALID_TOKEN},
- status=status.HTTP_401_UNAUTHORIZED,
- content_type="application/json")
+ logger.debug(
+ f"JWT token verification failed with exception: {e}", exc_info=True
+ )
+ return Response(
+ {"message": messages.INVALID_TOKEN},
+ status=status.HTTP_401_UNAUTHORIZED,
+ content_type="application/json",
+ )
return new_func
diff --git a/services/workshop/utils/messages.py b/services/workshop/utils/messages.py
index 6393f961..b899ef2b 100644
--- a/services/workshop/utils/messages.py
+++ b/services/workshop/utils/messages.py
@@ -31,16 +31,25 @@
ORDER_CREATED = "Order sent successfully."
ORDER_RETURNED_PENDING = "This order is already requested for returning!"
ORDER_ALREADY_RETURNED = "This order is already returned!"
-ORDER_RETURNING = "Please use the following QR code to return your order to a UPS store!"
-COUPON_ALREADY_APPLIED = "Coupon code is already claimed by you!! Please try with another coupon code"
+ORDER_RETURNING = (
+ "Please use the following QR code to return your order to a UPS store!"
+)
+COUPON_ALREADY_APPLIED = (
+ "Coupon code is already claimed by you!! Please try with another coupon code"
+)
COUPON_APPLIED = "Coupon successfully applied!"
COUPON_NOT_FOUND = "Coupon not found"
RESTRICTED = "You are not allowed to access this resource!"
-INVALID_STATUS = "The value of 'status' has to be 'delivered','return pending' or 'returned'"
-INVALID_SERVICE_REQUEST_STATUS = "The value of 'status' has to be 'Pending' or 'Finished'"
+INVALID_STATUS = (
+ "The value of 'status' has to be 'delivered','return pending' or 'returned'"
+)
+INVALID_SERVICE_REQUEST_STATUS = (
+ "The value of 'status' has to be 'Pending' or 'Finished'"
+)
REPORT_ID_MISSING = "Please enter the report_id value."
INVALID_REPORT_ID = "Please enter a valid report_id value."
REPORT_DOES_NOT_EXIST = "The Report does not exist for given report_id."
COULD_NOT_CONNECT = "Could not connect to mechanic api."
INVALID_LIMIT_OR_OFFSET = "Param limit and offset values should be integers."
-NO_USER_DETAILS = "No user details found."
\ No newline at end of file
+NO_USER_DETAILS = "No user details found."
+NO_OBJECT_FOUND = "No object found."
diff --git a/services/workshop/utils/mock_methods.py b/services/workshop/utils/mock_methods.py
index 185399b7..ee94db09 100644
--- a/services/workshop/utils/mock_methods.py
+++ b/services/workshop/utils/mock_methods.py
@@ -19,6 +19,7 @@
from utils import messages
from crapi.user.models import User
from faker import Faker
+
Faker.seed(4321)
@@ -29,6 +30,7 @@
logger = logging.getLogger()
+
def get_sample_mechanic_data():
"""
gives mechanic data which can be used for testing
@@ -42,6 +44,7 @@ def get_sample_mechanic_data():
"password": "admin",
}
+
def get_sample_user_data():
"""
gives user data which can be used for testing
@@ -54,8 +57,10 @@ def get_sample_user_data():
"password": "password",
}
+
def fake_phone_number(fake: Faker) -> str:
- return f'{fake.msisdn()[3:]}'
+ return f"{fake.msisdn()[3:]}"
+
def get_sample_users(users_count=100):
"""
@@ -64,15 +69,18 @@ def get_sample_users(users_count=100):
fake = Faker()
users = []
for i in range(users_count):
- users.append({
- "name": fake.name(),
- "email": fake.email(),
- "number": fake_phone_number(fake),
- "password": fake.password(),
- "role": fake.random_element(elements=dict(User.ROLE_CHOICES).keys()),
- })
+ users.append(
+ {
+ "name": fake.name(),
+ "email": fake.email(),
+ "number": fake_phone_number(fake),
+ "password": fake.password(),
+ "role": fake.random_element(elements=dict(User.ROLE_CHOICES).keys()),
+ }
+ )
return users
+
def get_sample_admin_user():
"""
gives admin user which can be used for testing
@@ -86,7 +94,6 @@ def get_sample_admin_user():
}
-
def mock_jwt_auth_required(func):
"""
mock function to validate jwt
@@ -95,25 +102,32 @@ def mock_jwt_auth_required(func):
calls the actual view function if token is a valid email
returns error message if token is a invalid email
"""
+
@wraps(func)
def new_func(*args, **kwargs):
try:
request = args[1]
- if 'HTTP_AUTHORIZATION' in request.META \
- and request.META.get('HTTP_AUTHORIZATION')[0:7] == 'Bearer ':
- token = request.META.get('HTTP_AUTHORIZATION')[7:]
+ if (
+ "HTTP_AUTHORIZATION" in request.META
+ and request.META.get("HTTP_AUTHORIZATION")[0:7] == "Bearer "
+ ):
+ token = request.META.get("HTTP_AUTHORIZATION")[7:]
user = User.objects.get(email=token)
# Add user object to the view function if authorized
- kwargs['user'] = user
+ kwargs["user"] = user
return func(*args, **kwargs)
- return Response({'message': messages.JWT_REQUIRED},
- status=status.HTTP_401_UNAUTHORIZED,
- content_type="application/json")
+ return Response(
+ {"message": messages.JWT_REQUIRED},
+ status=status.HTTP_401_UNAUTHORIZED,
+ content_type="application/json",
+ )
except (jwt.exceptions.DecodeError, User.DoesNotExist):
- return Response({'message': messages.INVALID_TOKEN},
- status=status.HTTP_401_UNAUTHORIZED,
- content_type="application/json")
+ return Response(
+ {"message": messages.INVALID_TOKEN},
+ status=status.HTTP_401_UNAUTHORIZED,
+ content_type="application/json",
+ )
return new_func