Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[master] feat: allow users to get subscriptions with their consumer configuration in portal rest api #11089

Merged
merged 3 commits into from
Mar 12, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export interface GetSubscriptionByIdRequestParams {
/** Id of a subscription. */
subscriptionId: string;
/** Comma-separated list of related objects to include in the response. */
include?: Array<'keys'>;
include?: Array<'keys' | 'consumerConfiguration'>;
}

export interface GetSubscriptionsRequestParams {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ export * from './searchApplicationsLogsParams';
export * from './simpleApplicationSettings';
export * from './subscription';
export * from './subscriptionConfigurationInput';
export * from './subscriptionConsumerConfiguration';
export * from './subscriptionInput';
export * from './subscriptionsResponse';
export * from './themeLinks';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,5 +56,9 @@ export interface SearchApplicationsLogsParams {
* List of filters for response time ranges
*/
responseTimeRanges?: Array<ResponseTimeRange>;
/**
* Filter for text in either the request or response body
*/
bodyText?: string;
}

Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
* https://openapi-generator.tech
* Do not edit the class manually.
*/
import { SubscriptionConsumerConfiguration } from './subscriptionConsumerConfiguration';
import { Key } from './key';


Expand Down Expand Up @@ -88,6 +89,7 @@ export interface Subscription {
* Only returned with (*)/subscriptions/{subscriptionId}*. Need *include* query param to contain \'keys\'. List of APIKeys of the subscription.
*/
keys?: Array<Key>;
consumerConfiguration?: SubscriptionConsumerConfiguration;
}
export namespace Subscription {
export type StatusEnum = 'PENDING' | 'ACCEPTED' | 'CLOSED' | 'REJECTED' | 'PAUSED';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/**
* Gravitee.io Portal Rest API
* API dedicated to the devportal part of Gravitee
*
* Contact: contact@graviteesource.com
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/


/**
* Consumer configuration associated to the subscription in case it is attached to a push plan.
*/
export interface SubscriptionConsumerConfiguration {
/**
* The id of the targeted entrypoint
*/
entrypointId: string;
/**
* The channel to consume
*/
channel?: string;
/**
* The configuration to use at subscription time to push to the target service.
*/
entrypointConfiguration?: object;
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* Copyright © 2015 The Gravitee team (http://gravitee.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.gravitee.rest.api.portal.rest.mapper;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.gravitee.definition.jackson.datatype.GraviteeMapper;
import io.gravitee.rest.api.service.exceptions.TechnicalManagementException;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import org.mapstruct.Mapper;
import org.mapstruct.Named;
import org.mapstruct.factory.Mappers;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Mapper
public interface ConfigurationSerializationMapper {
ConfigurationSerializationMapper INSTANCE = Mappers.getMapper(ConfigurationSerializationMapper.class);
Logger logger = LoggerFactory.getLogger(ConfigurationSerializationMapper.class);

@Named("deserializeConfiguration")
default Object deserializeConfiguration(String configuration) {
if (Objects.isNull(configuration)) {
return null;
}

ObjectMapper mapper = new GraviteeMapper();
try {
return mapper.readValue(configuration, LinkedHashMap.class);
} catch (JsonProcessingException jse) {
logger.debug("Cannot parse configuration as LinkedHashMap: " + configuration);
}

try {
return mapper.readValue(configuration, List.class);
} catch (JsonProcessingException jse) {
logger.debug("Cannot parse configuration as List: " + configuration);
}

return configuration;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* Copyright © 2015 The Gravitee team (http://gravitee.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.gravitee.rest.api.portal.rest.mapper;

import java.time.Instant;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.util.Date;
import java.util.Objects;
import org.mapstruct.Mapper;
import org.mapstruct.Named;
import org.mapstruct.factory.Mappers;

@Mapper
public interface DateMapper {
DateMapper INSTANCE = Mappers.getMapper(DateMapper.class);

default OffsetDateTime map(Date value) {
return Objects.isNull(value) ? null : value.toInstant().atOffset(ZoneOffset.UTC);
}

default Date map(OffsetDateTime offsetDateTime) {
return Objects.isNull(offsetDateTime) ? null : Date.from(offsetDateTime.toInstant());
}

default OffsetDateTime map(Instant value) {
return Objects.isNull(value) ? null : value.atOffset(ZoneOffset.UTC);
}

default Instant mapToInstant(OffsetDateTime value) {
return Objects.isNull(value) ? null : value.toInstant();
}

@Named("mapTimestamp")
default OffsetDateTime mapTimestamp(String timestamp) {
return OffsetDateTime.parse(timestamp);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,53 +15,39 @@
*/
package io.gravitee.rest.api.portal.rest.mapper;

import io.gravitee.rest.api.model.SubscriptionConfigurationEntity;
import io.gravitee.rest.api.model.SubscriptionEntity;
import io.gravitee.rest.api.model.SubscriptionStatus;
import io.gravitee.rest.api.portal.rest.model.Subscription;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.util.Date;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import io.gravitee.rest.api.portal.rest.model.SubscriptionConsumerConfiguration;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* @author Florent CHAMFROY (florent.chamfroy at graviteesource.com)
* @author GraviteeSource Team
*/
@Mapper(uses = { ConfigurationSerializationMapper.class, DateMapper.class })
public interface SubscriptionMapper {
Logger log = LoggerFactory.getLogger(SubscriptionMapper.class);
SubscriptionMapper INSTANCE = Mappers.getMapper(SubscriptionMapper.class);

@Slf4j
@Component
public class SubscriptionMapper {
@Mapping(target = "keys", ignore = true)
@Mapping(target = "endAt", source = "endingAt")
@Mapping(target = "startAt", source = "startingAt")
Subscription map(SubscriptionEntity subscriptionEntity);

public Subscription convert(SubscriptionEntity subscriptionEntity) {
final Subscription subscriptionItem = new Subscription();
subscriptionItem.setId(subscriptionEntity.getId());
subscriptionItem.setApi(subscriptionEntity.getApi());
subscriptionItem.setApplication(subscriptionEntity.getApplication());
subscriptionItem.setCreatedAt(getDate(subscriptionEntity.getCreatedAt()));
subscriptionItem.setEndAt(getDate(subscriptionEntity.getEndingAt()));
subscriptionItem.setProcessedAt(getDate(subscriptionEntity.getProcessedAt()));
subscriptionItem.setStartAt(getDate(subscriptionEntity.getStartingAt()));
subscriptionItem.setPausedAt(getDate(subscriptionEntity.getPausedAt()));
subscriptionItem.setClosedAt(getDate(subscriptionEntity.getClosedAt()));
subscriptionItem.setPausedAt(getDate(subscriptionEntity.getPausedAt()));
subscriptionItem.setPlan(subscriptionEntity.getPlan());
subscriptionItem.setRequest(subscriptionEntity.getRequest());
subscriptionItem.setReason(subscriptionEntity.getReason());
subscriptionItem.setStatus(Subscription.StatusEnum.fromValue(subscriptionEntity.getStatus().name()));
subscriptionItem.setSubscribedBy(subscriptionEntity.getSubscribedBy());
subscriptionItem.setOrigin(convert(subscriptionEntity.getOrigin()));
return subscriptionItem;
}
@Mapping(target = "entrypointConfiguration", qualifiedByName = "deserializeConfiguration")
SubscriptionConsumerConfiguration map(SubscriptionConfigurationEntity subscriptionConfigurationEntity);

private OffsetDateTime getDate(final Date date) {
if (date != null) {
return date.toInstant().atOffset(ZoneOffset.UTC);
}
return null;
default Subscription.StatusEnum map(SubscriptionStatus status) {
return Subscription.StatusEnum.fromValue(status.name());
}

private static Subscription.OriginEnum convert(String origin) {
default Subscription.OriginEnum convert(String origin) {
try {
return Subscription.OriginEnum.valueOf(origin);
} catch (IllegalArgumentException | NullPointerException e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.gravitee.rest.api.portal.rest.mapper;
package io.gravitee.rest.api.portal.rest.provider;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,6 @@ public class ApplicationsResource extends AbstractResource<Application, String>
@Inject
private SubscriptionService subscriptionService;

@Inject
private SubscriptionMapper subscriptionMapper;

@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
Expand Down Expand Up @@ -236,7 +233,7 @@ protected Map fillMetadata(ExecutionContext executionContext, Map metadata, List
final Map<String, List<Subscription>> subscriptions = subscriptionService
.search(executionContext, query)
.stream()
.map(subscriptionMapper::convert)
.map(SubscriptionMapper.INSTANCE::map)
.collect(groupingBy(Subscription::getApplication));
if (!subscriptions.isEmpty()) {
metadata.put(METADATA_SUBSCRIPTIONS_KEY, subscriptions);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,18 @@
*/
package io.gravitee.rest.api.portal.rest.resource;

import io.gravitee.rest.api.portal.rest.mapper.ObjectMapperResolver;
import io.gravitee.rest.api.portal.rest.provider.BadRequestExceptionMapper;
import io.gravitee.rest.api.portal.rest.provider.ByteArrayOutputStreamWriter;
import io.gravitee.rest.api.portal.rest.provider.ConstraintValidationExceptionMapper;
import io.gravitee.rest.api.portal.rest.provider.JsonMappingExceptionMapper;
import io.gravitee.rest.api.portal.rest.provider.ManagementExceptionMapper;
import io.gravitee.rest.api.portal.rest.provider.NotAllowedExceptionMapper;
import io.gravitee.rest.api.portal.rest.provider.NotFoundExceptionMapper;
import io.gravitee.rest.api.portal.rest.provider.ObjectMapperResolver;
import io.gravitee.rest.api.portal.rest.provider.PayloadInputBodyReader;
import io.gravitee.rest.api.portal.rest.provider.QueryParamExceptionMapper;
import io.gravitee.rest.api.portal.rest.provider.ThrowableMapper;
import io.gravitee.rest.api.portal.rest.provider.UnrecognizedPropertyExceptionMapper;
import io.gravitee.rest.api.portal.rest.resource.auth.ConsoleAuthenticationResource;
import io.gravitee.rest.api.portal.rest.resource.bootstrap.PortalUIBootstrapResource;
import io.gravitee.rest.api.rest.filter.GraviteeContextResponseFilter;
import io.gravitee.rest.api.rest.filter.MaintenanceFilter;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,13 +80,11 @@ public class SubscriptionResource extends AbstractResource {
@Inject
private KeyMapper keyMapper;

@Inject
private SubscriptionMapper subscriptionMapper;

@Inject
private GraviteeMapper graviteeMapper;

private static final String INCLUDE_KEYS = "keys";
private static final String INCLUDE_CONSUMER_CONFIGURATION = "consumerConfiguration";

@GET
@Produces(MediaType.APPLICATION_JSON)
Expand All @@ -96,11 +94,12 @@ public Response getSubscriptionBySubscriptionId(
) {
SubscriptionEntity subscriptionEntity = subscriptionService.findById(subscriptionId);
final ExecutionContext executionContext = GraviteeContext.getExecutionContext();

if (
hasPermission(executionContext, RolePermission.API_SUBSCRIPTION, subscriptionEntity.getApi(), RolePermissionAction.READ) ||
hasPermission(executionContext, APPLICATION_SUBSCRIPTION, subscriptionEntity.getApplication(), RolePermissionAction.READ)
) {
Subscription subscription = subscriptionMapper.convert(subscriptionEntity);
Subscription subscription = SubscriptionMapper.INSTANCE.map(subscriptionEntity);
if (include.contains(INCLUDE_KEYS)) {
List<Key> keys = apiKeyService
.findBySubscription(executionContext, subscriptionId)
Expand All @@ -110,6 +109,11 @@ public Response getSubscriptionBySubscriptionId(
.collect(Collectors.toList());
subscription.setKeys(keys);
}

if (include.contains(INCLUDE_CONSUMER_CONFIGURATION)) {
subscription.setConsumerConfiguration(SubscriptionMapper.INSTANCE.map(subscriptionEntity.getConfiguration()));
}

return Response.ok(subscription).build();
}
throw new ForbiddenAccessException();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,6 @@ public class SubscriptionsResource extends AbstractResource {
@Inject
private SubscriptionService subscriptionService;

@Inject
private SubscriptionMapper subscriptionMapper;

@Inject
private ApplicationService applicationService;

Expand Down Expand Up @@ -143,7 +140,7 @@ public Response createSubscription(@Valid @NotNull(message = "Input must not be
.map(keyMapper::convert)
.collect(Collectors.toList());

final Subscription subscription = subscriptionMapper.convert(createdSubscription);
final Subscription subscription = SubscriptionMapper.INSTANCE.map(createdSubscription);
subscription.setKeys(keys);

return Response.ok(subscription).build();
Expand Down Expand Up @@ -181,7 +178,10 @@ public Response getSubscriptions(
return createListResponse(executionContext, subscriptions, paginationParam, null, paginationParam.hasPagination());
}

final List<Subscription> subscriptionList = subscriptions.stream().map(subscriptionMapper::convert).collect(Collectors.toList());
final List<Subscription> subscriptionList = subscriptions
.stream()
.map(SubscriptionMapper.INSTANCE::map)
.collect(Collectors.toList());

SubscriptionMetadataQuery metadataQuery = new SubscriptionMetadataQuery(
GraviteeContext.getCurrentOrganization(),
Expand Down
Loading