Skip to content

Commit c417d84

Browse files
author
Chris Wiechmann
committed
Added the feature to export APIs as DAT-Files
Solves #191
1 parent 2528fd7 commit c417d84

12 files changed

Lines changed: 191 additions & 8 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
77
## [Unreleased]
88
### Fixed
99
- No change detected when changing authenticationProfile in outboundProfiles (See issue [#184](https://github.com/Axway-API-Management-Plus/apim-cli/issues/184)) by @ftriolet
10-
- A WSDL file with a long comment at the beginning is not recognized as a WSDL specification. (See issue [#190https://github.com/Axway-API-Management-Plus/apim-cli/issues/190))
10+
- A WSDL file with a long comment at the beginning is not recognized as a WSDL specification. (See issue [#190](https://github.com/Axway-API-Management-Plus/apim-cli/issues/190))
11+
12+
### Added
13+
- Added feature to export APIs as DAT-Files (See issue [#191](https://github.com/Axway-API-Management-Plus/apim-cli/issues/191))
1114

1215
## [1.3.9] 2021-07-02
1316
### Fixed

modules/apim-adapter/src/main/java/com/axway/apim/adapter/APIManagerAdapter.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ public void loginToAPIManager(boolean useAdminClient) throws AppException {
216216
int statusCode = httpResponse.getStatusLine().getStatusCode();
217217
if(statusCode != 303 && (statusCode < 200 || statusCode > 299)) {
218218
String response = EntityUtils.toString(httpResponse.getEntity());
219-
LOG.warn("Login failed with statusCode: " +statusCode+ ". Got response: '"+response+"' ... Try again in 1 second.");
219+
LOG.warn("Login failed with statusCode: " +statusCode+ " ... Try again in 1 second.");
220220
Thread.sleep(1000);
221221
httpResponse = loginRequest.execute();
222222
statusCode = httpResponse.getStatusLine().getStatusCode();

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

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -691,13 +691,58 @@ public void publishAPI(API api, String vhost) throws AppException {
691691
updateAPIStatus(api, API.STATE_PUBLISHED, vhost);
692692
}
693693

694+
public byte[] getAPIDatFile(API api, String password) throws AppException {
695+
URI uri;
696+
HttpResponse httpResponse = null;
697+
RestAPICall request;
698+
try {
699+
List <NameValuePair> parameters = new ArrayList <NameValuePair>();
700+
parameters.add(new BasicNameValuePair("filename", "api-export.dat"));
701+
parameters.add(new BasicNameValuePair("password", password));
702+
parameters.add(new BasicNameValuePair("id", api.getId()));
703+
HttpEntity entity = new UrlEncodedFormEntity(parameters);
704+
705+
uri = new URIBuilder(cmd.getAPIManagerURL())
706+
.setPath(cmd.getApiBasepath()+"/proxies/export")
707+
.build();
708+
request = new POSTRequest(entity, uri);
709+
httpResponse = request.execute();
710+
int statusCode = httpResponse.getStatusLine().getStatusCode();
711+
if(statusCode != 201){
712+
String response = EntityUtils.toString(httpResponse.getEntity());
713+
LOG.error("Error exporting DAT-File representation of API: "+api.getName()+" ("+api.getId()+"). Received Status-Code: " +statusCode+ ", Response: '" + response + "'");
714+
throw new AppException("Error exporting DAT-File representation of API: "+api.getName()+" ("+api.getId()+"). Received Status-Code: " +statusCode, ErrorCode.ERR_EXPORTING_API_DAT_FILE);
715+
} else {
716+
// The file can now be loaded from the returned Location header
717+
String locationHeader = httpResponse.getHeaders("Location")[0].getValue();
718+
uri = new URI(cmd.getAPIManagerURL() + locationHeader);
719+
request = new GETRequest(uri);
720+
httpResponse = request.execute();
721+
statusCode = httpResponse.getStatusLine().getStatusCode();
722+
if(statusCode != 200){
723+
String response = EntityUtils.toString(httpResponse.getEntity());
724+
LOG.error("Error getting DAT-File representation of API: "+api.getName()+" ("+api.getId()+"). Received Status-Code: " +statusCode+ ", Response: '" + response + "'");
725+
throw new AppException("Error getting DAT-File representation of API: "+api.getName()+" ("+api.getId()+"). Received Status-Code: " +statusCode, ErrorCode.ERR_EXPORTING_API_DAT_FILE);
726+
}
727+
return EntityUtils.toByteArray(httpResponse.getEntity());
728+
}
729+
} catch (Exception e) {
730+
throw new AppException("Cannot export API-DAT file.", ErrorCode.ERR_EXPORTING_API_DAT_FILE, e);
731+
} finally {
732+
try {
733+
if(httpResponse!=null)
734+
((CloseableHttpResponse)httpResponse).close();
735+
} catch (Exception ignore) {}
736+
}
737+
}
738+
694739
public void updateAPIStatus(API api, String desiredState, String vhost) throws AppException {
695740
LOG.debug("Update API-Proxy status to: " + api.getState());
696741
URI uri;
697742
HttpResponse httpResponse = null;
698743
RestAPICall request;
699744
try {
700-
uri = new URIBuilder(cmd.getAPIManagerURL())
745+
uri = new URIBuilder(cmd.getAPIManagerURL())
701746
.setPath(cmd.getApiBasepath()+"/proxies/"+api.getId()+"/"+StatusEndpoint.valueOf(desiredState).endpoint)
702747
.build();
703748
if(vhost!=null && desiredState.equals(API.STATE_PUBLISHED)) { // During publish, it might be required to also set the VHost (See issue: #98)

modules/apim-adapter/src/main/java/com/axway/apim/lib/StandardExportCLIOptions.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,9 @@ public void addOptions() {
4848
option.setArgName("my/apis");
4949
cliOptions.addOption(option);
5050

51-
option = new Option("o", "output", true, "Controls the output format. By default the console is used. CSV is not supported for all entities.");
51+
option = new Option("o", "output", true, "Controls the output format. By default the console is used. CSV and DAT is not supported for all entities.");
5252
option.setRequired(false);
53-
option.setArgName("console|json|csv");
53+
option.setArgName("console|json|csv|dat");
5454
cliOptions.addOption(option);
5555
}
5656

modules/apim-adapter/src/main/java/com/axway/apim/lib/StandardExportParams.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ public static enum Wide {
1111
public static enum OutputFormat {
1212
console,
1313
json,
14-
csv;
14+
csv,
15+
dat;
1516

1617
public static OutputFormat getFormat(String name) {
1718
if(name==null || valueOf(name)==null) return console;

modules/apim-adapter/src/main/java/com/axway/apim/lib/errorHandling/ErrorCode.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ public enum ErrorCode {
5656
ERR_DELETING_API (91, "API could not be deleted.", false),
5757
ERR_DELETING_ORG (92, "Organization could not be deleted.", false),
5858
ERR_GRANTING_ACCESS_TO_API (93, "Error granting access to an API.", false),
59+
ERR_EXPORTING_API_DAT_FILE (94, "Error exporting API-Date file.", false),
5960
UNXPECTED_ERROR (99, "An unexpected error occured.");
6061

6162
private final int code;

modules/apis/src/main/java/com/axway/apim/APIExportApp.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public class APIExportApp implements APIMCLIServiceProvider {
4343
static ErrorCodeMapper errorCodeMapper = new ErrorCodeMapper();
4444

4545
public static void main(String args[]) {
46-
int rc = grantAccess(args);
46+
int rc = exportAPI(args);
4747
System.exit(rc);
4848
}
4949

@@ -72,6 +72,8 @@ public int exportAPI(APIExportParams params) {
7272
return execute(params, APIListImpl.JSON_EXPORTER);
7373
case csv:
7474
return execute(params, APIListImpl.CSV_EXPORTER);
75+
case dat:
76+
return execute(params, APIListImpl.DAT_EXPORTER);
7577
default:
7678
return execute(params, APIListImpl.CONSOLE_EXPORTER);
7779
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ public enum APIListImpl {
4747
JSON_EXPORTER(JsonAPIExporter.class),
4848
CONSOLE_EXPORTER(ConsoleAPIExporter.class),
4949
CSV_EXPORTER(CSVAPIExporter.class),
50+
DAT_EXPORTER(DATAPIExporter.class),
5051
API_DELETE_HANDLER(DeleteAPIHandler.class),
5152
API_PUBLISH_HANDLER(PublishAPIHandler.class),
5253
API_UNPUBLISH_HANDLER(UnpublishAPIHandler.class),
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
package com.axway.apim.api.export.impl;
2+
3+
import java.io.File;
4+
import java.io.FileOutputStream;
5+
import java.io.IOException;
6+
import java.util.List;
7+
8+
import org.apache.commons.io.FileUtils;
9+
import org.slf4j.Logger;
10+
import org.slf4j.LoggerFactory;
11+
12+
import com.axway.apim.adapter.APIManagerAdapter;
13+
import com.axway.apim.adapter.apis.APIFilter;
14+
import com.axway.apim.adapter.apis.APIFilter.Builder;
15+
import com.axway.apim.adapter.apis.OrgFilter;
16+
import com.axway.apim.api.API;
17+
import com.axway.apim.api.export.lib.params.APIExportParams;
18+
import com.axway.apim.lib.errorHandling.AppException;
19+
import com.axway.apim.lib.errorHandling.ErrorCode;
20+
21+
public class DATAPIExporter extends APIResultHandler {
22+
private static Logger LOG = LoggerFactory.getLogger(DATAPIExporter.class);
23+
24+
/** Where to store the exported API-Definition */
25+
private String givenExportFolder = null;
26+
27+
private String datPassword = null;
28+
29+
APIManagerAdapter apiManager = APIManagerAdapter.getInstance();
30+
31+
public DATAPIExporter(APIExportParams params) throws AppException {
32+
super(params);
33+
this.givenExportFolder = params.getTarget();
34+
this.datPassword = params.getDatPassword();
35+
}
36+
37+
@Override
38+
public void execute(List<API> apis) throws AppException {
39+
for (API api : apis) {
40+
try {
41+
saveAPILocally(api);
42+
} catch (AppException e) {
43+
LOG.error("Can't export API: " + e.getMessage() + " as DAT-File. Please check in API-Manager UI the API is valid.", e);
44+
}
45+
}
46+
return;
47+
}
48+
49+
@Override
50+
public APIFilter getFilter() {
51+
Builder builder = getBaseAPIFilterBuilder();
52+
return builder.build();
53+
}
54+
55+
private void saveAPILocally(API api) throws AppException {
56+
String apiPath = getAPIExportFolder(api.getPath());
57+
File localFolder = new File(this.givenExportFolder +File.separator+ getVHost(api) + apiPath);
58+
LOG.info("Going to export API into folder: " + localFolder);
59+
if(localFolder.exists()) {
60+
if(params.isDeleteTarget()) {
61+
LOG.debug("Existing local export folder: " + localFolder + " already exists and will be deleted.");
62+
try {
63+
FileUtils.deleteDirectory(localFolder);
64+
} catch (IOException e) {
65+
throw new AppException("Error deleting local folder", ErrorCode.UNXPECTED_ERROR, e);
66+
}
67+
} else {
68+
LOG.warn("Local export folder: " + localFolder + " already exists. API will not be exported. (You may set -deleteTarget)");
69+
return;
70+
}
71+
}
72+
if (!localFolder.mkdirs()) {
73+
throw new AppException("Cant create export folder: " + localFolder, ErrorCode.UNXPECTED_ERROR);
74+
}
75+
byte[] datFileContent = apiManager.apiAdapter.getAPIDatFile(api, datPassword);
76+
77+
String targetFile = null;
78+
try {
79+
targetFile = localFolder.getCanonicalPath() + "/" + api.getName() + ".dat";
80+
writeBytesToFile(datFileContent, targetFile);
81+
} catch (IOException e) {
82+
throw new AppException("Can't save API-DAT file locally: " + targetFile,
83+
ErrorCode.UNXPECTED_ERROR, e);
84+
}
85+
LOG.info("Successfully exported API as DAT-File into folder: " + localFolder.getAbsolutePath());
86+
}
87+
88+
private String getVHost(API api) throws AppException {
89+
if(api.getVhost()!=null) return api.getVhost() + File.separator;
90+
String orgVHost = APIManagerAdapter.getInstance().orgAdapter.getOrg(new OrgFilter.Builder().hasId(api.getOrganizationId()).build()).getVirtualHost();
91+
if(orgVHost!=null) return orgVHost+File.separator;
92+
return "";
93+
}
94+
95+
96+
private static void writeBytesToFile(byte[] bFile, String fileDest) throws AppException {
97+
98+
try (FileOutputStream fileOuputStream = new FileOutputStream(fileDest)) {
99+
fileOuputStream.write(bFile);
100+
} catch (IOException e) {
101+
throw new AppException("Can't write file", ErrorCode.UNXPECTED_ERROR, e);
102+
}
103+
}
104+
105+
private String getAPIExportFolder(String apiExposurePath) {
106+
if (apiExposurePath.startsWith("/"))
107+
apiExposurePath = apiExposurePath.replaceFirst("/", "");
108+
if (apiExposurePath.endsWith("/"))
109+
apiExposurePath = apiExposurePath.substring(0, apiExposurePath.length() - 1);
110+
apiExposurePath = apiExposurePath.replace("/", "-");
111+
return apiExposurePath;
112+
}
113+
}

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ public static CLIOptions create(String[] args) {
2828
public Parameters getParams() {
2929
APIExportParams params = new APIExportParams();
3030
params.setUseFEAPIDefinition(hasOption("useFEAPIDefinition"));
31+
params.setDatPassword(getValue("datPassword"));
3132
return params;
3233
}
3334

@@ -37,6 +38,11 @@ public void addOptions() {
3738
+ "from the FE-API instead of the original imported API. But the specification contains the host, basePath and scheme from the backend.");
3839
option.setRequired(false);
3940
addOption(option);
41+
42+
option = new Option("datPassword", true, "Password used when exporting APIs in a DAT-Format.");
43+
option.setRequired(false);
44+
option.setArgName("myExportPassword");
45+
addOption(option);
4046
}
4147

4248
@Override

0 commit comments

Comments
 (0)