Skip to content

Commit 131f51e

Browse files
authored
Merge pull request #590 from Axway-API-Management-Plus/issue556
support #556
2 parents ed0f074 + 554117c commit 131f51e

7 files changed

Lines changed: 99 additions & 3 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
1010

1111

1212
### Added
13+
- API import supports `-safeUpdate` to prevent updates when an existing API path belongs to a different API name [#556](https://github.com/Axway-API-Management-Plus/apim-cli/issues/556)
1314
- Allow deletion of application credentials like oauth/apikey [#583](https://github.com/Axway-API-Management-Plus/apim-cli/issues/583)
1415
- Extend the functionality of "override_certificates" environment property [#565](https://github.com/Axway-API-Management-Plus/apim-cli/issues/565)
1516

@@ -213,7 +214,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
213214

214215
### Added
215216
- Support of All / Global quotas for API and application. (See issue [#362](https://github.com/Axway-API-Management-Plus/apim-cli/issues/362))
216-
- Host docker cli images on github docker registry (See issue [#373](https://github.com/Axway-API-Management-Plus/apim-cli/issues/373)
217+
- Host docker cli images on github docker registry (See issue [#373](https://github.com/Axway-API-Management-Plus/apim-cli/issues/373))
217218

218219

219220
## [1.13.5] 2023-03-15

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,8 @@ public enum ErrorCode {
7070
GRANT_ACCESS_APPLICATION_ERR(102, "Error granting application access to API."),
7171
REVOKE_ACCESS_APPLICATION_ERR(103, "Error revoking application access to API."),
7272
INVALID_SECURITY_PROFILE_CONFIG(104, "The given security profile is invalid.", false),
73-
ERR_IMPORTING_API_DAT_FILE(105, "Error importing API-Dat file.", false);
73+
ERR_IMPORTING_API_DAT_FILE(105, "Error importing API-Dat file.", false),
74+
API_PATH_IN_USE_BY_DIFFERENT_API(106, "Configured API path is already used by a different API.", false);
7475

7576

7677
private final int code;

‎modules/apis/src/main/java/com/axway/apim/APIImportApp.java‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ public int importAPI(APIImportParams params) {
126126
.useFEAPIDefinition(params.isUseFEAPIDefinition()) // Should API-Definition load from the FE-API?
127127
.build();
128128
API actualAPI = apimAdapter.getApiAdapter().getAPI(filter, true);
129+
validateSafeUpdate(actualAPI, desiredAPI, params);
129130
APIChangeState changes = new APIChangeState(actualAPI, desiredAPI, params);
130131
new APIImportManager().applyChanges(changes, params.isForceUpdate(), params.isUpdateOnly());
131132
APIPropertiesExport.getInstance().store();
@@ -146,6 +147,20 @@ public int importAPI(APIImportParams params) {
146147
}
147148
}
148149

150+
void validateSafeUpdate(API actualAPI, API desiredAPI, APIImportParams params) throws AppException {
151+
if (!params.isSafeUpdate()) return;
152+
if (actualAPI == null) return;
153+
if (actualAPI.getName() == null || desiredAPI.getName() == null) return;
154+
if (actualAPI.getName().equals(desiredAPI.getName())) return;
155+
if (desiredAPI.getId() != null && desiredAPI.getId().equals(actualAPI.getId())) return;
156+
157+
throw new AppException(
158+
"Safe update blocked: Path '" + desiredAPI.getPath() + "' is already used by API '" + actualAPI.getName() +
159+
"' (ID: " + actualAPI.getId() + "). Desired API name is '" + desiredAPI.getName() + "'.",
160+
ErrorCode.API_PATH_IN_USE_BY_DIFFERENT_API
161+
);
162+
}
163+
149164
@Override
150165
public String getGroupId() {
151166
return "api";

‎modules/apis/src/main/java/com/axway/apim/apiimport/lib/cli/CLIAPIImportOptions.java‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,10 @@ public void addOptions() {
9292
option.setRequired(false);
9393
addOption(option);
9494

95+
option = new Option("safeUpdate", "If set, import fails when the same API path is already used by a different API name (unless apiId matches).");
96+
option.setRequired(false);
97+
addOption(option);
98+
9599
option = new Option("zeroDowntimeUpdate", true,"Always update a published APIs by creating a new API and switch clients to it. Defaults to false");
96100
option.setRequired(false);
97101
addOption(option);
@@ -131,6 +135,7 @@ public Parameters getParams() {
131135
params.setConfig(getValue("config"));
132136
params.setApiDefinition(getValue("apidefinition"));
133137
params.setForceUpdate(hasOption("forceUpdate"));
138+
params.setSafeUpdate(hasOption("safeUpdate"));
134139
params.setChangeOrganization(hasOption("changeOrganization"));
135140
params.setUseFEAPIDefinition(hasOption("useFEAPIDefinition"));
136141
params.setIgnoreQuotas(hasOption("ignoreQuotas"));

‎modules/apis/src/main/java/com/axway/apim/apiimport/lib/params/APIImportParams.java‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
public class APIImportParams extends StandardImportParams implements Parameters {
88

99
private boolean forceUpdate;
10+
private boolean safeUpdate;
1011
private boolean useFEAPIDefinition;
1112
private boolean validateRemoteHost;
1213
private boolean updateOnly;
@@ -32,6 +33,12 @@ public boolean isForceUpdate() {
3233
public void setForceUpdate(boolean forceUpdate) {
3334
this.forceUpdate = forceUpdate;
3435
}
36+
public boolean isSafeUpdate() {
37+
return safeUpdate;
38+
}
39+
public void setSafeUpdate(boolean safeUpdate) {
40+
this.safeUpdate = safeUpdate;
41+
}
3542
public boolean isValidateRemoteHost() {
3643
return validateRemoteHost;
3744
}

‎modules/apis/src/test/java/com/axway/apim/APIImportAppTest.java‎

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
import org.testng.annotations.BeforeClass;
99
import org.testng.annotations.Test;
1010

11+
import com.axway.apim.lib.error.AppException;
12+
import com.axway.apim.lib.error.ErrorCode;
13+
1114
public class APIImportAppTest extends WiremockWrapper {
1215

1316
@BeforeClass
@@ -59,4 +62,59 @@ public void testEmptyPassword() throws Exception {
5962
Assert.assertEquals("",desiredAPI.getAuthenticationProfiles().get(0).getParameters().get("password"));
6063

6164
}
65+
66+
@Test
67+
public void safeUpdateAllowsSameName() throws AppException {
68+
APIImportApp app = new APIImportApp();
69+
APIImportParams params = new APIImportParams();
70+
params.setSafeUpdate(true);
71+
72+
API actualApi = new API();
73+
actualApi.setId("actual-id");
74+
actualApi.setName("Order API");
75+
76+
API desiredApi = new API();
77+
desiredApi.setId("desired-id");
78+
desiredApi.setName("Order API");
79+
desiredApi.setPath("/api/orders/v1");
80+
81+
app.validateSafeUpdate(actualApi, desiredApi, params);
82+
}
83+
84+
@Test
85+
public void safeUpdateAllowsNameMismatchWhenApiIdMatches() throws AppException {
86+
APIImportApp app = new APIImportApp();
87+
APIImportParams params = new APIImportParams();
88+
params.setSafeUpdate(true);
89+
90+
API actualApi = new API();
91+
actualApi.setId("same-id");
92+
actualApi.setName("Order API");
93+
94+
API desiredApi = new API();
95+
desiredApi.setId("same-id");
96+
desiredApi.setName("Inventory API");
97+
desiredApi.setPath("/api/orders/v1");
98+
99+
app.validateSafeUpdate(actualApi, desiredApi, params);
100+
}
101+
102+
@Test
103+
public void safeUpdateBlocksNameMismatchForSamePath() {
104+
APIImportApp app = new APIImportApp();
105+
APIImportParams params = new APIImportParams();
106+
params.setSafeUpdate(true);
107+
108+
API actualApi = new API();
109+
actualApi.setId("actual-id");
110+
actualApi.setName("Order API");
111+
112+
API desiredApi = new API();
113+
desiredApi.setId("different-id");
114+
desiredApi.setName("Inventory API");
115+
desiredApi.setPath("/api/orders/v1");
116+
117+
AppException exception = Assert.expectThrows(AppException.class, () -> app.validateSafeUpdate(actualApi, desiredApi, params));
118+
Assert.assertEquals(exception.getError(), ErrorCode.API_PATH_IN_USE_BY_DIFFERENT_API);
119+
}
62120
}

‎modules/apis/src/test/java/com/axway/apim/apiimport/lib/cli/APIImportCLIOptionsTest.java‎

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,12 @@ public void testAPIImportParameter() throws AppException {
4848

4949
@Test
5050
public void testToggles() throws AppException {
51-
String[] args = {"-s", "prod", "-c", "myConfig.json", "-rollback", "true", "-force", "-forceUpdate", "-ignoreCache", "-useFEAPIDefinition", "-changeOrganization", "-ignoreQuotas", "-updateOnly"};
51+
String[] args = {"-s", "prod", "-c", "myConfig.json", "-rollback", "true", "-force", "-forceUpdate", "-safeUpdate", "-ignoreCache", "-useFEAPIDefinition", "-changeOrganization", "-ignoreQuotas", "-updateOnly"};
5252
CLIOptions options = CLIAPIImportOptions.create(args);
5353
APIImportParams params = (APIImportParams) options.getParams();
5454
Assert.assertTrue(params.isForce());
5555
Assert.assertTrue(params.isForceUpdate());
56+
Assert.assertTrue(params.isSafeUpdate());
5657
Assert.assertTrue(params.isIgnoreCache());
5758
Assert.assertTrue(params.isUpdateOnly());
5859
Assert.assertTrue(params.isChangeOrganization());
@@ -96,4 +97,12 @@ public void testRetireFlagAndDate() throws AppException {
9697
Assert.assertTrue(params.isReferenceAPIRetire());
9798
Assert.assertEquals(params.getReferenceAPIRetirementDate(), "31.12.2027");
9899
}
100+
101+
@Test
102+
public void testSafeUpdateDefault() throws AppException {
103+
String[] args = {"-s", "prod", "-c", "myConfig.json"};
104+
CLIOptions options = CLIAPIImportOptions.create(args);
105+
APIImportParams params = (APIImportParams) options.getParams();
106+
Assert.assertFalse(params.isSafeUpdate());
107+
}
99108
}

0 commit comments

Comments
 (0)