Skip to content

Commit

Permalink
feat: Add swagger UI (#193)
Browse files Browse the repository at this point in the history
* add swagger ui

Signed-off-by: Patricia Reinoso <[email protected]>

* fix format

Signed-off-by: Patricia Reinoso <[email protected]>

* add license and fix format

Signed-off-by: Patricia Reinoso <[email protected]>

* restore go.sum and go.mod

Signed-off-by: Patricia Reinoso <[email protected]>

* add swag dependencies

Signed-off-by: Patricia Reinoso <[email protected]>

* fix lint

Signed-off-by: Patricia Reinoso <[email protected]>

* use build tag for conditional build

Signed-off-by: Patricia Reinoso <[email protected]>

* use remote ip for swagger

Signed-off-by: Patricia Reinoso <[email protected]>

* fix format

Signed-off-by: Patricia Reinoso <[email protected]>

* keep function name and comment aligned

Signed-off-by: Patricia Reinoso <[email protected]>

---------

Signed-off-by: Patricia Reinoso <[email protected]>
Co-authored-by: Ajay Lotan Thakur <[email protected]>
Co-authored-by: gab-arrobo <[email protected]>
  • Loading branch information
3 people authored Aug 12, 2024
1 parent b2199e1 commit ec9874f
Show file tree
Hide file tree
Showing 11 changed files with 916 additions and 14 deletions.
32 changes: 32 additions & 0 deletions backend/webui_service/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<!--
SPDX-License-Identifier: Apache-2.0
Copyright 2024 Canonical Ltd.
-->

# Swagger UI Service

The webconsole can optionally serve a [swagger UI](https://github.com/swaggo/swag).
The API documentation is automatically generated from code annotations.

To generate the swagger UI files run:
```
swag init -g backend/webui_service/swagger_ui_service.go --outputTypes go
```
The `docs.go` file will automatically be created in `webconsole/docs`

The swagger UI operations are executed by default on `localhost`. If the webconsole server runs remotely, set the following environment variable.
```
export WEBUI_ENDPOINT=<webconsole-ip>:5000
```
Build the webconsole including the UI option:
```
make webconsole-ui
```
Access the swagger UI at:
```
http://<webconsole-ip>:5000/swagger/index.html
```
The `doc.json` file, which can be integrated in a frontend implementation, is available at:
```
http://<webconsole-ip>:5000/swagger/doc.json
```
15 changes: 15 additions & 0 deletions backend/webui_service/dummy_swagger_ui_service.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2024 Canonical Ltd

//go:build !ui

package webui_service

import (
"github.com/gin-gonic/gin"
"github.com/omec-project/webconsole/backend/logger"
)

func AddSwaggerUiService(engine *gin.Engine) {
logger.WebUILog.Infoln("Swagger UI service will not be added")
}
36 changes: 36 additions & 0 deletions backend/webui_service/swagger_ui_service.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2024 Canonical Ltd.

// +build ui

package webui_service

import (
"os"

"github.com/gin-gonic/gin"
"github.com/omec-project/webconsole/backend/logger"
"github.com/omec-project/webconsole/docs"
swaggerFiles "github.com/swaggo/files"
ginSwagger "github.com/swaggo/gin-swagger"
)

// @title Webconsole API Documentation
// @version 1.0

// @contact.name OMEC Project - Webconsole
// @contact.url https://github.com/omec-project/webconsole

// @license.name Apache 2.0
// @license.url http://www.apache.org/licenses/LICENSE-2.0.html

// @host localhost:5000
// @BasePath /
func AddSwaggerUiService(engine *gin.Engine) {
logger.WebUILog.Infoln("Adding Swagger UI service")
endpoint := os.Getenv("WEBUI_ENDPOINT")
if endpoint != "" {
docs.SwaggerInfo.Host = endpoint
}
engine.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
}
1 change: 1 addition & 0 deletions backend/webui_service/webui_init.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ func (webui *WEBUI) Start() {

/* First HTTP Server running at port to receive Config from ROC */
subconfig_router := logger_util.NewGinWithLogrus(logger.GinLog)
AddSwaggerUiService(subconfig_router)
AddUiService(subconfig_router)
configapi.AddServiceSub(subconfig_router)
configapi.AddService(subconfig_router)
Expand Down
1 change: 0 additions & 1 deletion configapi/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,3 @@ docker run --rm -it configapi
Endpoints sharing a common path may result in issues. For example, `/v2/pet/findByTags` and `/v2/pet/:petId` will result in an issue with the Gin framework. For more information about this known limitation, please refer to [gin-gonic/gin#388](https://github.com/gin-gonic/gin/issues/388) for more information.

A workaround is to manually update the path and handler. Please refer to [gin-gonic/gin/issues/205#issuecomment-296155497](https://github.com/gin-gonic/gin/issues/205#issuecomment-296155497) for more information.

75 changes: 67 additions & 8 deletions configapi/api_default.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,13 @@ const (
sliceDataColl = "webconsoleData.snapshots.sliceData"
)

// GetDeviceGroups -
// GetDeviceGroups godoc
// @Description Return the list of device groups
// @Tags Device Groups
// @Produce json
// @Success 200 {array} string "List of device group names"
// @Failure 500 {object} nil "Error retrieving device groups"
// @Router /config/v1/device-group/ [get]
func GetDeviceGroups(c *gin.Context) {
setCorsHeader(c)
logger.WebUILog.Infoln("Get all Device Groups")
Expand All @@ -47,7 +53,15 @@ func GetDeviceGroups(c *gin.Context) {
c.JSON(http.StatusOK, deviceGroups)
}

// GetDeviceGroupsByName -
// GetDeviceGroupByName godoc
// @Description Return the device group
// @Tags Device Groups
// @Param deviceGroupName path string true " "
// @Produce json
// @Success 200 {object} configmodels.DeviceGroups "Device group"
// @Failure 404 {object} nil "Device group not found"
// @Failure 500 {object} nil "Error retrieving device group"
// @Router /config/v1/device-group/{deviceGroupName} [get]
func GetDeviceGroupByName(c *gin.Context) {
setCorsHeader(c)
logger.WebUILog.Infoln("Get Device Group by name")
Expand All @@ -67,7 +81,14 @@ func GetDeviceGroupByName(c *gin.Context) {
}
}

// DeviceGroupGroupNameDelete -
// DeviceGroupGroupNameDelete godoc
// @Description Delete an existing device group
// @Tags Device Groups
// @Param deviceGroupName path string true " "
// @Success 200 {object} nil "Device group deleted successfully"
// @Failure 400 {object} nil "Invalid device group name provided"
// @Failure 500 {object} nil "Error deleting device group"
// @Router /config/v1/device-group/{deviceGroupName} [delete]
func DeviceGroupGroupNameDelete(c *gin.Context) {
logger.ConfigLog.Debugf("DeviceGroupGroupNameDelete")
if ret := DeviceGroupDeleteHandler(c); ret == true {
Expand All @@ -93,7 +114,15 @@ func DeviceGroupGroupNamePatch(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{})
}

// DeviceGroupGroupNamePost -
// DeviceGroupGroupNamePost godoc
// @Description Create a new device group
// @Tags Device Groups
// @Param deviceGroupName path string true " "
// @Param content body configmodels.DeviceGroups true " "
// @Success 200 {object} nil "Device group created"
// @Failure 400 {object} nil "Invalid device group content"
// @Failure 500 {object} nil "Error creating device group"
// @Router /config/v1/device-group/{deviceGroupName} [post]
func DeviceGroupGroupNamePost(c *gin.Context) {
logger.ConfigLog.Debugf("DeviceGroupGroupNamePost")
if ret := DeviceGroupPostHandler(c, configmodels.Post_op); ret == true {
Expand All @@ -103,7 +132,13 @@ func DeviceGroupGroupNamePost(c *gin.Context) {
}
}

// GetNetworkSlices -
// GetNetworkSlices godoc
// @Description Return the list of network slices
// @Tags Network Slices
// @Produce json
// @Success 200 {array} string "List of network slice names"
// @Failure 500 {object} nil "Error retrieving network slices"
// @Router /config/v1/network-slice/ [get]
func GetNetworkSlices(c *gin.Context) {
setCorsHeader(c)
logger.WebUILog.Infoln("Get all Network Slices")
Expand All @@ -120,7 +155,15 @@ func GetNetworkSlices(c *gin.Context) {
c.JSON(http.StatusOK, networkSlices)
}

// GetNetworkSliceByName -
// GetNetworkSliceByName godoc
// @Description Return the network slice
// @Tags Network Slices
// @Produce json
// @Param sliceName path string true " "
// @Success 200 {object} configmodels.Slice "Network slice"
// @Failure 404 {object} nil "Network slices not found"
// @Failure 500 {object} nil "Error retrieving network slice"
// @Router /config/v1/network-slice/{sliceName} [get]
func GetNetworkSliceByName(c *gin.Context) {
setCorsHeader(c)
logger.WebUILog.Infoln("Get Network Slice by name")
Expand All @@ -140,7 +183,15 @@ func GetNetworkSliceByName(c *gin.Context) {
}
}

// NetworkSliceSliceNameDelete -
// NetworkSliceSliceNameDelete godoc
// @Description Delete an existing network slice
// @Tags Network Slices
// @Produce json
// @Param sliceName path string true " "
// @Success 202 {object} nil "Network slice deleted successfully"
// @Failure 400 {object} nil "Invalid network slice name provided"
// @Failure 500 {object} nil "Error deleting network slice"
// @Router /config/v1/network-slice/{sliceName} [delete]
func NetworkSliceSliceNameDelete(c *gin.Context) {
logger.ConfigLog.Debugf("Received NetworkSliceSliceNameDelete ")
if ret := NetworkSliceDeleteHandler(c); ret == true {
Expand All @@ -150,7 +201,15 @@ func NetworkSliceSliceNameDelete(c *gin.Context) {
}
}

// NetworkSliceSliceNamePost -
// NetworkSliceSliceNamePost godoc
// @Description Create a new network slice
// @Tags Network Slices
// @Param sliceName path string true " "
// @Param content body configmodels.Slice true " "
// @Success 200 {object} nil "Network slice created"
// @Failure 400 {object} nil "Invalid network slice content"
// @Failure 500 {object} nil "Error creating network slice"
// @Router /config/v1/network-slice/{sliceName} [post]
func NetworkSliceSliceNamePost(c *gin.Context) {
logger.ConfigLog.Debugf("Received NetworkSliceSliceNamePost ")
if ret := NetworkSlicePostHandler(c, configmodels.Post_op); ret == true {
Expand Down
2 changes: 1 addition & 1 deletion configapi/api_slice_mgmt.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ func NetworkSliceDeleteHandler(c *gin.Context) bool {
msg.MsgType = configmodels.Network_slice
msg.SliceName = sliceName
configChannel <- &msg
configLog.Infof("Successfully Added Device Group [%v] with delete_op to config channel.", sliceName)
configLog.Infof("Successfully Added Network Slice [%v] with delete_op to config channel.", sliceName)
return true
}

Expand Down
37 changes: 33 additions & 4 deletions configapi/api_sub_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"github.com/omec-project/webconsole/configmodels"
"github.com/omec-project/webconsole/dbadapter"
"go.mongodb.org/mongo-driver/bson"

)

const (
Expand Down Expand Up @@ -268,7 +269,13 @@ func GetSampleJSON(c *gin.Context) {
c.JSON(http.StatusOK, subsData)
}

// Get all subscribers list
// GetSubscribers godoc
// @Description Return the list of subscribers
// @Tags Subscribers
// @Produce json
// @Success 200 {object} configmodels.SubsListIE "List of subscribers. Null if there are no subscribers"
// @Failure 500 {object} nil "Error retrieving subscribers"
// @Router /api/subscriber/ [get]
func GetSubscribers(c *gin.Context) {
setCorsHeader(c)

Expand All @@ -295,7 +302,15 @@ func GetSubscribers(c *gin.Context) {
c.JSON(http.StatusOK, subsList)
}

// Get subscriber by IMSI(ueId))
// GetSubscriberByID godoc
// @Description Get subscriber by IMSI (UE ID)
// @Tags Subscribers
// @Param imsi path string true "IMSI (UE ID)" example(imsi-208930100007487)
// @Produce json
// @Success 200 {object} nil "Subscriber"
// @Failure 404 {object} nil "Subscriber not found"
// @Failure 500 {object} nil "Error retrieving subscriber"
// @Router /api/subscriber/{imsi} [get]
func GetSubscriberByID(c *gin.Context) {
setCorsHeader(c)

Expand Down Expand Up @@ -357,7 +372,15 @@ func GetSubscriberByID(c *gin.Context) {
c.JSON(http.StatusOK, subsData)
}

// Post subscriber by IMSI(ueId)
// PostSubscriberByID godoc
// @Description Create subscriber by IMSI (UE ID)
// @Tags Subscribers
// @Param imsi path string true "IMSI (UE ID)"
// @Param content body configmodels.SubsOverrideData true " "
// @Success 201 {object} nil "Subscriber created"
// @Failure 400 {object} nil "Invalid subscriber content"
// @Failure 500 {object} nil "Error creating subscriber"
// @Router /api/subscriber/{imsi} [post]
func PostSubscriberByID(c *gin.Context) {
setCorsHeader(c)

Expand Down Expand Up @@ -449,7 +472,13 @@ func PatchSubscriberByID(c *gin.Context) {
logger.WebUILog.Infoln("Patch One Subscriber Data")
}

// Delete subscriber by IMSI(ueId)
// DeleteSubscriberByID godoc
// @Description Delete an existing subscriber
// @Tags Subscribers
// @Param imsi path string true "IMSI (UE ID)"
// @Success 204 {object} nil "Subscriber deleted successfully"
// @Failure 500 {object} nil "Error deleting subscriber"
// @Router /api/subscriber/{imsi} [delete]
func DeleteSubscriberByID(c *gin.Context) {
setCorsHeader(c)
logger.WebUILog.Infoln("Delete One Subscriber Data")
Expand Down
Loading

0 comments on commit ec9874f

Please sign in to comment.