diff --git a/api/http/handler/endpoints/endpoint_list.go b/api/http/handler/endpoints/endpoint_list.go index 1d98296bf..c68f8d9ec 100644 --- a/api/http/handler/endpoints/endpoint_list.go +++ b/api/http/handler/endpoints/endpoint_list.go @@ -30,6 +30,12 @@ func (handler *Handler) endpointList(w http.ResponseWriter, r *http.Request) *ht limit, _ := request.RetrieveNumericQueryParameter(r, "limit", true) endpointType, _ := request.RetrieveNumericQueryParameter(r, "type", true) + var tagIDs []portainer.TagID + request.RetrieveJSONQueryParameter(r, "tagIds", &tagIDs, true) + + var endpointIDs []portainer.EndpointID + request.RetrieveJSONQueryParameter(r, "endpointIds", &endpointIDs, true) + endpointGroups, err := handler.EndpointGroupService.EndpointGroups() if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve endpoint groups from the database", err} @@ -47,6 +53,10 @@ func (handler *Handler) endpointList(w http.ResponseWriter, r *http.Request) *ht filteredEndpoints := security.FilterEndpoints(endpoints, endpointGroups, securityContext) + if endpointIDs != nil { + filteredEndpoints = filteredEndpointsByIds(filteredEndpoints, endpointIDs) + } + if groupID != 0 { filteredEndpoints = filterEndpointsByGroupID(filteredEndpoints, portainer.EndpointGroupID(groupID)) } @@ -67,6 +77,10 @@ func (handler *Handler) endpointList(w http.ResponseWriter, r *http.Request) *ht filteredEndpoints = filterEndpointsByType(filteredEndpoints, portainer.EndpointType(endpointType)) } + if tagIDs != nil { + filteredEndpoints = filteredEndpointsByTags(filteredEndpoints, tagIDs, endpointGroups) + } + filteredEndpointCount := len(filteredEndpoints) paginatedEndpoints := paginateEndpoints(filteredEndpoints, start, limit) @@ -187,3 +201,58 @@ func convertTagIDsToTags(tagsMap map[portainer.TagID]string, tagIDs []portainer. } return tags } + +func filteredEndpointsByTags(endpoints []portainer.Endpoint, tagIDs []portainer.TagID, endpointGroups []portainer.EndpointGroup) []portainer.Endpoint { + filteredEndpoints := make([]portainer.Endpoint, 0) + + for _, endpoint := range endpoints { + missingTags := make(map[portainer.TagID]bool) + for _, tagID := range tagIDs { + missingTags[tagID] = true + } + for _, tagID := range endpoint.TagIDs { + if missingTags[tagID] { + delete(missingTags, tagID) + } + } + missingTags = endpointGroupHasTags(endpoint.GroupID, endpointGroups, missingTags) + if len(missingTags) == 0 { + filteredEndpoints = append(filteredEndpoints, endpoint) + } + } + return filteredEndpoints +} + +func endpointGroupHasTags(groupID portainer.EndpointGroupID, groups []portainer.EndpointGroup, missingTags map[portainer.TagID]bool) map[portainer.TagID]bool { + var endpointGroup portainer.EndpointGroup + for _, group := range groups { + if group.ID == groupID { + endpointGroup = group + break + } + } + for _, tagID := range endpointGroup.TagIDs { + if missingTags[tagID] { + delete(missingTags, tagID) + } + } + return missingTags +} + +func filteredEndpointsByIds(endpoints []portainer.Endpoint, ids []portainer.EndpointID) []portainer.Endpoint { + filteredEndpoints := make([]portainer.Endpoint, 0) + + idsSet := make(map[portainer.EndpointID]bool) + for _, id := range ids { + idsSet[id] = true + } + + for _, endpoint := range endpoints { + if idsSet[endpoint.ID] { + filteredEndpoints = append(filteredEndpoints, endpoint) + } + } + + return filteredEndpoints + +} diff --git a/app/portainer/services/api/endpointService.js b/app/portainer/services/api/endpointService.js index 6de03711d..7c60887fc 100644 --- a/app/portainer/services/api/endpointService.js +++ b/app/portainer/services/api/endpointService.js @@ -8,8 +8,8 @@ function EndpointServiceFactory($q, Endpoints, FileUploadService) { return Endpoints.get({id: endpointID}).$promise; }; - service.endpoints = function(start, limit, search) { - return Endpoints.query({start, limit, search}).$promise; + service.endpoints = function(start, limit, { search, type, tagIds, endpointIds } = {}) { + return Endpoints.query({ start, limit, search, type, tagIds, endpointIds }).$promise; }; service.snapshotEndpoints = function() { diff --git a/app/portainer/views/endpoints/endpointsController.js b/app/portainer/views/endpoints/endpointsController.js index 3b2313700..2f4f1d268 100644 --- a/app/portainer/views/endpoints/endpointsController.js +++ b/app/portainer/views/endpoints/endpointsController.js @@ -22,10 +22,10 @@ function ($q, $scope, $state, EndpointService, GroupService, EndpointHelper, Not }; $scope.getPaginatedEndpoints = getPaginatedEndpoints; - function getPaginatedEndpoints(lastId, limit, filter) { + function getPaginatedEndpoints(lastId, limit, search) { const deferred = $q.defer(); $q.all({ - endpoints: EndpointService.endpoints(lastId, limit, filter), + endpoints: EndpointService.endpoints(lastId, limit, { search }), groups: GroupService.groups() }) .then(function success(data) { diff --git a/app/portainer/views/home/homeController.js b/app/portainer/views/home/homeController.js index 255c71146..319fd5a08 100644 --- a/app/portainer/views/home/homeController.js +++ b/app/portainer/views/home/homeController.js @@ -146,10 +146,10 @@ angular.module('portainer.app') } $scope.getPaginatedEndpoints = getPaginatedEndpoints; - function getPaginatedEndpoints(lastId, limit, filter) { + function getPaginatedEndpoints(lastId, limit, search) { const deferred = $q.defer(); $q.all({ - endpoints: EndpointService.endpoints(lastId, limit, filter), + endpoints: EndpointService.endpoints(lastId, limit, {search}), groups: GroupService.groups() }) .then(function success(data) {