Skip to content

Commit

Permalink
Layer improvements (#327)
Browse files Browse the repository at this point in the history
* feat(cli) add filter to create layer cmd

* fix(cli) apply A/** to A too

* feat(api) add get layer objs endpoint

* fix(cli) remove deprecated test

* refactor(cli) change modify filters

* feat(cli) communicate with 3d for layers

* fix(cli) layer tests

* fix(cli) missing layer type for modify

* fix(cli) layer tests

* fix(doc) update for layers
  • Loading branch information
helderbetiol authored Jan 4, 2024
1 parent b4abc9d commit fa04d0d
Show file tree
Hide file tree
Showing 15 changed files with 273 additions and 68 deletions.
104 changes: 104 additions & 0 deletions API/controllers/entityController.go
Original file line number Diff line number Diff line change
Expand Up @@ -619,6 +619,110 @@ func GetEntity(w http.ResponseWriter, r *http.Request) {
}
}

// swagger:operation GET /api/layers/{id}/objects Objects GetLayerObjects
// Gets the object of a given layer.
// Apply the layer filters to get children objects of a given root query param.
// ---
// security:
// - bearer: []
// produces:
// - application/json
// parameters:
// - name: id
// in: path
// description: 'ID of desired layer.'
// required: true
// type: string
// default: "layer_slug"
// - name: root
// in: query
// description: 'Mandatory, accepts IDs. The root object from where to apply the layer'
// required: true
// - name: recursive
// in: query
// description: 'Accepts true or false. If true, get objects
// from all levels beneath root. If false, get objects directly under root.'
// responses:
// '200':
// description: 'Found. A response body will be returned with
// a meaningful message.'
// '400':
// description: Bad request. An error message will be returned.
// '404':
// description: Not Found. An error message will be returned.

func GetLayerObjects(w http.ResponseWriter, r *http.Request) {
fmt.Println("******************************************************")
fmt.Println("FUNCTION CALL: GetLayerObjects ")
fmt.Println("******************************************************")
DispRequestMetaData(r)
var data map[string]interface{}
var id string
var canParse bool
var modelErr *u.Error

// Get user roles for permissions
user := getUserFromToken(w, r)
if user == nil {
return
}

// Get query params
var filters u.LayerObjsFilters
decoder.Decode(&filters, r.URL.Query())
if filters.Root == "" {
//error
w.WriteHeader(http.StatusBadRequest)
u.Respond(w, u.Message("Query param root is mandatory"))
return
}

if id, canParse = mux.Vars(r)["slug"]; canParse {
// Get layer
data, modelErr = models.GetObject(bson.M{"slug": id}, u.EntityToString(u.LAYER), u.RequestFilters{}, user.Roles)
if modelErr != nil {
u.RespondWithError(w, modelErr)
}

// Apply layer to get objects request
req := bson.M{}
for filterName, filterValue := range data["filters"].(map[string]interface{}) {
u.AddFilterToReq(req, filterName, filterValue.(string))
}
var searchId string
if filters.IsRecursive {
searchId = filters.Root + ".**.*"
} else {
searchId = filters.Root + ".*"
}
u.AddFilterToReq(req, "id", searchId)

// Get objects
matchingObjects := []map[string]interface{}{}
entities := u.GetEntitiesByNamespace(u.Any, searchId)
for _, entStr := range entities {
entData, err := models.GetManyObjects(entStr, req, u.RequestFilters{}, user.Roles)
if err != nil {
u.RespondWithError(w, err)
return
}
matchingObjects = append(matchingObjects, entData...)
}

// Respond
if r.Method == "OPTIONS" {
w.Header().Add("Content-Type", "application/json")
w.Header().Add("Allow", "GET, DELETE, OPTIONS, PATCH, PUT")
} else {
u.Respond(w, u.RespDataWrapper("successfully processed request", matchingObjects))
}
} else {
w.WriteHeader(http.StatusBadRequest)
u.Respond(w, u.Message("Error while parsing path parameters"))
return
}
}

// swagger:operation GET /api/{entity} Objects GetAllEntities
// Gets all present objects for specified entity (category).
// Returns JSON body with all specified objects of type.
Expand Down
4 changes: 4 additions & 0 deletions API/router/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,10 @@ func Router(jwt func(next http.Handler) http.Handler) *mux.Router {
router.HandleFunc("/api/{entity}s/{id}",
controllers.GetEntity).Methods("GET", "HEAD", "OPTIONS")

//GET LAYER
router.HandleFunc("/api/layers/{slug}/objects",
controllers.GetLayerObjects).Methods("GET", "HEAD", "OPTIONS")

// GET ALL ENTITY
router.HandleFunc("/api/{entity}s",
controllers.GetAllEntities).Methods("HEAD", "GET")
Expand Down
50 changes: 50 additions & 0 deletions API/swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,56 @@
}
}
},
"/api/layers/{id}/objects": {
"get": {
"security": [
{
"bearer": []
}
],
"description": "Apply the layer filters to get children objects of a given root query param.",
"produces": [
"application/json"
],
"tags": [
"Objects"
],
"summary": "Gets the object of a given layer.",
"operationId": "GetLayerObjects",
"parameters": [
{
"type": "string",
"default": "layer_slug",
"description": "ID of desired layer.",
"name": "id",
"in": "path",
"required": true
},
{
"description": "Mandatory, accepts IDs. The root object from where to apply the layer",
"name": "root",
"in": "query",
"required": true
},
{
"description": "Accepts true or false. If true, get objects from all levels beneath root. If false, get objects directly under root.",
"name": "recursive",
"in": "query"
}
],
"responses": {
"200": {
"description": "Found. A response body will be returned with a meaningful message."
},
"400": {
"description": "Bad request. An error message will be returned."
},
"404": {
"description": "Not Found. An error message will be returned."
}
}
}
},
"/api/login": {
"post": {
"description": "Create a new JWT Key. This can also be used to verify credentials\nThe authorize and 'Try it out' buttons don't work",
Expand Down
58 changes: 34 additions & 24 deletions API/utils/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (

"github.com/elliotchance/pie/v2"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
)

var BuildHash string
Expand Down Expand Up @@ -75,6 +76,11 @@ type RequestFilters struct {
Id string `schema:"id"`
}

type LayerObjsFilters struct {
Root string `schema:"root"`
IsRecursive bool `schema:"recursive"`
}

type HierarchyFilters struct {
Namespace Namespace `schema:"namespace"`
StartDate string `schema:"startDate"`
Expand Down Expand Up @@ -173,35 +179,39 @@ func FilteredReqFromQueryParams(link *url.URL) bson.M {
for key := range queryValues {
if key != "fieldOnly" && key != "startDate" && key != "endDate" &&
key != "limit" && key != "namespace" {
var keyValue interface{}
keyValue = queryValues.Get(key)

if key == "parentId" {
regex := applyWildcards(keyValue.(string)) + `\.(` + NAME_REGEX + ")"
bsonMap["id"] = regexToMongoFilter(regex)
continue
} else if key == "tag" {
// tag is in tags list
bsonMap["tags"] = bson.M{"$eq": keyValue}
continue
} else if strings.Contains(keyValue.(string), "*") {
regex := applyWildcards(keyValue.(string))
keyValue = regexToMongoFilter(regex)
}

switch key {
case "id", "name", "category",
"description", "domain",
"createdDate", "lastUpdated", "slug":
bsonMap[key] = keyValue
default:
bsonMap["attributes."+key] = keyValue
}
keyValue := queryValues.Get(key)
AddFilterToReq(bsonMap, key, keyValue)
}
}
return bsonMap
}

func AddFilterToReq(bsonMap primitive.M, key string, value string) {
var keyValue interface{}
keyValue = value
if key == "parentId" {
regex := applyWildcards(keyValue.(string)) + `\.(` + NAME_REGEX + ")"
bsonMap["id"] = regexToMongoFilter(regex)
return
} else if key == "tag" {
// tag is in tags list
bsonMap["tags"] = bson.M{"$eq": keyValue}
return
} else if strings.Contains(keyValue.(string), "*") {
regex := applyWildcards(keyValue.(string))
keyValue = regexToMongoFilter(regex)
}

switch key {
case "id", "name", "category",
"description", "domain",
"createdDate", "lastUpdated", "slug":
bsonMap[key] = keyValue
default:
bsonMap["attributes."+key] = keyValue
}
}

func ErrTypeToStatusCode(errType ErrType) int {
switch errType {
case ErrForbidden:
Expand Down
9 changes: 8 additions & 1 deletion CLI/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -1262,6 +1262,8 @@ func (n *createTagNode) execute() (interface{}, error) {
type createLayerNode struct {
slug node
applicability node
filterName string
filterValue node
}

func (n *createLayerNode) execute() (interface{}, error) {
Expand All @@ -1275,7 +1277,12 @@ func (n *createLayerNode) execute() (interface{}, error) {
return nil, err
}

return nil, cmd.C.CreateLayer(slug, applicability)
filterValue, err := nodeToString(n.filterValue, "filterValue")
if err != nil {
return nil, err
}

return nil, cmd.C.CreateLayer(slug, applicability, n.filterName, filterValue)
}

type createCorridorNode struct {
Expand Down
6 changes: 5 additions & 1 deletion CLI/controllers/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,11 @@ func (controller Controller) PostObj(ent int, entity string, data map[string]any

if models.EntityCreationMustBeInformed(ent) && IsInObjForUnity(entity) {
entInt := models.EntityStrToInt(entity)
Ogree3D.InformOptional("PostObj", entInt, map[string]any{"type": "create", "data": resp.Body["data"]})
createType := "create"
if entInt == models.LAYER {
createType = "create-layer"
}
Ogree3D.InformOptional("PostObj", entInt, map[string]any{"type": createType, "data": resp.Body["data"]})
}

if models.IsLayer(path) {
Expand Down
3 changes: 3 additions & 0 deletions CLI/controllers/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ func (controller Controller) DeleteObj(path string) ([]string, error) {
}
if models.IsLayer(path) {
State.Hierarchy.Children["Logical"].Children["Layers"].IsCached = false
if IsEntityTypeForOGrEE3D(models.LAYER) {
controller.Ogree3D.InformOptional("DeleteObj", -1, map[string]any{"type": "delete-layer", "data": obj["slug"].(string)})
}
}
}
if path == State.CurrPath {
Expand Down
1 change: 1 addition & 0 deletions CLI/controllers/initController.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ func SetObjsForUnity(objs []string) []int {
}
res = append(res, models.DOMAIN)
res = append(res, models.TAG)
res = append(res, models.LAYER)
}
return res
}
Expand Down
18 changes: 13 additions & 5 deletions CLI/controllers/layer.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,15 +140,15 @@ func (controller Controller) GetLayer(id string) (string, models.Layer, error) {
return realID, layer, nil
}

func (controller Controller) CreateLayer(slug, applicability string) error {
func (controller Controller) CreateLayer(slug, applicability, filterName, filterValue string) error {
applicability, err := TranslateApplicability(applicability)
if err != nil {
return err
}

return controller.PostObj(models.LAYER, models.EntityToString(models.LAYER), map[string]any{
"slug": slug,
models.LayerFilters: map[string]any{},
models.LayerFilters: map[string]any{filterName: filterValue},
models.LayerApplicability: applicability,
}, models.LayersPath+slug)
}
Expand All @@ -166,10 +166,18 @@ func (controller Controller) UpdateLayer(path string, attributeName string, valu
_, err = controller.UpdateObj(path, map[string]any{attributeName: applicability})
case models.LayerFiltersRemove:
_, err = controller.UpdateObj(path, map[string]any{attributeName: value})
default:
filters := map[string]any{attributeName: value}

case models.LayerFiltersAdd:
values := strings.Split(value.(string), "=")
if len(values) != 2 || len(values[0]) == 0 || len(values[0]) == 0 {
return fmt.Errorf("invalid filter format")
}
filters := map[string]any{values[0]: values[1]}
_, err = controller.UpdateObj(path, map[string]any{models.LayerFilters: filters})
default:
_, err = controller.UpdateObj(path, map[string]any{attributeName: value})
if attributeName == "slug" {
State.Hierarchy.Children["Logical"].Children["Layers"].IsCached = false
}
}

return err
Expand Down
Loading

0 comments on commit fa04d0d

Please sign in to comment.