Skip to content

Commit

Permalink
fix: Access public APIs listed in a category when user is logged out
Browse files Browse the repository at this point in the history
  • Loading branch information
aditya-goyal01 committed Feb 11, 2025
1 parent c7d749f commit 8ad2ec7
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 50 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { afterAll, beforeAll, describe, test, expect } from '@jest/globals';
import { afterAll, beforeAll, describe, expect, test } from '@jest/globals';

import { forManagementAsAdminUser, forPortalAsAdminUser } from '@gravitee/utils/configuration';
import { ANONYMOUS, forManagementAsAdminUser, forPortal, forPortalAsAdminUser } from '@gravitee/utils/configuration';
import { APIsApi } from '@gravitee/management-webclient-sdk/src/lib/apis/APIsApi';
import { ApisFaker } from '@gravitee/fixtures/management/ApisFaker';
import { UpdateApiEntity, UpdateApiEntityFromJSON } from '@gravitee/management-webclient-sdk/src/lib/models/UpdateApiEntity';
Expand All @@ -26,13 +26,15 @@ import { FilterApiQuery } from '@gravitee/portal-webclient-sdk/src/lib/models/Fi
import { ApiLifecycleState } from '@gravitee/management-webclient-sdk/src/lib/models/ApiLifecycleState';
import { ConfigurationApi } from '@gravitee/management-webclient-sdk/src/lib/apis/ConfigurationApi';
import { CategoryEntity } from '@gravitee/management-webclient-sdk/src/lib/models/CategoryEntity';
import { Visibility } from '../../../../lib/management-v2-webclient-sdk/src/lib';

const orgId = 'DEFAULT';
const envId = 'DEFAULT';

const apisManagementApiAsAdmin = new APIsApi(forManagementAsAdminUser());
const apiPortalApiAsAdmin = new ApiApi(forPortalAsAdminUser());
const configurationApiAsAdmin = new ConfigurationApi(forManagementAsAdminUser());
const portalApiAsAnonymous = new ApiApi(forPortal({ auth: ANONYMOUS }));

async function createAndPublish(apiManagementClient: APIsApi, attributes?: Partial<UpdateApiEntity>): Promise<ApiEntity> {
let createdApi = await apiManagementClient.createApi({
Expand All @@ -54,6 +56,7 @@ describe('Portal - View and search APIs', () => {
let apiWith2Labels: ApiEntity;
let apiFeatured: ApiEntity;
let apiWithCategory: ApiEntity;
let privateApiWithCategory: ApiEntity;
let createdCategory: CategoryEntity;

beforeAll(async () => {
Expand All @@ -65,11 +68,25 @@ describe('Portal - View and search APIs', () => {
newCategoryEntity: { name: 'cat1' },
}),
);
apiWithCategory = await createAndPublish(apisManagementApiAsAdmin, { categories: ['cat1'], description: 'API with one category' });
apiWith1Label = await createAndPublish(apisManagementApiAsAdmin, { labels: ['testlabel1'], description: 'API with one label' });
apiWithCategory = await createAndPublish(apisManagementApiAsAdmin, {
categories: ['cat1'],
description: 'API with one category',
visibility: Visibility.PUBLIC,
});
privateApiWithCategory = await createAndPublish(apisManagementApiAsAdmin, {
categories: ['cat1'],
description: 'Private API with one category',
visibility: Visibility.PRIVATE,
});
apiWith1Label = await createAndPublish(apisManagementApiAsAdmin, {
labels: ['testlabel1'],
description: 'API with one label',
visibility: Visibility.PUBLIC,
});
apiWith2Labels = await createAndPublish(apisManagementApiAsAdmin, {
labels: ['testlabel1', 'testlabel2'],
description: 'API with two labels',
visibility: Visibility.PUBLIC,
});
apiFeatured = await createAndPublish(apisManagementApiAsAdmin, { description: 'Featured API' });
await succeed(configurationApiAsAdmin.createTopApiRaw({ orgId, envId, newTopApiEntity: { api: apiFeatured.id } }));
Expand Down Expand Up @@ -116,8 +133,15 @@ describe('Portal - View and search APIs', () => {
describe('Filter API list regarding category', function () {
test('should list all APIs with certain category', async () => {
let getApisResponse = await succeed(apiPortalApiAsAdmin.getApisRaw({ category: 'cat1' }));
expect(getApisResponse.data).toHaveLength(2);
expect(getApisResponse.data.some((filteredApi) => filteredApi.id === apiWithCategory.id)).toBeTruthy();
expect(getApisResponse.data.some((filteredApi) => filteredApi.id === privateApiWithCategory.id)).toBeTruthy();
});

test('should list only public APIs with certain category for anonymous user', async () => {
let getApisResponse = await succeed(portalApiAsAnonymous.getApisRaw({ category: 'cat1' }));
expect(getApisResponse.data).toHaveLength(1);
expect(getApisResponse.data[0].id).toBe(apiWithCategory.id);
expect(getApisResponse.data.some((filteredApi) => filteredApi.id === apiWithCategory.id)).toBeTruthy();
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,23 @@
import io.gravitee.rest.api.service.filtering.FilteringService;
import io.gravitee.rest.api.service.v4.ApiCategoryService;
import jakarta.inject.Inject;
import jakarta.ws.rs.*;
import jakarta.ws.rs.BeanParam;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.container.ResourceContext;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.Response;
import java.time.OffsetDateTime;
import java.util.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
Expand Down Expand Up @@ -83,19 +94,19 @@ public class ApisResource extends AbstractResource<Api, String> {
@RequirePortalAuth
public Response listCategories(@BeanParam ApisParam apisParam) {
Set<CategoryEntity> categories = filteringService.listCategories(
GraviteeContext.getExecutionContext(),
getAuthenticatedUserOrNull(),
convert(apisParam.getFilter()),
convert(apisParam.getExcludedFilter())
GraviteeContext.getExecutionContext(),
getAuthenticatedUserOrNull(),
convert(apisParam.getFilter()),
convert(apisParam.getExcludedFilter())
);

var countByCategory = apiCategoryService.countApisPublishedGroupedByCategoriesForUser(getAuthenticatedUserOrNull());

List<Category> categoryList = categories
.stream()
.peek(categoryEntity -> categoryEntity.setTotalApis(countByCategory.applyAsLong(categoryEntity.getId())))
.map(categoryEntity -> categoryMapper.convert(categoryEntity, uriInfo.getBaseUriBuilder()))
.collect(Collectors.toList());
.stream()
.peek(categoryEntity -> categoryEntity.setTotalApis(countByCategory.applyAsLong(categoryEntity.getId())))
.map(categoryEntity -> categoryMapper.convert(categoryEntity, uriInfo.getBaseUriBuilder()))
.collect(Collectors.toList());

return Response.ok(new DataResponse().data(categoryList)).build();
}
Expand All @@ -106,7 +117,7 @@ public Response listCategories(@BeanParam ApisParam apisParam) {
public Response getApis(@BeanParam PaginationParam paginationParam, @BeanParam ApisParam apisParam) {
final ExecutionContext executionContext = GraviteeContext.getExecutionContext();
Collection<String> filteredApis = new ArrayList<>(
findApisForCurrentUser(executionContext, apisParam, createQueryFromParam(apisParam))
findApisForCurrentUser(executionContext, apisParam, createQueryFromParam(apisParam))
);

if (!filteredApis.isEmpty() && apisParam.getPromoted() != null) {
Expand All @@ -117,7 +128,7 @@ public Response getApis(@BeanParam PaginationParam paginationParam, @BeanParam A
// If apis are searched in a category, looks for the category highlighted API (HL API) and if this HL API is in the searchResult.
// If it is, then the HL API becomes the promoted API
String highlightedApiId =
this.categoryService.findById(apisParam.getCategory(), GraviteeContext.getCurrentEnvironment()).getHighlightApi();
this.categoryService.findById(apisParam.getCategory(), GraviteeContext.getCurrentEnvironment()).getHighlightApi();
if (highlightedApiId != null && filteredApis.contains(highlightedApiId)) {
promotedApiId = highlightedApiId;
}
Expand All @@ -144,9 +155,9 @@ public Response getApis(@BeanParam PaginationParam paginationParam, @BeanParam A
@Produces(MediaType.APPLICATION_JSON)
@RequirePortalAuth
public Response searchApis(
@QueryParam("q") String query,
@QueryParam("category") String category,
@BeanParam PaginationParam paginationParam
@QueryParam("q") String query,
@QueryParam("category") String category,
@BeanParam PaginationParam paginationParam
) {
try {
final ExecutionContext executionContext = GraviteeContext.getExecutionContext();
Expand All @@ -173,28 +184,28 @@ protected List<Api> transformPageContent(ExecutionContext executionContext, List
return Collections.emptyList();
}
final boolean apiShowTagsInApiHeaders = parameterService.findAsBoolean(
executionContext,
Key.PORTAL_APIS_SHOW_TAGS_IN_APIHEADER,
ParameterReferenceType.ENVIRONMENT
executionContext,
Key.PORTAL_APIS_SHOW_TAGS_IN_APIHEADER,
ParameterReferenceType.ENVIRONMENT
);

ApiQuery apiQuery = new ApiQuery();
apiQuery.setIds(pageContent);
Collection<GenericApiEntity> apiEntities = apiSearchService.search(executionContext, apiQuery);
Comparator<String> orderingComparator = Comparator.comparingInt(pageContent::indexOf);
return apiEntities
.stream()
.map(apiEntity -> {
Api api = apiMapper.convert(executionContext, apiEntity);
return addApiLinks(api);
})
.peek(api -> {
if (!apiShowTagsInApiHeaders) {
api.setLabels(List.of());
}
})
.sorted((o1, o2) -> orderingComparator.compare(o1.getId(), o2.getId()))
.collect(Collectors.toList());
.stream()
.map(apiEntity -> {
Api api = apiMapper.convert(executionContext, apiEntity);
return addApiLinks(api);
})
.peek(api -> {
if (!apiShowTagsInApiHeaders) {
api.setLabels(List.of());
}
})
.sorted((o1, o2) -> orderingComparator.compare(o1.getId(), o2.getId()))
.collect(Collectors.toList());
}

private ApiQuery createQueryFromParam(ApisParam apisParam) {
Expand Down Expand Up @@ -231,20 +242,20 @@ private FilteringService.FilterType convert(FilterApiQuery filter) {
}

private Collection<String> findApisForCurrentUser(final ExecutionContext executionContext, ApisParam apisParam, ApiQuery apiQuery) {
if (!apisParam.isCategoryMode()) {
if (!apisParam.isCategoryMode() || !isAuthenticated()) {
return filteringService.filterApis(
executionContext,
getAuthenticatedUserOrNull(),
convert(apisParam.getFilter()),
convert(apisParam.getExcludedFilter()),
apiQuery
executionContext,
getAuthenticatedUserOrNull(),
convert(apisParam.getFilter()),
convert(apisParam.getExcludedFilter()),
apiQuery
);
}
return getCategoryApisUseCase
.execute(new GetCategoryApisUseCase.Input(executionContext, apisParam.getCategory(), getAuthenticatedUser(), false, true))
.results()
.stream()
.map(result -> result.api().getId())
.toList();
.execute(new GetCategoryApisUseCase.Input(executionContext, apisParam.getCategory(), getAuthenticatedUser(), false, true))
.results()
.stream()
.map(result -> result.api().getId())
.toList();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,13 @@
package io.gravitee.rest.api.portal.rest.resource;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;

import io.gravitee.common.http.HttpStatusCode;
import io.gravitee.rest.api.model.Visibility;
import io.gravitee.rest.api.model.api.ApiEntity;
import io.gravitee.rest.api.model.api.ApiLifecycleState;
import io.gravitee.rest.api.portal.rest.model.Api;
Expand Down Expand Up @@ -57,27 +59,41 @@ public void init() {
publishedApi.setLifecycleState(ApiLifecycleState.PUBLISHED);
publishedApi.setName("A");
publishedApi.setId("A");
publishedApi.setVisibility(Visibility.PUBLIC);
publishedApi.setCategories(Set.of("Category1", "Category2"));

ApiEntity unpublishedApi = new ApiEntity();
unpublishedApi.setLifecycleState(ApiLifecycleState.UNPUBLISHED);
unpublishedApi.setName("B");
unpublishedApi.setId("B");
publishedApi.setVisibility(Visibility.PUBLIC);
publishedApi.setCategories(Set.of("Category1", "Category2"));

ApiEntity anotherPublishedApi = new ApiEntity();
anotherPublishedApi.setLifecycleState(ApiLifecycleState.PUBLISHED);
anotherPublishedApi.setName("C");
anotherPublishedApi.setId("C");
publishedApi.setVisibility(Visibility.PUBLIC);
publishedApi.setCategories(Set.of("Category1", "Category2"));

ApiEntity publishedApi1 = new ApiEntity();
publishedApi1.setLifecycleState(ApiLifecycleState.PUBLISHED);
publishedApi1.setName("D");
publishedApi1.setId("D");
publishedApi1.setVisibility(Visibility.PRIVATE);
publishedApi1.setCategories(Set.of("Category1", "Category2"));

doReturn(Arrays.asList("A", "C")).when(filteringService).filterApis(any(), any(), any(), any(), any());
doReturn(List.of(publishedApi, anotherPublishedApi))
.when(apiSearchService)
.search(eq(GraviteeContext.getExecutionContext()), any());
.when(apiSearchService)
.search(eq(GraviteeContext.getExecutionContext()), any());

doReturn(false).when(ratingService).isEnabled(GraviteeContext.getExecutionContext());

doReturn(new Api().name("A").id("A")).when(apiMapper).convert(GraviteeContext.getExecutionContext(), publishedApi);
doReturn(new Api().name("B").id("B")).when(apiMapper).convert(GraviteeContext.getExecutionContext(), unpublishedApi);
doReturn(new Api().name("C").id("C")).when(apiMapper).convert(GraviteeContext.getExecutionContext(), anotherPublishedApi);
doReturn(new Api().name("D").id("D")).when(apiMapper).convert(GraviteeContext.getExecutionContext(), publishedApi1);
}

@Test
Expand All @@ -86,8 +102,21 @@ public void shouldGetPublishedApi() {
assertEquals(HttpStatusCode.OK_200, response.getStatus());

ApisResponse apiResponse = response.readEntity(ApisResponse.class);
assertNotNull(apiResponse.getData());
assertEquals(2, apiResponse.getData().size());
assertEquals("A", apiResponse.getData().get(0).getName());
assertEquals("C", apiResponse.getData().get(1).getName());
}

@Test
public void shouldReturnPublishedPublicApiWhenQueryByCategory() {
final Response response = target().queryParam("category", "Category1").request().get();
assertEquals(HttpStatusCode.OK_200, response.getStatus());

ApisResponse apiResponse = response.readEntity(ApisResponse.class);
assertNotNull(apiResponse.getData());
assertEquals(2, apiResponse.getData().size());
assertEquals("A", ((Api) apiResponse.getData().get(0)).getName());
assertEquals("C", ((Api) apiResponse.getData().get(1)).getName());
assertEquals("A", apiResponse.getData().get(0).getName());
assertEquals("C", apiResponse.getData().get(1).getName());
}
}

0 comments on commit 8ad2ec7

Please sign in to comment.