From 1fdd98f9e39adc50440b5094ca20532e07f8f8d3 Mon Sep 17 00:00:00 2001 From: Savindu Dimal Date: Wed, 22 Jan 2025 17:12:09 +0530 Subject: [PATCH 1/6] Add publisher REST API changes for organization based subscription policies --- .../org/wso2/carbon/apimgt/api/model/API.java | 45 ++++++- .../apimgt/api/model/OrganizationTiers.java | 58 ++++++++ .../wso2/carbon/apimgt/api/model/Tier.java | 2 + .../wso2/carbon/apimgt/impl/APIConstants.java | 1 + .../carbon/apimgt/impl/utils/APIUtil.java | 19 +++ .../apimgt/persistence/APIConstants.java | 1 + .../persistence/dto/OrganizationTiers.java | 50 +++++++ .../apimgt/persistence/dto/PublisherAPI.java | 9 ++ .../utils/RegistryPersistenceUtil.java | 10 ++ .../src/main/resources/publisher-api.yaml | 24 ++++ .../rest/api/publisher/v1/dto/APIDTO.java | 26 +++- .../v1/dto/OrganizationPoliciesDTO.java | 124 ++++++++++++++++++ .../v1/common/mappings/APIMappingUtil.java | 60 ++++++++- .../common/mappings/PublisherCommonUtils.java | 63 ++++++++- .../src/main/resources/publisher-api.yaml | 24 ++++ 15 files changed, 508 insertions(+), 8 deletions(-) create mode 100644 components/apimgt/org.wso2.carbon.apimgt.api/src/main/java/org/wso2/carbon/apimgt/api/model/OrganizationTiers.java create mode 100644 components/apimgt/org.wso2.carbon.apimgt.persistence/src/main/java/org/wso2/carbon/apimgt/persistence/dto/OrganizationTiers.java create mode 100644 components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1.common/src/gen/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/dto/OrganizationPoliciesDTO.java diff --git a/components/apimgt/org.wso2.carbon.apimgt.api/src/main/java/org/wso2/carbon/apimgt/api/model/API.java b/components/apimgt/org.wso2.carbon.apimgt.api/src/main/java/org/wso2/carbon/apimgt/api/model/API.java index 9b19fc8724c2..9997a8a91f6e 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.api/src/main/java/org/wso2/carbon/apimgt/api/model/API.java +++ b/components/apimgt/org.wso2.carbon.apimgt.api/src/main/java/org/wso2/carbon/apimgt/api/model/API.java @@ -30,6 +30,7 @@ import java.io.Serializable; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.Date; import java.util.LinkedHashSet; @@ -74,6 +75,7 @@ public class API implements Serializable { private Date lastUpdated; private String updatedBy; private Set availableTiers = new LinkedHashSet(); + private Set availableTiersForOrganizations = new LinkedHashSet<>(); private Set availableSubscriptionLevelPolicies = new LinkedHashSet(); private String apiLevelPolicy; private AuthorizationPolicy authorizationPolicy; @@ -766,8 +768,47 @@ public void removeAllPolicies() { availableSubscriptionLevelPolicies.clear(); } - public void removeAvailableTiers(Set availableTiers) { - this.availableTiers.removeAll(availableTiers); + public Set getAvailableTiersForOrganizations() { + return Collections.unmodifiableSet(availableTiersForOrganizations); + } + + public void setAvailableTiersForOrganizations(Set availableTiersForOrganizations) { + this.availableTiersForOrganizations = availableTiersForOrganizations; + } + + public void removeAllTiersForOrganizations() { + availableTiersForOrganizations.clear(); + } + + public void setAvailableTiersForOrganizationsFromString(String tiersString) { + + if (tiersString == null || tiersString.isEmpty()) { + return; + } + try { + ObjectMapper objectMapper = new ObjectMapper(); + OrganizationTiers[] tiersArray = objectMapper.readValue(tiersString, OrganizationTiers[].class); + availableTiersForOrganizations = new LinkedHashSet<>(Arrays.asList(tiersArray)); + } catch (Exception e) { + log.error("Error while converting string to availableTiersForOrganizations object for API : " + getUUID(), + e); + } + } + + public String getAvailableTiersForOrganizationsAsString() { + if (availableTiersForOrganizations == null || availableTiersForOrganizations.isEmpty()) { + return null; + } + try { + ObjectMapper objectMapper = new ObjectMapper(); + return objectMapper.writeValueAsString(availableTiersForOrganizations); + } catch (JsonProcessingException e) { + log.error("Error while converting availableTiersForOrganizations to string for API : " + getUUID(), e); + return null; + } catch (Exception e) { + log.error("Unexpected error while processing availableTiersForOrganizations for API : " + getUUID(), e); + return null; + } } public Set getUriTemplates() { diff --git a/components/apimgt/org.wso2.carbon.apimgt.api/src/main/java/org/wso2/carbon/apimgt/api/model/OrganizationTiers.java b/components/apimgt/org.wso2.carbon.apimgt.api/src/main/java/org/wso2/carbon/apimgt/api/model/OrganizationTiers.java new file mode 100644 index 000000000000..e280c330e569 --- /dev/null +++ b/components/apimgt/org.wso2.carbon.apimgt.api/src/main/java/org/wso2/carbon/apimgt/api/model/OrganizationTiers.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2025, WSO2 LLC. (http://www.wso2.com) All Rights Reserved. + * + * WSO2 LLC. licenses this file to you 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 org.wso2.carbon.apimgt.api.model; + +import java.util.Set; + +/** + * This class represent the Tier + */ +public class OrganizationTiers { + + private String organizationID; + private String organizationName; + private Set tiers; + + public String getOrganizationID() { + return organizationID; + } + + public void setOrganizationID(String organizationID) { + this.organizationID = organizationID; + } + + public String getOrganizationName() { + return organizationName; + } + + public void setOrganizationName(String organizationName) { + this.organizationName = organizationName; + } + + public Set getTiers() { + return tiers; + } + + public void setTiers(Set tiers) { + this.tiers = tiers; + } + + public void removeAllTiers() { + tiers.clear(); + } +} diff --git a/components/apimgt/org.wso2.carbon.apimgt.api/src/main/java/org/wso2/carbon/apimgt/api/model/Tier.java b/components/apimgt/org.wso2.carbon.apimgt.api/src/main/java/org/wso2/carbon/apimgt/api/model/Tier.java index 4d3317d2cb99..7837ec54402b 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.api/src/main/java/org/wso2/carbon/apimgt/api/model/Tier.java +++ b/components/apimgt/org.wso2.carbon.apimgt.api/src/main/java/org/wso2/carbon/apimgt/api/model/Tier.java @@ -59,6 +59,8 @@ public void setMonetizationAttributes(Map monetizationAttributes this.monetizationAttributes = monetizationAttributes; } + public Tier() {} + public Tier(String name) { this.name = name; } diff --git a/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/APIConstants.java b/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/APIConstants.java index dd6909f89a58..af055bbe2016 100755 --- a/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/APIConstants.java +++ b/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/APIConstants.java @@ -307,6 +307,7 @@ public final class APIConstants { public static final String API_OVERVIEW_THUMBNAIL_URL = "overview_thumbnail"; public static final String API_OVERVIEW_STATUS = "overview_status"; public static final String API_OVERVIEW_TIER = "overview_tier"; + public static final String API_OVERVIEW_ORGANIZATION_TIERS = "overview_organizationTiers"; public static final String API_OVERVIEW_SUB_POLICY = "overview_subPolicy"; public static final String API_OVERVIEW_API_POLICY = "overview_apiPolicy"; public static final String API_OVERVIEW_IS_LATEST = "overview_isLatest"; diff --git a/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/utils/APIUtil.java b/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/utils/APIUtil.java index fc457ff5a6e0..0cd3133a15db 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/utils/APIUtil.java +++ b/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/utils/APIUtil.java @@ -691,6 +691,10 @@ public static API getAPI(GovernanceArtifact artifact) } } + // Set available tiers for organizations + String organizationTiers = artifact.getAttribute(APIConstants.API_OVERVIEW_ORGANIZATION_TIERS); + api.setAvailableTiersForOrganizationsFromString(organizationTiers); + api.addAvailableTiers(availablePolicy); String tenantDomainName = MultitenantUtils.getTenantDomain(replaceEmailDomainBack(providerName)); api.setMonetizationCategory(getAPIMonetizationCategory(availablePolicy, tenantDomainName)); @@ -815,6 +819,11 @@ public static API getLightWeightAPI(GovernanceArtifact artifact) } } api.addAvailableTiers(availablePolicy); + + // Set available tiers for organizations + String organizationTiers = artifact.getAttribute(APIConstants.API_OVERVIEW_ORGANIZATION_TIERS); + api.setAvailableTiersForOrganizationsFromString(organizationTiers); + String tenantDomainName = MultitenantUtils.getTenantDomain(replaceEmailDomainBack(providerName)); api.setMonetizationCategory(getAPIMonetizationCategory(availablePolicy, tenantDomainName)); @@ -994,6 +1003,11 @@ public static GenericArtifact createAPIArtifactContent(GenericArtifact artifact, artifact.setAttribute(APIConstants.API_OVERVIEW_TIER, tiers); } + if (api.getAvailableTiersForOrganizationsAsString() != null) { + artifact.setAttribute(APIConstants.API_OVERVIEW_ORGANIZATION_TIERS, + api.getAvailableTiersForOrganizationsAsString()); + } + if (APIConstants.PUBLISHED.equals(apiStatus)) { artifact.setAttribute(APIConstants.API_OVERVIEW_IS_LATEST, "true"); } @@ -1050,6 +1064,7 @@ public static GenericArtifact createAPIArtifactContent(GenericArtifact artifact, if (apiSecurity != null && !apiSecurity.contains(APIConstants.DEFAULT_API_SECURITY_OAUTH2) && !apiSecurity.contains(APIConstants.API_SECURITY_API_KEY)) { artifact.setAttribute(APIConstants.API_OVERVIEW_TIER, ""); + artifact.setAttribute(APIConstants.API_OVERVIEW_ORGANIZATION_TIERS, ""); } } catch (GovernanceException e) { String msg = "Failed to create API for : " + api.getId().getApiName(); @@ -2705,6 +2720,10 @@ public static API getAPI(GovernanceArtifact artifact, Registry registry, APIIden Set availableTier = getAvailableTiers(definedTiers, tiers, apiName); api.addAvailableTiers(availableTier); + // Set available tiers for organizations + String organizationTiers = artifact.getAttribute(APIConstants.API_OVERVIEW_ORGANIZATION_TIERS); + api.setAvailableTiersForOrganizationsFromString(organizationTiers); + api.setContext(artifact.getAttribute(APIConstants.API_OVERVIEW_CONTEXT)); api.setContextTemplate(artifact.getAttribute(APIConstants.API_OVERVIEW_CONTEXT_TEMPLATE)); api.setLatest(Boolean.parseBoolean(artifact.getAttribute(APIConstants.API_OVERVIEW_IS_LATEST))); diff --git a/components/apimgt/org.wso2.carbon.apimgt.persistence/src/main/java/org/wso2/carbon/apimgt/persistence/APIConstants.java b/components/apimgt/org.wso2.carbon.apimgt.persistence/src/main/java/org/wso2/carbon/apimgt/persistence/APIConstants.java index 903d2d8d9148..e77c43ba0e7d 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.persistence/src/main/java/org/wso2/carbon/apimgt/persistence/APIConstants.java +++ b/components/apimgt/org.wso2.carbon.apimgt.persistence/src/main/java/org/wso2/carbon/apimgt/persistence/APIConstants.java @@ -41,6 +41,7 @@ public final class APIConstants { public static final String API_OVERVIEW_THUMBNAIL_URL = "overview_thumbnail"; public static final String API_OVERVIEW_STATUS = "overview_status"; public static final String API_OVERVIEW_TIER = "overview_tier"; + public static final String API_OVERVIEW_ORGANIZATION_TIERS = "overview_organizationTiers"; public static final String API_OVERVIEW_SUB_POLICY = "overview_subPolicy"; public static final String API_OVERVIEW_API_POLICY = "overview_apiPolicy"; public static final String API_OVERVIEW_IS_LATEST = "overview_isLatest"; diff --git a/components/apimgt/org.wso2.carbon.apimgt.persistence/src/main/java/org/wso2/carbon/apimgt/persistence/dto/OrganizationTiers.java b/components/apimgt/org.wso2.carbon.apimgt.persistence/src/main/java/org/wso2/carbon/apimgt/persistence/dto/OrganizationTiers.java new file mode 100644 index 000000000000..cba7452931b7 --- /dev/null +++ b/components/apimgt/org.wso2.carbon.apimgt.persistence/src/main/java/org/wso2/carbon/apimgt/persistence/dto/OrganizationTiers.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2025, WSO2 LLC. (http://www.wso2.com) All Rights Reserved. + * + * WSO2 LLC. licenses this file to you 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 org.wso2.carbon.apimgt.persistence.dto; + +import java.util.Set; + +public class OrganizationTiers { + private String organizationID; + private String organizationName; + private Set tiers; + + public String getOrganizationID() { + return organizationID; + } + + public void setOrganizationID(String organizationID) { + this.organizationID = organizationID; + } + + public String getOrganizationName() { + return organizationName; + } + + public void setOrganizationName(String organizationName) { + this.organizationName = organizationName; + } + + public Set getTiers() { + return tiers; + } + + public void setTiers(Set tiers) { + this.tiers = tiers; + } +} diff --git a/components/apimgt/org.wso2.carbon.apimgt.persistence/src/main/java/org/wso2/carbon/apimgt/persistence/dto/PublisherAPI.java b/components/apimgt/org.wso2.carbon.apimgt.persistence/src/main/java/org/wso2/carbon/apimgt/persistence/dto/PublisherAPI.java index fb3f51727431..da3ae93d52ab 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.persistence/src/main/java/org/wso2/carbon/apimgt/persistence/dto/PublisherAPI.java +++ b/components/apimgt/org.wso2.carbon.apimgt.persistence/src/main/java/org/wso2/carbon/apimgt/persistence/dto/PublisherAPI.java @@ -78,6 +78,7 @@ public class PublisherAPI extends PublisherAPIInfo { private String testKey; private String contextTemplate; private Set availableTierNames; + private Set availableTiersForOrganizations; private Set environments; private CORSConfiguration corsConfiguration; private WebsubSubscriptionConfiguration websubSubscriptionConfiguration; @@ -464,6 +465,14 @@ public void setAvailableTierNames(Set availableTierNames) { this.availableTierNames = availableTierNames; } + public Set getAvailableTiersForOrganizations() { + return availableTiersForOrganizations; + } + + public void setAvailableTiersForOrganizations(Set availableTiersForOrganizations) { + this.availableTiersForOrganizations = availableTiersForOrganizations; + } + public Set getEnvironments() { return environments; } diff --git a/components/apimgt/org.wso2.carbon.apimgt.persistence/src/main/java/org/wso2/carbon/apimgt/persistence/utils/RegistryPersistenceUtil.java b/components/apimgt/org.wso2.carbon.apimgt.persistence/src/main/java/org/wso2/carbon/apimgt/persistence/utils/RegistryPersistenceUtil.java index b06cbef4515f..97c072c1142b 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.persistence/src/main/java/org/wso2/carbon/apimgt/persistence/utils/RegistryPersistenceUtil.java +++ b/components/apimgt/org.wso2.carbon.apimgt.persistence/src/main/java/org/wso2/carbon/apimgt/persistence/utils/RegistryPersistenceUtil.java @@ -259,6 +259,11 @@ public static GenericArtifact createAPIArtifactContent(GenericArtifact artifact, artifact.setAttribute(APIConstants.API_OVERVIEW_TIER, tiers); } + if (api.getAvailableTiersForOrganizationsAsString() != null) { + artifact.setAttribute(APIConstants.API_OVERVIEW_ORGANIZATION_TIERS, + api.getAvailableTiersForOrganizationsAsString()); + } + if (APIConstants.PUBLISHED.equals(apiStatus)) { artifact.setAttribute(APIConstants.API_OVERVIEW_IS_LATEST, "true"); } @@ -318,6 +323,7 @@ public static GenericArtifact createAPIArtifactContent(GenericArtifact artifact, if (apiSecurity != null && !apiSecurity.contains(APIConstants.DEFAULT_API_SECURITY_OAUTH2) && !apiSecurity .contains(APIConstants.API_SECURITY_API_KEY)) { artifact.setAttribute(APIConstants.API_OVERVIEW_TIER, ""); + artifact.setAttribute(APIConstants.API_OVERVIEW_ORGANIZATION_TIERS, ""); } //set gateway vendor for the API @@ -737,6 +743,10 @@ public static API getAPI(GovernanceArtifact artifact, Registry registry) } api.setAvailableTiers(availableTiers ); + // Set available tiers for organizations + String organizationTiers = artifact.getAttribute(APIConstants.API_OVERVIEW_ORGANIZATION_TIERS); + api.setAvailableTiersForOrganizationsFromString(organizationTiers); + // This contains the resolved context api.setContext(artifact.getAttribute(APIConstants.API_OVERVIEW_CONTEXT)); // We set the context template here diff --git a/components/apimgt/org.wso2.carbon.apimgt.rest.api.common/src/main/resources/publisher-api.yaml b/components/apimgt/org.wso2.carbon.apimgt.rest.api.common/src/main/resources/publisher-api.yaml index f741ee334688..7f36e216fb2f 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.rest.api.common/src/main/resources/publisher-api.yaml +++ b/components/apimgt/org.wso2.carbon.apimgt.rest.api.common/src/main/resources/publisher-api.yaml @@ -10127,6 +10127,13 @@ components: x-otherScopes: - apim:api_publish - apim:api_manage + organizationPolicies: + type: array + items: + $ref: '#/components/schemas/OrganizationPolicies' + x-otherScopes: + - apim:api_publish + - apim:api_manage apiThrottlingPolicy: type: string description: The API level throttling policy selected for the particular @@ -12469,6 +12476,23 @@ components: example: "" operationPolicies: $ref: '#/components/schemas/APIOperationPolicies' + OrganizationPolicies: + title: Organization based Subscription Policies + type: object + required: + - organizationID + properties: + organizationID: + type: string + example: "36c87e00-57f0-4000-9f97-b379acc4e577" + organizationName: + type: string + example: "wso2" + policies: + type: array + example: ["Silver", "Gold", "Unlimited"] + items: + type: string ScopeList: title: Scope List type: object diff --git a/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1.common/src/gen/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/dto/APIDTO.java b/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1.common/src/gen/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/dto/APIDTO.java index 9255b21c4f96..8fb3bc950326 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1.common/src/gen/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/dto/APIDTO.java +++ b/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1.common/src/gen/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/dto/APIDTO.java @@ -22,6 +22,7 @@ import org.wso2.carbon.apimgt.rest.api.publisher.v1.dto.APIThreatProtectionPoliciesDTO; import org.wso2.carbon.apimgt.rest.api.publisher.v1.dto.AdvertiseInfoDTO; import org.wso2.carbon.apimgt.rest.api.publisher.v1.dto.MediationPolicyDTO; +import org.wso2.carbon.apimgt.rest.api.publisher.v1.dto.OrganizationPoliciesDTO; import org.wso2.carbon.apimgt.rest.api.publisher.v1.dto.WSDLInfoDTO; import org.wso2.carbon.apimgt.rest.api.publisher.v1.dto.WebsubSubscriptionConfigurationDTO; import javax.validation.constraints.*; @@ -144,6 +145,9 @@ public static AudienceEnum fromValue(String v) { private List policies = new ArrayList(); @Scope(name = "apim:api_publish", description="", value ="") @Scope(name = "apim:api_manage", description="", value ="") + private List organizationPolicies = new ArrayList(); + @Scope(name = "apim:api_publish", description="", value ="") + @Scope(name = "apim:api_manage", description="", value ="") private String apiThrottlingPolicy = null; private String authorizationHeader = null; private String apiKeyHeader = null; @@ -745,6 +749,24 @@ public void setPolicies(List policies) { this.policies = policies; } + /** + **/ + public APIDTO organizationPolicies(List organizationPolicies) { + this.organizationPolicies = organizationPolicies; + return this; + } + + + @ApiModelProperty(value = "") + @Valid + @JsonProperty("organizationPolicies") + public List getOrganizationPolicies() { + return organizationPolicies; + } + public void setOrganizationPolicies(List organizationPolicies) { + this.organizationPolicies = organizationPolicies; + } + /** * The API level throttling policy selected for the particular API **/ @@ -1476,6 +1498,7 @@ public boolean equals(java.lang.Object o) { Objects.equals(transport, API.transport) && Objects.equals(tags, API.tags) && Objects.equals(policies, API.policies) && + Objects.equals(organizationPolicies, API.organizationPolicies) && Objects.equals(apiThrottlingPolicy, API.apiThrottlingPolicy) && Objects.equals(authorizationHeader, API.authorizationHeader) && Objects.equals(apiKeyHeader, API.apiKeyHeader) && @@ -1519,7 +1542,7 @@ public boolean equals(java.lang.Object o) { @Override public int hashCode() { - return Objects.hash(id, name, description, context, version, provider, lifeCycleStatus, wsdlInfo, wsdlUrl, responseCachingEnabled, cacheTimeout, hasThumbnail, isDefaultVersion, isRevision, revisionedApiId, revisionId, enableSchemaValidation, enableSubscriberVerification, type, audience, audiences, transport, tags, policies, apiThrottlingPolicy, authorizationHeader, apiKeyHeader, securityScheme, maxTps, visibility, visibleRoles, visibleTenants, visibleOrganizations, mediationPolicies, apiPolicies, subscriptionAvailability, subscriptionAvailableTenants, additionalProperties, additionalPropertiesMap, monetization, accessControl, accessControlRoles, businessInformation, corsConfiguration, websubSubscriptionConfiguration, workflowStatus, createdTime, lastUpdatedTimestamp, lastUpdatedTime, endpointConfig, endpointImplementationType, subtypeConfiguration, scopes, operations, threatProtectionPolicies, categories, keyManagers, serviceInfo, advertiseInfo, gatewayVendor, gatewayType, asyncTransportProtocols, egress); + return Objects.hash(id, name, description, context, version, provider, lifeCycleStatus, wsdlInfo, wsdlUrl, responseCachingEnabled, cacheTimeout, hasThumbnail, isDefaultVersion, isRevision, revisionedApiId, revisionId, enableSchemaValidation, enableSubscriberVerification, type, audience, audiences, transport, tags, policies, organizationPolicies, apiThrottlingPolicy, authorizationHeader, apiKeyHeader, securityScheme, maxTps, visibility, visibleRoles, visibleTenants, visibleOrganizations, mediationPolicies, apiPolicies, subscriptionAvailability, subscriptionAvailableTenants, additionalProperties, additionalPropertiesMap, monetization, accessControl, accessControlRoles, businessInformation, corsConfiguration, websubSubscriptionConfiguration, workflowStatus, createdTime, lastUpdatedTimestamp, lastUpdatedTime, endpointConfig, endpointImplementationType, subtypeConfiguration, scopes, operations, threatProtectionPolicies, categories, keyManagers, serviceInfo, advertiseInfo, gatewayVendor, gatewayType, asyncTransportProtocols, egress); } @Override @@ -1551,6 +1574,7 @@ public String toString() { sb.append(" transport: ").append(toIndentedString(transport)).append("\n"); sb.append(" tags: ").append(toIndentedString(tags)).append("\n"); sb.append(" policies: ").append(toIndentedString(policies)).append("\n"); + sb.append(" organizationPolicies: ").append(toIndentedString(organizationPolicies)).append("\n"); sb.append(" apiThrottlingPolicy: ").append(toIndentedString(apiThrottlingPolicy)).append("\n"); sb.append(" authorizationHeader: ").append(toIndentedString(authorizationHeader)).append("\n"); sb.append(" apiKeyHeader: ").append(toIndentedString(apiKeyHeader)).append("\n"); diff --git a/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1.common/src/gen/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/dto/OrganizationPoliciesDTO.java b/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1.common/src/gen/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/dto/OrganizationPoliciesDTO.java new file mode 100644 index 000000000000..c6c30f0c3120 --- /dev/null +++ b/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1.common/src/gen/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/dto/OrganizationPoliciesDTO.java @@ -0,0 +1,124 @@ +package org.wso2.carbon.apimgt.rest.api.publisher.v1.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import java.util.ArrayList; +import java.util.List; +import javax.validation.constraints.*; + + +import io.swagger.annotations.*; +import java.util.Objects; + +import javax.xml.bind.annotation.*; +import org.wso2.carbon.apimgt.rest.api.common.annotations.Scope; +import com.fasterxml.jackson.annotation.JsonCreator; + +import javax.validation.Valid; + + + +public class OrganizationPoliciesDTO { + + private String organizationID = null; + private String organizationName = null; + private List policies = new ArrayList(); + + /** + **/ + public OrganizationPoliciesDTO organizationID(String organizationID) { + this.organizationID = organizationID; + return this; + } + + + @ApiModelProperty(example = "36c87e00-57f0-4000-9f97-b379acc4e577", required = true, value = "") + @JsonProperty("organizationID") + @NotNull + public String getOrganizationID() { + return organizationID; + } + public void setOrganizationID(String organizationID) { + this.organizationID = organizationID; + } + + /** + **/ + public OrganizationPoliciesDTO organizationName(String organizationName) { + this.organizationName = organizationName; + return this; + } + + + @ApiModelProperty(example = "wso2", value = "") + @JsonProperty("organizationName") + public String getOrganizationName() { + return organizationName; + } + public void setOrganizationName(String organizationName) { + this.organizationName = organizationName; + } + + /** + **/ + public OrganizationPoliciesDTO policies(List policies) { + this.policies = policies; + return this; + } + + + @ApiModelProperty(example = "[\"Silver\",\"Gold\",\"Unlimited\"]", value = "") + @JsonProperty("policies") + public List getPolicies() { + return policies; + } + public void setPolicies(List policies) { + this.policies = policies; + } + + + @Override + public boolean equals(java.lang.Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + OrganizationPoliciesDTO organizationPolicies = (OrganizationPoliciesDTO) o; + return Objects.equals(organizationID, organizationPolicies.organizationID) && + Objects.equals(organizationName, organizationPolicies.organizationName) && + Objects.equals(policies, organizationPolicies.policies); + } + + @Override + public int hashCode() { + return Objects.hash(organizationID, organizationName, policies); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class OrganizationPoliciesDTO {\n"); + + sb.append(" organizationID: ").append(toIndentedString(organizationID)).append("\n"); + sb.append(" organizationName: ").append(toIndentedString(organizationName)).append("\n"); + sb.append(" policies: ").append(toIndentedString(policies)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(java.lang.Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } +} + diff --git a/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1.common/src/main/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/common/mappings/APIMappingUtil.java b/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1.common/src/main/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/common/mappings/APIMappingUtil.java index 19c85bd93fe7..3ae246b15ae4 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1.common/src/main/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/common/mappings/APIMappingUtil.java +++ b/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1.common/src/main/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/common/mappings/APIMappingUtil.java @@ -54,6 +54,7 @@ import org.wso2.carbon.apimgt.api.model.LifeCycleEvent; import org.wso2.carbon.apimgt.api.model.Mediation; import org.wso2.carbon.apimgt.api.model.OperationPolicy; +import org.wso2.carbon.apimgt.api.model.OrganizationTiers; import org.wso2.carbon.apimgt.api.model.ResourcePath; import org.wso2.carbon.apimgt.api.model.Scope; import org.wso2.carbon.apimgt.api.model.SequenceBackendData; @@ -113,6 +114,7 @@ import org.wso2.carbon.apimgt.rest.api.publisher.v1.dto.MockResponsePayloadListDTO; import org.wso2.carbon.apimgt.rest.api.publisher.v1.dto.OpenAPIDefinitionValidationResponseDTO; import org.wso2.carbon.apimgt.rest.api.publisher.v1.dto.OpenAPIDefinitionValidationResponseInfoDTO; +import org.wso2.carbon.apimgt.rest.api.publisher.v1.dto.OrganizationPoliciesDTO; import org.wso2.carbon.apimgt.rest.api.publisher.v1.dto.PaginationDTO; import org.wso2.carbon.apimgt.rest.api.publisher.v1.dto.ProductAPIDTO; import org.wso2.carbon.apimgt.rest.api.publisher.v1.dto.ResourcePathDTO; @@ -302,12 +304,34 @@ public static API fromDTOtoAPI(APIDTO dto, String provider) throws APIManagement model.addTags(apiTags); } + // Set tiers without considering organizations Set apiTiers = new HashSet<>(); List tiersFromDTO = dto.getPolicies(); for (String tier : tiersFromDTO) { apiTiers.add(new Tier(tier)); } model.addAvailableTiers(apiTiers); + + if (APIUtil.isOrganizationAccessControlEnabled()) { + // Set tiers for organizations + Set organizationAPITiers = new HashSet<>(); + List organizationPoliciesDTOs = dto.getOrganizationPolicies(); + + for (OrganizationPoliciesDTO organizationPoliciesDTO : organizationPoliciesDTOs) { + OrganizationTiers organizationTiers = new OrganizationTiers(); + Set apiTiersForOrganization = new HashSet<>(); + for (String tier : organizationPoliciesDTO.getPolicies()) { + apiTiersForOrganization.add(new Tier(tier)); + } + organizationTiers.setOrganizationID(organizationPoliciesDTO.getOrganizationID()); + organizationTiers.setOrganizationName(organizationPoliciesDTO.getOrganizationName()); + organizationTiers.setTiers(apiTiersForOrganization); + organizationAPITiers.add(organizationTiers); + } + model.setAvailableTiersForOrganizations(organizationAPITiers); + } + + model.setApiLevelPolicy(dto.getApiThrottlingPolicy()); String transports = StringUtils.join(dto.getTransport(), ','); @@ -1368,6 +1392,17 @@ public static APIDTO fromAPItoDTO(API model, boolean preserveCredentials, dto.setPolicies(tiersToReturn); dto.setApiThrottlingPolicy(model.getApiLevelPolicy()); + if (APIUtil.isOrganizationAccessControlEnabled() && model.getAvailableTiersForOrganizations() != null) { + Set organizationAPITiers = model.getAvailableTiersForOrganizations(); + List organizationPolicies = new ArrayList<>(); + + for (OrganizationTiers organizationTiers : organizationAPITiers) { + OrganizationPoliciesDTO organizationPoliciesDTO = getOrganizationPoliciesDTO(organizationTiers); + organizationPolicies.add(organizationPoliciesDTO); + } + dto.setOrganizationPolicies(organizationPolicies); + } + //APIs created with type set to "NULL" will be considered as "HTTP" if (model.getType() == null || model.getType().toLowerCase().equals("null")) { dto.setType(APIDTO.TypeEnum.HTTP); @@ -2724,6 +2759,24 @@ public static APIProductDTO fromAPIProducttoDTO(APIProduct product) throws APIMa return productDto; } + /** + * This method converts OrganizationTiers object to OrganizationPoliciesDTO. + * @param organizationTiers OrganizationTiers object + * @return + */ + private static OrganizationPoliciesDTO getOrganizationPoliciesDTO(OrganizationTiers organizationTiers) { + OrganizationPoliciesDTO organizationPoliciesDTO = new OrganizationPoliciesDTO(); + Set apiTiers = organizationTiers.getTiers(); + List tiersToReturn = new ArrayList<>(); + for (Tier tier : apiTiers) { + tiersToReturn.add(tier.getName()); + } + organizationPoliciesDTO.setPolicies(tiersToReturn); + organizationPoliciesDTO.setOrganizationID(organizationTiers.getOrganizationID()); + organizationPoliciesDTO.setOrganizationName(organizationTiers.getOrganizationName()); + return organizationPoliciesDTO; + } + private static APIProductDTO.SubscriptionAvailabilityEnum mapSubscriptionAvailabilityFromAPIProducttoDTO( String subscriptionAvailability) { @@ -2826,9 +2879,6 @@ public static APIProduct fromDTOtoAPIProduct(APIProductDTO dto, String provider) product.setAudiences(new HashSet<>(audiences)); } - Set apiTiers = new HashSet<>(); - List tiersFromDTO = dto.getPolicies(); - if (dto.getVisibility() != null) { product.setVisibility(mapVisibilityFromDTOtoAPIProduct(dto.getVisibility())); } @@ -2850,11 +2900,13 @@ public static APIProduct fromDTOtoAPIProduct(APIProductDTO dto, String provider) product.setAccessControl(APIConstants.API_RESTRICTED_VISIBILITY); } + // Set Tiers without considering organizations + Set apiTiers = new HashSet<>(); + List tiersFromDTO = dto.getPolicies(); for (String tier : tiersFromDTO) { apiTiers.add(new Tier(tier)); } product.setAvailableTiers(apiTiers); - product.setProductLevelPolicy(dto.getApiThrottlingPolicy()); product.setGatewayVendor(dto.getGatewayVendor()); diff --git a/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1.common/src/main/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/common/mappings/PublisherCommonUtils.java b/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1.common/src/main/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/common/mappings/PublisherCommonUtils.java index f5ad71b81532..8f121daa7295 100755 --- a/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1.common/src/main/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/common/mappings/PublisherCommonUtils.java +++ b/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1.common/src/main/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/common/mappings/PublisherCommonUtils.java @@ -98,6 +98,7 @@ import org.wso2.carbon.apimgt.rest.api.publisher.v1.dto.GraphQLValidationResponseGraphQLInfoDTO; import org.wso2.carbon.apimgt.rest.api.publisher.v1.dto.LifecycleHistoryDTO; import org.wso2.carbon.apimgt.rest.api.publisher.v1.dto.LifecycleStateDTO; +import org.wso2.carbon.apimgt.rest.api.publisher.v1.dto.OrganizationPoliciesDTO; import org.wso2.carbon.core.util.CryptoException; import org.wso2.carbon.core.util.CryptoUtil; import org.wso2.carbon.utils.multitenancy.MultitenantConstants; @@ -429,6 +430,7 @@ private static API prepareForUpdateApi(API originalAPI, APIDTO apiDtoToUpdate, A List apiSecurity = apiDtoToUpdate.getSecurityScheme(); //validation for tiers List tiersFromDTO = apiDtoToUpdate.getPolicies(); + List organizationPoliciesDTOs = apiDtoToUpdate.getOrganizationPolicies(); // Remove the subscriptionless tier if other tiers are available. if (tiersFromDTO != null && tiersFromDTO.size() > 1) { String tierToDrop = null; @@ -478,9 +480,9 @@ private static API prepareForUpdateApi(API originalAPI, APIDTO apiDtoToUpdate, A } } + Set definedTiers = apiProvider.getTiers(); if (tiersFromDTO != null && !tiersFromDTO.isEmpty()) { //check whether the added API's tiers are all valid - Set definedTiers = apiProvider.getTiers(); List invalidTiers = getInvalidTierNames(definedTiers, tiersFromDTO); if (invalidTiers.size() > 0) { throw new APIManagementException( @@ -488,6 +490,65 @@ private static API prepareForUpdateApi(API originalAPI, APIDTO apiDtoToUpdate, A ExceptionCodes.TIER_NAME_INVALID); } } + // Organization based subscription policies + if (APIUtil.isOrganizationAccessControlEnabled()) { + for (OrganizationPoliciesDTO organizationPoliciesDTO : organizationPoliciesDTOs) { + List organizationTiersFromDTO = organizationPoliciesDTO.getPolicies(); + + // Remove the subscriptionless tier if other tiers are available. + if (organizationTiersFromDTO != null && organizationTiersFromDTO.size() > 1) { + String tierToDrop = null; + for (String tier : organizationTiersFromDTO) { + if (tier.contains(APIConstants.DEFAULT_SUB_POLICY_SUBSCRIPTIONLESS)) { + tierToDrop = tier; + break; + } + } + if (tierToDrop != null) { + organizationTiersFromDTO.remove(tierToDrop); + organizationPoliciesDTO.setPolicies(tiersFromDTO); + } + } + boolean conditionForOrganization = ( + (organizationTiersFromDTO == null || organizationTiersFromDTO.isEmpty() && !( + APIConstants.CREATED.equals(originalStatus) || APIConstants.PROTOTYPED.equals( + originalStatus))) && !apiDtoToUpdate.getAdvertiseInfo().isAdvertised()); + if (!APIUtil.isSubscriptionValidationDisablingAllowed(tenantDomain)) { + if (apiSecurity != null && (apiSecurity.contains(APIConstants.DEFAULT_API_SECURITY_OAUTH2) + || apiSecurity.contains(APIConstants.API_SECURITY_API_KEY)) && conditionForOrganization) { + throw new APIManagementException("A tier should be defined if the API is not in CREATED " + + "or PROTOTYPED state", ExceptionCodes.TIER_CANNOT_BE_NULL); + } + } else { + if (apiSecurity != null) { + if (apiSecurity.contains(APIConstants.API_SECURITY_API_KEY) && conditionForOrganization) { + throw new APIManagementException("A tier should be defined if the API is not in CREATED " + + "or PROTOTYPED state", ExceptionCodes.TIER_CANNOT_BE_NULL); + } else if (apiSecurity.contains(APIConstants.DEFAULT_API_SECURITY_OAUTH2)) { + // Internally set the default tier when no tiers are defined in order to support + // subscription validation disabling for OAuth2 secured APIs + if (organizationTiersFromDTO != null && organizationTiersFromDTO.isEmpty()) { + if (isAsyncAPI) { + organizationTiersFromDTO.add( + APIConstants.DEFAULT_SUB_POLICY_ASYNC_SUBSCRIPTIONLESS); + } else { + organizationTiersFromDTO.add(APIConstants.DEFAULT_SUB_POLICY_SUBSCRIPTIONLESS); + } + organizationPoliciesDTO.setPolicies(organizationTiersFromDTO); + } + } + } + } + if (organizationTiersFromDTO != null && !organizationTiersFromDTO.isEmpty()) { + List invalidTiers = getInvalidTierNames(definedTiers, organizationTiersFromDTO); + if (invalidTiers.size() > 0) { + throw new APIManagementException("Specified tier(s) " + Arrays.toString(invalidTiers.toArray()) + + " are invalid", ExceptionCodes.TIER_NAME_INVALID); + } + } + } + } + if (apiDtoToUpdate.getAccessControlRoles() != null) { String errorMessage = validateUserRoles(apiDtoToUpdate.getAccessControlRoles()); if (!errorMessage.isEmpty()) { diff --git a/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1/src/main/resources/publisher-api.yaml b/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1/src/main/resources/publisher-api.yaml index f741ee334688..7f36e216fb2f 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1/src/main/resources/publisher-api.yaml +++ b/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1/src/main/resources/publisher-api.yaml @@ -10127,6 +10127,13 @@ components: x-otherScopes: - apim:api_publish - apim:api_manage + organizationPolicies: + type: array + items: + $ref: '#/components/schemas/OrganizationPolicies' + x-otherScopes: + - apim:api_publish + - apim:api_manage apiThrottlingPolicy: type: string description: The API level throttling policy selected for the particular @@ -12469,6 +12476,23 @@ components: example: "" operationPolicies: $ref: '#/components/schemas/APIOperationPolicies' + OrganizationPolicies: + title: Organization based Subscription Policies + type: object + required: + - organizationID + properties: + organizationID: + type: string + example: "36c87e00-57f0-4000-9f97-b379acc4e577" + organizationName: + type: string + example: "wso2" + policies: + type: array + example: ["Silver", "Gold", "Unlimited"] + items: + type: string ScopeList: title: Scope List type: object From 78e37b5cc811cf2ff9476a8f5c2a2bd62a199cd1 Mon Sep 17 00:00:00 2001 From: Savindu Dimal Date: Thu, 30 Jan 2025 19:36:13 +0530 Subject: [PATCH 2/6] Add organizationID query parameter when fetching subscription policies --- .../src/main/resources/publisher-api.yaml | 8 ++++++ .../apimgt/rest/api/publisher/v1/ApisApi.java | 4 +-- .../rest/api/publisher/v1/ApisApiService.java | 2 +- .../publisher/v1/impl/ApisApiServiceImpl.java | 4 +-- .../v1/utils/RestApiPublisherUtils.java | 28 +++++++++++++++++++ .../src/main/resources/publisher-api.yaml | 8 ++++++ 6 files changed, 49 insertions(+), 5 deletions(-) diff --git a/components/apimgt/org.wso2.carbon.apimgt.rest.api.common/src/main/resources/publisher-api.yaml b/components/apimgt/org.wso2.carbon.apimgt.rest.api.common/src/main/resources/publisher-api.yaml index 7f36e216fb2f..c8a77b2c0fea 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.rest.api.common/src/main/resources/publisher-api.yaml +++ b/components/apimgt/org.wso2.carbon.apimgt.rest.api.common/src/main/resources/publisher-api.yaml @@ -1294,6 +1294,7 @@ paths: - $ref: '#/components/parameters/requestedTenant' - $ref: '#/components/parameters/If-None-Match' - $ref: '#/components/parameters/isAiApi' + - $ref: '#/components/parameters/organizationID' responses: 200: description: | @@ -14311,6 +14312,13 @@ components: schema: type: boolean default: false + organizationID: + name: organizationID + in: query + description: | + Indicates the organization ID + schema: + type: string offset: name: offset in: query diff --git a/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1/src/gen/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/ApisApi.java b/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1/src/gen/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/ApisApi.java index 6eafedcdaadf..a38adfd3e19e 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1/src/gen/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/ApisApi.java +++ b/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1/src/gen/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/ApisApi.java @@ -1113,8 +1113,8 @@ public Response getAPISpecificOperationPolicyContentByPolicyId(@ApiParam(value = @ApiResponse(code = 304, message = "Not Modified. Empty body because the client has already the latest version of the requested resource. ", response = Void.class), @ApiResponse(code = 404, message = "Not Found. The specified resource does not exist.", response = ErrorDTO.class), @ApiResponse(code = 406, message = "Not Acceptable. The requested media type is not supported.", response = ErrorDTO.class) }) - public Response getAPISubscriptionPolicies(@ApiParam(value = "**API ID** consisting of the **UUID** of the API. ",required=true) @PathParam("apiId") String apiId, @ApiParam(value = "For cross-tenant invocations, this is used to specify the tenant domain, where the resource need to be retirieved from. " )@HeaderParam("X-WSO2-Tenant") String xWSO2Tenant, @ApiParam(value = "Validator for conditional requests; based on the ETag of the formerly retrieved variant of the resource. " )@HeaderParam("If-None-Match") String ifNoneMatch, @ApiParam(value = "Indicates the quota policy type to be AI API quota or not. ", defaultValue="false") @DefaultValue("false") @QueryParam("isAiApi") Boolean isAiApi) throws APIManagementException{ - return delegate.getAPISubscriptionPolicies(apiId, xWSO2Tenant, ifNoneMatch, isAiApi, securityContext); + public Response getAPISubscriptionPolicies(@ApiParam(value = "**API ID** consisting of the **UUID** of the API. ",required=true) @PathParam("apiId") String apiId, @ApiParam(value = "For cross-tenant invocations, this is used to specify the tenant domain, where the resource need to be retirieved from. " )@HeaderParam("X-WSO2-Tenant") String xWSO2Tenant, @ApiParam(value = "Validator for conditional requests; based on the ETag of the formerly retrieved variant of the resource. " )@HeaderParam("If-None-Match") String ifNoneMatch, @ApiParam(value = "Indicates the quota policy type to be AI API quota or not. ", defaultValue="false") @DefaultValue("false") @QueryParam("isAiApi") Boolean isAiApi, @ApiParam(value = "Indicates the organization ID ") @QueryParam("organizationID") String organizationID) throws APIManagementException{ + return delegate.getAPISubscriptionPolicies(apiId, xWSO2Tenant, ifNoneMatch, isAiApi, organizationID, securityContext); } @GET diff --git a/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1/src/gen/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/ApisApiService.java b/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1/src/gen/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/ApisApiService.java index 8d7b71fe1339..3dca596e45e6 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1/src/gen/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/ApisApiService.java +++ b/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1/src/gen/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/ApisApiService.java @@ -116,7 +116,7 @@ public interface ApisApiService { public Response getAPIRevisionDeployments(String apiId, MessageContext messageContext) throws APIManagementException; public Response getAPIRevisions(String apiId, String query, MessageContext messageContext) throws APIManagementException; public Response getAPISpecificOperationPolicyContentByPolicyId(String apiId, String operationPolicyId, MessageContext messageContext) throws APIManagementException; - public Response getAPISubscriptionPolicies(String apiId, String xWSO2Tenant, String ifNoneMatch, Boolean isAiApi, MessageContext messageContext) throws APIManagementException; + public Response getAPISubscriptionPolicies(String apiId, String xWSO2Tenant, String ifNoneMatch, Boolean isAiApi, String organizationID, MessageContext messageContext) throws APIManagementException; public Response getAPISwagger(String apiId, String ifNoneMatch, MessageContext messageContext) throws APIManagementException; public Response getAPIThumbnail(String apiId, String ifNoneMatch, MessageContext messageContext) throws APIManagementException; public Response getAllAPISpecificOperationPolicies(String apiId, Integer limit, Integer offset, String query, MessageContext messageContext) throws APIManagementException; diff --git a/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/impl/ApisApiServiceImpl.java b/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/impl/ApisApiServiceImpl.java index 7a262e761e26..51d7a50287bc 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/impl/ApisApiServiceImpl.java +++ b/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/impl/ApisApiServiceImpl.java @@ -3689,7 +3689,7 @@ public Response generateMockScripts(String apiId, String ifNoneMatch, MessageCon @Override public Response getAPISubscriptionPolicies(String apiId, String xWSO2Tenant, String ifNoneMatch, Boolean isAiApi, - MessageContext messageContext) throws APIManagementException { + String organizationID, MessageContext messageContext) throws APIManagementException { APIProvider apiProvider = RestApiCommonUtil.getLoggedInUserProvider(); String organization = RestApiUtil.getValidatedOrganization(messageContext); APIDTO apiInfo = getAPIByID(apiId, apiProvider, organization); @@ -3697,7 +3697,7 @@ public Response getAPISubscriptionPolicies(String apiId, String xWSO2Tenant, Str ThrottlingPolicyDTO.PolicyLevelEnum.SUBSCRIPTION.toString(), true, isAiApi); if (apiInfo != null) { - List apiPolicies = apiInfo.getPolicies(); + List apiPolicies = RestApiPublisherUtils.getSubscriptionPoliciesForOrganization(apiInfo, organizationID); List apiThrottlingPolicies = ApisApiServiceImplUtils.filterAPIThrottlingPolicies(apiPolicies, availableThrottlingPolicyList); return Response.ok().entity(apiThrottlingPolicies).build(); diff --git a/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/utils/RestApiPublisherUtils.java b/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/utils/RestApiPublisherUtils.java index cd7022132f65..470f7bba124c 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/utils/RestApiPublisherUtils.java +++ b/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/publisher/v1/utils/RestApiPublisherUtils.java @@ -45,6 +45,8 @@ import org.wso2.carbon.apimgt.rest.api.common.RestApiCommonUtil; import org.wso2.carbon.apimgt.rest.api.common.RestApiConstants; import org.wso2.carbon.apimgt.rest.api.publisher.v1.common.mappings.PublisherCommonUtils; +import org.wso2.carbon.apimgt.rest.api.publisher.v1.dto.APIDTO; +import org.wso2.carbon.apimgt.rest.api.publisher.v1.dto.OrganizationPoliciesDTO; import org.wso2.carbon.apimgt.rest.api.util.utils.RestApiUtil; import org.xml.sax.InputSource; import org.xml.sax.SAXException; @@ -57,6 +59,8 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; import java.util.Map; public class RestApiPublisherUtils { @@ -470,4 +474,28 @@ private static Path resolveFilePath(final String baseDirPathString, return resolvedPath; } + + /** + * Fetches subscription policies for the relevant organization ID. + * @param apiInfo APIDTO object + * @param organizationID Organziation ID + * @return List of subscription policies + */ + public static List getSubscriptionPoliciesForOrganization(APIDTO apiInfo, String organizationID) { + + if (organizationID == null) { + return apiInfo.getPolicies(); + } + List policies = new ArrayList<>(); + List organizationPoliciesDTOs = apiInfo.getOrganizationPolicies(); + if (organizationPoliciesDTOs != null && !organizationPoliciesDTOs.isEmpty()) { + for (OrganizationPoliciesDTO organizationPoliciesDTO : organizationPoliciesDTOs) { + if (StringUtils.equals(organizationID, organizationPoliciesDTO.getOrganizationID())) { + policies = organizationPoliciesDTO.getPolicies(); + break; + } + } + } + return policies; + } } diff --git a/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1/src/main/resources/publisher-api.yaml b/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1/src/main/resources/publisher-api.yaml index 7f36e216fb2f..c8a77b2c0fea 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1/src/main/resources/publisher-api.yaml +++ b/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1/src/main/resources/publisher-api.yaml @@ -1294,6 +1294,7 @@ paths: - $ref: '#/components/parameters/requestedTenant' - $ref: '#/components/parameters/If-None-Match' - $ref: '#/components/parameters/isAiApi' + - $ref: '#/components/parameters/organizationID' responses: 200: description: | @@ -14311,6 +14312,13 @@ components: schema: type: boolean default: false + organizationID: + name: organizationID + in: query + description: | + Indicates the organization ID + schema: + type: string offset: name: offset in: query From 7ef005e9e6b0e35a70d74b3737d11c191ea73dba Mon Sep 17 00:00:00 2001 From: Savindu Dimal Date: Thu, 30 Jan 2025 19:36:42 +0530 Subject: [PATCH 3/6] Add devportal related backend changes for organization based subscriptions --- .../carbon/apimgt/impl/APIConsumerImpl.java | 38 +++++++++---- .../carbon/apimgt/impl/utils/APIUtil.java | 18 +++++++ .../persistence/RegistryPersistenceImpl.java | 3 ++ .../persistence/dto/DevPortalAPIInfo.java | 53 +++++++++++++++++++ .../persistence/dto/OrganizationTiers.java | 4 ++ .../api/store/v1/impl/ApisApiServiceImpl.java | 5 ++ .../v1/impl/SubscriptionsApiServiceImpl.java | 19 +++++++ .../rest/api/store/v1/utils/APIUtils.java | 19 +++++++ 8 files changed, 150 insertions(+), 9 deletions(-) diff --git a/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/APIConsumerImpl.java b/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/APIConsumerImpl.java index c49a699300d0..dd7383590c54 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/APIConsumerImpl.java +++ b/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/APIConsumerImpl.java @@ -77,6 +77,7 @@ import org.wso2.carbon.apimgt.api.model.OAuthAppRequest; import org.wso2.carbon.apimgt.api.model.OAuthApplicationInfo; import org.wso2.carbon.apimgt.api.model.OrganizationInfo; +import org.wso2.carbon.apimgt.api.model.OrganizationTiers; import org.wso2.carbon.apimgt.api.model.ResourceFile; import org.wso2.carbon.apimgt.api.model.Scope; import org.wso2.carbon.apimgt.api.model.SubscribedAPI; @@ -3854,7 +3855,7 @@ public Map searchPaginatedAPIs(String searchQuery, String organi Map properties = APIUtil.getUserProperties(userName); UserContext userCtx = new UserContext(userNameWithoutChange, org, properties, roles); - return searchPaginatedAPIs(searchQuery, start, end, org, userCtx); + return searchPaginatedAPIs(searchQuery, start, end, org, userCtx, null); } @Override @@ -3867,15 +3868,19 @@ public Map searchPaginatedAPIs(String searchQuery, OrganizationI UserContext userCtx = new UserContext(userNameWithoutChange, new Organization(organizationInfo.getOrganizationId()), properties, roles); - return searchPaginatedAPIs(searchQuery, start, end, org, userCtx); + return searchPaginatedAPIs(searchQuery, start, end, org, userCtx, organizationInfo); } private Map searchPaginatedAPIs(String searchQuery, int start, int end, Organization org, - UserContext userCtx) throws APIManagementException { + UserContext userCtx, OrganizationInfo orgInfo) throws APIManagementException { Map result = new HashMap(); if (log.isDebugEnabled()) { log.debug("Original search query received : " + searchQuery); } + String organizationID = null; + if (orgInfo != null) { + organizationID = orgInfo.getOrganizationId(); + } try { DevPortalAPISearchResult searchAPIs = apiPersistenceInstance.searchAPIsForDevPortal(org, searchQuery, @@ -3889,6 +3894,7 @@ private Map searchPaginatedAPIs(String searchQuery, int start, i List apiList = new ArrayList<>(); for (DevPortalAPIInfo devPortalAPIInfo : list) { API mappedAPI = APIMapper.INSTANCE.toApi(devPortalAPIInfo); + APIUtil.updateAvailableTiersByOrganization(devPortalAPIInfo, organizationID); try { mappedAPI.setRating(APIUtil.getAverageRating(mappedAPI.getUuid())); Set tierNameSet = devPortalAPIInfo.getAvailableTierNames(); @@ -4034,22 +4040,36 @@ private API addTiersToAPI(API api, String organization) throws APIManagementExce int tenantId = APIUtil.getInternalIdFromTenantDomainOrOrganization(organization); Set tierNames = api.getAvailableTiers(); Map definedTiers = APIUtil.getTiers(tenantId); - - Set availableTiers = new HashSet(); Set deniedTiers = getDeniedTiers(tenantId); + Set availableTiers = getAvailableTiers(tierNames, deniedTiers, definedTiers); + api.removeAllTiers(); + api.addAvailableTiers(availableTiers); + if (APIUtil.isOrganizationAccessControlEnabled() && api.getAvailableTiersForOrganizations() != null) { + Set organizationTiersSet = api.getAvailableTiersForOrganizations(); + for (OrganizationTiers organizationTiers : organizationTiersSet) { + Set tierNamesForOrganization = organizationTiers.getTiers(); + Set availableTiersForOrganization = getAvailableTiers(tierNamesForOrganization, deniedTiers, definedTiers); + organizationTiers.removeAllTiers(); + organizationTiers.setTiers(availableTiersForOrganization); + } + } + return api; + } + + private Set getAvailableTiers(Set tierNames, Set deniedTiers, Map definedTiers) { + + Set availableTiers = new HashSet(); for (Tier tierName : tierNames) { Tier definedTier = definedTiers.get(tierName.getName()); if (definedTier != null && (!deniedTiers.contains(definedTier.getName()))) { availableTiers.add(definedTier); } else { - log.warn("Unknown tier: " + tierName + " found on API: "); + log.warn("Unknown tier: " + tierName + " found"); } } availableTiers.removeIf(tier -> deniedTiers.contains(tier.getName())); - api.removeAllTiers(); - api.addAvailableTiers(availableTiers); - return api; + return availableTiers; } private APIProduct addTiersToAPI(APIProduct apiProduct, String organization) throws APIManagementException { diff --git a/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/utils/APIUtil.java b/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/utils/APIUtil.java index 0cd3133a15db..ecc045df1305 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/utils/APIUtil.java +++ b/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/utils/APIUtil.java @@ -182,6 +182,8 @@ import org.wso2.carbon.apimgt.impl.notifier.exceptions.NotifierException; import org.wso2.carbon.apimgt.impl.recommendationmgt.RecommendationEnvironment; import org.wso2.carbon.apimgt.impl.resolver.OnPremResolver; +import org.wso2.carbon.apimgt.persistence.dto.DevPortalAPIInfo; +import org.wso2.carbon.apimgt.persistence.dto.OrganizationTiers; import org.wso2.carbon.base.MultitenantConstants; import org.wso2.carbon.base.ServerConfiguration; import org.wso2.carbon.context.CarbonContext; @@ -3596,6 +3598,22 @@ public static float getAverageRating(int apiId) throws APIManagementException { return ApiMgtDAO.getInstance().getAverageRating(apiId); } + public static void updateAvailableTiersByOrganization(DevPortalAPIInfo devPortalAPIInfo, String organization) { + + Set availableTiers = devPortalAPIInfo.getAvailableTierNames(); + Set availableTiersForOrganizations = devPortalAPIInfo.getAvailableTiersForOrganizations(); + if (organization != null) { + for (OrganizationTiers organizationTiers : availableTiersForOrganizations) { + String orgName = organizationTiers.getOrganizationID(); + if (organization.equals(orgName)) { + availableTiers = organizationTiers.getTiers(); + break; + } + } + } + devPortalAPIInfo.setAvailableTierNames(availableTiers); + } + public static List getAllTenantsWithSuperTenant() throws UserStoreException { Tenant[] tenants = ServiceReferenceHolder.getInstance().getRealmService().getTenantManager().getAllTenants(); diff --git a/components/apimgt/org.wso2.carbon.apimgt.persistence/src/main/java/org/wso2/carbon/apimgt/persistence/RegistryPersistenceImpl.java b/components/apimgt/org.wso2.carbon.apimgt.persistence/src/main/java/org/wso2/carbon/apimgt/persistence/RegistryPersistenceImpl.java index 77fe3dc2c40b..77c057fd577f 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.persistence/src/main/java/org/wso2/carbon/apimgt/persistence/RegistryPersistenceImpl.java +++ b/components/apimgt/org.wso2.carbon.apimgt.persistence/src/main/java/org/wso2/carbon/apimgt/persistence/RegistryPersistenceImpl.java @@ -1292,6 +1292,9 @@ private DevPortalAPISearchResult searchPaginatedDevPortalAPIs(Registry userRegis } } apiInfo.setAvailableTierNames(availableTiers); + // Set available tiers for organizations + String organizationTiers = artifact.getAttribute(APIConstants.API_OVERVIEW_ORGANIZATION_TIERS); + apiInfo.setAvailableTiersForOrganizationsFromString(organizationTiers); apiInfo.setSubscriptionAvailability( artifact.getAttribute(APIConstants.API_OVERVIEW_SUBSCRIPTION_AVAILABILITY)); apiInfo.setSubscriptionAvailableOrgs( diff --git a/components/apimgt/org.wso2.carbon.apimgt.persistence/src/main/java/org/wso2/carbon/apimgt/persistence/dto/DevPortalAPIInfo.java b/components/apimgt/org.wso2.carbon.apimgt.persistence/src/main/java/org/wso2/carbon/apimgt/persistence/dto/DevPortalAPIInfo.java index 4b0c8fd2ad9c..aface03db92d 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.persistence/src/main/java/org/wso2/carbon/apimgt/persistence/dto/DevPortalAPIInfo.java +++ b/components/apimgt/org.wso2.carbon.apimgt.persistence/src/main/java/org/wso2/carbon/apimgt/persistence/dto/DevPortalAPIInfo.java @@ -15,6 +15,14 @@ */ package org.wso2.carbon.apimgt.persistence.dto; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.apimgt.api.model.API; +import org.wso2.carbon.apimgt.api.model.Tier; + +import java.util.Arrays; +import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; @@ -24,6 +32,7 @@ * APIs in DevPortal which are stored in the persistence layer are included in this. */ public class DevPortalAPIInfo { + private static final Log log = LogFactory.getLog(DevPortalAPIInfo.class); private String id; private String apiName; private String version; @@ -34,6 +43,7 @@ public class DevPortalAPIInfo { private String businessOwner; private String status; private Set availableTierNames; + private Set availableTiersForOrganizations = new LinkedHashSet<>();; private String subscriptionAvailability; private String subscriptionAvailableOrgs; private String createdTime; @@ -131,6 +141,49 @@ public Set getAvailableTierNames() { public void setAvailableTierNames(Set availableTierNames) { this.availableTierNames = availableTierNames; } + + public Set getAvailableTiersForOrganizations() { + return availableTiersForOrganizations; + } + + public void setAvailableTiersForOrganizations(Set availableTiersForOrganizations) { + this.availableTiersForOrganizations = availableTiersForOrganizations; + } + + public void setAvailableTiersForOrganizationsFromString(String tiersString) { + + if (tiersString == null || tiersString.isEmpty()) { + return; + } + try { + ObjectMapper objectMapper = new ObjectMapper(); + org.wso2.carbon.apimgt.api.model.OrganizationTiers[] tiersArray = objectMapper.readValue(tiersString, + org.wso2.carbon.apimgt.api.model.OrganizationTiers[].class); + for (org.wso2.carbon.apimgt.api.model.OrganizationTiers organizationTiersToMap : tiersArray) { + OrganizationTiers organizationTiers = getOrganizationTiers(organizationTiersToMap); + availableTiersForOrganizations.add(organizationTiers); + } + } catch (Exception e) { + log.error("Error while converting string to availableTiersForOrganizations object for API : " + getId(), + e); + } + } + + private static OrganizationTiers getOrganizationTiers( + org.wso2.carbon.apimgt.api.model.OrganizationTiers organizationTiersToMap) { + + OrganizationTiers organizationTiers = new OrganizationTiers(); + organizationTiers.setOrganizationID(organizationTiersToMap.getOrganizationID()); + organizationTiers.setOrganizationName(organizationTiersToMap.getOrganizationName()); + Set tiersToMap = organizationTiersToMap.getTiers(); + Set tiers = new LinkedHashSet<>(); + for (Tier tierToMap : tiersToMap) { + tiers.add(tierToMap.getName()); + } + organizationTiers.setTiers(tiers); + return organizationTiers; + } + public String getSubscriptionAvailableOrgs() { return subscriptionAvailableOrgs; } diff --git a/components/apimgt/org.wso2.carbon.apimgt.persistence/src/main/java/org/wso2/carbon/apimgt/persistence/dto/OrganizationTiers.java b/components/apimgt/org.wso2.carbon.apimgt.persistence/src/main/java/org/wso2/carbon/apimgt/persistence/dto/OrganizationTiers.java index cba7452931b7..629b440c501d 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.persistence/src/main/java/org/wso2/carbon/apimgt/persistence/dto/OrganizationTiers.java +++ b/components/apimgt/org.wso2.carbon.apimgt.persistence/src/main/java/org/wso2/carbon/apimgt/persistence/dto/OrganizationTiers.java @@ -47,4 +47,8 @@ public Set getTiers() { public void setTiers(Set tiers) { this.tiers = tiers; } + + public void removeAllTiers() { + tiers.clear(); + } } diff --git a/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/store/v1/impl/ApisApiServiceImpl.java b/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/store/v1/impl/ApisApiServiceImpl.java index 3901ac862886..6a652e0e8556 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/store/v1/impl/ApisApiServiceImpl.java +++ b/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/store/v1/impl/ApisApiServiceImpl.java @@ -1187,6 +1187,11 @@ private APIDTO getAPIByAPIId(String apiId, String organization, OrganizationInfo RestApiUtil.handleAuthorizationFailure(RestApiConstants.RESOURCE_API, apiId, log); } + if (!api.isAPIProduct()) { + org.wso2.carbon.apimgt.rest.api.store.v1.utils.APIUtils + .updateAvailableTiersByOrganization(api.getApi(), userOrg); + } + // Extracting clicked API name by the user, for the recommendation system apiConsumer.publishClickedAPI(api, userName, organization); diff --git a/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/store/v1/impl/SubscriptionsApiServiceImpl.java b/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/store/v1/impl/SubscriptionsApiServiceImpl.java index 2ff61d2bfc26..128e95344db1 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/store/v1/impl/SubscriptionsApiServiceImpl.java +++ b/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/store/v1/impl/SubscriptionsApiServiceImpl.java @@ -36,6 +36,7 @@ import org.wso2.carbon.apimgt.api.model.ApiTypeWrapper; import org.wso2.carbon.apimgt.api.model.Application; import org.wso2.carbon.apimgt.api.model.Monetization; +import org.wso2.carbon.apimgt.api.model.OrganizationInfo; import org.wso2.carbon.apimgt.api.model.SubscribedAPI; import org.wso2.carbon.apimgt.api.model.Subscriber; import org.wso2.carbon.apimgt.api.model.SubscriptionResponse; @@ -230,6 +231,12 @@ public Response subscriptionsPost(SubscriptionDTO body, String xWSO2Tenant, Mess ApiTypeWrapper apiTypeWrapper = apiConsumer.getAPIorAPIProductByUUID(body.getApiId(), organization); + OrganizationInfo userOrgInfo = RestApiUtil.getOrganizationInfo(messageContext); + userOrgInfo.setSuperOrganization(organization); + if (!apiTypeWrapper.isAPIProduct()) { + org.wso2.carbon.apimgt.rest.api.store.v1.utils.APIUtils + .updateAvailableTiersByOrganization(apiTypeWrapper.getApi(), userOrgInfo.getOrganizationId()); + } apiTypeWrapper.setTier(body.getThrottlingPolicy()); @@ -342,6 +349,12 @@ public Response subscriptionsSubscriptionIdPut(String subscriptionId, Subscripti ApiTypeWrapper apiTypeWrapper = apiConsumer.getAPIorAPIProductByUUID(body.getApiId(), organization); + OrganizationInfo userOrgInfo = RestApiUtil.getOrganizationInfo(messageContext); + userOrgInfo.setSuperOrganization(organization); + if (!apiTypeWrapper.isAPIProduct()) { + org.wso2.carbon.apimgt.rest.api.store.v1.utils.APIUtils + .updateAvailableTiersByOrganization(apiTypeWrapper.getApi(), userOrgInfo.getOrganizationId()); + } apiTypeWrapper.setTier(body.getThrottlingPolicy()); @@ -427,6 +440,12 @@ public Response subscriptionsMultiplePost(List body, String xWS ApiTypeWrapper apiTypeWrapper = apiConsumer.getAPIorAPIProductByUUID(subscriptionDTO.getApiId(), organization); + OrganizationInfo userOrgInfo = RestApiUtil.getOrganizationInfo(messageContext); + userOrgInfo.setSuperOrganization(organization); + if (!apiTypeWrapper.isAPIProduct()) { + org.wso2.carbon.apimgt.rest.api.store.v1.utils.APIUtils.updateAvailableTiersByOrganization( + apiTypeWrapper.getApi(), userOrgInfo.getOrganizationId()); + } apiTypeWrapper.setTier(subscriptionDTO.getThrottlingPolicy()); SubscriptionResponse subscriptionResponse = apiConsumer .addSubscription(apiTypeWrapper, username, application); diff --git a/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/store/v1/utils/APIUtils.java b/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/store/v1/utils/APIUtils.java index 09b5f28fc1d5..b7054d018495 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/store/v1/utils/APIUtils.java +++ b/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/store/v1/utils/APIUtils.java @@ -23,6 +23,8 @@ import org.wso2.carbon.apimgt.api.dto.KeyManagerConfigurationDTO; import org.wso2.carbon.apimgt.api.model.API; import org.wso2.carbon.apimgt.api.model.APIProduct; +import org.wso2.carbon.apimgt.api.model.OrganizationTiers; +import org.wso2.carbon.apimgt.api.model.Tier; import org.wso2.carbon.apimgt.impl.APIConstants; import org.wso2.carbon.apimgt.api.model.Environment; import org.wso2.carbon.apimgt.api.model.OrganizationInfo; @@ -218,4 +220,21 @@ public static List filterAllowedKeyManagersForOrgani return allowedList; } + +public static void updateAvailableTiersByOrganization(API api, String organization) { + + Set availableTiers = api.getAvailableTiers(); + Set availableTiersForOrganizations = api.getAvailableTiersForOrganizations(); + if (organization != null) { + for (OrganizationTiers organizationTiers : availableTiersForOrganizations) { + String orgName = organizationTiers.getOrganizationID(); + if (organization.equals(orgName)) { + availableTiers = organizationTiers.getTiers(); + break; + } + } + } + api.removeAllTiers(); + api.setAvailableTiers(availableTiers); +} } From 8046a925254ad9b8de7b7c2831103c9171131141 Mon Sep 17 00:00:00 2001 From: Savindu Dimal Date: Thu, 30 Jan 2025 21:59:45 +0530 Subject: [PATCH 4/6] Fix unit test failures --- .../src/main/java/org/wso2/carbon/apimgt/api/model/API.java | 2 +- .../src/test/java/APIMappingUtilTest.java | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/components/apimgt/org.wso2.carbon.apimgt.api/src/main/java/org/wso2/carbon/apimgt/api/model/API.java b/components/apimgt/org.wso2.carbon.apimgt.api/src/main/java/org/wso2/carbon/apimgt/api/model/API.java index 9997a8a91f6e..bc2d8acc6173 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.api/src/main/java/org/wso2/carbon/apimgt/api/model/API.java +++ b/components/apimgt/org.wso2.carbon.apimgt.api/src/main/java/org/wso2/carbon/apimgt/api/model/API.java @@ -769,7 +769,7 @@ public void removeAllPolicies() { } public Set getAvailableTiersForOrganizations() { - return Collections.unmodifiableSet(availableTiersForOrganizations); + return availableTiersForOrganizations; } public void setAvailableTiersForOrganizations(Set availableTiersForOrganizations) { diff --git a/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1.common/src/test/java/APIMappingUtilTest.java b/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1.common/src/test/java/APIMappingUtilTest.java index a249ab3ba57a..4bc2affe0974 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1.common/src/test/java/APIMappingUtilTest.java +++ b/components/apimgt/org.wso2.carbon.apimgt.rest.api.publisher.v1.common/src/test/java/APIMappingUtilTest.java @@ -30,6 +30,7 @@ import org.wso2.carbon.apimgt.impl.APIManagerConfiguration; import org.wso2.carbon.apimgt.impl.APIManagerConfigurationService; import org.wso2.carbon.apimgt.impl.APIManagerConfigurationServiceImpl; +import org.wso2.carbon.apimgt.impl.dto.OrgAccessControl; import org.wso2.carbon.apimgt.impl.internal.ServiceReferenceHolder; import org.wso2.carbon.apimgt.rest.api.publisher.v1.common.mappings.APIMappingUtil; import org.wso2.carbon.apimgt.rest.api.publisher.v1.dto.APIDTO; @@ -74,6 +75,7 @@ public void setup() { apidto.setOperations(operationList); apidto.setLifeCycleStatus(APIConstants.CREATED); config = Mockito.mock(APIManagerConfiguration.class); + OrgAccessControl orgAccessControl = Mockito.mock(OrgAccessControl.class); APIManagerConfigurationService apiManagerConfigurationService = new APIManagerConfigurationServiceImpl(config); ServiceReferenceHolder.getInstance().setAPIManagerConfigurationService(apiManagerConfigurationService); APIManagerConfiguration config = ServiceReferenceHolder.getInstance().getAPIManagerConfigurationService() @@ -84,6 +86,8 @@ public void setup() { .thenReturn(ALLOW_METHOD); Mockito.when(config.getFirstProperty(APIConstants.CORS_CONFIGURATION_ACCESS_CTL_ALLOW_ORIGIN)) .thenReturn(ALLOW_ORIGIN); + Mockito.when(config.getOrgAccessControl()).thenReturn(orgAccessControl); + Mockito.when(orgAccessControl.isEnabled()).thenReturn(false); } @Test From 2641b031ffb5852d45020b86d1b68b01960fa213 Mon Sep 17 00:00:00 2001 From: Savindu Dimal Date: Thu, 30 Jan 2025 22:21:38 +0530 Subject: [PATCH 5/6] Refactor code --- .../apimgt/api/model/OrganizationTiers.java | 2 +- .../carbon/apimgt/impl/APIConsumerImpl.java | 3 +- .../carbon/apimgt/impl/utils/APIUtil.java | 5 +++ .../persistence/dto/DevPortalAPIInfo.java | 2 -- .../rest/api/store/v1/utils/APIUtils.java | 31 +++++++++++-------- 5 files changed, 26 insertions(+), 17 deletions(-) diff --git a/components/apimgt/org.wso2.carbon.apimgt.api/src/main/java/org/wso2/carbon/apimgt/api/model/OrganizationTiers.java b/components/apimgt/org.wso2.carbon.apimgt.api/src/main/java/org/wso2/carbon/apimgt/api/model/OrganizationTiers.java index e280c330e569..fd602604eb47 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.api/src/main/java/org/wso2/carbon/apimgt/api/model/OrganizationTiers.java +++ b/components/apimgt/org.wso2.carbon.apimgt.api/src/main/java/org/wso2/carbon/apimgt/api/model/OrganizationTiers.java @@ -20,7 +20,7 @@ import java.util.Set; /** - * This class represent the Tier + * This class represent Organization Tiers */ public class OrganizationTiers { diff --git a/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/APIConsumerImpl.java b/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/APIConsumerImpl.java index dd7383590c54..7e94b505fd8a 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/APIConsumerImpl.java +++ b/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/APIConsumerImpl.java @@ -4049,7 +4049,8 @@ private API addTiersToAPI(API api, String organization) throws APIManagementExce Set organizationTiersSet = api.getAvailableTiersForOrganizations(); for (OrganizationTiers organizationTiers : organizationTiersSet) { Set tierNamesForOrganization = organizationTiers.getTiers(); - Set availableTiersForOrganization = getAvailableTiers(tierNamesForOrganization, deniedTiers, definedTiers); + Set availableTiersForOrganization = getAvailableTiers(tierNamesForOrganization, deniedTiers, + definedTiers); organizationTiers.removeAllTiers(); organizationTiers.setTiers(availableTiersForOrganization); } diff --git a/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/utils/APIUtil.java b/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/utils/APIUtil.java index ecc045df1305..386e5ded55e3 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/utils/APIUtil.java +++ b/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/utils/APIUtil.java @@ -3598,6 +3598,11 @@ public static float getAverageRating(int apiId) throws APIManagementException { return ApiMgtDAO.getInstance().getAverageRating(apiId); } + /** + * Update available tiers in the DevPortalAPIInfo according to the organization. + * @param devPortalAPIInfo DevPortalAPIInfo object + * @param organization Organization ID + */ public static void updateAvailableTiersByOrganization(DevPortalAPIInfo devPortalAPIInfo, String organization) { Set availableTiers = devPortalAPIInfo.getAvailableTierNames(); diff --git a/components/apimgt/org.wso2.carbon.apimgt.persistence/src/main/java/org/wso2/carbon/apimgt/persistence/dto/DevPortalAPIInfo.java b/components/apimgt/org.wso2.carbon.apimgt.persistence/src/main/java/org/wso2/carbon/apimgt/persistence/dto/DevPortalAPIInfo.java index aface03db92d..aab9f6562622 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.persistence/src/main/java/org/wso2/carbon/apimgt/persistence/dto/DevPortalAPIInfo.java +++ b/components/apimgt/org.wso2.carbon.apimgt.persistence/src/main/java/org/wso2/carbon/apimgt/persistence/dto/DevPortalAPIInfo.java @@ -18,10 +18,8 @@ import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.wso2.carbon.apimgt.api.model.API; import org.wso2.carbon.apimgt.api.model.Tier; -import java.util.Arrays; import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; diff --git a/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/store/v1/utils/APIUtils.java b/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/store/v1/utils/APIUtils.java index b7054d018495..728ec19853f8 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/store/v1/utils/APIUtils.java +++ b/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/store/v1/utils/APIUtils.java @@ -221,20 +221,25 @@ public static List filterAllowedKeyManagersForOrgani return allowedList; } -public static void updateAvailableTiersByOrganization(API api, String organization) { - - Set availableTiers = api.getAvailableTiers(); - Set availableTiersForOrganizations = api.getAvailableTiersForOrganizations(); - if (organization != null) { - for (OrganizationTiers organizationTiers : availableTiersForOrganizations) { - String orgName = organizationTiers.getOrganizationID(); - if (organization.equals(orgName)) { - availableTiers = organizationTiers.getTiers(); - break; + /** + * Update available tiers in the API according to the organization. + * @param api API object + * @param organization Organization name + */ + public static void updateAvailableTiersByOrganization(API api, String organization) { + + Set availableTiers = api.getAvailableTiers(); + Set availableTiersForOrganizations = api.getAvailableTiersForOrganizations(); + if (organization != null) { + for (OrganizationTiers organizationTiers : availableTiersForOrganizations) { + String orgName = organizationTiers.getOrganizationID(); + if (organization.equals(orgName)) { + availableTiers = organizationTiers.getTiers(); + break; + } } } + api.removeAllTiers(); + api.setAvailableTiers(availableTiers); } - api.removeAllTiers(); - api.setAvailableTiers(availableTiers); -} } From 962cb0f80d521c9c7d00781d737f82dbc0a014d0 Mon Sep 17 00:00:00 2001 From: Savindu Dimal Date: Fri, 31 Jan 2025 17:50:43 +0530 Subject: [PATCH 6/6] Get organization ID from external ID when creating a subscription --- .../wso2/carbon/apimgt/impl/APIConsumerImpl.java | 3 ++- .../org/wso2/carbon/apimgt/impl/utils/APIUtil.java | 4 ++-- .../rest/api/store/v1/impl/ApisApiServiceImpl.java | 6 ++++-- .../store/v1/impl/SubscriptionsApiServiceImpl.java | 13 ++++++++++--- 4 files changed, 18 insertions(+), 8 deletions(-) diff --git a/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/APIConsumerImpl.java b/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/APIConsumerImpl.java index 7e94b505fd8a..038f5e1bae45 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/APIConsumerImpl.java +++ b/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/APIConsumerImpl.java @@ -3879,7 +3879,8 @@ private Map searchPaginatedAPIs(String searchQuery, int start, i } String organizationID = null; if (orgInfo != null) { - organizationID = orgInfo.getOrganizationId(); + organizationID = APIUtil.getOrganizationIdFromExternalReference(orgInfo.getOrganizationId(), + orgInfo.getName(), tenantDomain); } try { diff --git a/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/utils/APIUtil.java b/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/utils/APIUtil.java index 386e5ded55e3..9bf536e26e80 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/utils/APIUtil.java +++ b/components/apimgt/org.wso2.carbon.apimgt.impl/src/main/java/org/wso2/carbon/apimgt/impl/utils/APIUtil.java @@ -3609,8 +3609,8 @@ public static void updateAvailableTiersByOrganization(DevPortalAPIInfo devPortal Set availableTiersForOrganizations = devPortalAPIInfo.getAvailableTiersForOrganizations(); if (organization != null) { for (OrganizationTiers organizationTiers : availableTiersForOrganizations) { - String orgName = organizationTiers.getOrganizationID(); - if (organization.equals(orgName)) { + String orgID = organizationTiers.getOrganizationID(); + if (organization.equals(orgID)) { availableTiers = organizationTiers.getTiers(); break; } diff --git a/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/store/v1/impl/ApisApiServiceImpl.java b/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/store/v1/impl/ApisApiServiceImpl.java index 6a652e0e8556..b7a103a7ca01 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/store/v1/impl/ApisApiServiceImpl.java +++ b/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/store/v1/impl/ApisApiServiceImpl.java @@ -1188,8 +1188,10 @@ private APIDTO getAPIByAPIId(String apiId, String organization, OrganizationInfo } if (!api.isAPIProduct()) { - org.wso2.carbon.apimgt.rest.api.store.v1.utils.APIUtils - .updateAvailableTiersByOrganization(api.getApi(), userOrg); + String organizationID = APIUtil.getOrganizationIdFromExternalReference(userOrgInfo.getOrganizationId(), + userOrgInfo.getName(), organization); + org.wso2.carbon.apimgt.rest.api.store.v1.utils.APIUtils.updateAvailableTiersByOrganization( + api.getApi(), organizationID); } // Extracting clicked API name by the user, for the recommendation system diff --git a/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/store/v1/impl/SubscriptionsApiServiceImpl.java b/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/store/v1/impl/SubscriptionsApiServiceImpl.java index 128e95344db1..fd7fd225e1ae 100644 --- a/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/store/v1/impl/SubscriptionsApiServiceImpl.java +++ b/components/apimgt/org.wso2.carbon.apimgt.rest.api.store.v1/src/main/java/org/wso2/carbon/apimgt/rest/api/store/v1/impl/SubscriptionsApiServiceImpl.java @@ -41,6 +41,7 @@ import org.wso2.carbon.apimgt.api.model.Subscriber; import org.wso2.carbon.apimgt.api.model.SubscriptionResponse; import org.wso2.carbon.apimgt.impl.APIConstants; +import org.wso2.carbon.apimgt.impl.utils.APIUtil; import org.wso2.carbon.apimgt.impl.workflow.HttpWorkflowResponse; import org.wso2.carbon.apimgt.rest.api.common.RestApiCommonUtil; import org.wso2.carbon.apimgt.rest.api.common.RestApiConstants; @@ -234,8 +235,10 @@ public Response subscriptionsPost(SubscriptionDTO body, String xWSO2Tenant, Mess OrganizationInfo userOrgInfo = RestApiUtil.getOrganizationInfo(messageContext); userOrgInfo.setSuperOrganization(organization); if (!apiTypeWrapper.isAPIProduct()) { + String organizationID = APIUtil.getOrganizationIdFromExternalReference(userOrgInfo.getOrganizationId(), + userOrgInfo.getName(), organization); org.wso2.carbon.apimgt.rest.api.store.v1.utils.APIUtils - .updateAvailableTiersByOrganization(apiTypeWrapper.getApi(), userOrgInfo.getOrganizationId()); + .updateAvailableTiersByOrganization(apiTypeWrapper.getApi(), organizationID); } apiTypeWrapper.setTier(body.getThrottlingPolicy()); @@ -352,8 +355,10 @@ public Response subscriptionsSubscriptionIdPut(String subscriptionId, Subscripti OrganizationInfo userOrgInfo = RestApiUtil.getOrganizationInfo(messageContext); userOrgInfo.setSuperOrganization(organization); if (!apiTypeWrapper.isAPIProduct()) { + String organizationID = APIUtil.getOrganizationIdFromExternalReference(userOrgInfo.getOrganizationId(), + userOrgInfo.getName(), organization); org.wso2.carbon.apimgt.rest.api.store.v1.utils.APIUtils - .updateAvailableTiersByOrganization(apiTypeWrapper.getApi(), userOrgInfo.getOrganizationId()); + .updateAvailableTiersByOrganization(apiTypeWrapper.getApi(), organizationID); } apiTypeWrapper.setTier(body.getThrottlingPolicy()); @@ -443,8 +448,10 @@ public Response subscriptionsMultiplePost(List body, String xWS OrganizationInfo userOrgInfo = RestApiUtil.getOrganizationInfo(messageContext); userOrgInfo.setSuperOrganization(organization); if (!apiTypeWrapper.isAPIProduct()) { + String organizationID = APIUtil.getOrganizationIdFromExternalReference(userOrgInfo.getOrganizationId(), + userOrgInfo.getName(), organization); org.wso2.carbon.apimgt.rest.api.store.v1.utils.APIUtils.updateAvailableTiersByOrganization( - apiTypeWrapper.getApi(), userOrgInfo.getOrganizationId()); + apiTypeWrapper.getApi(), organizationID); } apiTypeWrapper.setTier(subscriptionDTO.getThrottlingPolicy()); SubscriptionResponse subscriptionResponse = apiConsumer