From 5e66b759bdc04227850df7e87d3438ac74770e27 Mon Sep 17 00:00:00 2001 From: mgold1234 Date: Thu, 6 Jul 2023 13:58:52 +0300 Subject: [PATCH] add support for /devices end point --- pkg/models/devices_api.go | 118 ++++++++++++++++++++++++++++++++++++++ pkg/routes/devices.go | 92 +++++++++++++---------------- pkg/routes/imagesets.go | 22 +++---- 3 files changed, 170 insertions(+), 62 deletions(-) create mode 100644 pkg/models/devices_api.go diff --git a/pkg/models/devices_api.go b/pkg/models/devices_api.go new file mode 100644 index 000000000..32cb7d4b2 --- /dev/null +++ b/pkg/models/devices_api.go @@ -0,0 +1,118 @@ +package models + +// EdgeDeviceAPI is the entity that represents and Edge Device +// It is a combination of the data of a Device owned by Inventory API +// and the Device data saved on Edge API +type EdgeDeviceAPI struct { + UUID string `json:"UUID"` + AvailableHash string `json:"AvailableHash,omitempty"` + RHCClientID string `json:"RHCClientID"` + Connected bool `json:"Connected"` + Name string `json:"Name"` + LastSeen EdgeAPITime `json:"LastSeen"` + CurrentHash string `json:"CurrentHash,omitempty"` + ImageID uint `json:"ImageID"` + UpdateAvailable bool `json:"UpdateAvailable"` + DevicesGroups []DeviceGroupAPI + UpdateTransaction *[]UpdateTransaction `json:"UpdateTransaction"` + DeviceName string + Booted bool // Booted status is referring to the LastDeployment of this device +} + +// DeviceAPI is entity for device +type DeviceAPI struct { + UUID string `json:"UUID"` + AvailableHash string `json:"AvailableHash,omitempty"` + RHCClientID string `json:"RHCClientID"` + Connected bool `json:"Connected"` + Name string `json:"Name"` + LastSeen EdgeAPITime `json:"LastSeen"` + CurrentHash string `json:"CurrentHash,omitempty"` + ImageID uint `json:"ImageID"` + UpdateAvailable bool `json:"UpdateAvailable"` + DevicesGroups []DeviceGroupAPI + UpdateTransaction *[]UpdateTransaction `json:"UpdateTransaction"` + DeviceName string + Booted bool // Booted status is referring to the LastDeployment of this device +} + +// DeviceGroupAPI is a record of Edge Devices Groups +// Account is the account associated with the device group +// Type is the device group type and must be "static" or "dynamic" +type DeviceGroupAPI struct { + Name string `json:"Name"` + Type string `json:"Type"` + Devices []DeviceAPI `json:"Devices"` + ValidUpdate bool `json:"ValidUpdate"` +} + +// DispatchRecordAPI represents the combination of a Playbook Dispatcher (https://github.com/RedHatInsights/playbook-dispatcher), +// of a PlaybookURL, a pointer to a Device, and the status. +// This is used within UpdateTransaction for accounting purposes. +type DispatchRecordAPI struct { + PlaybookURL string `json:"PlaybookURL"` + DeviceID uint `json:"DeviceID"` + Device *DeviceAPI `json:"Device"` + Status string `json:"Status"` + Reason string `json:"Reason"` + PlaybookDispatcherID string `json:"PlaybookDispatcherID"` +} + +// UpdateTransactionAPI represents the combination of an OSTree commit and a set of Inventory +type UpdateTransactionAPI struct { + Commit *Commit `json:"Commit"` + CommitID uint `json:"CommitID"` + OldCommits []Commit `json:"OldCommits"` + Devices []DeviceAPI `json:"Devices"` + Tag string `json:"Tag"` + Status string `json:"Status"` + RepoID *uint `json:"RepoID"` + Repo *Repo `json:"Repo"` + ChangesRefs bool `json:"ChangesRefs"` + DispatchRecords []DispatchRecordAPI `json:"DispatchRecords"` +} + +// DeviceDetailsAPI is a Device with Image and Update transactions +// It contains data from multiple tables on the database +type DeviceDetailsAPI struct { + Device EdgeDeviceAPI `json:"Device,omitempty"` + Image *ImageInfo `json:"ImageInfo"` + UpdateTransactions *[]UpdateTransactionAPI `json:"UpdateTransactions,omitempty"` + DevicesGroups *[]DeviceGroupAPI `json:"DevicesGroups,omitempty"` + Updating *bool `json:"DeviceUpdating,omitempty"` +} + +// DeviceDetailsListAPI is the list of devices with details from Inventory and Edge API +type DeviceDetailsListAPI struct { + Total int `json:"total"` + Count int `json:"count"` + Devices []DeviceDetailsAPI `json:"data"` +} + +// DeviceDeviceGroupAPI is a struct of device group name and id needed for DeviceView +type DeviceDeviceGroupAPI struct { + ID uint + Name string +} + +// DeviceViewAPI is the device information needed for the UI +type DeviceViewAPI struct { + DeviceID uint `json:"DeviceID"` + DeviceName string `json:"DeviceName"` + DeviceUUID string `json:"DeviceUUID"` + ImageID uint `json:"ImageID"` + ImageName string `json:"ImageName"` + LastSeen EdgeAPITime `json:"LastSeen"` + UpdateAvailable bool `json:"UpdateAvailable"` + Status string `json:"Status"` + ImageSetID uint `json:"ImageSetID"` + DeviceGroups []DeviceDeviceGroupAPI `json:"DeviceGroups"` + DispatcherStatus string `json:"DispatcherStatus"` + DispatcherReason string `json:"DispatcherReason"` +} + +// DeviceViewListAPI is the list of devices for a given account, formatted for the UI +type DeviceViewListAPI struct { + Total int64 `json:"total"` + Devices []DeviceViewAPI `json:"devices"` +} diff --git a/pkg/routes/devices.go b/pkg/routes/devices.go index 92b96d159..9b4320781 100644 --- a/pkg/routes/devices.go +++ b/pkg/routes/devices.go @@ -147,17 +147,18 @@ func ValidateGetDevicesViewFilterParams(next http.Handler) http.Handler { } // GetUpdateAvailableForDevice returns if exists update for the current image at the device. -// @Summary Placeholder summary -// @Description This is a placeholder description +// @ID GetUpdateAvailableForDevice +// @Summary Return list of available updates for a device. +// @Description Return list of available updates for a device. // @Tags Devices (Systems) // @Accept json // @Produce json -// @Param required_parm query string true "A placeholder for required parameter" example(cat) -// @Param optional_parm query int false "A placeholder for optional parameter" example(42) -// @Success 200 {object} models.SuccessPlaceholderResponse -// @Failure 400 {object} errors.BadRequest -// @Failure 500 {object} errors.InternalServerError -// @Router /devices/{DeviceUUID}/updates [get] +// @Param DeviceUUID path string true "DeviceUUID" +// @Param latest query string false "query the latest or all updates" +// @Success 200 {object} models.Image +// @Failure 400 {object} errors.BadRequest "The request sent couldn't be processed." +// @Failure 500 {object} errors.InternalServerError "There was an internal server error." +// @Router /updates/device/{DeviceUUID}/updates [get] func GetUpdateAvailableForDevice(w http.ResponseWriter, r *http.Request) { contextServices := dependencies.ServicesFromContext(r.Context()) dc, ok := r.Context().Value(deviceContextKey).(DeviceContext) @@ -187,18 +188,7 @@ func GetUpdateAvailableForDevice(w http.ResponseWriter, r *http.Request) { respondWithJSONBody(w, contextServices.Log, result) } -// GetDeviceImageInfo returns the information of a running image for a device -// @Summary Placeholder summary -// @Description This is a placeholder description -// @Tags Devices (Systems) -// @Accept json -// @Produce json -// @Param required_parm query string true "A placeholder for required parameter" example(cat) -// @Param optional_parm query int false "A placeholder for optional parameter" example(42) -// @Success 200 {object} models.SuccessPlaceholderResponse -// @Failure 400 {object} errors.BadRequest -// @Failure 500 {object} errors.InternalServerError -// @Router /devices/{DeviceUUID}/image [get] +// GetDeviceImageInfo returns the information of a running image func GetDeviceImageInfo(w http.ResponseWriter, r *http.Request) { contextServices := dependencies.ServicesFromContext(r.Context()) dc, ok := r.Context().Value(deviceContextKey).(DeviceContext) @@ -226,17 +216,18 @@ func GetDeviceImageInfo(w http.ResponseWriter, r *http.Request) { // Returns the information of a running image and previous image in case of a rollback. // Returns updates available to a device. // Returns updates transactions for that device, if any. -// @Summary Placeholder summary -// @Description This is a placeholder description +// @ID GetDevice +// @Summary Get a device by UUID. +// @Description Get a device by UUID. // @Tags Devices (Systems) // @Accept json // @Produce json -// @Param required_parm query string true "A placeholder for required parameter" example(cat) -// @Param optional_parm query int false "A placeholder for optional parameter" example(42) -// @Success 200 {object} models.SuccessPlaceholderResponse -// @Failure 400 {object} errors.BadRequest -// @Failure 500 {object} errors.InternalServerError -// @Router /devices/{DeviceUUID}/ [get] +// @Param DeviceUUID path string true "DeviceUUID" +// @Success 200 {object} models.DeviceDetailsAPI +// @Failure 400 {object} errors.BadRequest "The request sent couldn't be processed." +// @Failure 404 {object} errors.NotFound "The device was not found." +// @Failure 500 {object} errors.InternalServerError "There was an internal server error." +// @Router /devices/{DeviceUUID} [get] func GetDevice(w http.ResponseWriter, r *http.Request) { contextServices := dependencies.ServicesFromContext(r.Context()) dc, ok := r.Context().Value(deviceContextKey).(DeviceContext) @@ -291,15 +282,19 @@ func deviceListFilters(v url.Values) *inventory.Params { } // GetDevices return the device data both on Edge API and InventoryAPI -// @Summary Get system data +// @ID GetDevices +// @Summary Get All Devices. // @Description Get combined system data from Edge API and Inventory API // @Tags Devices (Systems) // @Accept json // @Produce json -// @Param order_by query string false "Order by display_name, updated or operating_system" -// @Success 200 {object} models.DeviceDetailsList -// @Failure 400 {object} errors.BadRequest -// @Failure 500 {object} errors.InternalServerError +// @Param per_page query int false "field: maximum devices per page" +// @Param page query int false "field: which page to query from" +// @Param order_by query string false "field: order by display_name, updated or operating_system" +// @Param order_how query string false "field: choose to order ASC or DESC when order_by is being used" +// @Param hostname_or_id query string false "field: filter by hostname_or_id" +// @Success 200 {object} models.DeviceDetailsListAPI +// @Failure 500 {object} errors.InternalServerError "There was an internal server error." // @Router /devices [get] func GetDevices(w http.ResponseWriter, r *http.Request) { contextServices := dependencies.ServicesFromContext(r.Context()) @@ -313,17 +308,6 @@ func GetDevices(w http.ResponseWriter, r *http.Request) { } // GetDeviceDBInfo return the device data on EdgeAPI DB -// @Summary Placeholder summary -// @Description This is a placeholder description -// @Tags Devices (Systems) -// @Accept json -// @Produce json -// @Param required_parm query string true "A placeholder for required parameter" example(cat) -// @Param optional_parm query int false "A placeholder for optional parameter" example(42) -// @Success 200 {object} models.SuccessPlaceholderResponse -// @Failure 400 {object} errors.BadRequest -// @Failure 500 {object} errors.InternalServerError -// @Router /devices/{DeviceUUID}/dbinfo [get] func GetDeviceDBInfo(w http.ResponseWriter, r *http.Request) { contextServices := dependencies.ServicesFromContext(r.Context()) var devices []models.Device @@ -345,16 +329,22 @@ func GetDeviceDBInfo(w http.ResponseWriter, r *http.Request) { } // GetDevicesView returns all data needed to display customers devices -// @Summary Placeholder summary -// @Description This is a placeholder description +// @ID GetDevicesView +// @Summary Return all data of Devices. +// @Description Return all data of Devices. // @Tags Devices (Systems) // @Accept json // @Produce json -// @Param required_parm query string true "A placeholder for required parameter" example(cat) -// @Param optional_parm query int false "A placeholder for optional parameter" example(42) -// @Success 200 {object} models.SuccessPlaceholderResponse -// @Failure 400 {object} errors.BadRequest -// @Failure 500 {object} errors.InternalServerError +// @Param sort_by query string false "fields: name, uuid, update_available, image_id. To sort DESC use - before the fields." +// @Param name query string false "field: filter by name" +// @Param update_available query boolean false "field: filter by update_available" +// @Param uuid query string false "field: filter by uuid" +// @Param created_at query string false "field: filter by creation date" +// @Param image_id query int false "field: filter by image id" +// @Param limit query int false "field: return number of devices until limit is reached. Default is 100." +// @Param offset query int false "field: return number of devices begining at the offset." +// @Success 200 {object} models.DeviceViewListAPI +// @Failure 500 {object} errors.InternalServerError "There was an internal server error." // @Router /devices/devicesview [get] func GetDevicesView(w http.ResponseWriter, r *http.Request) { contextServices := dependencies.ServicesFromContext(r.Context()) diff --git a/pkg/routes/imagesets.go b/pkg/routes/imagesets.go index 0fd86f766..e002a962f 100644 --- a/pkg/routes/imagesets.go +++ b/pkg/routes/imagesets.go @@ -189,8 +189,8 @@ type ImageSetInstallerURL struct { // @Param sort_by query string false "Define sort fields: created_at, updated_at, name. To sort DESC use -" // @Param name query string false "field: filter by name" // @Param status query string false "field: filter by status" -// @Param limit query integer false "field: return number of image-set view until limit is reached. Default is 100." -// @Param offset query integer false "field: return number of image-set view beginning at the offset." +// @Param limit query int false "field: return number of image-set view until limit is reached. Default is 100." +// @Param offset query int false "field: return number of image-set view beginning at the offset." // @Success 200 {object} models.ImageSetsResponseAPI // @Failure 400 {object} errors.BadRequest "The request sent couldn't be processed." // @Failure 404 {object} errors.NotFound "The Image Set was not found." @@ -301,13 +301,13 @@ type ImageSetImagePackages struct { // @Tags Image-Sets // @Accept json // @Produce json -// @Param imageSetID path integer true "Image Set ID" +// @Param imageSetID path int true "Image Set ID" // @Param sort_by query string false "Define sort fields: created_at, updated_at, name. To sort DESC use -" // @Param name query string false "field: filter by name" // @Param status query string false "field: filter by status" // @Param version query string false "field: filter by version" -// @Param limit query integer false "field: return number of image-set view until limit is reached. Default is 100." -// @Param offset query integer false "field: return number of image-set view beginning at the offset." +// @Param limit query int false "field: return number of image-set view until limit is reached. Default is 100." +// @Param offset query int false "field: return number of image-set view beginning at the offset." // @Success 200 {object} models.ImageSetDetailsResponseAPI // @Failure 400 {object} errors.BadRequest "The request sent couldn't be processed." // @Failure 404 {object} errors.NotFound "image-set was not found." @@ -519,9 +519,9 @@ func returnImageDetails(images []models.Image, s *dependencies.EdgeAPIServices) // @Param sort_by query string false "Define sort fields: created_at, updated_at, name. To sort DESC use -" // @Param name query string false "field: filter by name" // @Param status query string false "field: filter by status" -// @Param id query integer false "field: filter by id" -// @Param limit query integer false "field: return number of image-set view until limit is reached. Default is 30." -// @Param offset query integer false "field: return number of image-set view beginning at the offset." +// @Param id query int false "field: filter by id" +// @Param limit query int false "field: return number of image-set view until limit is reached. Default is 30." +// @Param offset query int false "field: return number of image-set view beginning at the offset." // @Success 200 {object} models.ImageSetsViewResponseAPI // @Failure 500 {object} errors.InternalServerError "There was an internal server error." // @Router /image-sets/view [get] @@ -605,7 +605,7 @@ func getContextImageSet(w http.ResponseWriter, r *http.Request) *models.ImageSet // @Tags Image-Sets // @Accept json // @Produce json -// @Param image_set_id path integer true "the image-set id" +// @Param image_set_id path int true "the image-set id" // @Success 200 {object} models.ImageSetIDViewAPI // @Failure 400 {object} errors.BadRequest "The request sent couldn't be processed." // @Failure 404 {object} errors.NotFound "The Image-Set was not found." @@ -649,8 +649,8 @@ func GetImageSetViewByID(w http.ResponseWriter, r *http.Request) { // @Param sort_by query string false "Define sort fields: created_at, version, To sort DESC use -" // @Param status query string false "field: filter by status" // @Param version query string false "field: filter by version" -// @Param limit query integer false "field: return number of images until limit is reached. Default is 100." -// @Param offset query integer false "field: return number of images beginning at the offset." +// @Param limit query int false "field: return number of images until limit is reached. Default is 100." +// @Param offset query int false "field: return number of images beginning at the offset." // @Success 200 {object} models.ImagesViewDataAPI // @Failure 400 {object} errors.BadRequest "The request sent couldn't be processed." // @Failure 404 {object} errors.NotFound "The Image-Set was not found."