fix(edge/groups): filter selected environments [EE-5891] (#10050)

pull/10071/head
Chaim Lev-Ari 2023-08-16 12:24:37 +03:00 committed by GitHub
parent a27cc6c0e5
commit a1e610a39a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 57 additions and 25 deletions

View File

@ -34,6 +34,7 @@ type EnvironmentsQuery struct {
edgeCheckInPassedSeconds int edgeCheckInPassedSeconds int
edgeStackId portainer.EdgeStackID edgeStackId portainer.EdgeStackID
edgeStackStatus *portainer.EdgeStackStatusType edgeStackStatus *portainer.EdgeStackStatusType
excludeIds []portainer.EndpointID
} }
func parseQuery(r *http.Request) (EnvironmentsQuery, error) { func parseQuery(r *http.Request) (EnvironmentsQuery, error) {
@ -69,6 +70,11 @@ func parseQuery(r *http.Request) (EnvironmentsQuery, error) {
return EnvironmentsQuery{}, err return EnvironmentsQuery{}, err
} }
excludeIDs, err := getNumberArrayQueryParameter[portainer.EndpointID](r, "excludeIds")
if err != nil {
return EnvironmentsQuery{}, err
}
agentVersions := getArrayQueryParameter(r, "agentVersions") agentVersions := getArrayQueryParameter(r, "agentVersions")
name, _ := request.RetrieveQueryParameter(r, "name", true) name, _ := request.RetrieveQueryParameter(r, "name", true)
@ -97,6 +103,7 @@ func parseQuery(r *http.Request) (EnvironmentsQuery, error) {
types: endpointTypes, types: endpointTypes,
tagIds: tagIDs, tagIds: tagIDs,
endpointIds: endpointIDs, endpointIds: endpointIDs,
excludeIds: excludeIDs,
tagsPartialMatch: tagsPartialMatch, tagsPartialMatch: tagsPartialMatch,
groupIds: groupIDs, groupIds: groupIDs,
status: status, status: status,
@ -118,6 +125,12 @@ func (handler *Handler) filterEndpointsByQuery(filteredEndpoints []portainer.End
filteredEndpoints = filteredEndpointsByIds(filteredEndpoints, query.endpointIds) filteredEndpoints = filteredEndpointsByIds(filteredEndpoints, query.endpointIds)
} }
if len(query.excludeIds) > 0 {
filteredEndpoints = filter(filteredEndpoints, func(endpoint portainer.Endpoint) bool {
return !slices.Contains(query.excludeIds, endpoint.ID)
})
}
if len(query.groupIds) > 0 { if len(query.groupIds) > 0 {
filteredEndpoints = filterEndpointsByGroupIDs(filteredEndpoints, query.groupIds) filteredEndpoints = filterEndpointsByGroupIDs(filteredEndpoints, query.groupIds)
} }

View File

@ -5,6 +5,7 @@ import (
portainer "github.com/portainer/portainer/api" portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/datastore" "github.com/portainer/portainer/api/datastore"
"github.com/portainer/portainer/api/internal/slices"
"github.com/portainer/portainer/api/internal/testhelpers" "github.com/portainer/portainer/api/internal/testhelpers"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@ -124,6 +125,28 @@ func Test_Filter_edgeFilter(t *testing.T) {
runTests(tests, t, handler, endpoints) runTests(tests, t, handler, endpoints)
} }
func Test_Filter_excludeIDs(t *testing.T) {
ids := []portainer.EndpointID{1, 2, 3, 4, 5, 6, 7, 8, 9}
environments := slices.Map(ids, func(id portainer.EndpointID) portainer.Endpoint {
return portainer.Endpoint{ID: id, GroupID: 1, Type: portainer.DockerEnvironment}
})
handler := setupFilterTest(t, environments)
tests := []filterTest{
{
title: "should exclude IDs 2,5,8",
expected: []portainer.EndpointID{1, 3, 4, 6, 7, 9},
query: EnvironmentsQuery{
excludeIds: []portainer.EndpointID{2, 5, 8},
},
},
}
runTests(tests, t, handler, environments)
}
func runTests(tests []filterTest, t *testing.T, handler *Handler, endpoints []portainer.Endpoint) { func runTests(tests []filterTest, t *testing.T, handler *Handler, endpoints []portainer.Endpoint) {
for _, test := range tests { for _, test := range tests {
t.Run(test.title, func(t *testing.T) { t.Run(test.title, func(t *testing.T) {

View File

@ -63,3 +63,12 @@ func RemoveIndex[T any](s []T, index int) []T {
s[index] = s[len(s)-1] s[index] = s[len(s)-1]
return s[:len(s)-1] return s[:len(s)-1]
} }
// Map applies the given function to each element of the slice and returns a new slice with the results
func Map[T, U any](s []T, f func(T) U) []U {
result := make([]U, len(s))
for i, v := range s {
result[i] = f(v)
}
return result
}

View File

@ -92,7 +92,6 @@ export const componentsModule = angular
'query', 'query',
'title', 'title',
'data-cy', 'data-cy',
'hideEnvironmentIds',
]) ])
) )
.component( .component(

View File

@ -28,6 +28,7 @@ export function AssociatedEdgeEnvironmentsSelector({
emptyContentLabel="No environment available" emptyContentLabel="No environment available"
query={{ query={{
types: EdgeTypes, types: EdgeTypes,
excludeIds: value,
}} }}
onClickRow={(env) => { onClickRow={(env) => {
if (!value.includes(env.Id)) { if (!value.includes(env.Id)) {
@ -35,7 +36,6 @@ export function AssociatedEdgeEnvironmentsSelector({
} }
}} }}
data-cy="edgeGroupCreate-availableEndpoints" data-cy="edgeGroupCreate-availableEndpoints"
hideEnvironmentIds={value}
/> />
</div> </div>
<div className="w-1/2"> <div className="w-1/2">

View File

@ -3,10 +3,7 @@ import { truncate } from 'lodash';
import { useMemo, useState } from 'react'; import { useMemo, useState } from 'react';
import { useEnvironmentList } from '@/react/portainer/environments/queries'; import { useEnvironmentList } from '@/react/portainer/environments/queries';
import { import { Environment } from '@/react/portainer/environments/types';
Environment,
EnvironmentId,
} from '@/react/portainer/environments/types';
import { useGroups } from '@/react/portainer/environments/environment-groups/queries'; import { useGroups } from '@/react/portainer/environments/environment-groups/queries';
import { useTags } from '@/portainer/tags/queries'; import { useTags } from '@/portainer/tags/queries';
import { EnvironmentsQueryParams } from '@/react/portainer/environments/environment.service'; import { EnvironmentsQueryParams } from '@/react/portainer/environments/environment.service';
@ -47,13 +44,11 @@ export function EdgeGroupAssociationTable({
emptyContentLabel, emptyContentLabel,
onClickRow, onClickRow,
'data-cy': dataCy, 'data-cy': dataCy,
hideEnvironmentIds = [],
}: { }: {
title: string; title: string;
query: EnvironmentsQueryParams; query: EnvironmentsQueryParams;
emptyContentLabel: string; emptyContentLabel: string;
onClickRow: (env: Environment) => void; onClickRow: (env: Environment) => void;
hideEnvironmentIds?: EnvironmentId[];
} & AutomationTestingProps) { } & AutomationTestingProps) {
const tableState = useTableStateWithoutStorage('Name'); const tableState = useTableStateWithoutStorage('Name');
const [page, setPage] = useState(1); const [page, setPage] = useState(1);
@ -74,25 +69,17 @@ export function EdgeGroupAssociationTable({
const environments: Array<DecoratedEnvironment> = useMemo( const environments: Array<DecoratedEnvironment> = useMemo(
() => () =>
environmentsQuery.environments environmentsQuery.environments.map((env) => ({
.filter((e) => !hideEnvironmentIds.includes(e.Id)) ...env,
.map((env) => ({ Group: groupsQuery.data?.find((g) => g.Id === env.GroupId)?.Name || '',
...env, Tags: env.TagIds.map(
Group: (tagId) => tagsQuery.data?.find((t) => t.ID === tagId)?.Name || ''
groupsQuery.data?.find((g) => g.Id === env.GroupId)?.Name || '', ),
Tags: env.TagIds.map( })),
(tagId) => tagsQuery.data?.find((t) => t.ID === tagId)?.Name || '' [environmentsQuery.environments, groupsQuery.data, tagsQuery.data]
),
})),
[
environmentsQuery.environments,
groupsQuery.data,
hideEnvironmentIds,
tagsQuery.data,
]
); );
const totalCount = environmentsQuery.totalCount - hideEnvironmentIds.length; const { totalCount } = environmentsQuery;
return ( return (
<Datatable<DecoratedEnvironment> <Datatable<DecoratedEnvironment>

View File

@ -32,6 +32,7 @@ export interface BaseEnvironmentsQueryParams {
types?: EnvironmentType[] | readonly EnvironmentType[]; types?: EnvironmentType[] | readonly EnvironmentType[];
tagIds?: TagId[]; tagIds?: TagId[];
endpointIds?: EnvironmentId[]; endpointIds?: EnvironmentId[];
excludeIds?: EnvironmentId[];
tagsPartialMatch?: boolean; tagsPartialMatch?: boolean;
groupIds?: EnvironmentGroupId[]; groupIds?: EnvironmentGroupId[];
status?: EnvironmentStatus[]; status?: EnvironmentStatus[];