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 750ce35 commit 165c451
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 19 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 @@ -480,12 +480,8 @@ void should_debug_api_and_filter_closed_plan() throws JsonProcessingException {
final HttpClient mockHttpClient = mock(HttpClient.class);
when(vertx.createHttpClient(any(HttpClientOptions.class))).thenReturn(mockHttpClient);

// Mock successful Buffer body in HttpClientResponse
// Mock successful HttpClientResponse
final HttpClientResponse httpClientResponse = mock(HttpClientResponse.class);
when(httpClientResponse.statusCode()).thenReturn(200);
final Buffer bodyBuffer = Buffer.buffer("response body");
when(httpClientResponse.rxBody()).thenReturn(Single.just(bodyBuffer));

// Mock successful HttpClientRequest
final HttpClientRequest httpClientRequest = mock(HttpClientRequest.class);
when(mockHttpClient.rxRequest(any())).thenReturn(Single.just(httpClientRequest));
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 @@ -105,9 +116,7 @@ public Response listCategories(@BeanParam ApisParam apisParam) {
@RequirePortalAuth
public Response getApis(@BeanParam PaginationParam paginationParam, @BeanParam ApisParam apisParam) {
final ExecutionContext executionContext = GraviteeContext.getExecutionContext();
Collection<String> filteredApis = new ArrayList<>(
findApisForCurrentUser(executionContext, apisParam, createQueryFromParam(apisParam))
);
Collection<String> filteredApis = new ArrayList<>(findApis(executionContext, apisParam, createQueryFromParam(apisParam)));

if (!filteredApis.isEmpty() && apisParam.getPromoted() != null) {
//By default, the promoted API is the first of the list;
Expand Down Expand Up @@ -230,8 +239,8 @@ private FilteringService.FilterType convert(FilterApiQuery filter) {
return filter != null ? FilteringService.FilterType.valueOf(filter.name()) : null;
}

private Collection<String> findApisForCurrentUser(final ExecutionContext executionContext, ApisParam apisParam, ApiQuery apiQuery) {
if (!apisParam.isCategoryMode()) {
private Collection<String> findApis(final ExecutionContext executionContext, ApisParam apisParam, ApiQuery apiQuery) {
if (isAnonymousUser(apisParam)) {
return filteringService.filterApis(
executionContext,
getAuthenticatedUserOrNull(),
Expand All @@ -247,4 +256,8 @@ private Collection<String> findApisForCurrentUser(final ExecutionContext executi
.map(result -> result.api().getId())
.toList();
}

private boolean isAnonymousUser(ApisParam apisParam) {
return !apisParam.isCategoryMode() || !isAuthenticated();
}
}
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,16 +59,29 @@ 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))
Expand All @@ -78,6 +93,7 @@ public void init() {
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 165c451

Please sign in to comment.