Skip to content

Commit cc7ab94

Browse files
author
Chris Wiechmann
authored
Merge pull request #159 from Axway-API-Management-Plus/158-export-original-backend-basepath
Export original backend basepath
2 parents e39735b + aea8613 commit cc7ab94

12 files changed

Lines changed: 132 additions & 13 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
1111

1212
### Changed
1313
- Retry request at API access endpoint also for return code 404 (See issue [#157](https://github.com/Axway-API-Management-Plus/apim-cli/issues/157))
14+
- If the useFEAPIDefinition flag is set, then the exported API specification is updated with the backend API information (host, basePath, schemes). (See issue [#158](https://github.com/Axway-API-Management-Plus/apim-cli/issues/158))
1415

1516
### Added
1617
- Exported API-Manager settings now include Null-Values for not set properties (See issue [#150](https://github.com/Axway-API-Management-Plus/apim-cli/issues/150))

misc/documentation/Images.pptx

48.7 KB
Binary file not shown.

misc/images/Image-Sources.pptx

126 KB
Binary file not shown.
211 KB
Loading

modules/apim-adapter/src/main/java/com/axway/apim/adapter/apis/APIManagerAPIAdapter.java

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -485,7 +485,7 @@ private void addClientApplications(API api, APIFilter filter) throws AppExceptio
485485
}
486486
}
487487

488-
private static void addOriginalAPIDefinitionFromAPIM(API api, APIFilter filter) throws AppException {
488+
private void addOriginalAPIDefinitionFromAPIM(API api, APIFilter filter) throws AppException {
489489
if(!filter.isIncludeOriginalAPIDefinition()) return;
490490
URI uri;
491491
APISpecification apiDefinition;
@@ -507,6 +507,7 @@ private static void addOriginalAPIDefinitionFromAPIM(API api, APIFilter filter)
507507
origFilename = httpResponse.getHeaders("Content-Disposition")[0].getValue();
508508
}
509509
apiDefinition = APISpecificationFactory.getAPISpecification(res.getBytes(StandardCharsets.UTF_8), origFilename.substring(origFilename.indexOf("filename=")+9), api.getName(), filter.isFailOnError());
510+
addBackendResourcePath(api, apiDefinition, filter.isUseFEAPIDefinition());
510511
api.setApiDefinition(apiDefinition);
511512
} catch (Exception e) {
512513
throw new AppException("Cannot parse API-Definition for API: '" + api.getName() + "' ("+api.getVersion()+") on path: '"+api.getPath()+"'", ErrorCode.CANT_READ_API_DEFINITION_FILE, e);
@@ -518,6 +519,53 @@ private static void addOriginalAPIDefinitionFromAPIM(API api, APIFilter filter)
518519
}
519520
}
520521

522+
private void addBackendResourcePath(API api, APISpecification apiDefinition, boolean exportFEAPIDefinition) throws AppException {
523+
URI uri;
524+
HttpResponse httpResponse = null;
525+
try {
526+
uri = new URIBuilder(cmd.getAPIManagerURL()).setPath(cmd.getApiBasepath() + "/apirepo/"+api.getApiId()).build();
527+
RestAPICall request = new GETRequest(uri, APIManagerAdapter.hasAdminAccount());
528+
httpResponse=request.execute();
529+
int statusCode = httpResponse.getStatusLine().getStatusCode();
530+
String response = EntityUtils.toString(httpResponse.getEntity());
531+
if(statusCode != 200){
532+
if((statusCode >= 400 && statusCode<=499) && response.contains("Unknown API")) {
533+
LOG.warn("Got unexpected error: 'Unknown API' while trying to read Backend-API ... Try again in 1 second.");
534+
Thread.sleep(1000);
535+
httpResponse = request.execute();
536+
statusCode = httpResponse.getStatusLine().getStatusCode();
537+
if(statusCode != 200) {
538+
LOG.error("Error reading backend API in order to update API-Specification. Received Status-Code: " +statusCode + ", Response: " + EntityUtils.toString(httpResponse.getEntity()));
539+
throw new AppException("Error reading backend API in order to update API-Specification. Received Status-Code: " +statusCode, ErrorCode.CANT_CREATE_BE_API);
540+
} else {
541+
LOG.info("Successfully retrieved backend API information on second request.");
542+
response = EntityUtils.toString(httpResponse.getEntity());
543+
}
544+
} else {
545+
LOG.error("Error reading backend API in order to update API-Specification. Received Status-Code: " +statusCode+ ", Response: '" + response + "'");
546+
throw new AppException("Error reading backend API in order to update API-Specification. ", ErrorCode.UNXPECTED_ERROR);
547+
}
548+
}
549+
JsonNode jsonNode = mapper.readTree(response);
550+
String resourcePath = jsonNode.get("resourcePath").asText();
551+
String basePath = jsonNode.get("basePath").asText();
552+
// Only adjust the API-Specification when exporting the FE-API-Spec otherwise we need the originally imported API-Spec
553+
if(exportFEAPIDefinition) {
554+
apiDefinition.configureBasepath(basePath + resourcePath);
555+
}
556+
// In any case, we save the backend resource path, as it is necessary for the full backendBasepath in the exported API config.
557+
api.setBackendResourcePath(resourcePath);
558+
return;
559+
} catch (Exception e) {
560+
throw new AppException("Cannot parse Backend-API for API: '" + api.toStringHuman()+"' in order to change API-Specification", ErrorCode.CANT_READ_API_DEFINITION_FILE, e);
561+
} finally {
562+
try {
563+
if(httpResponse!=null)
564+
((CloseableHttpResponse)httpResponse).close();
565+
} catch (Exception ignore) {}
566+
}
567+
}
568+
521569
public API createAPIProxy(API api) throws AppException {
522570
LOG.debug("Create Front-End API: '"+api.getName()+"' (API-Proxy)");
523571
URI uri;

modules/apim-adapter/src/main/java/com/axway/apim/api/API.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,9 @@ public class API implements CustomPropertiesEntity {
168168
protected String apiId = null;
169169

170170
protected String deprecated = null;
171+
172+
@JsonIgnore
173+
protected String resourcePath = null;
171174

172175
@APIPropertyAnnotation(isBreaking = false, copyProp = false,
173176
writableStates = {API.STATE_UNPUBLISHED, API.STATE_PUBLISHED, API.STATE_DEPRECATED})
@@ -502,6 +505,20 @@ public RemoteHost getRemotehost() {
502505
public void setRemotehost(RemoteHost remotehost) {
503506
this.remoteHost = remotehost;
504507
}
508+
509+
/**
510+
* @return path of the resource registered for the belonging backend API or null if not set
511+
*/
512+
public String getResourcePath() {
513+
return resourcePath;
514+
}
515+
516+
/**
517+
* @param resourcePath is the path of the resource registered for the belonging backend API
518+
*/
519+
public void setBackendResourcePath(String resourcePath) {
520+
this.resourcePath = resourcePath;
521+
}
505522

506523
/**
507524
* requestForAllOrgs is used to decide if an API should grant access to ALL organizations.

modules/apim-adapter/src/main/java/com/axway/apim/api/definition/Swagger2xSpecification.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,12 @@ public void configureBasepath(String backendBasepath) throws AppException {
4848

4949

5050
if(swagger.get("host")==null) {
51-
LOG.debug("Adding new host '"+url.getHost()+port+"' to Swagger-File based on configured backendBasepath: '"+backendBasepath+"'");
51+
LOG.debug("Adding new host '"+url.getHost()+port+"' to Swagger-File based on backendBasepath: '"+backendBasepath+"'");
5252
backendBasepathAdjusted = true;
5353
((ObjectNode)swagger).put("host", url.getHost()+port);
5454
} else {
5555
if(swagger.get("host").asText().equals(url.getHost()+port)) {
56-
LOG.debug("Swagger Host: '"+swagger.get("host").asText()+"' already matches configured backendBasepath: '"+backendBasepath+"'. Nothing to do.");
56+
LOG.debug("Swagger Host: '"+swagger.get("host").asText()+"' already matches backendBasepath: '"+backendBasepath+"'. Nothing to do.");
5757
} else {
5858
LOG.debug("Replacing existing host: '"+swagger.get("host").asText()+"' in Swagger-File to '"+url.getHost()+port+"' based on configured backendBasepath: '"+backendBasepath+"'");
5959
backendBasepathAdjusted = true;
@@ -62,12 +62,12 @@ public void configureBasepath(String backendBasepath) throws AppException {
6262
}
6363
if(url.getPath()!=null && !url.getPath().equals("")) {
6464
if(swagger.get("basePath")!=null && swagger.get("basePath").asText().equals(url.getPath())) {
65-
LOG.debug("Swagger basePath: '"+swagger.get("basePath").asText()+"' already matches configured backendBasepath: '"+url.getPath()+"'. Nothing to do.");
65+
LOG.debug("Swagger basePath: '"+swagger.get("basePath").asText()+"' already matches backendBasepath: '"+url.getPath()+"'. Nothing to do.");
6666
} else {
6767
if(swagger.get("basePath")!=null) {
6868
LOG.debug("Replacing existing basePath: '"+swagger.get("basePath").asText()+"' in Swagger-File to '"+url.getPath()+"' based on configured backendBasepath: '"+backendBasepath+"'");
6969
} else {
70-
LOG.debug("Setup basePath in Swagger-File to '"+url.getPath()+"' based on configured backendBasepath: '"+backendBasepath+"'");
70+
LOG.debug("Setup basePath in Swagger-File to '"+url.getPath()+"' based on backendBasepath: '"+backendBasepath+"'");
7171
}
7272
backendBasepathAdjusted = true;
7373
((ObjectNode)swagger).put("basePath", url.getPath());
@@ -81,13 +81,13 @@ public void configureBasepath(String backendBasepath) throws AppException {
8181
((ObjectNode)swagger).set("schemes", newSchemes);
8282
} else {
8383
if(swagger.get("schemes").size()!=1 || !swagger.get("schemes").get(0).asText().equals(url.getProtocol())) {
84-
LOG.debug("Replacing existing protocol(s): '"+swagger.get("schemes")+"' with '"+url.getProtocol()+"' according to configured backendBasePath: '"+backendBasepath+"'.");
84+
LOG.debug("Replacing existing protocol(s): '"+swagger.get("schemes")+"' with '"+url.getProtocol()+"' according to backendBasePath: '"+backendBasepath+"'.");
8585
backendBasepathAdjusted = true;
8686
((ObjectNode)swagger).replace("schemes", newSchemes);
8787
}
8888
}
8989
if(backendBasepathAdjusted) {
90-
LOG.info("Used the configured backendBasepath: '"+backendBasepath+"' to adjust the Swagger definition.");
90+
LOG.info("Used the backendBasepath: '"+backendBasepath+"' to adjust the API-Specification.");
9191
}
9292
this.apiSpecificationContent = this.mapper.writeValueAsBytes(swagger);
9393
}

modules/apis/src/main/java/com/axway/apim/api/export/ExportAPI.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,18 @@ public String getApiDefinitionImport() {
311311

312312

313313
public String getBackendBasepath() {
314-
return this.getServiceProfiles().get("_default").getBasePath();
314+
if(this.actualAPIProxy.getResourcePath()!=null) {
315+
// The API Manager composes the actual backend path from the host + path and backend resource path
316+
// specified in the frontend.
317+
// So if the backend was imported with the resourcepath /v2 and the backend is configured with
318+
// https://my.backend.host.com/another/path, the following backend results: https://my.backend.host.com/another/path/v2.
319+
// So, in order for the exported backendBasepath to exactly match the configured backend, it must be
320+
// composed of both properties.
321+
// See issue: https://github.com/Axway-API-Management-Plus/apim-cli/issues/158
322+
// https://github.com/Axway-API-Management-Plus/apim-cli/blob/develop/misc/images/behavior-useFEAPIDefinition.png
323+
return this.getServiceProfiles().get("_default").getBasePath() + this.actualAPIProxy.getResourcePath();
324+
} else {
325+
return this.getServiceProfiles().get("_default").getBasePath();
326+
}
315327
}
316328
}

modules/apis/src/main/java/com/axway/apim/api/export/impl/JsonAPIExporter.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ private void saveAPILocally(ExportAPI exportAPI) throws AppException {
122122
if(exportAPI.getCaCerts()!=null && !exportAPI.getCaCerts().isEmpty()) {
123123
storeCaCerts(localFolder, exportAPI.getCaCerts());
124124
}
125-
LOG.info("Successfully exported API into folder: " + localFolder);
125+
LOG.info("Successfully exported API into folder: " + localFolder.getAbsolutePath());
126126
if(!APIManagerAdapter.hasAdminAccount()) {
127127
LOG.warn("Export has been done with an Org-Admin account only. Export is restricted by the following: ");
128128
LOG.warn("- No Quotas has been exported for the API");

modules/apis/src/main/java/com/axway/apim/api/export/lib/cli/CLIAPIExportOptions.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ public Parameters getParams() {
3333

3434
@Override
3535
public void addOptions() {
36-
Option option = new Option("useFEAPIDefinition", "If this flag is set, the export API contains the API-Definition (e.g. Swagger) from the FE-API instead of the original imported API.");
36+
Option option = new Option("useFEAPIDefinition", "If this flag is set, the exported API contains the API-Specification (e.g. Swagger-File) "
37+
+ "from the FE-API instead of the original imported API. But the specification contains the host, basePath and scheme from the backend.");
3738
option.setRequired(false);
3839
addOption(option);
3940
}

0 commit comments

Comments
 (0)