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

Add a new resource to get the overview markdown content of an API #12942

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
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 @@ -230,6 +230,17 @@ Documentation getDocumentation(String apiId, String docId, String organization)
DocumentationContent getDocumentationContent(String apiId, String docId, String organization)
throws APIManagementException;

/**
* Get the markdown overview documentation Content by api id
*
* @param apiId ID of the API
* @param organization Identifier of an organization
* @return DocumentationContent
* @throws APIManagementException if failed to get Documentation
*/
DocumentationContent getMarkdownOverviewContent(String apiId, String organization)
throws APIManagementException;

/**
* Returns the GraphqlComplexityInfo object for a given API ID
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,8 @@ public class API implements Serializable {
private String implementation = "ENDPOINT";

private String monetizationCategory;

private Boolean isMarkdownOverview;

private List<SOAPToRestSequence> soapToRestSequences;

Expand Down Expand Up @@ -1404,6 +1406,14 @@ public void setTestKey(String testKey) {
this.testKey = testKey;
}

public Boolean isMarkdownOverview() {
return isMarkdownOverview;
}

public void setMarkdownOverview(Boolean isMarkdownOverview) {
this.isMarkdownOverview = isMarkdownOverview;
}

/**
* This method returns endpoints according to the given endpoint config
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ public class APIProduct implements Serializable {
private JSONObject monetizationProperties = new JSONObject();
private boolean isMonetizationEnabled = false;
private String versionTimestamp;
private Boolean isMarkdownOverview;

/**
* API security at the gateway level.
Expand Down Expand Up @@ -716,4 +717,12 @@ public int isEgress() {
public void setEgress(int egress) {
isEgress = egress;
}

public Boolean isMarkdownOverview() {
return isMarkdownOverview;
}

public void setMarkdownOverview(Boolean isMarkdownOverview) {
this.isMarkdownOverview = isMarkdownOverview;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3960,6 +3960,14 @@ public ApiTypeWrapper getAPIorAPIProductByUUID(String uuid, String organization)
checkVisibilityPermission(userNameWithoutChange, devPortalApi.getVisibility(),
devPortalApi.getVisibleRoles(), devPortalApi.getPublisherAccessControl(),
devPortalApi.getPublisherAccessControlRoles());
List<Documentation> existingDocs = getAllDocumentation(uuid, organization);
devPortalApi.setMarkdownOverview(false);
for (Documentation doc : existingDocs) {
if (doc.getOtherTypeName() != null && doc.getOtherTypeName().equals("_overview")) {
devPortalApi.setMarkdownOverview(true);
break;
}
}
if (APIConstants.API_PRODUCT.equalsIgnoreCase(devPortalApi.getType())) {
APIProduct apiProduct = APIMapper.INSTANCE.toApiProduct(devPortalApi);
apiProduct.setID(new APIProductIdentifier(devPortalApi.getProviderName(),
Expand Down Expand Up @@ -4414,6 +4422,14 @@ public DocumentationContent getDocumentationContent(String apiId, String docId,
return super.getDocumentationContent(apiId, docId, organization);
}

@Override
public DocumentationContent getMarkdownOverviewContent(String apiId, String organization)
throws APIManagementException {

checkAPIVisibilityRestriction(apiId, organization);
return super.getMarkdownOverviewContent(apiId, organization);
}

@Override
public String getAsyncAPIDefinitionForLabel(Identifier apiId, String labelName) throws APIManagementException {
String updatedDefinition = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -498,6 +498,23 @@ public DocumentationContent getDocumentationContent(String apiId, String docId,
}
}

@Override
public DocumentationContent getMarkdownOverviewContent(String apiId, String organization)
throws APIManagementException {

try {
DocumentSearchResult result = apiPersistenceInstance
.searchDocumentation(new Organization(organization), apiId, 0, 0,
"other:_overview", null);
if (result == null || result.getDocumentationList() == null || result.getDocumentationList().isEmpty()) {
return null;
}
return getDocumentationContent(apiId, result.getDocumentationList().get(0).getId(), organization);
} catch (DocumentationPersistenceException e) {
throw new APIManagementException("Error while retrieving document content ", e);
}
}

public GraphqlComplexityInfo getComplexityDetails(String uuid) throws APIManagementException {

return apiMgtDAO.getComplexityDetails(uuid);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,24 +37,7 @@
import org.wso2.carbon.apimgt.api.ExceptionCodes;
import org.wso2.carbon.apimgt.api.WorkflowStatus;
import org.wso2.carbon.apimgt.api.dto.KeyManagerConfigurationDTO;
import org.wso2.carbon.apimgt.api.model.API;
import org.wso2.carbon.apimgt.api.model.APIIdentifier;
import org.wso2.carbon.apimgt.api.model.APIKey;
import org.wso2.carbon.apimgt.api.model.APIRating;
import org.wso2.carbon.apimgt.api.model.AccessTokenInfo;
import org.wso2.carbon.apimgt.api.model.AccessTokenRequest;
import org.wso2.carbon.apimgt.api.model.ApiTypeWrapper;
import org.wso2.carbon.apimgt.api.model.Application;
import org.wso2.carbon.apimgt.api.model.Comment;
import org.wso2.carbon.apimgt.api.model.KeyManager;
import org.wso2.carbon.apimgt.api.model.KeyManagerConfiguration;
import org.wso2.carbon.apimgt.api.model.OAuthAppRequest;
import org.wso2.carbon.apimgt.api.model.OAuthApplicationInfo;
import org.wso2.carbon.apimgt.api.model.Scope;
import org.wso2.carbon.apimgt.api.model.SubscribedAPI;
import org.wso2.carbon.apimgt.api.model.Subscriber;
import org.wso2.carbon.apimgt.api.model.SubscriptionResponse;
import org.wso2.carbon.apimgt.api.model.Tier;
import org.wso2.carbon.apimgt.api.model.*;
import org.wso2.carbon.apimgt.impl.dao.ApiMgtDAO;
import org.wso2.carbon.apimgt.impl.dto.SubscriptionWorkflowDTO;
import org.wso2.carbon.apimgt.impl.dto.WorkflowDTO;
Expand Down Expand Up @@ -1009,4 +992,21 @@ public void testGetApplicationKeys() throws APIManagementException {
assertEquals(apiKeySet.size(), 2);
assertNotNull(apiKeySet.iterator().next().getAccessToken());
}

@Test
public void testGetMarkdownOverviewContent() throws Exception {
String apiId = "api123";
String organization = "wso2";
DocumentationContent mockContent = new DocumentationContent();

APIConsumerImpl apiConsumer = PowerMockito.spy(new APIConsumerImplWrapper());

PowerMockito.doNothing().when(apiConsumer).checkAPIVisibilityRestriction(apiId, organization);
PowerMockito.doReturn(mockContent).when(apiConsumer, "getMarkdownOverviewContent", apiId, organization);

DocumentationContent result = apiConsumer.getMarkdownOverviewContent(apiId, organization);

Assert.assertNotNull(result);
Assert.assertEquals(mockContent, result);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,36 @@ public void testGetDocumentationFromId() throws Exception {

}

@Test
public void testGetMarkdownOverviewContent() throws Exception {
String apiId = "sample-api-id";
String organization = "sample-org";

AbstractAPIManager abstractAPIManager = new AbstractAPIManagerWrapper(apiPersistenceInstance);

PowerMockito.mockStatic(ServiceReferenceHolder.class);
ServiceReferenceHolder sh = Mockito.mock(ServiceReferenceHolder.class);
APIManagerConfigurationService amConfigService = Mockito.mock(APIManagerConfigurationService.class);
APIManagerConfiguration amConfig = Mockito.mock(APIManagerConfiguration.class);

PowerMockito.when(ServiceReferenceHolder.getInstance()).thenReturn(sh);
PowerMockito.when(sh.getAPIManagerConfigurationService()).thenReturn(amConfigService);
PowerMockito.when(amConfigService.getAPIManagerConfiguration()).thenReturn(amConfig);

DocumentSearchResult searchResult = Mockito.mock(DocumentSearchResult.class);
Mockito.when(searchResult.getDocumentationList()).thenReturn(Collections.emptyList());
Mockito.when(apiPersistenceInstance.searchDocumentation(
Mockito.any(Organization.class),
Mockito.eq(apiId),
Mockito.eq(0),
Mockito.eq(0),
Mockito.eq("other:_overview"),
Mockito.isNull()
)).thenReturn(searchResult);

DocumentationContent result = abstractAPIManager.getMarkdownOverviewContent(apiId, organization);
Assert.assertNull(result);
}

@Test
public void testIsContextExist() throws APIManagementException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2812,6 +2812,13 @@ public DocumentSearchResult searchDocumentation(Organization org, String apiId,
if (doc.getName().equalsIgnoreCase(requestedDocName)) {
documentationList.add(doc);
}
}
else if (searchQuery.toLowerCase().startsWith("other:")) {
String requestedDocOtherTypeName = searchQuery.split(":")[1];
if (doc.getOtherTypeName() != null
&& doc.getOtherTypeName().equalsIgnoreCase(requestedDocOtherTypeName)) {
documentationList.add(doc);
}
} else {
log.warn("Document search not implemented for the query " + searchQuery);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ public class DevPortalAPI extends DevPortalAPIInfo {
private String asyncTransportProtocols;
private String publisherAccessControl;
private String publisherAccessControlRoles;
private Boolean isMarkdownOverview;

public String getContextTemplate() {
return contextTemplate;
Expand Down Expand Up @@ -357,6 +358,14 @@ public void setAsyncTransportProtocols(String asyncTransportProtocols) {
this.asyncTransportProtocols = asyncTransportProtocols;
}

public Boolean isMarkdownOverview() {
return isMarkdownOverview;
}

public void setMarkdownOverview(Boolean isMarkdownOverview) {
this.isMarkdownOverview = isMarkdownOverview;
}

@Override
public String toString() {
return "DevPortalAPI [status=" + status + ", isDefaultVersion=" + isDefaultVersion + ", description="
Expand All @@ -375,7 +384,7 @@ public String toString() {
+ ", type=" + type + ", advertisedOnly=" + advertisedOnly + ", swaggerDefinition=" + swaggerDefinition
+ ", contextTemplate=" + contextTemplate + ", apiSecurity=" + apiSecurity + ", visibility=" + visibility
+ ", visibleRoles=" + visibleRoles + ", gatewayVendor=" + gatewayVendor + ", asyncTransportProtocols="
+ asyncTransportProtocols + "]";
+ asyncTransportProtocols + ", isMarkdownOverview=" + isMarkdownOverview + "]";
}

public String getApiSecurity() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -836,6 +836,60 @@ paths:
- lang: Curl
source: curl -k "https://localhost:9443/api/am/devportal/v3/apis/890a4f4d-09eb-4877-a323-57f6ce2ed79b/documents/0bcb7f05-599d-4e1a-adce-5cb89bfe58d5/content"

/apis/{apiId}/markdown-content:
get:
tags:
- APIs
summary: |
Get the Content of the API Overview Markdown Document
description: |
This operation can be used to retrieve the overview markdown document content of an API. You need to provide the Id of the API to retrieve it.

The content of the document will be retrieved in `text/plain` content type

`X-WSO2-Tenant` header can be used to retrieve the content of the overview markdown document of an API that belongs to a different tenant domain. If not specified super tenant will be used. If Authorization header is present in the request, the user's tenant associated with the access token will be used.

**NOTE:**
* This operation does not require an Authorization header by default. But in order to see a restricted API's overview markdown document content, you need to provide Authorization header.
operationId: getMarkdownContentOfAPI
parameters:
- $ref: '#/components/parameters/apiId'
- $ref: '#/components/parameters/requestedTenant'
- $ref: '#/components/parameters/If-None-Match'
responses:
200:
description: |
OK.
Inline content returned.
headers:
ETag:
description: |
Entity Tag of the response resource.
Used by caches, or in conditional requests.
schema:
type: string
Last-Modified:
description: |
Date and time the resource has been modified the last time.
Used by caches, or in conditional requests.
schema:
type: string
content: { }
304:
description: |
Not Modified.
Empty body because the client has already the latest version of the requested resource.
content: { }
404:
$ref: '#/components/responses/NotFound'
406:
$ref: '#/components/responses/NotAcceptable'
security:
- OAuth2Security: [ ]
x-code-samples:
- lang: Curl
source: curl -k "https://localhost:9443/api/am/devportal/v3/apis/890a4f4d-09eb-4877-a323-57f6ce2ed79b/markdown-content"

/apis/{apiId}/thumbnail:
get:
tags:
Expand Down Expand Up @@ -4079,6 +4133,9 @@ components:
description: |
If the provider value is not given user invoking the api will be used as the provider.
example: admin
isMarkdownOverview:
type: boolean
example: true
apiDefinition:
type: string
description: |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -479,6 +479,24 @@ public Response getAllCommentsOfAPI(@ApiParam(value = "**API ID** consisting of
return delegate.getCommentOfAPI(commentId, apiId, xWSO2Tenant, ifNoneMatch, includeCommenterInfo, replyLimit, replyOffset, securityContext);
}

@GET
@Path("/{apiId}/markdown-content")

@Produces({ "application/json" })
@ApiOperation(value = "Get the Content of the API Overview Markdown Document ", notes = "This operation can be used to retrieve the overview markdown document content of an API. You need to provide the Id of the API to retrieve it. The content of the document will be retrieved in `text/plain` content type `X-WSO2-Tenant` header can be used to retrieve the content of the overview markdown document of an API that belongs to a different tenant domain. If not specified super tenant will be used. If Authorization header is present in the request, the user's tenant associated with the access token will be used. **NOTE:** * This operation does not require an Authorization header by default. But in order to see a restricted API's overview markdown document content, you need to provide Authorization header. ", response = Void.class, authorizations = {
@Authorization(value = "OAuth2Security", scopes = {

})
}, tags={ "APIs", })
@ApiResponses(value = {
@ApiResponse(code = 200, message = "OK. Inline content returned. ", response = Void.class),
@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 getMarkdownContentOfAPI(@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 retrieved from. " )@HeaderParam("X-WSO2-Tenant") String xWSO2Tenant, @ApiParam(value = "Validator for conditional requests; based on the ETag of the formerly retrieved variant of the resourec. " )@HeaderParam("If-None-Match") String ifNoneMatch) throws APIManagementException{
return delegate.getMarkdownContentOfAPI(apiId, xWSO2Tenant, ifNoneMatch, securityContext);
}

@GET
@Path("/{apiId}/comments/{commentId}/replies")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ public interface ApisApiService {
public Response editCommentOfAPI(String commentId, String apiId, PatchRequestBodyDTO patchRequestBodyDTO, MessageContext messageContext) throws APIManagementException;
public Response getAllCommentsOfAPI(String apiId, String xWSO2Tenant, Integer limit, Integer offset, Boolean includeCommenterInfo, MessageContext messageContext) throws APIManagementException;
public Response getCommentOfAPI(String commentId, String apiId, String xWSO2Tenant, String ifNoneMatch, Boolean includeCommenterInfo, Integer replyLimit, Integer replyOffset, MessageContext messageContext) throws APIManagementException;
public Response getMarkdownContentOfAPI(String apiId, String xWSO2Tenant, String ifNoneMatch, MessageContext messageContext) throws APIManagementException;
public Response getRepliesOfComment(String commentId, String apiId, String xWSO2Tenant, Integer limit, Integer offset, String ifNoneMatch, Boolean includeCommenterInfo, MessageContext messageContext) throws APIManagementException;
public Response getWSDLOfAPI(String apiId, String environmentName, String ifNoneMatch, String xWSO2Tenant, MessageContext messageContext) throws APIManagementException;
}
Loading
Loading