Skip to content

Commit

Permalink
Merge pull request #119 from Alonza0314/refactor/backend-subscriber-c…
Browse files Browse the repository at this point in the history
…reate

Refactor/backend subscriber create
  • Loading branch information
ianchen0119 authored Nov 12, 2024
2 parents f3c2be7 + bae7045 commit 2253502
Show file tree
Hide file tree
Showing 30 changed files with 3,619 additions and 161 deletions.
200 changes: 196 additions & 4 deletions backend/WebUI/api_webui.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ const (
userDataColl = "userData"
tenantDataColl = "tenantData"
identityDataColl = "subscriptionData.identityData"
profileDataColl = "profileData" // store profile data
)

var jwtKey = "" // for generating JWT
Expand Down Expand Up @@ -1334,10 +1335,8 @@ func PostSubscriberByID(c *gin.Context) {
return
}
if len(authSubsDataInterface) > 0 {
if authSubsDataInterface["tenantId"].(string) != claims["tenantId"].(string) {
c.JSON(http.StatusUnprocessableEntity, gin.H{})
return
}
c.JSON(http.StatusConflict, gin.H{"cause": ueId + " already exists"})
return
}
}
dbOperation(ueId, servingPlmnId, "post", &subsData, claims)
Expand Down Expand Up @@ -1952,3 +1951,196 @@ func OptionsSubscribers(c *gin.Context) {

c.JSON(http.StatusNoContent, gin.H{})
}

// Delete profile by profileName
func DeleteProfile(c *gin.Context) {
setCorsHeader(c)
logger.ProcLog.Infoln("Delete One Profile Data")

profileName := c.Param("profileName")
pf, err := mongoapi.RestfulAPIGetOne(profileDataColl, bson.M{"profileName": profileName})
if err != nil {
logger.ProcLog.Errorf("DeleteProfile err: %+v", err)
c.JSON(http.StatusInternalServerError, gin.H{"cause": err.Error()})
return
}
if len(pf) == 0 {
c.JSON(http.StatusNotFound, gin.H{"cause": profileName + " does not exist"})
return
}

if err = dbProfileOperation(profileName, "delete", nil); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"cause": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"success": profileName + " has already been deleted"})
}

// Get profile list
func GetProfiles(c *gin.Context) {
setCorsHeader(c)
logger.ProcLog.Infoln("Get All Profiles List")

_, err := GetTenantId(c)
if err != nil {
logger.ProcLog.Errorln(err.Error())
c.JSON(http.StatusBadRequest, gin.H{"cause": "Illegal Token"})
return
}

pfs := make([]string, 0)
profileList, err := mongoapi.RestfulAPIGetMany(profileDataColl, bson.M{})
if err != nil {
logger.ProcLog.Errorf("GetProfiles err: %+v", err)
c.JSON(http.StatusInternalServerError, gin.H{"cause": err.Error()})
return
}
for _, profile := range profileList {
profileName := profile["profileName"]

pfs = append(pfs, profileName.(string))
}
c.JSON(http.StatusOK, pfs)
}

// Get profile by profileName
func GetProfile(c *gin.Context) {
setCorsHeader(c)
logger.ProcLog.Infoln("Get One Profile Data")

profileName := c.Param("profileName")

profile, err := mongoapi.RestfulAPIGetOne(profileDataColl, bson.M{"profileName": profileName})
if err != nil {
logger.ProcLog.Errorf("GetProfile err: %+v", err)
c.JSON(http.StatusInternalServerError, gin.H{"cause": err.Error()})
return
}
var pf Profile
err = json.Unmarshal(mapToByte(profile), &pf)
if err != nil {
logger.ProcLog.Errorf("JSON Unmarshal err: %+v", err)
c.JSON(http.StatusInternalServerError, gin.H{"cause": err.Error()})
return
}
c.JSON(http.StatusOK, pf)
}

// Post profile
func PostProfile(c *gin.Context) {
setCorsHeader(c)
logger.ProcLog.Infoln("Post One Profile Data")

tokenStr := c.GetHeader("Token")
_, err := ParseJWT(tokenStr)
if err != nil {
logger.ProcLog.Errorln(err.Error())
c.JSON(http.StatusBadRequest, gin.H{"cause": "Illegal Token"})
return
}

var profile Profile
if err = c.ShouldBindJSON(&profile); err != nil {
logger.ProcLog.Errorf("PostProfile err: %+v", err)
c.JSON(http.StatusBadRequest, gin.H{"cause": "JSON format incorrect"})
return
}

tenantData, err := mongoapi.RestfulAPIGetOne(tenantDataColl, bson.M{"tenantName": "admin"})
if err != nil {
logger.ProcLog.Errorf("GetProfile err: %+v", err)
c.JSON(http.StatusInternalServerError, gin.H{"cause": err.Error()})
return
}
profile.TenantId = tenantData["tenantId"].(string)

pf, err := mongoapi.RestfulAPIGetOne(profileDataColl, bson.M{"profileName": profile.ProfileName})
if err != nil {
logger.ProcLog.Errorf("GetProfile err: %+v", err)
c.JSON(http.StatusInternalServerError, gin.H{"cause": err.Error()})
return
}
if len(pf) != 0 {
c.JSON(http.StatusConflict, gin.H{"cause": profile.ProfileName + " already exists"})
return
}

logger.ProcLog.Infof("PostProfile: %+v", profile.ProfileName)
if err = dbProfileOperation(profile.ProfileName, "post", &profile); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"cause": err.Error()})
return
}
c.JSON(http.StatusOK, profile)
}

// Put profile by profileName
func PutProfile(c *gin.Context) {
setCorsHeader(c)
logger.ProcLog.Infoln("Put One Profile Data")

profileName := c.Param("profileName")

var profile Profile
if err := c.ShouldBindJSON(&profile); err != nil {
logger.ProcLog.Errorf("PutProfile err: %+v", err)
c.JSON(http.StatusBadRequest, gin.H{"cause": "JSON format incorrect"})
return
}

pf, err := mongoapi.RestfulAPIGetOne(profileDataColl, bson.M{"profileName": profile.ProfileName})
if err != nil {
logger.ProcLog.Errorf("PutProfile err: %+v", err)
c.JSON(http.StatusInternalServerError, gin.H{"cause": err.Error()})
return
}
if len(pf) == 0 {
c.JSON(http.StatusNotFound, gin.H{"cause": profileName + " does not exist"})
return
}

tenantData, err := mongoapi.RestfulAPIGetOne(tenantDataColl, bson.M{"tenantName": "admin"})
if err != nil {
logger.ProcLog.Errorf("GetProfile err: %+v", err)
c.JSON(http.StatusInternalServerError, gin.H{"cause": err.Error()})
return
}
profile.TenantId = tenantData["tenantId"].(string)

logger.ProcLog.Infof("PutProfile: %+v", profile.ProfileName)
if err = dbProfileOperation(profileName, "put", &profile); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"cause": err.Error()})
return
}
c.JSON(http.StatusOK, profile)
}

func dbProfileOperation(profileName string, method string, profile *Profile) (err error) {
err = nil
filter := bson.M{"profileName": profileName}

// Replace all data with new one
if method == "put" {
if err = mongoapi.RestfulAPIDeleteOne(profileDataColl, filter); err != nil {
logger.ProcLog.Errorf("PutSubscriberByID err: %+v", err)
}
} else if method == "delete" {
if err = mongoapi.RestfulAPIDeleteOne(profileDataColl, filter); err != nil {
logger.ProcLog.Errorf("DeleteSubscriberByID err: %+v", err)
}
}

// Deal with error & early return
if err != nil {
return err
}

// Insert data
if method == "post" || method == "put" {
profileBsonM := toBsonM(profile)
if _, err = mongoapi.RestfulAPIPost(profileDataColl, filter, profileBsonM); err != nil {
logger.ProcLog.Errorf("PutSubscriberByID err: %+v", err)
}
}

return err
}
16 changes: 16 additions & 0 deletions backend/WebUI/model_profile_data.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package WebUI

import "github.com/free5gc/openapi/models"

type Profile struct {
ProfileName string `json:"profileName"`
TenantId string `json:"tenantId"`
AccessAndMobilitySubscriptionData models.AccessAndMobilitySubscriptionData `json:"AccessAndMobilitySubscriptionData"`
SessionManagementSubscriptionData []models.SessionManagementSubscriptionData `json:"SessionManagementSubscriptionData"`
SmfSelectionSubscriptionData models.SmfSelectionSubscriptionData `json:"SmfSelectionSubscriptionData"`
AmPolicyData models.AmPolicyData `json:"AmPolicyData"`
SmPolicyData models.SmPolicyData `json:"SmPolicyData"`
FlowRules []FlowRule `json:"FlowRules"`
QosFlows []QosFlow `json:"QosFlows"`
ChargingDatas []ChargingData
}
35 changes: 35 additions & 0 deletions backend/WebUI/routers.go
Original file line number Diff line number Diff line change
Expand Up @@ -250,4 +250,39 @@ var routes = Routes{
"/verify-staticip",
VerifyStaticIP,
},

{
"Delete Profile",
http.MethodDelete,
"/profile/:profileName",
DeleteProfile,
},

{
"Get Profile List",
http.MethodGet,
"/profile",
GetProfiles,
},

{
"Get Profile",
http.MethodGet,
"/profile/:profileName",
GetProfile,
},

{
"Post Profile",
http.MethodPost,
"/profile",
PostProfile,
},

{
"Put Profile",
http.MethodPut,
"/profile/:profileName",
PutProfile,
},
}
1 change: 1 addition & 0 deletions backend/webui_service/middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ func verifyDestPath(requestedURI string) string {
"status",
"analysis",
"subscriber",
"profile",
"tenant",
"charging",
"login",
Expand Down
102 changes: 100 additions & 2 deletions free5gc-Webconsole.postman_collection.json

Large diffs are not rendered by default.

11 changes: 11 additions & 0 deletions frontend/openapi-generator-docker.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#! /bin/bash

# prerequisites
# - docker

# use Docker to run OpenAPI Generator
docker run --rm -v $PWD:/local openapitools/openapi-generator-cli generate -i /local/webconsole.yaml -g typescript-axios -o /local/src/api

# replace Time with Date in the file
sed 's/: Time/: Date/g' /local/src/api/api.ts > /local/src/api/api.ts.mod
mv /local/src/api/api.ts.mod /local/src/api/api.ts # rename the replaced file to the original file
35 changes: 35 additions & 0 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ import ChangePassword from "./pages/ChangePassword";
import ChargingTable from "./pages/Charging/ChargingTable";
import { ProtectedRoute } from "./ProtectedRoute";
import { LoginContext, User } from "./LoginContext";
import ProfileList from "./pages/ProfileList";
import ProfileCreate from "./pages/ProfileCreate";
import ProfileRead from "./pages/ProfileRead";

export default function App() {
const [user, setUser] = useState<User | null>(() => {
Expand Down Expand Up @@ -182,6 +185,38 @@ export default function App() {
</ProtectedRoute>
}
/>
<Route
path="/profile"
element={
<ProtectedRoute>
<ProfileList />
</ProtectedRoute>
}
/>
<Route
path="/profile/create"
element={
<ProtectedRoute>
<ProfileCreate />
</ProtectedRoute>
}
/>
<Route
path="/profile/create/:profileName"
element={
<ProtectedRoute>
<ProfileCreate />
</ProtectedRoute>
}
/>
<Route
path="/profile/:profileName"
element={
<ProtectedRoute>
<ProfileRead />
</ProtectedRoute>
}
/>
</Routes>
</BrowserRouter>
</LoginContext.Provider>
Expand Down
9 changes: 9 additions & 0 deletions frontend/src/ListItems.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import PhoneAndroid from "@mui/icons-material/PhoneAndroid";
import FontDownload from "@mui/icons-material/FontDownload";
import SupervisorAccountOutlinedIcon from "@mui/icons-material/SupervisorAccountOutlined";
import AttachMoneyOutlinedIcon from "@mui/icons-material/AttachMoneyOutlined";
import PersonIcon from "@mui/icons-material/Person";

import { Link } from "react-router-dom";
import { LoginContext } from "./LoginContext";
Expand Down Expand Up @@ -43,6 +44,14 @@ export const MainListItems = () => {
<ListItemText primary="SUBSCRIBERS" />
</ListItemButton>
</Link>
<Link to="/profile" style={{ color: "inherit", textDecoration: "inherit" }}>
<ListItemButton>
<ListItemIcon>
<PersonIcon />
</ListItemIcon>
<ListItemText primary="PROFILE" />
</ListItemButton>
</Link>
<Link to="/analysis" style={{ color: "inherit", textDecoration: "inherit" }}>
<ListItemButton>
<ListItemIcon>
Expand Down
Loading

0 comments on commit 2253502

Please sign in to comment.