Skip to content

Commit

Permalink
rest-dsl binding part 1 (apache#13688)
Browse files Browse the repository at this point in the history
CAMEL-20557: Rest DSL to use openapi spec directly
  • Loading branch information
davsclaus authored Apr 3, 2024
1 parent a3f0c85 commit 0c5dae9
Show file tree
Hide file tree
Showing 14 changed files with 821 additions and 235 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* 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.camel.component.platform.http.vertx;

import org.apache.camel.CamelContext;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.component.platform.http.vertx.model.Pet;
import org.apache.camel.model.rest.RestBindingMode;
import org.junit.jupiter.api.Test;

import static io.restassured.RestAssured.given;
import static org.hamcrest.Matchers.equalTo;

public class PlatformHttpRestOpenApiConsumerRestDslBindingTest {

@Test
public void testRestOpenApi() throws Exception {
final CamelContext context = VertxPlatformHttpEngineTest.createCamelContext();

try {
context.addRoutes(new RouteBuilder() {
@Override
public void configure() {
restConfiguration().bindingMode(RestBindingMode.json);

rest().openApi().specification("openapi-v3.json").missingOperation("ignore");

from("direct:getPetById")
.process(e -> {
// build response body as POJO
Pet pet = new Pet();
pet.setId(e.getMessage().getHeader("petId", long.class));
pet.setName("tony the tiger");
pet.setStatus(Pet.Status.AVAILABLE);
e.getMessage().setBody(pet);
});
}
});

context.start();

given()
.when()
.get("/api/v3/pet/123")
.then()
.statusCode(200)
.body(equalTo("{\"id\":123,\"name\":\"tony the tiger\",\"status\":\"AVAILABLE\"}"));

} finally {
context.stop();
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import static io.restassured.RestAssured.given;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.equalToCompressingWhiteSpace;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.fail;

public class PlatformHttpRestOpenApiConsumerRestDslTest {
Expand All @@ -42,6 +43,9 @@ public void configure() {
rest().openApi().specification("openapi-v3.json").missingOperation("ignore");

from("direct:getPetById")
.process(e -> {
assertEquals("123", e.getMessage().getHeader("petId"));
})
.setBody().constant("{\"pet\": \"tony the tiger\"}");
}
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,41 +14,34 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.camel.component.rest.openapi.validator;
package org.apache.camel.component.platform.http.vertx.model;

import io.swagger.v3.oas.models.Operation;
import org.apache.camel.support.RestConsumerContextPathMatcher;
import jakarta.xml.bind.annotation.XmlRootElement;

class RestOpenApiPath implements RestConsumerContextPathMatcher.ConsumerPath<Operation> {
import com.fasterxml.jackson.annotation.JsonInclude;

private final String verb;
private final String path;
private final Operation consumer;

public RestOpenApiPath(String verb, String path, Operation consumer) {
this.verb = verb;
this.path = path;
this.consumer = consumer;
}

@Override
public String getRestrictMethod() {
return verb;
/**
* The structure of this class must adhere to the schema defined in the Pet Store OpenAPI specification JSON / YAML.
*/
@JsonInclude(JsonInclude.Include.NON_NULL)
@XmlRootElement(name = "Category")
public class Category {
private Long id;
private String name;

public Long getId() {
return id;
}

@Override
public String getConsumerPath() {
return path;
public void setId(Long id) {
this.id = id;
}

@Override
public Operation getConsumer() {
return consumer;
public String getName() {
return name;
}

@Override
public boolean isMatchOnUriPrefix() {
return false;
public void setName(String name) {
this.name = name;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/*
* 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.camel.component.platform.http.vertx.model;

import java.util.List;

import jakarta.xml.bind.annotation.XmlElement;
import jakarta.xml.bind.annotation.XmlRootElement;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonInclude;

/**
* The structure of this class must adhere to the schema defined in the Pet Store OpenAPI specification JSON / YAML.
*/
@JsonInclude(JsonInclude.Include.NON_NULL)
@XmlRootElement(name = "Pet")
public class Pet {
@XmlElement
private Long id;
private String name;
private Category category;
private List<String> photoUrls;
private List<Tag> tags;
private Status status;

public Long getId() {
return id;
}

public void setId(long id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public Category getCategory() {
return category;
}

public void setCategory(Category category) {
this.category = category;
}

public List<Tag> getTags() {
return tags;
}

public void setTags(List<Tag> tags) {
this.tags = tags;
}

public List<String> getPhotoUrls() {
return photoUrls;
}

public void setPhotoUrls(List<String> photoUrls) {
this.photoUrls = photoUrls;
}

public Status getStatus() {
return status;
}

public void setStatus(Status status) {
this.status = status;
}

public enum Status {
AVAILABLE,
PENDING,
SOLD;

@JsonCreator
public static Status fromString(String status) {
return Status.valueOf(status.toUpperCase());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* 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.camel.component.platform.http.vertx.model;

import jakarta.xml.bind.annotation.XmlRootElement;

import com.fasterxml.jackson.annotation.JsonInclude;

/**
* The structure of this class must adhere to the schema defined in the Pet Store OpenAPI specification JSON / YAML.
*/
@JsonInclude(JsonInclude.Include.NON_NULL)
@XmlRootElement(name = "Tag")
public class Tag {
private Long id;
private String name;

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import io.swagger.v3.oas.models.OpenAPI;
Expand All @@ -41,6 +42,7 @@
import org.apache.camel.support.ExchangeHelper;
import org.apache.camel.support.PluginHelper;
import org.apache.camel.support.cache.DefaultProducerCache;
import org.apache.camel.support.processor.RestBindingAdvice;
import org.apache.camel.support.service.ServiceHelper;
import org.apache.camel.support.service.ServiceSupport;
import org.apache.camel.util.FileUtil;
Expand Down Expand Up @@ -161,22 +163,39 @@ public boolean processApiSpecification(String specificationUri, Exchange exchang
}

@Override
public boolean process(Operation operation, String path, Exchange exchange, AsyncCallback callback) {
public boolean process(
Operation operation, String path,
RestBindingAdvice binding,
Exchange exchange, AsyncCallback callback) {

if ("mock".equalsIgnoreCase(missingOperation)) {
// check if there is a route
Endpoint e = camelContext.hasEndpoint(component + ":" + operation.getOperationId());
if (e == null) {
// no route then try to load mock data as the answer
loadMockData(operation, path, exchange);
callback.done(true);
return true;
}
}

Endpoint e = camelContext.getEndpoint(component + ":" + operation.getOperationId());
AsyncProducer p = producerCache.acquireProducer(e);
Map<String, Object> state;
try {
state = binding.before(exchange);
} catch (Exception e) {
exchange.setException(e);
callback.done(true);
return true;
}

final Endpoint e = camelContext.getEndpoint(component + ":" + operation.getOperationId());
final AsyncProducer p = producerCache.acquireProducer(e);
return p.process(exchange, doneSync -> {
try {
producerCache.releaseProducer(e, p);
binding.after(exchange, state);
} catch (Exception ex) {
exchange.setException(ex);
} finally {
callback.done(doneSync);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,21 @@

import io.swagger.v3.oas.models.Operation;
import org.apache.camel.support.RestConsumerContextPathMatcher;
import org.apache.camel.support.processor.RestBindingAdvice;

class RestOpenApiConsumerPath implements RestConsumerContextPathMatcher.ConsumerPath<Operation> {

private final String verb;
private final String path;
private final Operation consumer;
private final RestBindingAdvice binding;

public RestOpenApiConsumerPath(String verb, String path, Operation consumer) {
public RestOpenApiConsumerPath(String verb, String path, Operation consumer,
RestBindingAdvice binding) {
this.verb = verb;
this.path = path;
this.consumer = consumer;
this.binding = binding;
}

@Override
Expand All @@ -50,4 +54,8 @@ public Operation getConsumer() {
public boolean isMatchOnUriPrefix() {
return false;
}

public RestBindingAdvice getBinding() {
return binding;
}
}
Loading

0 comments on commit 0c5dae9

Please sign in to comment.