-
Notifications
You must be signed in to change notification settings - Fork 802
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[SCB-1959] add rest invocation creator
- Loading branch information
Showing
5 changed files
with
398 additions
and
0 deletions.
There are no files selected for viewing
127 changes: 127 additions & 0 deletions
127
...-rest/src/main/java/org/apache/servicecomb/common/rest/RestProducerInvocationCreator.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
/* | ||
* Licensed to the Apache Software Foundation (ASF) under one or more | ||
* contributor license agreements. See the NOTICE file distributed with | ||
* this work for additional information regarding copyright ownership. | ||
* The ASF licenses this file to You 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 org.apache.servicecomb.common.rest; | ||
|
||
import static javax.ws.rs.core.Response.Status.NOT_ACCEPTABLE; | ||
import static javax.ws.rs.core.Response.Status.NOT_FOUND; | ||
import static org.apache.servicecomb.core.exception.ExceptionCodes.GENERIC_CLIENT; | ||
import static org.apache.servicecomb.core.exception.ExceptionCodes.NOT_DEFINED_ANY_SCHEMA; | ||
|
||
import java.util.Map; | ||
|
||
import javax.annotation.Nonnull; | ||
import javax.ws.rs.core.HttpHeaders; | ||
|
||
import org.apache.commons.lang3.StringUtils; | ||
import org.apache.servicecomb.common.rest.codec.produce.ProduceProcessor; | ||
import org.apache.servicecomb.common.rest.definition.RestOperationMeta; | ||
import org.apache.servicecomb.common.rest.locator.OperationLocator; | ||
import org.apache.servicecomb.common.rest.locator.ServicePathManager; | ||
import org.apache.servicecomb.core.Const; | ||
import org.apache.servicecomb.core.Endpoint; | ||
import org.apache.servicecomb.core.Invocation; | ||
import org.apache.servicecomb.core.definition.MicroserviceMeta; | ||
import org.apache.servicecomb.core.exception.Exceptions; | ||
import org.apache.servicecomb.core.invocation.InvocationCreator; | ||
import org.apache.servicecomb.core.invocation.InvocationFactory; | ||
import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; | ||
import org.apache.servicecomb.foundation.vertx.http.HttpServletResponseEx; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
import io.vertx.core.json.Json; | ||
|
||
public abstract class RestProducerInvocationCreator implements InvocationCreator { | ||
private static final Logger LOGGER = LoggerFactory.getLogger(RestVertxProducerInvocationCreator.class); | ||
|
||
protected final MicroserviceMeta microserviceMeta; | ||
|
||
protected final Endpoint endpoint; | ||
|
||
protected final HttpServletRequestEx requestEx; | ||
|
||
protected final HttpServletResponseEx responseEx; | ||
|
||
private RestOperationMeta restOperationMeta; | ||
|
||
protected ProduceProcessor produceProcessor; | ||
|
||
public RestProducerInvocationCreator(@Nonnull MicroserviceMeta microserviceMeta, @Nonnull Endpoint endpoint, | ||
@Nonnull HttpServletRequestEx requestEx, @Nonnull HttpServletResponseEx responseEx) { | ||
this.microserviceMeta = microserviceMeta; | ||
this.endpoint = endpoint; | ||
this.requestEx = requestEx; | ||
this.responseEx = responseEx; | ||
} | ||
|
||
@Override | ||
public Invocation create() { | ||
initRestOperation(); | ||
|
||
Invocation invocation = InvocationFactory.forProvider(endpoint, | ||
restOperationMeta.getOperationMeta(), | ||
null); | ||
initInvocationContext(invocation); | ||
|
||
initProduceProcessor(); | ||
initTransportContext(invocation); | ||
|
||
invocation.addLocalContext(RestConst.REST_REQUEST, requestEx); | ||
|
||
return invocation; | ||
} | ||
|
||
protected void initInvocationContext(Invocation invocation) { | ||
String strCseContext = requestEx.getHeader(Const.CSE_CONTEXT); | ||
if (StringUtils.isEmpty(strCseContext)) { | ||
return; | ||
} | ||
|
||
@SuppressWarnings("unchecked") | ||
Map<String, String> invocationContext = Json.decodeValue(strCseContext, Map.class); | ||
invocation.mergeContext(invocationContext); | ||
} | ||
|
||
protected abstract void initTransportContext(Invocation invocation); | ||
|
||
private void initRestOperation() { | ||
OperationLocator locator = locateOperation(); | ||
requestEx.setAttribute(RestConst.PATH_PARAMETERS, locator.getPathVarMap()); | ||
restOperationMeta = locator.getOperation(); | ||
} | ||
|
||
private OperationLocator locateOperation() { | ||
ServicePathManager servicePathManager = ServicePathManager.getServicePathManager(microserviceMeta); | ||
if (servicePathManager == null) { | ||
LOGGER.error("No schema defined for {}:{}.", microserviceMeta.getAppId(), microserviceMeta.getMicroserviceName()); | ||
throw Exceptions.create(NOT_FOUND, NOT_DEFINED_ANY_SCHEMA, NOT_FOUND.getReasonPhrase()); | ||
} | ||
|
||
return servicePathManager.producerLocateOperation(requestEx.getRequestURI(), requestEx.getMethod()); | ||
} | ||
|
||
private void initProduceProcessor() { | ||
produceProcessor = restOperationMeta.ensureFindProduceProcessor(requestEx); | ||
if (produceProcessor == null) { | ||
LOGGER.error("Accept {} is not supported, operation={}.", requestEx.getHeader(HttpHeaders.ACCEPT), | ||
restOperationMeta.getOperationMeta().getMicroserviceQualifiedName()); | ||
|
||
String msg = String.format("Accept %s is not supported", requestEx.getHeader(HttpHeaders.ACCEPT)); | ||
throw Exceptions.create(NOT_ACCEPTABLE, GENERIC_CLIENT, msg); | ||
} | ||
} | ||
} |
46 changes: 46 additions & 0 deletions
46
.../src/main/java/org/apache/servicecomb/common/rest/RestVertxProducerInvocationCreator.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
/* | ||
* Licensed to the Apache Software Foundation (ASF) under one or more | ||
* contributor license agreements. See the NOTICE file distributed with | ||
* this work for additional information regarding copyright ownership. | ||
* The ASF licenses this file to You 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 org.apache.servicecomb.common.rest; | ||
|
||
import javax.annotation.Nonnull; | ||
|
||
import org.apache.servicecomb.core.Endpoint; | ||
import org.apache.servicecomb.core.Invocation; | ||
import org.apache.servicecomb.core.definition.MicroserviceMeta; | ||
import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; | ||
import org.apache.servicecomb.foundation.vertx.http.HttpServletResponseEx; | ||
|
||
import io.vertx.ext.web.RoutingContext; | ||
|
||
public class RestVertxProducerInvocationCreator extends RestProducerInvocationCreator { | ||
private final RoutingContext routingContext; | ||
|
||
public RestVertxProducerInvocationCreator(@Nonnull RoutingContext routingContext, | ||
@Nonnull MicroserviceMeta microserviceMeta, @Nonnull Endpoint endpoint, | ||
@Nonnull HttpServletRequestEx requestEx, @Nonnull HttpServletResponseEx responseEx) { | ||
super(microserviceMeta, endpoint, requestEx, responseEx); | ||
this.routingContext = routingContext; | ||
} | ||
|
||
@Override | ||
protected void initTransportContext(Invocation invocation) { | ||
VertxHttpTransportContext transportContext = new VertxHttpTransportContext(routingContext, requestEx, responseEx, | ||
produceProcessor); | ||
invocation.setTransportContext(transportContext); | ||
routingContext.put(RestConst.REST_INVOCATION_CONTEXT, invocation); | ||
} | ||
} |
184 changes: 184 additions & 0 deletions
184
...t/src/test/java/org/apache/servicecomb/common/rest/RestProducerInvocationCreatorTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,184 @@ | ||
/* | ||
* Licensed to the Apache Software Foundation (ASF) under one or more | ||
* contributor license agreements. See the NOTICE file distributed with | ||
* this work for additional information regarding copyright ownership. | ||
* The ASF licenses this file to You 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 org.apache.servicecomb.common.rest; | ||
|
||
import static javax.ws.rs.core.Response.Status.NOT_ACCEPTABLE; | ||
import static javax.ws.rs.core.Response.Status.NOT_FOUND; | ||
import static org.assertj.core.api.Assertions.assertThat; | ||
import static org.assertj.core.api.Assertions.catchThrowable; | ||
|
||
import javax.ws.rs.core.HttpHeaders; | ||
|
||
import org.apache.servicecomb.common.rest.definition.RestOperationMeta; | ||
import org.apache.servicecomb.common.rest.locator.ServicePathManager; | ||
import org.apache.servicecomb.config.ConfigUtil; | ||
import org.apache.servicecomb.core.Const; | ||
import org.apache.servicecomb.core.Endpoint; | ||
import org.apache.servicecomb.core.Invocation; | ||
import org.apache.servicecomb.core.SCBEngine; | ||
import org.apache.servicecomb.core.SCBStatus; | ||
import org.apache.servicecomb.core.bootstrap.SCBBootstrap; | ||
import org.apache.servicecomb.core.definition.MicroserviceMeta; | ||
import org.apache.servicecomb.foundation.test.scaffolding.config.ArchaiusUtils; | ||
import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; | ||
import org.apache.servicecomb.foundation.vertx.http.HttpServletResponseEx; | ||
import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; | ||
import org.apache.servicecomb.swagger.invocation.exception.InvocationException; | ||
import org.junit.AfterClass; | ||
import org.junit.Before; | ||
import org.junit.BeforeClass; | ||
import org.junit.Test; | ||
|
||
import io.vertx.core.json.Json; | ||
import io.vertx.ext.web.RoutingContext; | ||
import mockit.Expectations; | ||
import mockit.Injectable; | ||
import mockit.Mocked; | ||
import mockit.Verifications; | ||
|
||
public class RestProducerInvocationCreatorTest { | ||
@Injectable | ||
RoutingContext routingContext; | ||
|
||
@Injectable | ||
MicroserviceMeta microserviceMeta; | ||
|
||
@Injectable | ||
ServicePathManager servicePathManager; | ||
|
||
@Mocked | ||
RestOperationMeta restOperationMeta; | ||
|
||
@Injectable | ||
Endpoint endpoint; | ||
|
||
@Injectable | ||
HttpServletRequestEx requestEx; | ||
|
||
@Injectable | ||
HttpServletResponseEx responseEx; | ||
|
||
RestProducerInvocationCreator creator; | ||
|
||
static SCBEngine engine; | ||
|
||
@BeforeClass | ||
public static void beforeClass() throws Exception { | ||
ArchaiusUtils.resetConfig(); | ||
ConfigUtil.installDynamicConfig(); | ||
|
||
engine = SCBBootstrap.createSCBEngineForTest(); | ||
engine.setStatus(SCBStatus.UP); | ||
} | ||
|
||
@AfterClass | ||
public static void afterClass() throws Exception { | ||
engine.destroy(); | ||
ArchaiusUtils.resetConfig(); | ||
} | ||
|
||
@Before | ||
public void setUp() { | ||
creator = new RestVertxProducerInvocationCreator(routingContext, microserviceMeta, endpoint, | ||
requestEx, responseEx); | ||
} | ||
|
||
private void mockGetServicePathManager() { | ||
mockGetServicePathManager(servicePathManager); | ||
} | ||
|
||
private void mockGetServicePathManager(final ServicePathManager servicePathManager) { | ||
new Expectations(ServicePathManager.class) { | ||
{ | ||
ServicePathManager.getServicePathManager(microserviceMeta); | ||
result = servicePathManager; | ||
} | ||
}; | ||
} | ||
|
||
@Test | ||
public void should_failed_when_not_defined_any_schema() { | ||
mockGetServicePathManager(null); | ||
|
||
InvocationException throwable = (InvocationException) catchThrowable(() -> creator.create()); | ||
CommonExceptionData data = (CommonExceptionData) throwable.getErrorData(); | ||
|
||
assertThat(throwable.getStatusCode()).isEqualTo(NOT_FOUND.getStatusCode()); | ||
assertThat(Json.encode(data)).isEqualTo("{\"code\":\"SCB.0002\",\"message\":\"Not Found\"}"); | ||
} | ||
|
||
@Test | ||
public void should_failed_when_accept_is_not_support() { | ||
mockGetServicePathManager(); | ||
new Expectations() { | ||
{ | ||
requestEx.getHeader(HttpHeaders.ACCEPT); | ||
result = "test-type"; | ||
|
||
restOperationMeta.ensureFindProduceProcessor(requestEx); | ||
result = null; | ||
} | ||
}; | ||
|
||
InvocationException throwable = (InvocationException) catchThrowable(() -> creator.create()); | ||
CommonExceptionData data = (CommonExceptionData) throwable.getErrorData(); | ||
|
||
assertThat(throwable.getStatusCode()).isEqualTo(NOT_ACCEPTABLE.getStatusCode()); | ||
assertThat(Json.encode(data)) | ||
.isEqualTo("{\"code\":\"SCB.0000\",\"message\":\"Accept test-type is not supported\"}"); | ||
} | ||
|
||
@Test | ||
public void should_save_requestEx_in_invocation_context() { | ||
mockGetServicePathManager(); | ||
|
||
Invocation invocation = creator.create(); | ||
|
||
Object request = invocation.getLocalContext(RestConst.REST_REQUEST); | ||
assertThat(request).isSameAs(requestEx); | ||
} | ||
|
||
@Test | ||
public void should_save_path_var_map_in_requestEx() { | ||
mockGetServicePathManager(); | ||
|
||
creator.create(); | ||
|
||
new Verifications() { | ||
{ | ||
requestEx.setAttribute(RestConst.PATH_PARAMETERS, any); | ||
times = 1; | ||
} | ||
}; | ||
} | ||
|
||
@Test | ||
public void should_merge_invocation_context_from_request() { | ||
mockGetServicePathManager(); | ||
new Expectations() { | ||
{ | ||
requestEx.getHeader(Const.CSE_CONTEXT); | ||
result = "{\"k\":\"v\"}"; | ||
} | ||
}; | ||
|
||
Invocation invocation = creator.create(); | ||
|
||
assertThat(invocation.getContext("k")).isEqualTo("v"); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.