Skip to content

Commit

Permalink
applications can now be re-registered (users can add/remove contracts…
Browse files Browse the repository at this point in the history
… and then re-synch/re-register the app with the gateway(s))
  • Loading branch information
EricWittmann committed Nov 23, 2015
1 parent e586914 commit 17b74a2
Show file tree
Hide file tree
Showing 33 changed files with 349 additions and 119 deletions.
Expand Up @@ -17,6 +17,7 @@

import io.apiman.gateway.engine.IRegistry;
import io.apiman.gateway.engine.async.AsyncResultImpl;
import io.apiman.gateway.engine.async.IAsyncResult;
import io.apiman.gateway.engine.async.IAsyncResultHandler;
import io.apiman.gateway.engine.beans.Application;
import io.apiman.gateway.engine.beans.Contract;
Expand Down Expand Up @@ -98,31 +99,31 @@ public void registerApplication(Application application, IAsyncResultHandler<Voi
synchronized (mutex) {
// Validate the application first - we need to be able to resolve all the contracts.
for (Contract contract : application.getContracts()) {
String contractKey = getContractKey(contract);
if (getMap().containsKey(contractKey)) {
error = new RegistrationException(Messages.i18n.format("InMemoryRegistry.ContractAlreadyPublished", //$NON-NLS-1$
contract.getApiKey()));
break;
}
String svcKey = getServiceKey(contract.getServiceOrgId(), contract.getServiceId(), contract.getServiceVersion());
if (!getMap().containsKey(svcKey)) {
error = new RegistrationException(Messages.i18n.format("InMemoryRegistry.ServiceNotFoundInOrg", //$NON-NLS-1$
contract.getServiceId(), contract.getServiceOrgId()));
break;
}
}
String applicationKey = getApplicationKey(application);
if (getMap().containsKey(applicationKey)) {
error = new RegistrationException(Messages.i18n.format("InMemoryRegistry.AppAlreadyRegistered")); //$NON-NLS-1$
} else {
getMap().put(applicationKey, application);
for (Contract contract : application.getContracts()) {
String svcKey = getServiceKey(contract.getServiceOrgId(), contract.getServiceId(), contract.getServiceVersion());
Service service = (Service) getMap().get(svcKey);
ServiceContract sc = new ServiceContract(contract.getApiKey(), service, application, contract.getPlan(), contract.getPolicies());
String contractKey = getContractKey(contract);
getMap().put(contractKey, sc);

// Unregister the app (if it exists)
IAsyncResultHandler<Void> unregisterHandler = new IAsyncResultHandler<Void>() {
@Override
public void handle(IAsyncResult<Void> result) {
}
};
unregisterApplication(application, unregisterHandler);

// Now, register the app.
String applicationKey = getApplicationKey(application);
getMap().put(applicationKey, application);
for (Contract contract : application.getContracts()) {
String svcKey = getServiceKey(contract.getServiceOrgId(), contract.getServiceId(), contract.getServiceVersion());
Service service = (Service) getMap().get(svcKey);
ServiceContract sc = new ServiceContract(contract.getApiKey(), service, application, contract.getPlan(), contract.getPolicies());
String contractKey = getContractKey(contract);
getMap().put(contractKey, sc);
}
}
if (error == null) {
Expand Down
@@ -1,6 +1,4 @@
InMemoryRegistry.AppAlreadyRegistered=Application already registered.
InMemoryRegistry.AppNotFound=Application not found.
InMemoryRegistry.ContractAlreadyPublished=Contract with API Key {0} has already been published.
InMemoryRegistry.NoContractForAPIKey=No contract found for API Key {0}
InMemoryRegistry.ServiceAlreadyPublished=Service already published.
InMemoryRegistry.ServiceNotFound=Service not found.
Expand Down
Expand Up @@ -36,6 +36,7 @@
import io.searchbox.core.Index;
import io.searchbox.params.Parameters;

import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
Expand Down Expand Up @@ -137,19 +138,28 @@ public void handle(IAsyncResult<Void> result) {
try {
Index index = new Index.Builder(ESRegistryMarshalling.marshall(application).string())
.refresh(false).index(getIndexName())
.setParameter(Parameters.OP_TYPE, "create") //$NON-NLS-1$
.setParameter(Parameters.OP_TYPE, "index") //$NON-NLS-1$
.type("application").id(id).build(); //$NON-NLS-1$
getClient().executeAsync(index, new JestResultHandler<JestResult>() {
@Override
public void completed(JestResult result) {
if (!result.isSucceeded()) {
handler.handle(AsyncResultImpl.create(
new RegistrationException(Messages.i18n.format("ESRegistry.AppAlreadyRegistered")), //$NON-NLS-1$
new IOException(result.getErrorMessage()),
Void.class));
} else {
Iterator<Contract> iterator = application.getContracts().iterator();
application.setContracts(null);
registerContracts(application, iterator, svcMap, handler);
unregisterServiceContracts(application, new IAsyncResultHandler<Void>() {
@Override
public void handle(IAsyncResult<Void> result) {
if (result.isError()) {
handler.handle(result);
} else {
Iterator<Contract> iterator = application.getContracts().iterator();
application.setContracts(null);
registerContracts(application, iterator, svcMap, handler);
}
}
});
}
}
@Override
Expand Down
Expand Up @@ -117,12 +117,16 @@ public void registerApplication(final Application application, final IAsyncResul
String id = getApplicationId(application);
Index index = new Index.Builder(ESRegistryMarshalling.marshall(application).string())
.refresh(false).index(getIndexName())
.setParameter(Parameters.OP_TYPE, "create") //$NON-NLS-1$
.setParameter(Parameters.OP_TYPE, "index") //$NON-NLS-1$
.type("application").id(id).build(); //$NON-NLS-1$
JestResult result = getClient().execute(index);
if (!result.isSucceeded()) {
throw new RegistrationException(Messages.i18n.format("ESRegistry.AppAlreadyRegistered")); //$NON-NLS-1$
throw new IOException(result.getErrorMessage());
} else {
// Remove all the service contracts, then re-add them
unregisterServiceContracts(application);

// Register all the service contracts.
Set<Contract> contracts = application.getContracts();
application.setContracts(null);
for (Contract contract : contracts) {
Expand Down
@@ -1,4 +1,3 @@
ESRegistry.AppAlreadyRegistered=Application already registered.
ESRegistry.AppNotFound=Application not found.
ESRegistry.ContractAlreadyPublished=Contract with API Key {0} has already been published.
ESRegistry.NoContractForAPIKey=No contract found for API Key {0}
Expand Down
@@ -0,0 +1,32 @@
/*
* Copyright 2014 JBoss Inc
*
* Licensed 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 io.apiman.gateway.test;

import io.apiman.gateway.test.junit.GatewayRestTestPlan;
import io.apiman.gateway.test.junit.GatewayRestTester;

import org.junit.runner.RunWith;

/**
* Make sure that at App can be re-registered.
*
* @author eric.wittmann@redhat.com
*/
@RunWith(GatewayRestTester.class)
@GatewayRestTestPlan("test-plans/app-reregister/app-reregister-testPlan.xml")
public class AppReregisterTest {

}
Expand Up @@ -15,11 +15,4 @@ Content-Type: application/json
]
}
----
500
Content-Type: application/json
X-API-Gateway-Error: true

{
"errorType" : "RegistrationException",
"message" : "Application already registered."
}
204
@@ -0,0 +1,13 @@
GET /AppReregisterTest/echo-1/1.0/path/to/app/resource admin/admin
X-API-Key: service-1
Accept: application/json

----
200
Content-Type: application/json

{
"method" : "GET",
"resource" : "/echo-1/path/to/app/resource",
"uri" : "/echo-1/path/to/app/resource"
}
@@ -0,0 +1,13 @@
GET /AppReregisterTest/echo-2/1.0/path/to/app/resource admin/admin
X-API-Key: service-2
Accept: application/json

----
200
Content-Type: application/json

{
"method" : "GET",
"resource" : "/echo-2/path/to/app/resource",
"uri" : "/echo-2/path/to/app/resource"
}
@@ -0,0 +1,13 @@
PUT /services admin/admin
Content-Type: application/json

{
"organizationId" : "AppReregisterTest",
"serviceId" : "echo-1",
"version" : "1.0",
"endpointType" : "rest",
"endpointContentType" : "json",
"endpoint" : "${apiman-gateway-test.endpoints.echo}/echo-1"
}
----
204
@@ -0,0 +1,13 @@
PUT /services admin/admin
Content-Type: application/json

{
"organizationId" : "AppReregisterTest",
"serviceId" : "echo-2",
"version" : "1.0",
"endpointType" : "rest",
"endpointContentType" : "json",
"endpoint" : "${apiman-gateway-test.endpoints.echo}/echo-2"
}
----
204
@@ -0,0 +1,18 @@
PUT /applications admin/admin
Content-Type: application/json

{
"organizationId" : "AppReregisterTest",
"applicationId" : "test",
"version" : "1.0",
"contracts" : [
{
"apiKey" : "service-1",
"serviceOrgId" : "AppReregisterTest",
"serviceId" : "echo-1",
"serviceVersion" : "1.0"
}
]
}
----
204
@@ -0,0 +1,24 @@
PUT /applications admin/admin
Content-Type: application/json

{
"organizationId" : "AppReregisterTest",
"applicationId" : "test",
"version" : "1.0",
"contracts" : [
{
"apiKey" : "service-1",
"serviceOrgId" : "AppReregisterTest",
"serviceId" : "echo-1",
"serviceVersion" : "1.0"
},
{
"apiKey" : "service-2",
"serviceOrgId" : "AppReregisterTest",
"serviceId" : "echo-2",
"serviceVersion" : "1.0"
}
]
}
----
204
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<testPlan xmlns="urn:io.apiman.test:2014:02:testPlan">

<testGroup name="Publish the Services" endpoint="api">
<test name="Publish Service 1">test-plan-data/app-reregister/setup/001-publish-service-1.resttest</test>
<test name="Publish Service 2">test-plan-data/app-reregister/setup/002-publish-service-2.resttest</test>
</testGroup>

<testGroup name="Register the App" endpoint="api">
<test name="Register Application">test-plan-data/app-reregister/setup/003-register-app.resttest</test>
<test name="Update Application">test-plan-data/app-reregister/setup/004-reregister-app.resttest</test>
</testGroup>

<testGroup name="Test the updated App">
<test name="Echo Service 1 (GET)">test-plan-data/app-reregister/echo/001-service-1.resttest</test>
<test name="Echo Service 2 (GET)">test-plan-data/app-reregister/echo/002-service-2.resttest</test>
</testGroup>

</testPlan>
Expand Up @@ -326,8 +326,15 @@ private void registerApplication(ActionBean action) throws ActionException {
throw ExceptionFactory.actionException(Messages.i18n.format("ApplicationNotFound"), e); //$NON-NLS-1$
}

// Validate that it's ok to perform this action - application must be Ready.
if (versionBean.getStatus() != ApplicationStatus.Ready) {
// Validate that it's ok to perform this action - application must be Ready or Registered.
if (versionBean.getStatus() == ApplicationStatus.Registered) {
Date modOn = versionBean.getModifiedOn();
Date publishedOn = versionBean.getPublishedOn();
int c = modOn.compareTo(publishedOn);
if (c <= 0) {
throw ExceptionFactory.actionException(Messages.i18n.format("ApplicationReRegisterNotRequired")); //$NON-NLS-1$
}
} else if (versionBean.getStatus() != ApplicationStatus.Ready) {
throw ExceptionFactory.actionException(Messages.i18n.format("InvalidApplicationStatus")); //$NON-NLS-1$
}

Expand Down
Expand Up @@ -760,7 +760,7 @@ protected ContractBean createContractInternal(String organizationId, String appl
if (avb == null) {
throw ExceptionFactory.applicationNotFoundException(applicationId);
}
if (avb.getStatus() == ApplicationStatus.Registered || avb.getStatus() == ApplicationStatus.Retired) {
if (avb.getStatus() == ApplicationStatus.Retired) {
throw ExceptionFactory.invalidApplicationStatusException();
}
ServiceVersionBean svb = storage.getServiceVersion(bean.getServiceOrgId(), bean.getServiceId(), bean.getServiceVersion());
Expand Down Expand Up @@ -798,8 +798,8 @@ protected ContractBean createContractInternal(String organizationId, String appl
contract.setCreatedOn(new Date());
contract.setApikey(apiKeyGenerator.generate());

// Validate the state of the application.
if (applicationValidator.isReady(avb, true)) {
// Move the app to the "Ready" state if necessary.
if (avb.getStatus() == ApplicationStatus.Created && applicationValidator.isReady(avb, true)) {
avb.setStatus(ApplicationStatus.Ready);
}

Expand Down Expand Up @@ -911,13 +911,19 @@ public void deleteContract(String organizationId, String applicationId, String v
if (!contract.getApplication().getVersion().equals(version)) {
throw ExceptionFactory.contractNotFoundException(contractId);
}
if (contract.getApplication().getStatus() == ApplicationStatus.Registered || contract.getApplication().getStatus() == ApplicationStatus.Retired) {
if (contract.getApplication().getStatus() == ApplicationStatus.Retired) {
throw ExceptionFactory.invalidApplicationStatusException();
}
storage.deleteContract(contract);
storage.createAuditEntry(AuditUtils.contractBrokenFromApp(contract, securityContext));
storage.createAuditEntry(AuditUtils.contractBrokenToService(contract, securityContext));

// Update the version with new meta-data (e.g. modified-by)
ApplicationVersionBean appV = storage.getApplicationVersion(organizationId, applicationId, version);
appV.setModifiedBy(securityContext.getCurrentUser());
appV.setModifiedOn(new Date());
storage.updateApplicationVersion(appV);

storage.commitTx();
log.debug(String.format("Deleted contract: %s", contract)); //$NON-NLS-1$
} catch (AbstractRestException e) {
Expand Down
Expand Up @@ -61,4 +61,5 @@ OrganizationResourceImpl.MetricDataSetTooLarge=Your chosen date range and interv
FieldValidator.EmptyNameError=Invalid (empty) name provided for entity.
FieldValidator.EmptyVersionError=Invalid (empty) version provided for entity.
MissingOrInvalidParam=Missing or invalid paramter ''{0}''.
OrganizationResourceImpl.InvalidEndpointURL=Endpoint is not a valid URL.
OrganizationResourceImpl.InvalidEndpointURL=Endpoint is not a valid URL.
ApplicationReRegisterNotRequired=The application has not changed since it was last registered.

0 comments on commit 17b74a2

Please sign in to comment.